cleaned up old CVS left overs
[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.  Enhancements Copyright\r
6  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
7  * Foundation, Inc.\r
8  *\r
9  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
10  * which was written and is copyrighted by Wayne Christopher.\r
11  *\r
12  * The following terms apply to Digital Equipment Corporation's copyright\r
13  * interest in XBoard:\r
14  * ------------------------------------------------------------------------\r
15  * All Rights Reserved\r
16  *\r
17  * Permission to use, copy, modify, and distribute this software and its\r
18  * documentation for any purpose and without fee is hereby granted,\r
19  * provided that the above copyright notice appear in all copies and that\r
20  * both that copyright notice and this permission notice appear in\r
21  * supporting documentation, and that the name of Digital not be\r
22  * used in advertising or publicity pertaining to distribution of the\r
23  * software without specific, written prior permission.\r
24  *\r
25  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
26  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
27  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
28  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
29  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
30  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
31  * SOFTWARE.\r
32  * ------------------------------------------------------------------------\r
33  *\r
34  * The following terms apply to the enhanced version of XBoard\r
35  * distributed by the Free Software Foundation:\r
36  * ------------------------------------------------------------------------\r
37  *\r
38  * GNU XBoard is free software: you can redistribute it and/or modify\r
39  * it under the terms of the GNU General Public License as published by\r
40  * the Free Software Foundation, either version 3 of the License, or (at\r
41  * your option) any later version.\r
42  *\r
43  * GNU XBoard is distributed in the hope that it will be useful, but\r
44  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
45  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
46  * General Public License for more details.\r
47  *\r
48  * You should have received a copy of the GNU General Public License\r
49  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
50  *\r
51  *------------------------------------------------------------------------\r
52  ** See the file ChangeLog for a revision history.  */\r
53 \r
54 #include "config.h"\r
55 \r
56 #include <windows.h>\r
57 #include <winuser.h>\r
58 #include <winsock.h>\r
59 #include <commctrl.h>\r
60 \r
61 #include <stdio.h>\r
62 #include <stdlib.h>\r
63 #include <time.h>\r
64 #include <malloc.h>\r
65 #include <sys/stat.h>\r
66 #include <fcntl.h>\r
67 #include <math.h>\r
68 #include <commdlg.h>\r
69 #include <dlgs.h>\r
70 #include <richedit.h>\r
71 #include <mmsystem.h>\r
72 #include <ctype.h>\r
73 \r
74 #if __GNUC__\r
75 #include <errno.h>\r
76 #include <string.h>\r
77 #endif\r
78 \r
79 #include "common.h"\r
80 #include "winboard.h"\r
81 #include "frontend.h"\r
82 #include "backend.h"\r
83 #include "moves.h"\r
84 #include "wclipbrd.h"\r
85 #include "wgamelist.h"\r
86 #include "wedittags.h"\r
87 #include "woptions.h"\r
88 #include "wsockerr.h"\r
89 #include "defaults.h"\r
90 #include "help.h"\r
91 #include "wsnap.h"\r
92 \r
93 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
94 \r
95   int myrandom(void);\r
96   void mysrandom(unsigned int seed);\r
97 \r
98 extern int whiteFlag, blackFlag;\r
99 Boolean flipClock = FALSE;\r
100 \r
101 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
102 VOID NewVariantPopup(HWND hwnd);\r
103 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
104                    /*char*/int promoChar));\r
105 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
106 void DisplayMove P((int moveNumber));\r
107 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int fromX, fromY, toX, toY, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo;\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "I C S Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY;\r
156 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
157 static int squareSize, lineGap, minorSize;\r
158 static int winWidth, winHeight, winW, winH;\r
159 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
160 static int logoHeight = 0;\r
161 static char messageText[MESSAGE_TEXT_MAX];\r
162 static int clockTimerEvent = 0;\r
163 static int loadGameTimerEvent = 0;\r
164 static int analysisTimerEvent = 0;\r
165 static DelayedEventCallback delayedTimerCallback;\r
166 static int delayedTimerEvent = 0;\r
167 static int buttonCount = 2;\r
168 char *icsTextMenuString;\r
169 char *icsNames;\r
170 char *firstChessProgramNames;\r
171 char *secondChessProgramNames;\r
172 \r
173 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
174 \r
175 #define PALETTESIZE 256\r
176 \r
177 HINSTANCE hInst;          /* current instance */\r
178 HWND hwndMain = NULL;        /* root window*/\r
179 HWND hwndConsole = NULL;\r
180 BOOLEAN alwaysOnTop = FALSE;\r
181 RECT boardRect;\r
182 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
183   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
184 HPALETTE hPal;\r
185 ColorClass currentColorClass;\r
186 \r
187 HWND hCommPort = NULL;    /* currently open comm port */\r
188 static HWND hwndPause;    /* pause button */\r
189 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
190 static HBRUSH lightSquareBrush, darkSquareBrush,\r
191   blackSquareBrush, /* [HGM] for band between board and holdings */\r
192   explodeBrush,     /* [HGM] atomic */\r
193   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
194 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
195 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
196 static HPEN gridPen = NULL;\r
197 static HPEN highlightPen = NULL;\r
198 static HPEN premovePen = NULL;\r
199 static NPLOGPALETTE pLogPal;\r
200 static BOOL paletteChanged = FALSE;\r
201 static HICON iconWhite, iconBlack, iconCurrent;\r
202 static int doingSizing = FALSE;\r
203 static int lastSizing = 0;\r
204 static int prevStderrPort;\r
205 static HBITMAP userLogo;\r
206 \r
207 /* [AS] Support for background textures */\r
208 #define BACK_TEXTURE_MODE_DISABLED      0\r
209 #define BACK_TEXTURE_MODE_PLAIN         1\r
210 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
211 \r
212 static HBITMAP liteBackTexture = NULL;\r
213 static HBITMAP darkBackTexture = NULL;\r
214 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int backTextureSquareSize = 0;\r
217 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
218 \r
219 #if __GNUC__ && !defined(_winmajor)\r
220 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
221 #else\r
222 #define oldDialog (_winmajor < 4)\r
223 #endif\r
224 \r
225 char *defaultTextAttribs[] = \r
226 {\r
227   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
228   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
229   COLOR_NONE\r
230 };\r
231 \r
232 typedef struct {\r
233   char *name;\r
234   int squareSize;\r
235   int lineGap;\r
236   int smallLayout;\r
237   int tinyLayout;\r
238   int cliWidth, cliHeight;\r
239 } SizeInfo;\r
240 \r
241 SizeInfo sizeInfo[] = \r
242 {\r
243   { "tiny",     21, 0, 1, 1, 0, 0 },\r
244   { "teeny",    25, 1, 1, 1, 0, 0 },\r
245   { "dinky",    29, 1, 1, 1, 0, 0 },\r
246   { "petite",   33, 1, 1, 1, 0, 0 },\r
247   { "slim",     37, 2, 1, 0, 0, 0 },\r
248   { "small",    40, 2, 1, 0, 0, 0 },\r
249   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
250   { "middling", 49, 2, 0, 0, 0, 0 },\r
251   { "average",  54, 2, 0, 0, 0, 0 },\r
252   { "moderate", 58, 3, 0, 0, 0, 0 },\r
253   { "medium",   64, 3, 0, 0, 0, 0 },\r
254   { "bulky",    72, 3, 0, 0, 0, 0 },\r
255   { "large",    80, 3, 0, 0, 0, 0 },\r
256   { "big",      87, 3, 0, 0, 0, 0 },\r
257   { "huge",     95, 3, 0, 0, 0, 0 },\r
258   { "giant",    108, 3, 0, 0, 0, 0 },\r
259   { "colossal", 116, 4, 0, 0, 0, 0 },\r
260   { "titanic",  129, 4, 0, 0, 0, 0 },\r
261   { NULL, 0, 0, 0, 0, 0, 0 }\r
262 };\r
263 \r
264 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
265 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
266 {\r
267   { 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
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285 };\r
286 \r
287 MyFont *font[NUM_SIZES][NUM_FONTS];\r
288 \r
289 typedef struct {\r
290   char *label;\r
291   int id;\r
292   HWND hwnd;\r
293   WNDPROC wndproc;\r
294 } MyButtonDesc;\r
295 \r
296 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
297 #define N_BUTTONS 5\r
298 \r
299 MyButtonDesc buttonDesc[N_BUTTONS] =\r
300 {\r
301   {"<<", IDM_ToStart, NULL, NULL},\r
302   {"<", IDM_Backward, NULL, NULL},\r
303   {"P", IDM_Pause, NULL, NULL},\r
304   {">", IDM_Forward, NULL, NULL},\r
305   {">>", IDM_ToEnd, NULL, NULL},\r
306 };\r
307 \r
308 int tinyLayout = 0, smallLayout = 0;\r
309 #define MENU_BAR_ITEMS 6\r
310 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
311   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
312   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
313 };\r
314 \r
315 \r
316 MySound sounds[(int)NSoundClasses];\r
317 MyTextAttribs textAttribs[(int)NColorClasses];\r
318 \r
319 MyColorizeAttribs colorizeAttribs[] = {\r
320   { (COLORREF)0, 0, "Shout Text" },\r
321   { (COLORREF)0, 0, "SShout/CShout" },\r
322   { (COLORREF)0, 0, "Channel 1 Text" },\r
323   { (COLORREF)0, 0, "Channel Text" },\r
324   { (COLORREF)0, 0, "Kibitz Text" },\r
325   { (COLORREF)0, 0, "Tell Text" },\r
326   { (COLORREF)0, 0, "Challenge Text" },\r
327   { (COLORREF)0, 0, "Request Text" },\r
328   { (COLORREF)0, 0, "Seek Text" },\r
329   { (COLORREF)0, 0, "Normal Text" },\r
330   { (COLORREF)0, 0, "None" }\r
331 };\r
332 \r
333 \r
334 \r
335 static char *commentTitle;\r
336 static char *commentText;\r
337 static int commentIndex;\r
338 static Boolean editComment = FALSE;\r
339 HWND commentDialog = NULL;\r
340 BOOLEAN commentDialogUp = FALSE;\r
341 static int commentX, commentY, commentH, commentW;\r
342 \r
343 static char *analysisTitle;\r
344 static char *analysisText;\r
345 HWND analysisDialog = NULL;\r
346 BOOLEAN analysisDialogUp = FALSE;\r
347 static int analysisX, analysisY, analysisH, analysisW;\r
348 \r
349 char errorTitle[MSG_SIZ];\r
350 char errorMessage[2*MSG_SIZ];\r
351 HWND errorDialog = NULL;\r
352 BOOLEAN moveErrorMessageUp = FALSE;\r
353 BOOLEAN consoleEcho = TRUE;\r
354 CHARFORMAT consoleCF;\r
355 COLORREF consoleBackgroundColor;\r
356 \r
357 char *programVersion;\r
358 \r
359 #define CPReal 1\r
360 #define CPComm 2\r
361 #define CPSock 3\r
362 #define CPRcmd 4\r
363 typedef int CPKind;\r
364 \r
365 typedef struct {\r
366   CPKind kind;\r
367   HANDLE hProcess;\r
368   DWORD pid;\r
369   HANDLE hTo;\r
370   HANDLE hFrom;\r
371   SOCKET sock;\r
372   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
373 } ChildProc;\r
374 \r
375 #define INPUT_SOURCE_BUF_SIZE 4096\r
376 \r
377 typedef struct _InputSource {\r
378   CPKind kind;\r
379   HANDLE hFile;\r
380   SOCKET sock;\r
381   int lineByLine;\r
382   HANDLE hThread;\r
383   DWORD id;\r
384   char buf[INPUT_SOURCE_BUF_SIZE];\r
385   char *next;\r
386   DWORD count;\r
387   int error;\r
388   InputCallback func;\r
389   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
390   VOIDSTAR closure;\r
391 } InputSource;\r
392 \r
393 InputSource *consoleInputSource;\r
394 \r
395 DCB dcb;\r
396 \r
397 /* forward */\r
398 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
399 VOID ConsoleCreate();\r
400 LRESULT CALLBACK\r
401   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
402 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
403 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
404 VOID ParseCommSettings(char *arg, DCB *dcb);\r
405 LRESULT CALLBACK\r
406   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
407 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
408 void ParseIcsTextMenu(char *icsTextMenuString);\r
409 VOID PopUpMoveDialog(char firstchar);\r
410 VOID PopUpNameDialog(char firstchar);\r
411 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
412 \r
413 /* [AS] */\r
414 int NewGameFRC();\r
415 int GameListOptions();\r
416 \r
417 HWND moveHistoryDialog = NULL;\r
418 BOOLEAN moveHistoryDialogUp = FALSE;\r
419 \r
420 WindowPlacement wpMoveHistory;\r
421 \r
422 HWND evalGraphDialog = NULL;\r
423 BOOLEAN evalGraphDialogUp = FALSE;\r
424 \r
425 WindowPlacement wpEvalGraph;\r
426 \r
427 HWND engineOutputDialog = NULL;\r
428 BOOLEAN engineOutputDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpEngineOutput;\r
431 WindowPlacement wpGameList;\r
432 WindowPlacement wpConsole;\r
433 \r
434 VOID MoveHistoryPopUp();\r
435 VOID MoveHistoryPopDown();\r
436 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
437 BOOL MoveHistoryIsUp();\r
438 \r
439 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 VOID EvalGraphPopUp();\r
441 VOID EvalGraphPopDown();\r
442 BOOL EvalGraphIsUp();\r
443 \r
444 VOID EngineOutputPopUp();\r
445 VOID EngineOutputPopDown();\r
446 BOOL EngineOutputIsUp();\r
447 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
448 \r
449 VOID GothicPopUp(char *title, VariantClass variant);\r
450 /*\r
451  * Setting "frozen" should disable all user input other than deleting\r
452  * the window.  We do this while engines are initializing themselves.\r
453  */\r
454 static int frozen = 0;\r
455 static int oldMenuItemState[MENU_BAR_ITEMS];\r
456 void FreezeUI()\r
457 {\r
458   HMENU hmenu;\r
459   int i;\r
460 \r
461   if (frozen) return;\r
462   frozen = 1;\r
463   hmenu = GetMenu(hwndMain);\r
464   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
465     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
466   }\r
467   DrawMenuBar(hwndMain);\r
468 }\r
469 \r
470 /* Undo a FreezeUI */\r
471 void ThawUI()\r
472 {\r
473   HMENU hmenu;\r
474   int i;\r
475 \r
476   if (!frozen) return;\r
477   frozen = 0;\r
478   hmenu = GetMenu(hwndMain);\r
479   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
480     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
481   }\r
482   DrawMenuBar(hwndMain);\r
483 }\r
484 \r
485 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
486 \r
487 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
488 #ifdef JAWS\r
489 #include "jaws.c"\r
490 #else\r
491 #define JAWS_INIT\r
492 #define JAWS_ALT_INTERCEPT\r
493 #define JAWS_KB_NAVIGATION\r
494 #define JAWS_MENU_ITEMS\r
495 #define JAWS_SILENCE\r
496 #define JAWS_REPLAY\r
497 #define JAWS_DELETE(X) X\r
498 #define SAYMACHINEMOVE()\r
499 #define SAY(X)\r
500 #endif\r
501 \r
502 /*---------------------------------------------------------------------------*\\r
503  *\r
504  * WinMain\r
505  *\r
506 \*---------------------------------------------------------------------------*/\r
507 \r
508 int APIENTRY\r
509 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
510         LPSTR lpCmdLine, int nCmdShow)\r
511 {\r
512   MSG msg;\r
513   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
514 //  INITCOMMONCONTROLSEX ex;\r
515 \r
516   debugFP = stderr;\r
517 \r
518   LoadLibrary("RICHED32.DLL");\r
519   consoleCF.cbSize = sizeof(CHARFORMAT);\r
520 \r
521   if (!InitApplication(hInstance)) {\r
522     return (FALSE);\r
523   }\r
524   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
525     return (FALSE);\r
526   }\r
527 \r
528   JAWS_INIT\r
529 \r
530 //  InitCommonControlsEx(&ex);\r
531   InitCommonControls();\r
532 \r
533   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
534   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
535   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
536 \r
537   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
538 \r
539   while (GetMessage(&msg, /* message structure */\r
540                     NULL, /* handle of window receiving the message */\r
541                     0,    /* lowest message to examine */\r
542                     0))   /* highest message to examine */\r
543     {\r
544 \r
545       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
546         // [HGM] navigate: switch between all windows with tab\r
547         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
548         int i, currentElement = 0;\r
549 \r
550         // first determine what element of the chain we come from (if any)\r
551         if(appData.icsActive) {\r
552             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
553             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
554         }\r
555         if(engineOutputDialog && EngineOutputIsUp()) {\r
556             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
557             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
558         }\r
559         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
560             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
561         }\r
562         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
563         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
564         if(msg.hwnd == e1)                 currentElement = 2; else\r
565         if(msg.hwnd == e2)                 currentElement = 3; else\r
566         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
567         if(msg.hwnd == mh)                currentElement = 4; else\r
568         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
569         if(msg.hwnd == hText)  currentElement = 5; else\r
570         if(msg.hwnd == hInput) currentElement = 6; else\r
571         for (i = 0; i < N_BUTTONS; i++) {\r
572             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
573         }\r
574 \r
575         // determine where to go to\r
576         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
577           do {\r
578             currentElement = (currentElement + direction) % 7;\r
579             switch(currentElement) {\r
580                 case 0:\r
581                   h = hwndMain; break; // passing this case always makes the loop exit\r
582                 case 1:\r
583                   h = buttonDesc[0].hwnd; break; // could be NULL\r
584                 case 2:\r
585                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
586                   h = e1; break;\r
587                 case 3:\r
588                   if(!EngineOutputIsUp()) continue;\r
589                   h = e2; break;\r
590                 case 4:\r
591                   if(!MoveHistoryIsUp()) continue;\r
592                   h = mh; break;\r
593 //              case 5: // input to eval graph does not seem to get here!\r
594 //                if(!EvalGraphIsUp()) continue;\r
595 //                h = evalGraphDialog; break;\r
596                 case 5:\r
597                   if(!appData.icsActive) continue;\r
598                   SAY("display");\r
599                   h = hText; break;\r
600                 case 6:\r
601                   if(!appData.icsActive) continue;\r
602                   SAY("input");\r
603                   h = hInput; break;\r
604             }\r
605           } while(h == 0);\r
606 \r
607           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
608           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
609           SetFocus(h);\r
610 \r
611           continue; // this message now has been processed\r
612         }\r
613       }\r
614 \r
615       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
616           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
617           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
618           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
619           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
620           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
621           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
622           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
623           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
624           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
625         TranslateMessage(&msg); /* Translates virtual key codes */\r
626         DispatchMessage(&msg);  /* Dispatches message to window */\r
627       }\r
628     }\r
629 \r
630 \r
631   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
632 }\r
633 \r
634 /*---------------------------------------------------------------------------*\\r
635  *\r
636  * Initialization functions\r
637  *\r
638 \*---------------------------------------------------------------------------*/\r
639 \r
640 void\r
641 SetUserLogo()\r
642 {   // update user logo if necessary\r
643     static char oldUserName[MSG_SIZ], *curName;\r
644 \r
645     if(appData.autoLogo) {\r
646           curName = UserName();\r
647           if(strcmp(curName, oldUserName)) {\r
648                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
649                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
650                 strcpy(oldUserName, curName);\r
651           }\r
652     }\r
653 }\r
654 \r
655 BOOL\r
656 InitApplication(HINSTANCE hInstance)\r
657 {\r
658   WNDCLASS wc;\r
659 \r
660   /* Fill in window class structure with parameters that describe the */\r
661   /* main window. */\r
662 \r
663   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
664   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
665   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
666   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
667   wc.hInstance     = hInstance;         /* Owner of this class */\r
668   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
669   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
670   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
671   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
672   wc.lpszClassName = szAppName;                 /* Name to register as */\r
673 \r
674   /* Register the window class and return success/failure code. */\r
675   if (!RegisterClass(&wc)) return FALSE;\r
676 \r
677   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
678   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
679   wc.cbClsExtra    = 0;\r
680   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
681   wc.hInstance     = hInstance;\r
682   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
683   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
684   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
685   wc.lpszMenuName  = NULL;\r
686   wc.lpszClassName = szConsoleName;\r
687 \r
688   if (!RegisterClass(&wc)) return FALSE;\r
689   return TRUE;\r
690 }\r
691 \r
692 \r
693 /* Set by InitInstance, used by EnsureOnScreen */\r
694 int screenHeight, screenWidth;\r
695 \r
696 void\r
697 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
698 {\r
699 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
700   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
701   if (*x > screenWidth - 32) *x = 0;\r
702   if (*y > screenHeight - 32) *y = 0;\r
703   if (*x < minX) *x = minX;\r
704   if (*y < minY) *y = minY;\r
705 }\r
706 \r
707 BOOL\r
708 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
709 {\r
710   HWND hwnd; /* Main window handle. */\r
711   int ibs;\r
712   WINDOWPLACEMENT wp;\r
713   char *filepart;\r
714 \r
715   hInst = hInstance;    /* Store instance handle in our global variable */\r
716 \r
717   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
718     *filepart = NULLCHAR;\r
719   } else {\r
720     GetCurrentDirectory(MSG_SIZ, installDir);\r
721   }\r
722   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
723   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
724   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
725   if (appData.debugMode) {\r
726     debugFP = fopen(appData.nameOfDebugFile, "w");\r
727     setbuf(debugFP, NULL);\r
728   }\r
729 \r
730   InitBackEnd1();\r
731 \r
732 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
733 //  InitEngineUCI( installDir, &second );\r
734 \r
735   /* Create a main window for this application instance. */\r
736   hwnd = CreateWindow(szAppName, szTitle,\r
737                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
738                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
739                       NULL, NULL, hInstance, NULL);\r
740   hwndMain = hwnd;\r
741 \r
742   /* If window could not be created, return "failure" */\r
743   if (!hwnd) {\r
744     return (FALSE);\r
745   }\r
746 \r
747   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
748   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
749       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
750 \r
751       if (first.programLogo == NULL && appData.debugMode) {\r
752           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
753       }\r
754   } else if(appData.autoLogo) {\r
755       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
756         char buf[MSG_SIZ];\r
757         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
758         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
759       }\r
760   }\r
761 \r
762   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
763       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
764 \r
765       if (second.programLogo == NULL && appData.debugMode) {\r
766           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
767       }\r
768   } else if(appData.autoLogo) {\r
769       char buf[MSG_SIZ];\r
770       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
771         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
772         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
773       } else\r
774       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
775         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
776         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
777       }\r
778   }\r
779 \r
780   SetUserLogo();\r
781 \r
782   iconWhite = LoadIcon(hInstance, "icon_white");\r
783   iconBlack = LoadIcon(hInstance, "icon_black");\r
784   iconCurrent = iconWhite;\r
785   InitDrawingColors();\r
786   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
787   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
788   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
789     /* Compute window size for each board size, and use the largest\r
790        size that fits on this screen as the default. */\r
791     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
792     if (boardSize == (BoardSize)-1 &&\r
793         winH <= screenHeight\r
794            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
795         && winW <= screenWidth) {\r
796       boardSize = (BoardSize)ibs;\r
797     }\r
798   }\r
799 \r
800   InitDrawingSizes(boardSize, 0);\r
801   InitMenuChecks();\r
802   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
803 \r
804   /* [AS] Load textures if specified */\r
805   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
806   \r
807   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
808       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
809       liteBackTextureMode = appData.liteBackTextureMode;\r
810 \r
811       if (liteBackTexture == NULL && appData.debugMode) {\r
812           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
813       }\r
814   }\r
815   \r
816   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
817       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
818       darkBackTextureMode = appData.darkBackTextureMode;\r
819 \r
820       if (darkBackTexture == NULL && appData.debugMode) {\r
821           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
822       }\r
823   }\r
824 \r
825   mysrandom( (unsigned) time(NULL) );\r
826 \r
827   /* [AS] Restore layout */\r
828   if( wpMoveHistory.visible ) {\r
829       MoveHistoryPopUp();\r
830   }\r
831 \r
832   if( wpEvalGraph.visible ) {\r
833       EvalGraphPopUp();\r
834   }\r
835 \r
836   if( wpEngineOutput.visible ) {\r
837       EngineOutputPopUp();\r
838   }\r
839 \r
840   InitBackEnd2();\r
841 \r
842   /* Make the window visible; update its client area; and return "success" */\r
843   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
844   wp.length = sizeof(WINDOWPLACEMENT);\r
845   wp.flags = 0;\r
846   wp.showCmd = nCmdShow;\r
847   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
848   wp.rcNormalPosition.left = boardX;\r
849   wp.rcNormalPosition.right = boardX + winWidth;\r
850   wp.rcNormalPosition.top = boardY;\r
851   wp.rcNormalPosition.bottom = boardY + winHeight;\r
852   SetWindowPlacement(hwndMain, &wp);\r
853 \r
854   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
855                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
856 \r
857   if (hwndConsole) {\r
858 #if AOT_CONSOLE\r
859     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
860                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
861 #endif\r
862     ShowWindow(hwndConsole, nCmdShow);\r
863   }\r
864   UpdateWindow(hwnd);\r
865 \r
866   return TRUE;\r
867 \r
868 }\r
869 \r
870 \r
871 typedef enum {\r
872   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
873   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
874   ArgSettingsFilename,\r
875   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
876 } ArgType;\r
877 \r
878 typedef struct {\r
879   char *argName;\r
880   ArgType argType;\r
881   /***\r
882   union {\r
883     String *pString;       // ArgString\r
884     int *pInt;             // ArgInt\r
885     float *pFloat;         // ArgFloat\r
886     Boolean *pBoolean;     // ArgBoolean\r
887     COLORREF *pColor;      // ArgColor\r
888     ColorClass cc;         // ArgAttribs\r
889     String *pFilename;     // ArgFilename\r
890     BoardSize *pBoardSize; // ArgBoardSize\r
891     int whichFont;         // ArgFont\r
892     DCB *pDCB;             // ArgCommSettings\r
893     String *pFilename;     // ArgSettingsFilename\r
894   } argLoc;\r
895   ***/\r
896   LPVOID argLoc;\r
897   BOOL save;\r
898 } ArgDescriptor;\r
899 \r
900 int junk;\r
901 ArgDescriptor argDescriptors[] = {\r
902   /* positional arguments */\r
903   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
904   { "", ArgNone, NULL },\r
905   /* keyword arguments */\r
906   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
907   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
908   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
909   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
910   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
911   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
912   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
913   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
914   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
915   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
916   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
917   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
918   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
919   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
920   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
921   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
922   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
923   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
924     FALSE },\r
925   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
926     FALSE },\r
927   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
928     FALSE },\r
929   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
930   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
931     FALSE },\r
932   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
933   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
934   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
935   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
936   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
937   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
938   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
939   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
940   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
941   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
942   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
943   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
944   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
945   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
946   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
947   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
948   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
949   /*!!bitmapDirectory?*/\r
950   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
951   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
952   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
953   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
954   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
955   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
956   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
957   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
958   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
959   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
960   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
961   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
962   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
963   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
964   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
965   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
966   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
967   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
968   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
969   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
970   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
971   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
972   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
973   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
974   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
975   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
976   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
977   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
978   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
979   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
980   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
981   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
982   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
983   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
984   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
985   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
986   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
987   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
988   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
989   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
990   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
991   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
992   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
993   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
994   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
995   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
996   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
997   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
998   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
999   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1000   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1001   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1002   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1003   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1004   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1005   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1006   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1007   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1008   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1009   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1010   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1011   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1012   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1013   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1014   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1015   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1016   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1017   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1018   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1019   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1020   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1021   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1022   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1023   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1024   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1025   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1026   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1027   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1028   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1029   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1030   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1031   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1032   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1033   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1034   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1035   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1036   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1037   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1038   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1039   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1040   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1041   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1042   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1043   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1044     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1045   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1046   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1047   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1048   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1049   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1050   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1051   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1052   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1053     TRUE }, /* must come after all fonts */\r
1054   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1055   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1056     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1057   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1058   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1059   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1060   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1061   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1062   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1063   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1064   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1065   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1066   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1067   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1068   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1069   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1070   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1071   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1072   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1073   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1074   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1075   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1076   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1077   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1078   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1079   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1080   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1081   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1082   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1083   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1084   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1085 #if 0\r
1086   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1087   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1088 #endif\r
1089   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1090   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1091   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1092   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1093   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1094   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1095   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1096   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1097   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1098   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1099   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1100   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1101   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1102   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1103   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1104   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1105   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1106   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1107   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1108   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1109   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1110   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1111   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1112   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1113   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1114   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1115   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1116   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1117   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1118   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1119   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1120   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1121   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1122   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1123   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1124   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1125   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1126   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1127   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1128   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1129   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1130   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1131   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1132   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1133   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1134   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1135   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1136   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1137   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1138   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1139   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1140   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1141   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1142   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1143   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1144   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1145   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1146   { "highlightLastMove", ArgBoolean,\r
1147     (LPVOID) &appData.highlightLastMove, TRUE },\r
1148   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1149   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1150   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1151   { "highlightDragging", ArgBoolean,\r
1152     (LPVOID) &appData.highlightDragging, TRUE },\r
1153   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1154   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1155   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1156   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1157   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1158   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1159   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1160   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1161   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1162   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1163   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1164   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1165   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1166   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1167   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1168   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1169   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1170   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1171   { "soundShout", ArgFilename,\r
1172     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1173   { "soundSShout", ArgFilename,\r
1174     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1175   { "soundChannel1", ArgFilename,\r
1176     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1177   { "soundChannel", ArgFilename,\r
1178     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1179   { "soundKibitz", ArgFilename,\r
1180     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1181   { "soundTell", ArgFilename,\r
1182     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1183   { "soundChallenge", ArgFilename,\r
1184     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1185   { "soundRequest", ArgFilename,\r
1186     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1187   { "soundSeek", ArgFilename,\r
1188     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1189   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1190   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1191   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1192   { "soundIcsLoss", ArgFilename, \r
1193     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1194   { "soundIcsDraw", ArgFilename, \r
1195     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1196   { "soundIcsUnfinished", ArgFilename, \r
1197     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1198   { "soundIcsAlarm", ArgFilename, \r
1199     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1200   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1201   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1202   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1203   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1204   { "reuseChessPrograms", ArgBoolean,\r
1205     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1206   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1207   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1208   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1209   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1210   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1211   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1212   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1213   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1214   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1215   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1216   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1217   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1218   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1219   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1220   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1221     TRUE },\r
1222   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1223     TRUE },\r
1224   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1225   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1226   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1227   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1228   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1229   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1230   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1231   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1232   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1233   /* [AS] New features */\r
1234   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1235   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1236   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1237   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1238   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1239   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1240   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1241   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1242   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1243   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1244   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1245   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1246   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1247   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1248   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1249   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1250   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1251   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1252   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1253   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1254   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1255   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1256   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1257   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1258   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1259   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1260   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1261   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1262   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1263   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1264   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1265   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1266   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1267   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1268   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1269   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1270   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1271   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1272   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1273   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1274   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1275   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1276   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1277   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1278   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1279   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1280   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1281   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1282   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1283   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1284 \r
1285   /* [HGM] board-size, adjudication and misc. options */\r
1286   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1287   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1288   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1289   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1290   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1291   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1292   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1293   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1294   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1295   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1296   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1297   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1298   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1299   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1300   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1301   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1302   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1303   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1304   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1305   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1306   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1307   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1308   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1309   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1310   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1311   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1312   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1313   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1314   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1315   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1316   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1317 \r
1318 #ifdef ZIPPY\r
1319   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1320   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1321   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1322   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1323   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1324   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1325   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1326   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1327   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1328   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1329   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1330   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1331   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1332     FALSE },\r
1333   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1334   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1335   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1336   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1337   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1338   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1339   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1340     FALSE },\r
1341   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1342   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1343   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1344   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1345   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1346   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1347   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1348   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1349   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1350   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1351   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1352   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1353   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1354   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1355   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1356   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1357   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1358   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1359   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1360 #endif\r
1361   /* [HGM] options for broadcasting and time odds */\r
1362   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1363   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1364   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1365   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1366   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1367   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1368   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1369   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1370   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1371   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1372   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1373 \r
1374   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1375   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1376   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1377   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1378   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1379   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1380   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1381   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1382   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1383   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1384   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1385   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1386   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1387   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1388   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1389   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1390   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1391   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1392   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1393   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1394   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1395   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1396   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1397   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1398   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1399   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1400   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1401   /* [AS] Layout stuff */\r
1402   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1403   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1404   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1405   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1406   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1407 \r
1408   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1409   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1410   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1411   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1412   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1413 \r
1414   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1415   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1416   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1417   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1418   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1419 \r
1420   { NULL, ArgNone, NULL, FALSE }\r
1421 };\r
1422 \r
1423 \r
1424 /* Kludge for indirection files on command line */\r
1425 char* lastIndirectionFilename;\r
1426 ArgDescriptor argDescriptorIndirection =\r
1427 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1428 \r
1429 \r
1430 VOID\r
1431 ExitArgError(char *msg, char *badArg)\r
1432 {\r
1433   char buf[MSG_SIZ];\r
1434 \r
1435   sprintf(buf, "%s %s", msg, badArg);\r
1436   DisplayFatalError(buf, 0, 2);\r
1437   exit(2);\r
1438 }\r
1439 \r
1440 /* Command line font name parser.  NULL name means do nothing.\r
1441    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1442    For backward compatibility, syntax without the colon is also\r
1443    accepted, but font names with digits in them won't work in that case.\r
1444 */\r
1445 VOID\r
1446 ParseFontName(char *name, MyFontParams *mfp)\r
1447 {\r
1448   char *p, *q;\r
1449   if (name == NULL) return;\r
1450   p = name;\r
1451   q = strchr(p, ':');\r
1452   if (q) {\r
1453     if (q - p >= sizeof(mfp->faceName))\r
1454       ExitArgError("Font name too long:", name);\r
1455     memcpy(mfp->faceName, p, q - p);\r
1456     mfp->faceName[q - p] = NULLCHAR;\r
1457     p = q + 1;\r
1458   } else {\r
1459     q = mfp->faceName;\r
1460     while (*p && !isdigit(*p)) {\r
1461       *q++ = *p++;\r
1462       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1463         ExitArgError("Font name too long:", name);\r
1464     }\r
1465     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1466     *q = NULLCHAR;\r
1467   }\r
1468   if (!*p) ExitArgError("Font point size missing:", name);\r
1469   mfp->pointSize = (float) atof(p);\r
1470   mfp->bold = (strchr(p, 'b') != NULL);\r
1471   mfp->italic = (strchr(p, 'i') != NULL);\r
1472   mfp->underline = (strchr(p, 'u') != NULL);\r
1473   mfp->strikeout = (strchr(p, 's') != NULL);\r
1474 }\r
1475 \r
1476 /* Color name parser.\r
1477    X version accepts X color names, but this one\r
1478    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1479 COLORREF\r
1480 ParseColorName(char *name)\r
1481 {\r
1482   int red, green, blue, count;\r
1483   char buf[MSG_SIZ];\r
1484 \r
1485   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1486   if (count != 3) {\r
1487     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1488       &red, &green, &blue);\r
1489   }\r
1490   if (count != 3) {\r
1491     sprintf(buf, "Can't parse color name %s", name);\r
1492     DisplayError(buf, 0);\r
1493     return RGB(0, 0, 0);\r
1494   }\r
1495   return PALETTERGB(red, green, blue);\r
1496 }\r
1497 \r
1498 \r
1499 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1500 {\r
1501   char *e = argValue;\r
1502   int eff = 0;\r
1503 \r
1504   while (*e) {\r
1505     if (*e == 'b')      eff |= CFE_BOLD;\r
1506     else if (*e == 'i') eff |= CFE_ITALIC;\r
1507     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1508     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1509     else if (*e == '#' || isdigit(*e)) break;\r
1510     e++;\r
1511   }\r
1512   *effects = eff;\r
1513   *color   = ParseColorName(e);\r
1514 }\r
1515 \r
1516 \r
1517 BoardSize\r
1518 ParseBoardSize(char *name)\r
1519 {\r
1520   BoardSize bs = SizeTiny;\r
1521   while (sizeInfo[bs].name != NULL) {\r
1522     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1523     bs++;\r
1524   }\r
1525   ExitArgError("Unrecognized board size value", name);\r
1526   return bs; /* not reached */\r
1527 }\r
1528 \r
1529 \r
1530 char\r
1531 StringGet(void *getClosure)\r
1532 {\r
1533   char **p = (char **) getClosure;\r
1534   return *((*p)++);\r
1535 }\r
1536 \r
1537 char\r
1538 FileGet(void *getClosure)\r
1539 {\r
1540   int c;\r
1541   FILE* f = (FILE*) getClosure;\r
1542 \r
1543   c = getc(f);\r
1544   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1545   if (c == EOF)\r
1546     return NULLCHAR;\r
1547   else\r
1548     return (char) c;\r
1549 }\r
1550 \r
1551 /* Parse settings file named "name". If file found, return the\r
1552    full name in fullname and return TRUE; else return FALSE */\r
1553 BOOLEAN\r
1554 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1555 {\r
1556   char *dummy;\r
1557   FILE *f;\r
1558   int ok; char buf[MSG_SIZ];\r
1559 \r
1560   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1561   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1562     sprintf(buf, "%s.ini", name);\r
1563     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1564   }\r
1565   if (ok) {\r
1566     f = fopen(fullname, "r");\r
1567     if (f != NULL) {\r
1568       ParseArgs(FileGet, f);\r
1569       fclose(f);\r
1570       return TRUE;\r
1571     }\r
1572   }\r
1573   return FALSE;\r
1574 }\r
1575 \r
1576 VOID\r
1577 ParseArgs(GetFunc get, void *cl)\r
1578 {\r
1579   char argName[ARG_MAX];\r
1580   char argValue[ARG_MAX];\r
1581   ArgDescriptor *ad;\r
1582   char start;\r
1583   char *q;\r
1584   int i, octval;\r
1585   char ch;\r
1586   int posarg = 0;\r
1587 \r
1588   ch = get(cl);\r
1589   for (;;) {\r
1590     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1591     if (ch == NULLCHAR) break;\r
1592     if (ch == ';') {\r
1593       /* Comment to end of line */\r
1594       ch = get(cl);\r
1595       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1596       continue;\r
1597     } else if (ch == '/' || ch == '-') {\r
1598       /* Switch */\r
1599       q = argName;\r
1600       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1601              ch != '\n' && ch != '\t') {\r
1602         *q++ = ch;\r
1603         ch = get(cl);\r
1604       }\r
1605       *q = NULLCHAR;\r
1606 \r
1607       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1608         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1609 \r
1610       if (ad->argName == NULL)\r
1611         ExitArgError("Unrecognized argument", argName);\r
1612 \r
1613     } else if (ch == '@') {\r
1614       /* Indirection file */\r
1615       ad = &argDescriptorIndirection;\r
1616       ch = get(cl);\r
1617     } else {\r
1618       /* Positional argument */\r
1619       ad = &argDescriptors[posarg++];\r
1620       strcpy(argName, ad->argName);\r
1621     }\r
1622 \r
1623     if (ad->argType == ArgTrue) {\r
1624       *(Boolean *) ad->argLoc = TRUE;\r
1625       continue;\r
1626     }\r
1627     if (ad->argType == ArgFalse) {\r
1628       *(Boolean *) ad->argLoc = FALSE;\r
1629       continue;\r
1630     }\r
1631 \r
1632     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1633     if (ch == NULLCHAR || ch == '\n') {\r
1634       ExitArgError("No value provided for argument", argName);\r
1635     }\r
1636     q = argValue;\r
1637     if (ch == '{') {\r
1638       // Quoting with { }.  No characters have to (or can) be escaped.\r
1639       // Thus the string cannot contain a '}' character.\r
1640       start = ch;\r
1641       ch = get(cl);\r
1642       while (start) {\r
1643         switch (ch) {\r
1644         case NULLCHAR:\r
1645           start = NULLCHAR;\r
1646           break;\r
1647           \r
1648         case '}':\r
1649           ch = get(cl);\r
1650           start = NULLCHAR;\r
1651           break;\r
1652 \r
1653         default:\r
1654           *q++ = ch;\r
1655           ch = get(cl);\r
1656           break;\r
1657         }\r
1658       }   \r
1659     } else if (ch == '\'' || ch == '"') {\r
1660       // Quoting with ' ' or " ", with \ as escape character.\r
1661       // Inconvenient for long strings that may contain Windows filenames.\r
1662       start = ch;\r
1663       ch = get(cl);\r
1664       while (start) {\r
1665         switch (ch) {\r
1666         case NULLCHAR:\r
1667           start = NULLCHAR;\r
1668           break;\r
1669 \r
1670         default:\r
1671         not_special:\r
1672           *q++ = ch;\r
1673           ch = get(cl);\r
1674           break;\r
1675 \r
1676         case '\'':\r
1677         case '\"':\r
1678           if (ch == start) {\r
1679             ch = get(cl);\r
1680             start = NULLCHAR;\r
1681             break;\r
1682           } else {\r
1683             goto not_special;\r
1684           }\r
1685 \r
1686         case '\\':\r
1687           if (ad->argType == ArgFilename\r
1688               || ad->argType == ArgSettingsFilename) {\r
1689               goto not_special;\r
1690           }\r
1691           ch = get(cl);\r
1692           switch (ch) {\r
1693           case NULLCHAR:\r
1694             ExitArgError("Incomplete \\ escape in value for", argName);\r
1695             break;\r
1696           case 'n':\r
1697             *q++ = '\n';\r
1698             ch = get(cl);\r
1699             break;\r
1700           case 'r':\r
1701             *q++ = '\r';\r
1702             ch = get(cl);\r
1703             break;\r
1704           case 't':\r
1705             *q++ = '\t';\r
1706             ch = get(cl);\r
1707             break;\r
1708           case 'b':\r
1709             *q++ = '\b';\r
1710             ch = get(cl);\r
1711             break;\r
1712           case 'f':\r
1713             *q++ = '\f';\r
1714             ch = get(cl);\r
1715             break;\r
1716           default:\r
1717             octval = 0;\r
1718             for (i = 0; i < 3; i++) {\r
1719               if (ch >= '0' && ch <= '7') {\r
1720                 octval = octval*8 + (ch - '0');\r
1721                 ch = get(cl);\r
1722               } else {\r
1723                 break;\r
1724               }\r
1725             }\r
1726             if (i > 0) {\r
1727               *q++ = (char) octval;\r
1728             } else {\r
1729               *q++ = ch;\r
1730               ch = get(cl);\r
1731             }\r
1732             break;\r
1733           }\r
1734           break;\r
1735         }\r
1736       }\r
1737     } else {\r
1738       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1739         *q++ = ch;\r
1740         ch = get(cl);\r
1741       }\r
1742     }\r
1743     *q = NULLCHAR;\r
1744 \r
1745     switch (ad->argType) {\r
1746     case ArgInt:\r
1747       *(int *) ad->argLoc = atoi(argValue);\r
1748       break;\r
1749 \r
1750     case ArgX:\r
1751       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1752       break;\r
1753 \r
1754     case ArgY:\r
1755       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1756       break;\r
1757 \r
1758     case ArgZ:\r
1759       *(int *) ad->argLoc = atoi(argValue);\r
1760       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1761       break;\r
1762 \r
1763     case ArgFloat:\r
1764       *(float *) ad->argLoc = (float) atof(argValue);\r
1765       break;\r
1766 \r
1767     case ArgString:\r
1768     case ArgFilename:\r
1769       *(char **) ad->argLoc = strdup(argValue);\r
1770       break;\r
1771 \r
1772     case ArgSettingsFilename:\r
1773       {\r
1774         char fullname[MSG_SIZ];\r
1775         if (ParseSettingsFile(argValue, fullname)) {\r
1776           if (ad->argLoc != NULL) {\r
1777             *(char **) ad->argLoc = strdup(fullname);\r
1778           }\r
1779         } else {\r
1780           if (ad->argLoc != NULL) {\r
1781           } else {\r
1782             ExitArgError("Failed to open indirection file", argValue);\r
1783           }\r
1784         }\r
1785       }\r
1786       break;\r
1787 \r
1788     case ArgBoolean:\r
1789       switch (argValue[0]) {\r
1790       case 't':\r
1791       case 'T':\r
1792         *(Boolean *) ad->argLoc = TRUE;\r
1793         break;\r
1794       case 'f':\r
1795       case 'F':\r
1796         *(Boolean *) ad->argLoc = FALSE;\r
1797         break;\r
1798       default:\r
1799         ExitArgError("Unrecognized boolean argument value", argValue);\r
1800         break;\r
1801       }\r
1802       break;\r
1803 \r
1804     case ArgColor:\r
1805       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1806       break;\r
1807 \r
1808     case ArgAttribs: {\r
1809       ColorClass cc = (ColorClass)ad->argLoc;\r
1810       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1811       }\r
1812       break;\r
1813       \r
1814     case ArgBoardSize:\r
1815       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1816       break;\r
1817 \r
1818     case ArgFont:\r
1819       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1820       break;\r
1821 \r
1822     case ArgCommSettings:\r
1823       ParseCommSettings(argValue, &dcb);\r
1824       break;\r
1825 \r
1826     case ArgNone:\r
1827       ExitArgError("Unrecognized argument", argValue);\r
1828       break;\r
1829     case ArgTrue:\r
1830     case ArgFalse: ;\r
1831     }\r
1832   }\r
1833 }\r
1834 \r
1835 VOID\r
1836 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1837 {\r
1838   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1839   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1840   DeleteDC(hdc);\r
1841   lf->lfWidth = 0;\r
1842   lf->lfEscapement = 0;\r
1843   lf->lfOrientation = 0;\r
1844   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1845   lf->lfItalic = mfp->italic;\r
1846   lf->lfUnderline = mfp->underline;\r
1847   lf->lfStrikeOut = mfp->strikeout;\r
1848   lf->lfCharSet = DEFAULT_CHARSET;\r
1849   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1850   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1851   lf->lfQuality = DEFAULT_QUALITY;\r
1852   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1853   strcpy(lf->lfFaceName, mfp->faceName);\r
1854 }\r
1855 \r
1856 VOID\r
1857 CreateFontInMF(MyFont *mf)\r
1858 {\r
1859   LFfromMFP(&mf->lf, &mf->mfp);\r
1860   if (mf->hf) DeleteObject(mf->hf);\r
1861   mf->hf = CreateFontIndirect(&mf->lf);\r
1862 }\r
1863 \r
1864 VOID\r
1865 SetDefaultTextAttribs()\r
1866 {\r
1867   ColorClass cc;\r
1868   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1869     ParseAttribs(&textAttribs[cc].color, \r
1870                  &textAttribs[cc].effects, \r
1871                  defaultTextAttribs[cc]);\r
1872   }\r
1873 }\r
1874 \r
1875 VOID\r
1876 SetDefaultSounds()\r
1877 {\r
1878   ColorClass cc;\r
1879   SoundClass sc;\r
1880   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1881     textAttribs[cc].sound.name = strdup("");\r
1882     textAttribs[cc].sound.data = NULL;\r
1883   }\r
1884   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1885     sounds[sc].name = strdup("");\r
1886     sounds[sc].data = NULL;\r
1887   }\r
1888   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1889 }\r
1890 \r
1891 VOID\r
1892 LoadAllSounds()\r
1893 {\r
1894   ColorClass cc;\r
1895   SoundClass sc;\r
1896   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1897     MyLoadSound(&textAttribs[cc].sound);\r
1898   }\r
1899   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1900     MyLoadSound(&sounds[sc]);\r
1901   }\r
1902 }\r
1903 \r
1904 VOID\r
1905 InitAppData(LPSTR lpCmdLine)\r
1906 {\r
1907   int i, j;\r
1908   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1909   char *dummy, *p;\r
1910 \r
1911   programName = szAppName;\r
1912 \r
1913   /* Initialize to defaults */\r
1914   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1915   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1916   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1917   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1918   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1919   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1920   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1921   SetDefaultTextAttribs();\r
1922   SetDefaultSounds();\r
1923   appData.movesPerSession = MOVES_PER_SESSION;\r
1924   appData.initString = INIT_STRING;\r
1925   appData.secondInitString = INIT_STRING;\r
1926   appData.firstComputerString = COMPUTER_STRING;\r
1927   appData.secondComputerString = COMPUTER_STRING;\r
1928   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1929   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1930   appData.firstPlaysBlack = FALSE;\r
1931   appData.noChessProgram = FALSE;\r
1932   chessProgram = FALSE;\r
1933   appData.firstHost = FIRST_HOST;\r
1934   appData.secondHost = SECOND_HOST;\r
1935   appData.firstDirectory = FIRST_DIRECTORY;\r
1936   appData.secondDirectory = SECOND_DIRECTORY;\r
1937   appData.bitmapDirectory = "";\r
1938   appData.remoteShell = REMOTE_SHELL;\r
1939   appData.remoteUser = "";\r
1940   appData.timeDelay = TIME_DELAY;\r
1941   appData.timeControl = TIME_CONTROL;\r
1942   appData.timeIncrement = TIME_INCREMENT;\r
1943   appData.icsActive = FALSE;\r
1944   appData.icsHost = "";\r
1945   appData.icsPort = ICS_PORT;\r
1946   appData.icsCommPort = ICS_COMM_PORT;\r
1947   appData.icsLogon = ICS_LOGON;\r
1948   appData.icsHelper = "";\r
1949   appData.useTelnet = FALSE;\r
1950   appData.telnetProgram = TELNET_PROGRAM;\r
1951   appData.gateway = "";\r
1952   appData.loadGameFile = "";\r
1953   appData.loadGameIndex = 0;\r
1954   appData.saveGameFile = "";\r
1955   appData.autoSaveGames = FALSE;\r
1956   appData.loadPositionFile = "";\r
1957   appData.loadPositionIndex = 1;\r
1958   appData.savePositionFile = "";\r
1959   appData.matchMode = FALSE;\r
1960   appData.matchGames = 0;\r
1961   appData.monoMode = FALSE;\r
1962   appData.debugMode = FALSE;\r
1963   appData.clockMode = TRUE;\r
1964   boardSize = (BoardSize) -1; /* determine by screen size */\r
1965   appData.Iconic = FALSE; /*unused*/\r
1966   appData.searchTime = "";\r
1967   appData.searchDepth = 0;\r
1968   appData.showCoords = FALSE;\r
1969   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1970   appData.autoCallFlag = FALSE;\r
1971   appData.flipView = FALSE;\r
1972   appData.autoFlipView = TRUE;\r
1973   appData.cmailGameName = "";\r
1974   appData.alwaysPromoteToQueen = FALSE;\r
1975   appData.oldSaveStyle = FALSE;\r
1976   appData.quietPlay = FALSE;\r
1977   appData.showThinking = FALSE;\r
1978   appData.ponderNextMove = TRUE;\r
1979   appData.periodicUpdates = TRUE;\r
1980   appData.popupExitMessage = TRUE;\r
1981   appData.popupMoveErrors = FALSE;\r
1982   appData.autoObserve = FALSE;\r
1983   appData.autoComment = FALSE;\r
1984   appData.animate = TRUE;\r
1985   appData.animSpeed = 10;\r
1986   appData.animateDragging = TRUE;\r
1987   appData.highlightLastMove = TRUE;\r
1988   appData.getMoveList = TRUE;\r
1989   appData.testLegality = TRUE;\r
1990   appData.premove = TRUE;\r
1991   appData.premoveWhite = FALSE;\r
1992   appData.premoveWhiteText = "";\r
1993   appData.premoveBlack = FALSE;\r
1994   appData.premoveBlackText = "";\r
1995   appData.icsAlarm = TRUE;\r
1996   appData.icsAlarmTime = 5000;\r
1997   appData.autoRaiseBoard = TRUE;\r
1998   appData.localLineEditing = TRUE;\r
1999   appData.colorize = TRUE;\r
2000   appData.reuseFirst = TRUE;\r
2001   appData.reuseSecond = TRUE;\r
2002   appData.blindfold = FALSE;\r
2003   appData.icsEngineAnalyze = FALSE;\r
2004   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2005   dcb.DCBlength = sizeof(DCB);\r
2006   dcb.BaudRate = 9600;\r
2007   dcb.fBinary = TRUE;\r
2008   dcb.fParity = FALSE;\r
2009   dcb.fOutxCtsFlow = FALSE;\r
2010   dcb.fOutxDsrFlow = FALSE;\r
2011   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2012   dcb.fDsrSensitivity = FALSE;\r
2013   dcb.fTXContinueOnXoff = TRUE;\r
2014   dcb.fOutX = FALSE;\r
2015   dcb.fInX = FALSE;\r
2016   dcb.fNull = FALSE;\r
2017   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2018   dcb.fAbortOnError = FALSE;\r
2019   dcb.ByteSize = 7;\r
2020   dcb.Parity = SPACEPARITY;\r
2021   dcb.StopBits = ONESTOPBIT;\r
2022   settingsFileName = SETTINGS_FILE;\r
2023   saveSettingsOnExit = TRUE;\r
2024   boardX = CW_USEDEFAULT;\r
2025   boardY = CW_USEDEFAULT;\r
2026   analysisX = CW_USEDEFAULT; \r
2027   analysisY = CW_USEDEFAULT; \r
2028   analysisW = CW_USEDEFAULT;\r
2029   analysisH = CW_USEDEFAULT;\r
2030   commentX = CW_USEDEFAULT; \r
2031   commentY = CW_USEDEFAULT; \r
2032   commentW = CW_USEDEFAULT;\r
2033   commentH = CW_USEDEFAULT;\r
2034   editTagsX = CW_USEDEFAULT; \r
2035   editTagsY = CW_USEDEFAULT; \r
2036   editTagsW = CW_USEDEFAULT;\r
2037   editTagsH = CW_USEDEFAULT;\r
2038   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2039   icsNames = ICS_NAMES;\r
2040   firstChessProgramNames = FCP_NAMES;\r
2041   secondChessProgramNames = SCP_NAMES;\r
2042   appData.initialMode = "";\r
2043   appData.variant = "normal";\r
2044   appData.firstProtocolVersion = PROTOVER;\r
2045   appData.secondProtocolVersion = PROTOVER;\r
2046   appData.showButtonBar = TRUE;\r
2047 \r
2048    /* [AS] New properties (see comments in header file) */\r
2049   appData.firstScoreIsAbsolute = FALSE;\r
2050   appData.secondScoreIsAbsolute = FALSE;\r
2051   appData.saveExtendedInfoInPGN = FALSE;\r
2052   appData.hideThinkingFromHuman = FALSE;\r
2053   appData.liteBackTextureFile = "";\r
2054   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2055   appData.darkBackTextureFile = "";\r
2056   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2057   appData.renderPiecesWithFont = "";\r
2058   appData.fontToPieceTable = "";\r
2059   appData.fontBackColorWhite = 0;\r
2060   appData.fontForeColorWhite = 0;\r
2061   appData.fontBackColorBlack = 0;\r
2062   appData.fontForeColorBlack = 0;\r
2063   appData.fontPieceSize = 80;\r
2064   appData.overrideLineGap = 1;\r
2065   appData.adjudicateLossThreshold = 0;\r
2066   appData.delayBeforeQuit = 0;\r
2067   appData.delayAfterQuit = 0;\r
2068   appData.nameOfDebugFile = "winboard.debug";\r
2069   appData.pgnEventHeader = "Computer Chess Game";\r
2070   appData.defaultFrcPosition = -1;\r
2071   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2072   appData.saveOutOfBookInfo = TRUE;\r
2073   appData.showEvalInMoveHistory = TRUE;\r
2074   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2075   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2076   appData.highlightMoveWithArrow = FALSE;\r
2077   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2078   appData.useStickyWindows = TRUE;\r
2079   appData.adjudicateDrawMoves = 0;\r
2080   appData.autoDisplayComment = TRUE;\r
2081   appData.autoDisplayTags = TRUE;\r
2082   appData.firstIsUCI = FALSE;\r
2083   appData.secondIsUCI = FALSE;\r
2084   appData.firstHasOwnBookUCI = TRUE;\r
2085   appData.secondHasOwnBookUCI = TRUE;\r
2086   appData.polyglotDir = "";\r
2087   appData.usePolyglotBook = FALSE;\r
2088   appData.polyglotBook = "";\r
2089   appData.defaultHashSize = 64;\r
2090   appData.defaultCacheSizeEGTB = 4;\r
2091   appData.defaultPathEGTB = "c:\\egtb";\r
2092   appData.firstOptions = "";\r
2093   appData.secondOptions = "";\r
2094 \r
2095   InitWindowPlacement( &wpGameList );\r
2096   InitWindowPlacement( &wpMoveHistory );\r
2097   InitWindowPlacement( &wpEvalGraph );\r
2098   InitWindowPlacement( &wpEngineOutput );\r
2099   InitWindowPlacement( &wpConsole );\r
2100 \r
2101   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2102   appData.NrFiles      = -1;\r
2103   appData.NrRanks      = -1;\r
2104   appData.holdingsSize = -1;\r
2105   appData.testClaims   = FALSE;\r
2106   appData.checkMates   = FALSE;\r
2107   appData.materialDraws= FALSE;\r
2108   appData.trivialDraws = FALSE;\r
2109   appData.ruleMoves    = 51;\r
2110   appData.drawRepeats  = 6;\r
2111   appData.matchPause   = 10000;\r
2112   appData.alphaRank    = FALSE;\r
2113   appData.allWhite     = FALSE;\r
2114   appData.upsideDown   = FALSE;\r
2115   appData.serverPause  = 15;\r
2116   appData.serverMovesName   = NULL;\r
2117   appData.suppressLoadMoves = FALSE;\r
2118   appData.firstTimeOdds  = 1;\r
2119   appData.secondTimeOdds = 1;\r
2120   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2121   appData.secondAccumulateTC = 1;\r
2122   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2123   appData.secondNPS = -1;\r
2124   appData.engineComments = 1;\r
2125   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2126   appData.egtFormats = "";\r
2127 \r
2128 #ifdef ZIPPY\r
2129   appData.zippyTalk = ZIPPY_TALK;\r
2130   appData.zippyPlay = ZIPPY_PLAY;\r
2131   appData.zippyLines = ZIPPY_LINES;\r
2132   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2133   appData.zippyPassword = ZIPPY_PASSWORD;\r
2134   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2135   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2136   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2137   appData.zippyUseI = ZIPPY_USE_I;\r
2138   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2139   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2140   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2141   appData.zippyGameStart = ZIPPY_GAME_START;\r
2142   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2143   appData.zippyAbort = ZIPPY_ABORT;\r
2144   appData.zippyVariants = ZIPPY_VARIANTS;\r
2145   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2146   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2147 #endif\r
2148 \r
2149   /* Point font array elements to structures and\r
2150      parse default font names */\r
2151   for (i=0; i<NUM_FONTS; i++) {\r
2152     for (j=0; j<NUM_SIZES; j++) {\r
2153       font[j][i] = &fontRec[j][i];\r
2154       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2155     }\r
2156   }\r
2157   \r
2158   /* Parse default settings file if any */\r
2159   if (ParseSettingsFile(settingsFileName, buf)) {\r
2160     settingsFileName = strdup(buf);\r
2161   }\r
2162 \r
2163   /* Parse command line */\r
2164   ParseArgs(StringGet, &lpCmdLine);\r
2165 \r
2166   /* [HGM] make sure board size is acceptable */\r
2167   if(appData.NrFiles > BOARD_SIZE ||\r
2168      appData.NrRanks > BOARD_SIZE   )\r
2169       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2170 \r
2171   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2172    * with options from the command line, we now make an even higher priority\r
2173    * overrule by WB options attached to the engine command line. This so that\r
2174    * tournament managers can use WB options (such as /timeOdds) that follow\r
2175    * the engines.\r
2176    */\r
2177   if(appData.firstChessProgram != NULL) {\r
2178       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2179       static char *f = "first";\r
2180       char buf[MSG_SIZ], *q = buf;\r
2181       if(p != NULL) { // engine command line contains WinBoard options\r
2182           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2183           ParseArgs(StringGet, &q);\r
2184           p[-1] = 0; // cut them offengine command line\r
2185       }\r
2186   }\r
2187   // now do same for second chess program\r
2188   if(appData.secondChessProgram != NULL) {\r
2189       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2190       static char *s = "second";\r
2191       char buf[MSG_SIZ], *q = buf;\r
2192       if(p != NULL) { // engine command line contains WinBoard options\r
2193           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2194           ParseArgs(StringGet, &q);\r
2195           p[-1] = 0; // cut them offengine command line\r
2196       }\r
2197   }\r
2198 \r
2199 \r
2200   /* Propagate options that affect others */\r
2201   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2202   if (appData.icsActive || appData.noChessProgram) {\r
2203      chessProgram = FALSE;  /* not local chess program mode */\r
2204   }\r
2205 \r
2206   /* Open startup dialog if needed */\r
2207   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2208       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2209       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2210                         *appData.secondChessProgram == NULLCHAR))) {\r
2211     FARPROC lpProc;\r
2212     \r
2213     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2214     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2215     FreeProcInstance(lpProc);\r
2216   }\r
2217 \r
2218   /* Make sure save files land in the right (?) directory */\r
2219   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2220     appData.saveGameFile = strdup(buf);\r
2221   }\r
2222   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2223     appData.savePositionFile = strdup(buf);\r
2224   }\r
2225 \r
2226   /* Finish initialization for fonts and sounds */\r
2227   for (i=0; i<NUM_FONTS; i++) {\r
2228     for (j=0; j<NUM_SIZES; j++) {\r
2229       CreateFontInMF(font[j][i]);\r
2230     }\r
2231   }\r
2232   /* xboard, and older WinBoards, controlled the move sound with the\r
2233      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2234      always turn the option on (so that the backend will call us),\r
2235      then let the user turn the sound off by setting it to silence if\r
2236      desired.  To accommodate old winboard.ini files saved by old\r
2237      versions of WinBoard, we also turn off the sound if the option\r
2238      was initially set to false. */\r
2239   if (!appData.ringBellAfterMoves) {\r
2240     sounds[(int)SoundMove].name = strdup("");\r
2241     appData.ringBellAfterMoves = TRUE;\r
2242   }\r
2243   GetCurrentDirectory(MSG_SIZ, currDir);\r
2244   SetCurrentDirectory(installDir);\r
2245   LoadAllSounds();\r
2246   SetCurrentDirectory(currDir);\r
2247 \r
2248   p = icsTextMenuString;\r
2249   if (p[0] == '@') {\r
2250     FILE* f = fopen(p + 1, "r");\r
2251     if (f == NULL) {\r
2252       DisplayFatalError(p + 1, errno, 2);\r
2253       return;\r
2254     }\r
2255     i = fread(buf, 1, sizeof(buf)-1, f);\r
2256     fclose(f);\r
2257     buf[i] = NULLCHAR;\r
2258     p = buf;\r
2259   }\r
2260   ParseIcsTextMenu(strdup(p));\r
2261 }\r
2262 \r
2263 \r
2264 VOID\r
2265 InitMenuChecks()\r
2266 {\r
2267   HMENU hmenu = GetMenu(hwndMain);\r
2268 \r
2269   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2270                         MF_BYCOMMAND|((appData.icsActive &&\r
2271                                        *appData.icsCommPort != NULLCHAR) ?\r
2272                                       MF_ENABLED : MF_GRAYED));\r
2273   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2274                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2275                                      MF_CHECKED : MF_UNCHECKED));\r
2276 }\r
2277 \r
2278 \r
2279 VOID\r
2280 SaveSettings(char* name)\r
2281 {\r
2282   FILE *f;\r
2283   ArgDescriptor *ad;\r
2284   WINDOWPLACEMENT wp;\r
2285   char dir[MSG_SIZ];\r
2286 \r
2287   if (!hwndMain) return;\r
2288 \r
2289   GetCurrentDirectory(MSG_SIZ, dir);\r
2290   SetCurrentDirectory(installDir);\r
2291   f = fopen(name, "w");\r
2292   SetCurrentDirectory(dir);\r
2293   if (f == NULL) {\r
2294     DisplayError(name, errno);\r
2295     return;\r
2296   }\r
2297   fprintf(f, ";\n");\r
2298   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2299   fprintf(f, ";\n");\r
2300   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2301   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2302   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2303   fprintf(f, ";\n");\r
2304 \r
2305   wp.length = sizeof(WINDOWPLACEMENT);\r
2306   GetWindowPlacement(hwndMain, &wp);\r
2307   boardX = wp.rcNormalPosition.left;\r
2308   boardY = wp.rcNormalPosition.top;\r
2309 \r
2310   if (hwndConsole) {\r
2311     GetWindowPlacement(hwndConsole, &wp);\r
2312     wpConsole.x = wp.rcNormalPosition.left;\r
2313     wpConsole.y = wp.rcNormalPosition.top;\r
2314     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2315     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2316   }\r
2317 \r
2318   if (analysisDialog) {\r
2319     GetWindowPlacement(analysisDialog, &wp);\r
2320     analysisX = wp.rcNormalPosition.left;\r
2321     analysisY = wp.rcNormalPosition.top;\r
2322     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2323     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2324   }\r
2325 \r
2326   if (commentDialog) {\r
2327     GetWindowPlacement(commentDialog, &wp);\r
2328     commentX = wp.rcNormalPosition.left;\r
2329     commentY = wp.rcNormalPosition.top;\r
2330     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2331     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2332   }\r
2333 \r
2334   if (editTagsDialog) {\r
2335     GetWindowPlacement(editTagsDialog, &wp);\r
2336     editTagsX = wp.rcNormalPosition.left;\r
2337     editTagsY = wp.rcNormalPosition.top;\r
2338     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2339     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2340   }\r
2341 \r
2342   if (gameListDialog) {\r
2343     GetWindowPlacement(gameListDialog, &wp);\r
2344     wpGameList.x = wp.rcNormalPosition.left;\r
2345     wpGameList.y = wp.rcNormalPosition.top;\r
2346     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2347     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2348   }\r
2349 \r
2350   /* [AS] Move history */\r
2351   wpMoveHistory.visible = MoveHistoryIsUp();\r
2352   \r
2353   if( moveHistoryDialog ) {\r
2354     GetWindowPlacement(moveHistoryDialog, &wp);\r
2355     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2356     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2357     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2358     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2359   }\r
2360 \r
2361   /* [AS] Eval graph */\r
2362   wpEvalGraph.visible = EvalGraphIsUp();\r
2363 \r
2364   if( evalGraphDialog ) {\r
2365     GetWindowPlacement(evalGraphDialog, &wp);\r
2366     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2367     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2368     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2369     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2370   }\r
2371 \r
2372   /* [AS] Engine output */\r
2373   wpEngineOutput.visible = EngineOutputIsUp();\r
2374 \r
2375   if( engineOutputDialog ) {\r
2376     GetWindowPlacement(engineOutputDialog, &wp);\r
2377     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2378     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2379     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2380     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2381   }\r
2382 \r
2383   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2384     if (!ad->save) continue;\r
2385     switch (ad->argType) {\r
2386     case ArgString:\r
2387       {\r
2388         char *p = *(char **)ad->argLoc;\r
2389         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2390           /* Quote multiline values or \-containing values\r
2391              with { } if possible */\r
2392           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2393         } else {\r
2394           /* Else quote with " " */\r
2395           fprintf(f, "/%s=\"", ad->argName);\r
2396           while (*p) {\r
2397             if (*p == '\n') fprintf(f, "\n");\r
2398             else if (*p == '\r') fprintf(f, "\\r");\r
2399             else if (*p == '\t') fprintf(f, "\\t");\r
2400             else if (*p == '\b') fprintf(f, "\\b");\r
2401             else if (*p == '\f') fprintf(f, "\\f");\r
2402             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2403             else if (*p == '\"') fprintf(f, "\\\"");\r
2404             else if (*p == '\\') fprintf(f, "\\\\");\r
2405             else putc(*p, f);\r
2406             p++;\r
2407           }\r
2408           fprintf(f, "\"\n");\r
2409         }\r
2410       }\r
2411       break;\r
2412     case ArgInt:\r
2413     case ArgZ:\r
2414       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2415       break;\r
2416     case ArgX:\r
2417       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2418       break;\r
2419     case ArgY:\r
2420       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2421       break;\r
2422     case ArgFloat:\r
2423       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2424       break;\r
2425     case ArgBoolean:\r
2426       fprintf(f, "/%s=%s\n", ad->argName, \r
2427         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2428       break;\r
2429     case ArgTrue:\r
2430       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2431       break;\r
2432     case ArgFalse:\r
2433       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2434       break;\r
2435     case ArgColor:\r
2436       {\r
2437         COLORREF color = *(COLORREF *)ad->argLoc;\r
2438         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2439           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2440       }\r
2441       break;\r
2442     case ArgAttribs:\r
2443       {\r
2444         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2445         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2446           (ta->effects & CFE_BOLD) ? "b" : "",\r
2447           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2448           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2449           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2450           (ta->effects) ? " " : "",\r
2451           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2452       }\r
2453       break;\r
2454     case ArgFilename:\r
2455       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2456         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2457       } else {\r
2458         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2459       }\r
2460       break;\r
2461     case ArgBoardSize:\r
2462       fprintf(f, "/%s=%s\n", ad->argName,\r
2463               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2464       break;\r
2465     case ArgFont:\r
2466       {\r
2467         int bs;\r
2468         for (bs=0; bs<NUM_SIZES; bs++) {\r
2469           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2470           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2471           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2472             ad->argName, mfp->faceName, mfp->pointSize,\r
2473             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2474             mfp->bold ? "b" : "",\r
2475             mfp->italic ? "i" : "",\r
2476             mfp->underline ? "u" : "",\r
2477             mfp->strikeout ? "s" : "");\r
2478         }\r
2479       }\r
2480       break;\r
2481     case ArgCommSettings:\r
2482       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2483     case ArgNone:\r
2484     case ArgSettingsFilename: ;\r
2485     }\r
2486   }\r
2487   fclose(f);\r
2488 }\r
2489 \r
2490 \r
2491 \r
2492 /*---------------------------------------------------------------------------*\\r
2493  *\r
2494  * GDI board drawing routines\r
2495  *\r
2496 \*---------------------------------------------------------------------------*/\r
2497 \r
2498 /* [AS] Draw square using background texture */\r
2499 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2500 {\r
2501     XFORM   x;\r
2502 \r
2503     if( mode == 0 ) {\r
2504         return; /* Should never happen! */\r
2505     }\r
2506 \r
2507     SetGraphicsMode( dst, GM_ADVANCED );\r
2508 \r
2509     switch( mode ) {\r
2510     case 1:\r
2511         /* Identity */\r
2512         break;\r
2513     case 2:\r
2514         /* X reflection */\r
2515         x.eM11 = -1.0;\r
2516         x.eM12 = 0;\r
2517         x.eM21 = 0;\r
2518         x.eM22 = 1.0;\r
2519         x.eDx = (FLOAT) dw + dx - 1;\r
2520         x.eDy = 0;\r
2521         dx = 0;\r
2522         SetWorldTransform( dst, &x );\r
2523         break;\r
2524     case 3:\r
2525         /* Y reflection */\r
2526         x.eM11 = 1.0;\r
2527         x.eM12 = 0;\r
2528         x.eM21 = 0;\r
2529         x.eM22 = -1.0;\r
2530         x.eDx = 0;\r
2531         x.eDy = (FLOAT) dh + dy - 1;\r
2532         dy = 0;\r
2533         SetWorldTransform( dst, &x );\r
2534         break;\r
2535     case 4:\r
2536         /* X/Y flip */\r
2537         x.eM11 = 0;\r
2538         x.eM12 = 1.0;\r
2539         x.eM21 = 1.0;\r
2540         x.eM22 = 0;\r
2541         x.eDx = (FLOAT) dx;\r
2542         x.eDy = (FLOAT) dy;\r
2543         dx = 0;\r
2544         dy = 0;\r
2545         SetWorldTransform( dst, &x );\r
2546         break;\r
2547     }\r
2548 \r
2549     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2550 \r
2551     x.eM11 = 1.0;\r
2552     x.eM12 = 0;\r
2553     x.eM21 = 0;\r
2554     x.eM22 = 1.0;\r
2555     x.eDx = 0;\r
2556     x.eDy = 0;\r
2557     SetWorldTransform( dst, &x );\r
2558 \r
2559     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2560 }\r
2561 \r
2562 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2563 enum {\r
2564     PM_WP = (int) WhitePawn, \r
2565     PM_WN = (int) WhiteKnight, \r
2566     PM_WB = (int) WhiteBishop, \r
2567     PM_WR = (int) WhiteRook, \r
2568     PM_WQ = (int) WhiteQueen, \r
2569     PM_WF = (int) WhiteFerz, \r
2570     PM_WW = (int) WhiteWazir, \r
2571     PM_WE = (int) WhiteAlfil, \r
2572     PM_WM = (int) WhiteMan, \r
2573     PM_WO = (int) WhiteCannon, \r
2574     PM_WU = (int) WhiteUnicorn, \r
2575     PM_WH = (int) WhiteNightrider, \r
2576     PM_WA = (int) WhiteAngel, \r
2577     PM_WC = (int) WhiteMarshall, \r
2578     PM_WAB = (int) WhiteCardinal, \r
2579     PM_WD = (int) WhiteDragon, \r
2580     PM_WL = (int) WhiteLance, \r
2581     PM_WS = (int) WhiteCobra, \r
2582     PM_WV = (int) WhiteFalcon, \r
2583     PM_WSG = (int) WhiteSilver, \r
2584     PM_WG = (int) WhiteGrasshopper, \r
2585     PM_WK = (int) WhiteKing,\r
2586     PM_BP = (int) BlackPawn, \r
2587     PM_BN = (int) BlackKnight, \r
2588     PM_BB = (int) BlackBishop, \r
2589     PM_BR = (int) BlackRook, \r
2590     PM_BQ = (int) BlackQueen, \r
2591     PM_BF = (int) BlackFerz, \r
2592     PM_BW = (int) BlackWazir, \r
2593     PM_BE = (int) BlackAlfil, \r
2594     PM_BM = (int) BlackMan,\r
2595     PM_BO = (int) BlackCannon, \r
2596     PM_BU = (int) BlackUnicorn, \r
2597     PM_BH = (int) BlackNightrider, \r
2598     PM_BA = (int) BlackAngel, \r
2599     PM_BC = (int) BlackMarshall, \r
2600     PM_BG = (int) BlackGrasshopper, \r
2601     PM_BAB = (int) BlackCardinal,\r
2602     PM_BD = (int) BlackDragon,\r
2603     PM_BL = (int) BlackLance,\r
2604     PM_BS = (int) BlackCobra,\r
2605     PM_BV = (int) BlackFalcon,\r
2606     PM_BSG = (int) BlackSilver,\r
2607     PM_BK = (int) BlackKing\r
2608 };\r
2609 \r
2610 static HFONT hPieceFont = NULL;\r
2611 static HBITMAP hPieceMask[(int) EmptySquare];\r
2612 static HBITMAP hPieceFace[(int) EmptySquare];\r
2613 static int fontBitmapSquareSize = 0;\r
2614 static char pieceToFontChar[(int) EmptySquare] =\r
2615                               { 'p', 'n', 'b', 'r', 'q', \r
2616                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2617                       'k', 'o', 'm', 'v', 't', 'w', \r
2618                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2619                                                               'l' };\r
2620 \r
2621 extern BOOL SetCharTable( char *table, const char * map );\r
2622 /* [HGM] moved to backend.c */\r
2623 \r
2624 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2625 {\r
2626     HBRUSH hbrush;\r
2627     BYTE r1 = GetRValue( color );\r
2628     BYTE g1 = GetGValue( color );\r
2629     BYTE b1 = GetBValue( color );\r
2630     BYTE r2 = r1 / 2;\r
2631     BYTE g2 = g1 / 2;\r
2632     BYTE b2 = b1 / 2;\r
2633     RECT rc;\r
2634 \r
2635     /* Create a uniform background first */\r
2636     hbrush = CreateSolidBrush( color );\r
2637     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2638     FillRect( hdc, &rc, hbrush );\r
2639     DeleteObject( hbrush );\r
2640     \r
2641     if( mode == 1 ) {\r
2642         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2643         int steps = squareSize / 2;\r
2644         int i;\r
2645 \r
2646         for( i=0; i<steps; i++ ) {\r
2647             BYTE r = r1 - (r1-r2) * i / steps;\r
2648             BYTE g = g1 - (g1-g2) * i / steps;\r
2649             BYTE b = b1 - (b1-b2) * i / steps;\r
2650 \r
2651             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2652             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2653             FillRect( hdc, &rc, hbrush );\r
2654             DeleteObject(hbrush);\r
2655         }\r
2656     }\r
2657     else if( mode == 2 ) {\r
2658         /* Diagonal gradient, good more or less for every piece */\r
2659         POINT triangle[3];\r
2660         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2661         HBRUSH hbrush_old;\r
2662         int steps = squareSize;\r
2663         int i;\r
2664 \r
2665         triangle[0].x = squareSize - steps;\r
2666         triangle[0].y = squareSize;\r
2667         triangle[1].x = squareSize;\r
2668         triangle[1].y = squareSize;\r
2669         triangle[2].x = squareSize;\r
2670         triangle[2].y = squareSize - steps;\r
2671 \r
2672         for( i=0; i<steps; i++ ) {\r
2673             BYTE r = r1 - (r1-r2) * i / steps;\r
2674             BYTE g = g1 - (g1-g2) * i / steps;\r
2675             BYTE b = b1 - (b1-b2) * i / steps;\r
2676 \r
2677             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2678             hbrush_old = SelectObject( hdc, hbrush );\r
2679             Polygon( hdc, triangle, 3 );\r
2680             SelectObject( hdc, hbrush_old );\r
2681             DeleteObject(hbrush);\r
2682             triangle[0].x++;\r
2683             triangle[2].y++;\r
2684         }\r
2685 \r
2686         SelectObject( hdc, hpen );\r
2687     }\r
2688 }\r
2689 \r
2690 /*\r
2691     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2692     seems to work ok. The main problem here is to find the "inside" of a chess\r
2693     piece: follow the steps as explained below.\r
2694 */\r
2695 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2696 {\r
2697     HBITMAP hbm;\r
2698     HBITMAP hbm_old;\r
2699     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2700     RECT rc;\r
2701     SIZE sz;\r
2702     POINT pt;\r
2703     int backColor = whitePieceColor; \r
2704     int foreColor = blackPieceColor;\r
2705     \r
2706     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2707         backColor = appData.fontBackColorWhite;\r
2708         foreColor = appData.fontForeColorWhite;\r
2709     }\r
2710     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2711         backColor = appData.fontBackColorBlack;\r
2712         foreColor = appData.fontForeColorBlack;\r
2713     }\r
2714 \r
2715     /* Mask */\r
2716     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2717 \r
2718     hbm_old = SelectObject( hdc, hbm );\r
2719 \r
2720     rc.left = 0;\r
2721     rc.top = 0;\r
2722     rc.right = squareSize;\r
2723     rc.bottom = squareSize;\r
2724 \r
2725     /* Step 1: background is now black */\r
2726     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2727 \r
2728     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2729 \r
2730     pt.x = (squareSize - sz.cx) / 2;\r
2731     pt.y = (squareSize - sz.cy) / 2;\r
2732 \r
2733     SetBkMode( hdc, TRANSPARENT );\r
2734     SetTextColor( hdc, chroma );\r
2735     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2736     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2737 \r
2738     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2739     /* Step 3: the area outside the piece is filled with white */\r
2740 //    FloodFill( hdc, 0, 0, chroma );\r
2741     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2742     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2743     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2744     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2745     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2746     /* \r
2747         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2748         but if the start point is not inside the piece we're lost!\r
2749         There should be a better way to do this... if we could create a region or path\r
2750         from the fill operation we would be fine for example.\r
2751     */\r
2752 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2753     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2754 \r
2755     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2756         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2757         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2758 \r
2759         SelectObject( dc2, bm2 );\r
2760         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2761         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2762         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2763         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2764         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2765 \r
2766         DeleteDC( dc2 );\r
2767         DeleteObject( bm2 );\r
2768     }\r
2769 \r
2770     SetTextColor( hdc, 0 );\r
2771     /* \r
2772         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2773         draw the piece again in black for safety.\r
2774     */\r
2775     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2776 \r
2777     SelectObject( hdc, hbm_old );\r
2778 \r
2779     if( hPieceMask[index] != NULL ) {\r
2780         DeleteObject( hPieceMask[index] );\r
2781     }\r
2782 \r
2783     hPieceMask[index] = hbm;\r
2784 \r
2785     /* Face */\r
2786     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2787 \r
2788     SelectObject( hdc, hbm );\r
2789 \r
2790     {\r
2791         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2792         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2793         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2794 \r
2795         SelectObject( dc1, hPieceMask[index] );\r
2796         SelectObject( dc2, bm2 );\r
2797         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2798         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2799         \r
2800         /* \r
2801             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2802             the piece background and deletes (makes transparent) the rest.\r
2803             Thanks to that mask, we are free to paint the background with the greates\r
2804             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2805             We use this, to make gradients and give the pieces a "roundish" look.\r
2806         */\r
2807         SetPieceBackground( hdc, backColor, 2 );\r
2808         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2809 \r
2810         DeleteDC( dc2 );\r
2811         DeleteDC( dc1 );\r
2812         DeleteObject( bm2 );\r
2813     }\r
2814 \r
2815     SetTextColor( hdc, foreColor );\r
2816     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2817 \r
2818     SelectObject( hdc, hbm_old );\r
2819 \r
2820     if( hPieceFace[index] != NULL ) {\r
2821         DeleteObject( hPieceFace[index] );\r
2822     }\r
2823 \r
2824     hPieceFace[index] = hbm;\r
2825 }\r
2826 \r
2827 static int TranslatePieceToFontPiece( int piece )\r
2828 {\r
2829     switch( piece ) {\r
2830     case BlackPawn:\r
2831         return PM_BP;\r
2832     case BlackKnight:\r
2833         return PM_BN;\r
2834     case BlackBishop:\r
2835         return PM_BB;\r
2836     case BlackRook:\r
2837         return PM_BR;\r
2838     case BlackQueen:\r
2839         return PM_BQ;\r
2840     case BlackKing:\r
2841         return PM_BK;\r
2842     case WhitePawn:\r
2843         return PM_WP;\r
2844     case WhiteKnight:\r
2845         return PM_WN;\r
2846     case WhiteBishop:\r
2847         return PM_WB;\r
2848     case WhiteRook:\r
2849         return PM_WR;\r
2850     case WhiteQueen:\r
2851         return PM_WQ;\r
2852     case WhiteKing:\r
2853         return PM_WK;\r
2854 \r
2855     case BlackAngel:\r
2856         return PM_BA;\r
2857     case BlackMarshall:\r
2858         return PM_BC;\r
2859     case BlackFerz:\r
2860         return PM_BF;\r
2861     case BlackNightrider:\r
2862         return PM_BH;\r
2863     case BlackAlfil:\r
2864         return PM_BE;\r
2865     case BlackWazir:\r
2866         return PM_BW;\r
2867     case BlackUnicorn:\r
2868         return PM_BU;\r
2869     case BlackCannon:\r
2870         return PM_BO;\r
2871     case BlackGrasshopper:\r
2872         return PM_BG;\r
2873     case BlackMan:\r
2874         return PM_BM;\r
2875     case BlackSilver:\r
2876         return PM_BSG;\r
2877     case BlackLance:\r
2878         return PM_BL;\r
2879     case BlackFalcon:\r
2880         return PM_BV;\r
2881     case BlackCobra:\r
2882         return PM_BS;\r
2883     case BlackCardinal:\r
2884         return PM_BAB;\r
2885     case BlackDragon:\r
2886         return PM_BD;\r
2887 \r
2888     case WhiteAngel:\r
2889         return PM_WA;\r
2890     case WhiteMarshall:\r
2891         return PM_WC;\r
2892     case WhiteFerz:\r
2893         return PM_WF;\r
2894     case WhiteNightrider:\r
2895         return PM_WH;\r
2896     case WhiteAlfil:\r
2897         return PM_WE;\r
2898     case WhiteWazir:\r
2899         return PM_WW;\r
2900     case WhiteUnicorn:\r
2901         return PM_WU;\r
2902     case WhiteCannon:\r
2903         return PM_WO;\r
2904     case WhiteGrasshopper:\r
2905         return PM_WG;\r
2906     case WhiteMan:\r
2907         return PM_WM;\r
2908     case WhiteSilver:\r
2909         return PM_WSG;\r
2910     case WhiteLance:\r
2911         return PM_WL;\r
2912     case WhiteFalcon:\r
2913         return PM_WV;\r
2914     case WhiteCobra:\r
2915         return PM_WS;\r
2916     case WhiteCardinal:\r
2917         return PM_WAB;\r
2918     case WhiteDragon:\r
2919         return PM_WD;\r
2920     }\r
2921 \r
2922     return 0;\r
2923 }\r
2924 \r
2925 void CreatePiecesFromFont()\r
2926 {\r
2927     LOGFONT lf;\r
2928     HDC hdc_window = NULL;\r
2929     HDC hdc = NULL;\r
2930     HFONT hfont_old;\r
2931     int fontHeight;\r
2932     int i;\r
2933 \r
2934     if( fontBitmapSquareSize < 0 ) {\r
2935         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2936         return;\r
2937     }\r
2938 \r
2939     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2940         fontBitmapSquareSize = -1;\r
2941         return;\r
2942     }\r
2943 \r
2944     if( fontBitmapSquareSize != squareSize ) {\r
2945         hdc_window = GetDC( hwndMain );\r
2946         hdc = CreateCompatibleDC( hdc_window );\r
2947 \r
2948         if( hPieceFont != NULL ) {\r
2949             DeleteObject( hPieceFont );\r
2950         }\r
2951         else {\r
2952             for( i=0; i<=(int)BlackKing; i++ ) {\r
2953                 hPieceMask[i] = NULL;\r
2954                 hPieceFace[i] = NULL;\r
2955             }\r
2956         }\r
2957 \r
2958         fontHeight = 75;\r
2959 \r
2960         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2961             fontHeight = appData.fontPieceSize;\r
2962         }\r
2963 \r
2964         fontHeight = (fontHeight * squareSize) / 100;\r
2965 \r
2966         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2967         lf.lfWidth = 0;\r
2968         lf.lfEscapement = 0;\r
2969         lf.lfOrientation = 0;\r
2970         lf.lfWeight = FW_NORMAL;\r
2971         lf.lfItalic = 0;\r
2972         lf.lfUnderline = 0;\r
2973         lf.lfStrikeOut = 0;\r
2974         lf.lfCharSet = DEFAULT_CHARSET;\r
2975         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2976         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2977         lf.lfQuality = PROOF_QUALITY;\r
2978         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2979         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2980         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2981 \r
2982         hPieceFont = CreateFontIndirect( &lf );\r
2983 \r
2984         if( hPieceFont == NULL ) {\r
2985             fontBitmapSquareSize = -2;\r
2986         }\r
2987         else {\r
2988             /* Setup font-to-piece character table */\r
2989             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2990                 /* No (or wrong) global settings, try to detect the font */\r
2991                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2992                     /* Alpha */\r
2993                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2994                 }\r
2995                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2996                     /* DiagramTT* family */\r
2997                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2998                 }\r
2999                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3000                     /* Fairy symbols */\r
3001                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3002                 }\r
3003                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3004                     /* Good Companion (Some characters get warped as literal :-( */\r
3005                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3006                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3007                     SetCharTable(pieceToFontChar, s);\r
3008                 }\r
3009                 else {\r
3010                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3011                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3012                 }\r
3013             }\r
3014 \r
3015             /* Create bitmaps */\r
3016             hfont_old = SelectObject( hdc, hPieceFont );\r
3017 #if 0\r
3018             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3019             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3020             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3021             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3022             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3023             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3024             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3030 \r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3033             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3035             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3063 #else\r
3064             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3065                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3066                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3067 #endif\r
3068             SelectObject( hdc, hfont_old );\r
3069 \r
3070             fontBitmapSquareSize = squareSize;\r
3071         }\r
3072     }\r
3073 \r
3074     if( hdc != NULL ) {\r
3075         DeleteDC( hdc );\r
3076     }\r
3077 \r
3078     if( hdc_window != NULL ) {\r
3079         ReleaseDC( hwndMain, hdc_window );\r
3080     }\r
3081 }\r
3082 \r
3083 HBITMAP\r
3084 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3085 {\r
3086   char name[128];\r
3087 \r
3088   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3089   if (gameInfo.event &&\r
3090       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3091       strcmp(name, "k80s") == 0) {\r
3092     strcpy(name, "tim");\r
3093   }\r
3094   return LoadBitmap(hinst, name);\r
3095 }\r
3096 \r
3097 \r
3098 /* Insert a color into the program's logical palette\r
3099    structure.  This code assumes the given color is\r
3100    the result of the RGB or PALETTERGB macro, and it\r
3101    knows how those macros work (which is documented).\r
3102 */\r
3103 VOID\r
3104 InsertInPalette(COLORREF color)\r
3105 {\r
3106   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3107 \r
3108   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3109     DisplayFatalError("Too many colors", 0, 1);\r
3110     pLogPal->palNumEntries--;\r
3111     return;\r
3112   }\r
3113 \r
3114   pe->peFlags = (char) 0;\r
3115   pe->peRed = (char) (0xFF & color);\r
3116   pe->peGreen = (char) (0xFF & (color >> 8));\r
3117   pe->peBlue = (char) (0xFF & (color >> 16));\r
3118   return;\r
3119 }\r
3120 \r
3121 \r
3122 VOID\r
3123 InitDrawingColors()\r
3124 {\r
3125   if (pLogPal == NULL) {\r
3126     /* Allocate enough memory for a logical palette with\r
3127      * PALETTESIZE entries and set the size and version fields\r
3128      * of the logical palette structure.\r
3129      */\r
3130     pLogPal = (NPLOGPALETTE)\r
3131       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3132                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3133     pLogPal->palVersion    = 0x300;\r
3134   }\r
3135   pLogPal->palNumEntries = 0;\r
3136 \r
3137   InsertInPalette(lightSquareColor);\r
3138   InsertInPalette(darkSquareColor);\r
3139   InsertInPalette(whitePieceColor);\r
3140   InsertInPalette(blackPieceColor);\r
3141   InsertInPalette(highlightSquareColor);\r
3142   InsertInPalette(premoveHighlightColor);\r
3143 \r
3144   /*  create a logical color palette according the information\r
3145    *  in the LOGPALETTE structure.\r
3146    */\r
3147   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3148 \r
3149   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3150   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3151   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3152   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3153   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3154   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3155   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3156   /* [AS] Force rendering of the font-based pieces */\r
3157   if( fontBitmapSquareSize > 0 ) {\r
3158     fontBitmapSquareSize = 0;\r
3159   }\r
3160 }\r
3161 \r
3162 \r
3163 int\r
3164 BoardWidth(int boardSize, int n)\r
3165 { /* [HGM] argument n added to allow different width and height */\r
3166   int lineGap = sizeInfo[boardSize].lineGap;\r
3167 \r
3168   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3169       lineGap = appData.overrideLineGap;\r
3170   }\r
3171 \r
3172   return (n + 1) * lineGap +\r
3173           n * sizeInfo[boardSize].squareSize;\r
3174 }\r
3175 \r
3176 /* Respond to board resize by dragging edge */\r
3177 VOID\r
3178 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3179 {\r
3180   BoardSize newSize = NUM_SIZES - 1;\r
3181   static int recurse = 0;\r
3182   if (IsIconic(hwndMain)) return;\r
3183   if (recurse > 0) return;\r
3184   recurse++;\r
3185   while (newSize > 0) {\r
3186         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3187         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3188            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3189     newSize--;\r
3190   } \r
3191   boardSize = newSize;\r
3192   InitDrawingSizes(boardSize, flags);\r
3193   recurse--;\r
3194 }\r
3195 \r
3196 \r
3197 \r
3198 VOID\r
3199 InitDrawingSizes(BoardSize boardSize, int flags)\r
3200 {\r
3201   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3202   ChessSquare piece;\r
3203   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3204   HDC hdc;\r
3205   SIZE clockSize, messageSize;\r
3206   HFONT oldFont;\r
3207   char buf[MSG_SIZ];\r
3208   char *str;\r
3209   HMENU hmenu = GetMenu(hwndMain);\r
3210   RECT crect, wrect, oldRect;\r
3211   int offby;\r
3212   LOGBRUSH logbrush;\r
3213 \r
3214   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3215   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3216 \r
3217   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3218   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3219 \r
3220   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3221   oldRect.top = boardY;\r
3222   oldRect.right = boardX + winWidth;\r
3223   oldRect.bottom = boardY + winHeight;\r
3224 \r
3225   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3226   smallLayout = sizeInfo[boardSize].smallLayout;\r
3227   squareSize = sizeInfo[boardSize].squareSize;\r
3228   lineGap = sizeInfo[boardSize].lineGap;\r
3229   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3230 \r
3231   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3232       lineGap = appData.overrideLineGap;\r
3233   }\r
3234 \r
3235   if (tinyLayout != oldTinyLayout) {\r
3236     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3237     if (tinyLayout) {\r
3238       style &= ~WS_SYSMENU;\r
3239       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3240                  "&Minimize\tCtrl+F4");\r
3241     } else {\r
3242       style |= WS_SYSMENU;\r
3243       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3244     }\r
3245     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3246 \r
3247     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3248       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3249         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3250     }\r
3251     DrawMenuBar(hwndMain);\r
3252   }\r
3253 \r
3254   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3255   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3256 \r
3257   /* Get text area sizes */\r
3258   hdc = GetDC(hwndMain);\r
3259   if (appData.clockMode) {\r
3260     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3261   } else {\r
3262     sprintf(buf, "White");\r
3263   }\r
3264   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3265   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3266   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3267   str = "We only care about the height here";\r
3268   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3269   SelectObject(hdc, oldFont);\r
3270   ReleaseDC(hwndMain, hdc);\r
3271 \r
3272   /* Compute where everything goes */\r
3273   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3274         /* [HGM] logo: if either logo is on, reserve space for it */\r
3275         logoHeight =  2*clockSize.cy;\r
3276         leftLogoRect.left   = OUTER_MARGIN;\r
3277         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3278         leftLogoRect.top    = OUTER_MARGIN;\r
3279         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3280 \r
3281         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3282         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3283         rightLogoRect.top    = OUTER_MARGIN;\r
3284         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3285 \r
3286 \r
3287     whiteRect.left = leftLogoRect.right;\r
3288     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3289     whiteRect.top = OUTER_MARGIN;\r
3290     whiteRect.bottom = whiteRect.top + logoHeight;\r
3291 \r
3292     blackRect.right = rightLogoRect.left;\r
3293     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3294     blackRect.top = whiteRect.top;\r
3295     blackRect.bottom = whiteRect.bottom;\r
3296   } else {\r
3297     whiteRect.left = OUTER_MARGIN;\r
3298     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3299     whiteRect.top = OUTER_MARGIN;\r
3300     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3301 \r
3302     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3303     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3304     blackRect.top = whiteRect.top;\r
3305     blackRect.bottom = whiteRect.bottom;\r
3306   }\r
3307 \r
3308   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3309   if (appData.showButtonBar) {\r
3310     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3311       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3312   } else {\r
3313     messageRect.right = OUTER_MARGIN + boardWidth;\r
3314   }\r
3315   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3316   messageRect.bottom = messageRect.top + messageSize.cy;\r
3317 \r
3318   boardRect.left = OUTER_MARGIN;\r
3319   boardRect.right = boardRect.left + boardWidth;\r
3320   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3321   boardRect.bottom = boardRect.top + boardHeight;\r
3322 \r
3323   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3324   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3325   oldBoardSize = boardSize;\r
3326   oldTinyLayout = tinyLayout;\r
3327   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3328   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3329     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3330   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3331   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3332   winHeight = winH; //       without disturbing window attachments\r
3333   GetWindowRect(hwndMain, &wrect);\r
3334   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3335                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3336 \r
3337   // [HGM] placement: let attached windows follow size change.\r
3338   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3339   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3340   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3341   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3342   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3343 \r
3344   /* compensate if menu bar wrapped */\r
3345   GetClientRect(hwndMain, &crect);\r
3346   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3347   winHeight += offby;\r
3348   switch (flags) {\r
3349   case WMSZ_TOPLEFT:\r
3350     SetWindowPos(hwndMain, NULL, \r
3351                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3352                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3353     break;\r
3354 \r
3355   case WMSZ_TOPRIGHT:\r
3356   case WMSZ_TOP:\r
3357     SetWindowPos(hwndMain, NULL, \r
3358                  wrect.left, wrect.bottom - winHeight, \r
3359                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3360     break;\r
3361 \r
3362   case WMSZ_BOTTOMLEFT:\r
3363   case WMSZ_LEFT:\r
3364     SetWindowPos(hwndMain, NULL, \r
3365                  wrect.right - winWidth, wrect.top, \r
3366                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3367     break;\r
3368 \r
3369   case WMSZ_BOTTOMRIGHT:\r
3370   case WMSZ_BOTTOM:\r
3371   case WMSZ_RIGHT:\r
3372   default:\r
3373     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3374                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3375     break;\r
3376   }\r
3377 \r
3378   hwndPause = NULL;\r
3379   for (i = 0; i < N_BUTTONS; i++) {\r
3380     if (buttonDesc[i].hwnd != NULL) {\r
3381       DestroyWindow(buttonDesc[i].hwnd);\r
3382       buttonDesc[i].hwnd = NULL;\r
3383     }\r
3384     if (appData.showButtonBar) {\r
3385       buttonDesc[i].hwnd =\r
3386         CreateWindow("BUTTON", buttonDesc[i].label,\r
3387                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3388                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3389                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3390                      (HMENU) buttonDesc[i].id,\r
3391                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3392       if (tinyLayout) {\r
3393         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3394                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3395                     MAKELPARAM(FALSE, 0));\r
3396       }\r
3397       if (buttonDesc[i].id == IDM_Pause)\r
3398         hwndPause = buttonDesc[i].hwnd;\r
3399       buttonDesc[i].wndproc = (WNDPROC)\r
3400         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3401     }\r
3402   }\r
3403   if (gridPen != NULL) DeleteObject(gridPen);\r
3404   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3405   if (premovePen != NULL) DeleteObject(premovePen);\r
3406   if (lineGap != 0) {\r
3407     logbrush.lbStyle = BS_SOLID;\r
3408     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3409     gridPen =\r
3410       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3411                    lineGap, &logbrush, 0, NULL);\r
3412     logbrush.lbColor = highlightSquareColor;\r
3413     highlightPen =\r
3414       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3415                    lineGap, &logbrush, 0, NULL);\r
3416 \r
3417     logbrush.lbColor = premoveHighlightColor; \r
3418     premovePen =\r
3419       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3420                    lineGap, &logbrush, 0, NULL);\r
3421 \r
3422     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3423     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3424       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3425       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3426         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3427       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3428         BOARD_WIDTH * (squareSize + lineGap);\r
3429       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3430     }\r
3431     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3432       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3433       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3434         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3435         lineGap / 2 + (i * (squareSize + lineGap));\r
3436       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3437         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3438       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3439     }\r
3440   }\r
3441 \r
3442   /* [HGM] Licensing requirement */\r
3443 #ifdef GOTHIC\r
3444   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3445 #endif\r
3446 #ifdef FALCON\r
3447   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3448 #endif\r
3449   GothicPopUp( "", VariantNormal);\r
3450 \r
3451 \r
3452 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3453 \r
3454   /* Load piece bitmaps for this board size */\r
3455   for (i=0; i<=2; i++) {\r
3456     for (piece = WhitePawn;\r
3457          (int) piece < (int) BlackPawn;\r
3458          piece = (ChessSquare) ((int) piece + 1)) {\r
3459       if (pieceBitmap[i][piece] != NULL)\r
3460         DeleteObject(pieceBitmap[i][piece]);\r
3461     }\r
3462   }\r
3463 \r
3464   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3465   // Orthodox Chess pieces\r
3466   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3467   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3468   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3469   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3470   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3471   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3472   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3473   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3474   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3475   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3476   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3477   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3478   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3479   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3480   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3481   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3482     // in Shogi, Hijack the unused Queen for Lance\r
3483     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3484     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3485     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3486   } else {\r
3487     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3488     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3489     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3490   }\r
3491 \r
3492   if(squareSize <= 72 && squareSize >= 33) { \r
3493     /* A & C are available in most sizes now */\r
3494     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3495       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3496       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3497       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3498       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3499       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3500       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3501       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3502       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3503       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3504       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3505       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3506       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3507     } else { // Smirf-like\r
3508       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3509       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3510       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3511     }\r
3512     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3513       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3514       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3515       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3516     } else { // WinBoard standard\r
3517       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3518       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3519       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3520     }\r
3521   }\r
3522 \r
3523 \r
3524   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3525     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3526     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3527     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3528     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3529     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3530     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3531     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3532     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3533     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3534     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3535     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3536     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3537     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3538     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3539     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3540     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3541     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3542     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3543     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3544     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3545     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3546     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3547     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3548     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3549     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3550     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3551     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3552     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3553     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3554     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3555 \r
3556     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3557       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3558       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3559       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3560       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3561       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3562       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3563       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3564       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3565       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3566       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3567       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3568       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3569     } else {\r
3570       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3571       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3572       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3573       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3574       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3575       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3576       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3577       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3578       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3579       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3580       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3581       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3582     }\r
3583 \r
3584   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3585     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3586     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3587     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3588     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3589     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3590     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3591     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3592     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3593     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3594     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3595     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3596     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3597     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3598     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3599   }\r
3600 \r
3601 \r
3602   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3603   /* special Shogi support in this size */\r
3604   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3605       for (piece = WhitePawn;\r
3606            (int) piece < (int) BlackPawn;\r
3607            piece = (ChessSquare) ((int) piece + 1)) {\r
3608         if (pieceBitmap[i][piece] != NULL)\r
3609           DeleteObject(pieceBitmap[i][piece]);\r
3610       }\r
3611     }\r
3612   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3613   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3614   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3615   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3616   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3617   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3618   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3619   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3620   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3621   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3622   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3623   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3624   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3625   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3626   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3627   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3628   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3629   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3630   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3631   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3632   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3633   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3634   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3635   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3636   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3637   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3638   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3639   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3640   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3641   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3642   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3643   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3644   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3645   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3646   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3647   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3648   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3649   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3650   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3651   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3652   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3653   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3654   minorSize = 0;\r
3655   }\r
3656 }\r
3657 \r
3658 HBITMAP\r
3659 PieceBitmap(ChessSquare p, int kind)\r
3660 {\r
3661   if ((int) p >= (int) BlackPawn)\r
3662     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3663 \r
3664   return pieceBitmap[kind][(int) p];\r
3665 }\r
3666 \r
3667 /***************************************************************/\r
3668 \r
3669 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3670 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3671 /*\r
3672 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3673 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3674 */\r
3675 \r
3676 VOID\r
3677 SquareToPos(int row, int column, int * x, int * y)\r
3678 {\r
3679   if (flipView) {\r
3680     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3681     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3682   } else {\r
3683     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3684     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3685   }\r
3686 }\r
3687 \r
3688 VOID\r
3689 DrawCoordsOnDC(HDC hdc)\r
3690 {\r
3691   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
3692   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
3693   char str[2] = { NULLCHAR, NULLCHAR };\r
3694   int oldMode, oldAlign, x, y, start, i;\r
3695   HFONT oldFont;\r
3696   HBRUSH oldBrush;\r
3697 \r
3698   if (!appData.showCoords)\r
3699     return;\r
3700 \r
3701   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3702 \r
3703   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3704   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3705   oldAlign = GetTextAlign(hdc);\r
3706   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3707 \r
3708   y = boardRect.top + lineGap;\r
3709   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3710 \r
3711   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3712   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3713     str[0] = files[start + i];\r
3714     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3715     y += squareSize + lineGap;\r
3716   }\r
3717 \r
3718   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3719 \r
3720   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3721   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3722     str[0] = ranks[start + i];\r
3723     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3724     x += squareSize + lineGap;\r
3725   }    \r
3726 \r
3727   SelectObject(hdc, oldBrush);\r
3728   SetBkMode(hdc, oldMode);\r
3729   SetTextAlign(hdc, oldAlign);\r
3730   SelectObject(hdc, oldFont);\r
3731 }\r
3732 \r
3733 VOID\r
3734 DrawGridOnDC(HDC hdc)\r
3735 {\r
3736   HPEN oldPen;\r
3737  \r
3738   if (lineGap != 0) {\r
3739     oldPen = SelectObject(hdc, gridPen);\r
3740     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3741     SelectObject(hdc, oldPen);\r
3742   }\r
3743 }\r
3744 \r
3745 #define HIGHLIGHT_PEN 0\r
3746 #define PREMOVE_PEN   1\r
3747 \r
3748 VOID\r
3749 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3750 {\r
3751   int x1, y1;\r
3752   HPEN oldPen, hPen;\r
3753   if (lineGap == 0) return;\r
3754   if (flipView) {\r
3755     x1 = boardRect.left +\r
3756       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3757     y1 = boardRect.top +\r
3758       lineGap/2 + y * (squareSize + lineGap);\r
3759   } else {\r
3760     x1 = boardRect.left +\r
3761       lineGap/2 + x * (squareSize + lineGap);\r
3762     y1 = boardRect.top +\r
3763       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3764   }\r
3765   hPen = pen ? premovePen : highlightPen;\r
3766   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3767   MoveToEx(hdc, x1, y1, NULL);\r
3768   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3769   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3770   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3771   LineTo(hdc, x1, y1);\r
3772   SelectObject(hdc, oldPen);\r
3773 }\r
3774 \r
3775 VOID\r
3776 DrawHighlightsOnDC(HDC hdc)\r
3777 {\r
3778   int i;\r
3779   for (i=0; i<2; i++) {\r
3780     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3781       DrawHighlightOnDC(hdc, TRUE,\r
3782                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3783                         HIGHLIGHT_PEN);\r
3784   }\r
3785   for (i=0; i<2; i++) {\r
3786     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3787         premoveHighlightInfo.sq[i].y >= 0) {\r
3788         DrawHighlightOnDC(hdc, TRUE,\r
3789                           premoveHighlightInfo.sq[i].x, \r
3790                           premoveHighlightInfo.sq[i].y,\r
3791                           PREMOVE_PEN);\r
3792     }\r
3793   }\r
3794 }\r
3795 \r
3796 /* Note: sqcolor is used only in monoMode */\r
3797 /* Note that this code is largely duplicated in woptions.c,\r
3798    function DrawSampleSquare, so that needs to be updated too */\r
3799 VOID\r
3800 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3801 {\r
3802   HBITMAP oldBitmap;\r
3803   HBRUSH oldBrush;\r
3804   int tmpSize;\r
3805 \r
3806   if (appData.blindfold) return;\r
3807 \r
3808   /* [AS] Use font-based pieces if needed */\r
3809   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3810     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3811     CreatePiecesFromFont();\r
3812 \r
3813     if( fontBitmapSquareSize == squareSize ) {\r
3814         int index = TranslatePieceToFontPiece(piece);\r
3815 \r
3816         SelectObject( tmphdc, hPieceMask[ index ] );\r
3817 \r
3818         BitBlt( hdc,\r
3819             x, y,\r
3820             squareSize, squareSize,\r
3821             tmphdc,\r
3822             0, 0,\r
3823             SRCAND );\r
3824 \r
3825         SelectObject( tmphdc, hPieceFace[ index ] );\r
3826 \r
3827         BitBlt( hdc,\r
3828             x, y,\r
3829             squareSize, squareSize,\r
3830             tmphdc,\r
3831             0, 0,\r
3832             SRCPAINT );\r
3833 \r
3834         return;\r
3835     }\r
3836   }\r
3837 \r
3838   if (appData.monoMode) {\r
3839     SelectObject(tmphdc, PieceBitmap(piece, \r
3840       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3841     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3842            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3843   } else {\r
3844     tmpSize = squareSize;\r
3845     if(minorSize &&\r
3846         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3847          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3848       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3849       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3850       x += (squareSize - minorSize)>>1;\r
3851       y += squareSize - minorSize - 2;\r
3852       tmpSize = minorSize;\r
3853     }\r
3854     if (color || appData.allWhite ) {\r
3855       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3856       if( color )\r
3857               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3858       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3859       if(appData.upsideDown && color==flipView)\r
3860         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3861       else\r
3862         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3863 #if 0\r
3864       /* Use black piece color for outline of white pieces */\r
3865       /* Not sure this looks really good (though xboard does it).\r
3866          Maybe better to have another selectable color, default black */\r
3867       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3868       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3869       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3870 #else\r
3871       /* Use black for outline of white pieces */\r
3872       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3873       if(appData.upsideDown && color==flipView)\r
3874         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3875       else\r
3876         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3877 #endif\r
3878     } else {\r
3879 #if 0\r
3880       /* Use white piece color for details of black pieces */\r
3881       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3882          WHITE_PIECE ones aren't always the right shape. */\r
3883       /* Not sure this looks really good (though xboard does it).\r
3884          Maybe better to have another selectable color, default medium gray? */\r
3885       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3886       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3887       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3888       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3889       SelectObject(hdc, blackPieceBrush);\r
3890       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3891 #else\r
3892       /* Use square color for details of black pieces */\r
3893       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3894       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3895       if(appData.upsideDown && !flipView)\r
3896         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3897       else\r
3898         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3899 #endif\r
3900     }\r
3901     SelectObject(hdc, oldBrush);\r
3902     SelectObject(tmphdc, oldBitmap);\r
3903   }\r
3904 }\r
3905 \r
3906 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3907 int GetBackTextureMode( int algo )\r
3908 {\r
3909     int result = BACK_TEXTURE_MODE_DISABLED;\r
3910 \r
3911     switch( algo ) \r
3912     {\r
3913         case BACK_TEXTURE_MODE_PLAIN:\r
3914             result = 1; /* Always use identity map */\r
3915             break;\r
3916         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3917             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3918             break;\r
3919     }\r
3920 \r
3921     return result;\r
3922 }\r
3923 \r
3924 /* \r
3925     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3926     to handle redraws cleanly (as random numbers would always be different).\r
3927 */\r
3928 VOID RebuildTextureSquareInfo()\r
3929 {\r
3930     BITMAP bi;\r
3931     int lite_w = 0;\r
3932     int lite_h = 0;\r
3933     int dark_w = 0;\r
3934     int dark_h = 0;\r
3935     int row;\r
3936     int col;\r
3937 \r
3938     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3939 \r
3940     if( liteBackTexture != NULL ) {\r
3941         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3942             lite_w = bi.bmWidth;\r
3943             lite_h = bi.bmHeight;\r
3944         }\r
3945     }\r
3946 \r
3947     if( darkBackTexture != NULL ) {\r
3948         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3949             dark_w = bi.bmWidth;\r
3950             dark_h = bi.bmHeight;\r
3951         }\r
3952     }\r
3953 \r
3954     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3955         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3956             if( (col + row) & 1 ) {\r
3957                 /* Lite square */\r
3958                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3959                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3960                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3961                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3962                 }\r
3963             }\r
3964             else {\r
3965                 /* Dark square */\r
3966                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3967                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3968                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3969                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3970                 }\r
3971             }\r
3972         }\r
3973     }\r
3974 }\r
3975 \r
3976 /* [AS] Arrow highlighting support */\r
3977 \r
3978 static int A_WIDTH = 5; /* Width of arrow body */\r
3979 \r
3980 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3981 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3982 \r
3983 static double Sqr( double x )\r
3984 {\r
3985     return x*x;\r
3986 }\r
3987 \r
3988 static int Round( double x )\r
3989 {\r
3990     return (int) (x + 0.5);\r
3991 }\r
3992 \r
3993 /* Draw an arrow between two points using current settings */\r
3994 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3995 {\r
3996     POINT arrow[7];\r
3997     double dx, dy, j, k, x, y;\r
3998 \r
3999     if( d_x == s_x ) {\r
4000         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4001 \r
4002         arrow[0].x = s_x + A_WIDTH;\r
4003         arrow[0].y = s_y;\r
4004 \r
4005         arrow[1].x = s_x + A_WIDTH;\r
4006         arrow[1].y = d_y - h;\r
4007 \r
4008         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4009         arrow[2].y = d_y - h;\r
4010 \r
4011         arrow[3].x = d_x;\r
4012         arrow[3].y = d_y;\r
4013 \r
4014         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4015         arrow[4].y = d_y - h;\r
4016 \r
4017         arrow[5].x = s_x - A_WIDTH;\r
4018         arrow[5].y = d_y - h;\r
4019 \r
4020         arrow[6].x = s_x - A_WIDTH;\r
4021         arrow[6].y = s_y;\r
4022     }\r
4023     else if( d_y == s_y ) {\r
4024         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4025 \r
4026         arrow[0].x = s_x;\r
4027         arrow[0].y = s_y + A_WIDTH;\r
4028 \r
4029         arrow[1].x = d_x - w;\r
4030         arrow[1].y = s_y + A_WIDTH;\r
4031 \r
4032         arrow[2].x = d_x - w;\r
4033         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4034 \r
4035         arrow[3].x = d_x;\r
4036         arrow[3].y = d_y;\r
4037 \r
4038         arrow[4].x = d_x - w;\r
4039         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4040 \r
4041         arrow[5].x = d_x - w;\r
4042         arrow[5].y = s_y - A_WIDTH;\r
4043 \r
4044         arrow[6].x = s_x;\r
4045         arrow[6].y = s_y - A_WIDTH;\r
4046     }\r
4047     else {\r
4048         /* [AS] Needed a lot of paper for this! :-) */\r
4049         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4050         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4051   \r
4052         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4053 \r
4054         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4055 \r
4056         x = s_x;\r
4057         y = s_y;\r
4058 \r
4059         arrow[0].x = Round(x - j);\r
4060         arrow[0].y = Round(y + j*dx);\r
4061 \r
4062         arrow[1].x = Round(x + j);\r
4063         arrow[1].y = Round(y - j*dx);\r
4064 \r
4065         if( d_x > s_x ) {\r
4066             x = (double) d_x - k;\r
4067             y = (double) d_y - k*dy;\r
4068         }\r
4069         else {\r
4070             x = (double) d_x + k;\r
4071             y = (double) d_y + k*dy;\r
4072         }\r
4073 \r
4074         arrow[2].x = Round(x + j);\r
4075         arrow[2].y = Round(y - j*dx);\r
4076 \r
4077         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4078         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4079 \r
4080         arrow[4].x = d_x;\r
4081         arrow[4].y = d_y;\r
4082 \r
4083         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4084         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4085 \r
4086         arrow[6].x = Round(x - j);\r
4087         arrow[6].y = Round(y + j*dx);\r
4088     }\r
4089 \r
4090     Polygon( hdc, arrow, 7 );\r
4091 }\r
4092 \r
4093 /* [AS] Draw an arrow between two squares */\r
4094 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4095 {\r
4096     int s_x, s_y, d_x, d_y;\r
4097     HPEN hpen;\r
4098     HPEN holdpen;\r
4099     HBRUSH hbrush;\r
4100     HBRUSH holdbrush;\r
4101     LOGBRUSH stLB;\r
4102 \r
4103     if( s_col == d_col && s_row == d_row ) {\r
4104         return;\r
4105     }\r
4106 \r
4107     /* Get source and destination points */\r
4108     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4109     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4110 \r
4111     if( d_y > s_y ) {\r
4112         d_y += squareSize / 4;\r
4113     }\r
4114     else if( d_y < s_y ) {\r
4115         d_y += 3 * squareSize / 4;\r
4116     }\r
4117     else {\r
4118         d_y += squareSize / 2;\r
4119     }\r
4120 \r
4121     if( d_x > s_x ) {\r
4122         d_x += squareSize / 4;\r
4123     }\r
4124     else if( d_x < s_x ) {\r
4125         d_x += 3 * squareSize / 4;\r
4126     }\r
4127     else {\r
4128         d_x += squareSize / 2;\r
4129     }\r
4130 \r
4131     s_x += squareSize / 2;\r
4132     s_y += squareSize / 2;\r
4133 \r
4134     /* Adjust width */\r
4135     A_WIDTH = squareSize / 14;\r
4136 \r
4137     /* Draw */\r
4138     stLB.lbStyle = BS_SOLID;\r
4139     stLB.lbColor = appData.highlightArrowColor;\r
4140     stLB.lbHatch = 0;\r
4141 \r
4142     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4143     holdpen = SelectObject( hdc, hpen );\r
4144     hbrush = CreateBrushIndirect( &stLB );\r
4145     holdbrush = SelectObject( hdc, hbrush );\r
4146 \r
4147     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4148 \r
4149     SelectObject( hdc, holdpen );\r
4150     SelectObject( hdc, holdbrush );\r
4151     DeleteObject( hpen );\r
4152     DeleteObject( hbrush );\r
4153 }\r
4154 \r
4155 BOOL HasHighlightInfo()\r
4156 {\r
4157     BOOL result = FALSE;\r
4158 \r
4159     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4160         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4161     {\r
4162         result = TRUE;\r
4163     }\r
4164 \r
4165     return result;\r
4166 }\r
4167 \r
4168 BOOL IsDrawArrowEnabled()\r
4169 {\r
4170     BOOL result = FALSE;\r
4171 \r
4172     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4173         result = TRUE;\r
4174     }\r
4175 \r
4176     return result;\r
4177 }\r
4178 \r
4179 VOID DrawArrowHighlight( HDC hdc )\r
4180 {\r
4181     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4182         DrawArrowBetweenSquares( hdc,\r
4183             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4184             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4185     }\r
4186 }\r
4187 \r
4188 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4189 {\r
4190     HRGN result = NULL;\r
4191 \r
4192     if( HasHighlightInfo() ) {\r
4193         int x1, y1, x2, y2;\r
4194         int sx, sy, dx, dy;\r
4195 \r
4196         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4197         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4198 \r
4199         sx = MIN( x1, x2 );\r
4200         sy = MIN( y1, y2 );\r
4201         dx = MAX( x1, x2 ) + squareSize;\r
4202         dy = MAX( y1, y2 ) + squareSize;\r
4203 \r
4204         result = CreateRectRgn( sx, sy, dx, dy );\r
4205     }\r
4206 \r
4207     return result;\r
4208 }\r
4209 \r
4210 /*\r
4211     Warning: this function modifies the behavior of several other functions. \r
4212     \r
4213     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4214     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4215     repaint is scattered all over the place, which is not good for features such as\r
4216     "arrow highlighting" that require a full repaint of the board.\r
4217 \r
4218     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4219     user interaction, when speed is not so important) but especially to avoid errors\r
4220     in the displayed graphics.\r
4221 \r
4222     In such patched places, I always try refer to this function so there is a single\r
4223     place to maintain knowledge.\r
4224     \r
4225     To restore the original behavior, just return FALSE unconditionally.\r
4226 */\r
4227 BOOL IsFullRepaintPreferrable()\r
4228 {\r
4229     BOOL result = FALSE;\r
4230 \r
4231     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4232         /* Arrow may appear on the board */\r
4233         result = TRUE;\r
4234     }\r
4235 \r
4236     return result;\r
4237 }\r
4238 \r
4239 /* \r
4240     This function is called by DrawPosition to know whether a full repaint must\r
4241     be forced or not.\r
4242 \r
4243     Only DrawPosition may directly call this function, which makes use of \r
4244     some state information. Other function should call DrawPosition specifying \r
4245     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4246 */\r
4247 BOOL DrawPositionNeedsFullRepaint()\r
4248 {\r
4249     BOOL result = FALSE;\r
4250 \r
4251     /* \r
4252         Probably a slightly better policy would be to trigger a full repaint\r
4253         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4254         but animation is fast enough that it's difficult to notice.\r
4255     */\r
4256     if( animInfo.piece == EmptySquare ) {\r
4257         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4258             result = TRUE;\r
4259         }\r
4260     }\r
4261 \r
4262     return result;\r
4263 }\r
4264 \r
4265 VOID\r
4266 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4267 {\r
4268   int row, column, x, y, square_color, piece_color;\r
4269   ChessSquare piece;\r
4270   HBRUSH oldBrush;\r
4271   HDC texture_hdc = NULL;\r
4272 \r
4273   /* [AS] Initialize background textures if needed */\r
4274   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4275       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4276       if( backTextureSquareSize != squareSize \r
4277        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4278           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4279           backTextureSquareSize = squareSize;\r
4280           RebuildTextureSquareInfo();\r
4281       }\r
4282 \r
4283       texture_hdc = CreateCompatibleDC( hdc );\r
4284   }\r
4285 \r
4286   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4287     for (column = 0; column < BOARD_WIDTH; column++) {\r
4288   \r
4289       SquareToPos(row, column, &x, &y);\r
4290 \r
4291       piece = board[row][column];\r
4292 \r
4293       square_color = ((column + row) % 2) == 1;\r
4294       if( gameInfo.variant == VariantXiangqi ) {\r
4295           square_color = !InPalace(row, column);\r
4296           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4297           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4298       }\r
4299       piece_color = (int) piece < (int) BlackPawn;\r
4300 \r
4301 \r
4302       /* [HGM] holdings file: light square or black */\r
4303       if(column == BOARD_LEFT-2) {\r
4304             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4305                 square_color = 1;\r
4306             else {\r
4307                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4308                 continue;\r
4309             }\r
4310       } else\r
4311       if(column == BOARD_RGHT + 1 ) {\r
4312             if( row < gameInfo.holdingsSize )\r
4313                 square_color = 1;\r
4314             else {\r
4315                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4316                 continue;\r
4317             }\r
4318       }\r
4319       if(column == BOARD_LEFT-1 ) /* left align */\r
4320             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4321       else if( column == BOARD_RGHT) /* right align */\r
4322             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4323       else\r
4324       if (appData.monoMode) {\r
4325         if (piece == EmptySquare) {\r
4326           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4327                  square_color ? WHITENESS : BLACKNESS);\r
4328         } else {\r
4329           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4330         }\r
4331       } \r
4332       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4333           /* [AS] Draw the square using a texture bitmap */\r
4334           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4335           int r = row, c = column; // [HGM] do not flip board in flipView\r
4336           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4337 \r
4338           DrawTile( x, y, \r
4339               squareSize, squareSize, \r
4340               hdc, \r
4341               texture_hdc,\r
4342               backTextureSquareInfo[r][c].mode,\r
4343               backTextureSquareInfo[r][c].x,\r
4344               backTextureSquareInfo[r][c].y );\r
4345 \r
4346           SelectObject( texture_hdc, hbm );\r
4347 \r
4348           if (piece != EmptySquare) {\r
4349               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4350           }\r
4351       }\r
4352       else {\r
4353         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4354 \r
4355         oldBrush = SelectObject(hdc, brush );\r
4356         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4357         SelectObject(hdc, oldBrush);\r
4358         if (piece != EmptySquare)\r
4359           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4360       }\r
4361     }\r
4362   }\r
4363 \r
4364   if( texture_hdc != NULL ) {\r
4365     DeleteDC( texture_hdc );\r
4366   }\r
4367 }\r
4368 \r
4369 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4370 void fputDW(FILE *f, int x)\r
4371 {\r
4372         fputc(x     & 255, f);\r
4373         fputc(x>>8  & 255, f);\r
4374         fputc(x>>16 & 255, f);\r
4375         fputc(x>>24 & 255, f);\r
4376 }\r
4377 \r
4378 #define MAX_CLIPS 200   /* more than enough */\r
4379 \r
4380 VOID\r
4381 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4382 {\r
4383 //  HBITMAP bufferBitmap;\r
4384   BITMAP bi;\r
4385 //  RECT Rect;\r
4386   HDC tmphdc;\r
4387   HBITMAP hbm;\r
4388   int w = 100, h = 50;\r
4389 \r
4390   if(logo == NULL) return;\r
4391 //  GetClientRect(hwndMain, &Rect);\r
4392 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4393 //                                      Rect.bottom-Rect.top+1);\r
4394   tmphdc = CreateCompatibleDC(hdc);\r
4395   hbm = SelectObject(tmphdc, logo);\r
4396   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4397             w = bi.bmWidth;\r
4398             h = bi.bmHeight;\r
4399   }\r
4400   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4401                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4402   SelectObject(tmphdc, hbm);\r
4403   DeleteDC(tmphdc);\r
4404 }\r
4405 \r
4406 VOID\r
4407 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4408 {\r
4409   static Board lastReq, lastDrawn;\r
4410   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4411   static int lastDrawnFlipView = 0;\r
4412   static int lastReqValid = 0, lastDrawnValid = 0;\r
4413   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4414   HDC tmphdc;\r
4415   HDC hdcmem;\r
4416   HBITMAP bufferBitmap;\r
4417   HBITMAP oldBitmap;\r
4418   RECT Rect;\r
4419   HRGN clips[MAX_CLIPS];\r
4420   ChessSquare dragged_piece = EmptySquare;\r
4421 \r
4422   /* I'm undecided on this - this function figures out whether a full\r
4423    * repaint is necessary on its own, so there's no real reason to have the\r
4424    * caller tell it that.  I think this can safely be set to FALSE - but\r
4425    * if we trust the callers not to request full repaints unnessesarily, then\r
4426    * we could skip some clipping work.  In other words, only request a full\r
4427    * redraw when the majority of pieces have changed positions (ie. flip, \r
4428    * gamestart and similar)  --Hawk\r
4429    */\r
4430   Boolean fullrepaint = repaint;\r
4431 \r
4432   if( DrawPositionNeedsFullRepaint() ) {\r
4433       fullrepaint = TRUE;\r
4434   }\r
4435 \r
4436 #if 0\r
4437   if( fullrepaint ) {\r
4438       static int repaint_count = 0;\r
4439       char buf[128];\r
4440 \r
4441       repaint_count++;\r
4442       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4443       OutputDebugString( buf );\r
4444   }\r
4445 #endif\r
4446 \r
4447   if (board == NULL) {\r
4448     if (!lastReqValid) {\r
4449       return;\r
4450     }\r
4451     board = lastReq;\r
4452   } else {\r
4453     CopyBoard(lastReq, board);\r
4454     lastReqValid = 1;\r
4455   }\r
4456 \r
4457   if (doingSizing) {\r
4458     return;\r
4459   }\r
4460 \r
4461   if (IsIconic(hwndMain)) {\r
4462     return;\r
4463   }\r
4464 \r
4465   if (hdc == NULL) {\r
4466     hdc = GetDC(hwndMain);\r
4467     if (!appData.monoMode) {\r
4468       SelectPalette(hdc, hPal, FALSE);\r
4469       RealizePalette(hdc);\r
4470     }\r
4471     releaseDC = TRUE;\r
4472   } else {\r
4473     releaseDC = FALSE;\r
4474   }\r
4475 \r
4476 #if 0\r
4477   fprintf(debugFP, "*******************************\n"\r
4478                    "repaint = %s\n"\r
4479                    "dragInfo.from (%d,%d)\n"\r
4480                    "dragInfo.start (%d,%d)\n"\r
4481                    "dragInfo.pos (%d,%d)\n"\r
4482                    "dragInfo.lastpos (%d,%d)\n", \r
4483                     repaint ? "TRUE" : "FALSE",\r
4484                     dragInfo.from.x, dragInfo.from.y, \r
4485                     dragInfo.start.x, dragInfo.start.y,\r
4486                     dragInfo.pos.x, dragInfo.pos.y,\r
4487                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4488   fprintf(debugFP, "prev:  ");\r
4489   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4490     for (column = 0; column < BOARD_WIDTH; column++) {\r
4491       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4492     }\r
4493   }\r
4494   fprintf(debugFP, "\n");\r
4495   fprintf(debugFP, "board: ");\r
4496   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4497     for (column = 0; column < BOARD_WIDTH; column++) {\r
4498       fprintf(debugFP, "%d ", board[row][column]);\r
4499     }\r
4500   }\r
4501   fprintf(debugFP, "\n");\r
4502   fflush(debugFP);\r
4503 #endif\r
4504 \r
4505   /* Create some work-DCs */\r
4506   hdcmem = CreateCompatibleDC(hdc);\r
4507   tmphdc = CreateCompatibleDC(hdc);\r
4508 \r
4509   /* If dragging is in progress, we temporarely remove the piece */\r
4510   /* [HGM] or temporarily decrease count if stacked              */\r
4511   /*       !! Moved to before board compare !!                   */\r
4512   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4513     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4514     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4515             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4516         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4517     } else \r
4518     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4519             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4520         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4521     } else \r
4522         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4523   }\r
4524 \r
4525   /* Figure out which squares need updating by comparing the \r
4526    * newest board with the last drawn board and checking if\r
4527    * flipping has changed.\r
4528    */\r
4529   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4530     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4531       for (column = 0; column < BOARD_WIDTH; column++) {\r
4532         if (lastDrawn[row][column] != board[row][column]) {\r
4533           SquareToPos(row, column, &x, &y);\r
4534           clips[num_clips++] =\r
4535             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4536         }\r
4537       }\r
4538     }\r
4539     for (i=0; i<2; i++) {\r
4540       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4541           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4542         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4543             lastDrawnHighlight.sq[i].y >= 0) {\r
4544           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4545                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4546           clips[num_clips++] =\r
4547             CreateRectRgn(x - lineGap, y - lineGap, \r
4548                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4549         }\r
4550         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4551           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4552           clips[num_clips++] =\r
4553             CreateRectRgn(x - lineGap, y - lineGap, \r
4554                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4555         }\r
4556       }\r
4557     }\r
4558     for (i=0; i<2; i++) {\r
4559       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4560           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4561         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4562             lastDrawnPremove.sq[i].y >= 0) {\r
4563           SquareToPos(lastDrawnPremove.sq[i].y,\r
4564                       lastDrawnPremove.sq[i].x, &x, &y);\r
4565           clips[num_clips++] =\r
4566             CreateRectRgn(x - lineGap, y - lineGap, \r
4567                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4568         }\r
4569         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4570             premoveHighlightInfo.sq[i].y >= 0) {\r
4571           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4572                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4573           clips[num_clips++] =\r
4574             CreateRectRgn(x - lineGap, y - lineGap, \r
4575                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4576         }\r
4577       }\r
4578     }\r
4579   } else {\r
4580     fullrepaint = TRUE;\r
4581   }\r
4582 \r
4583   /* Create a buffer bitmap - this is the actual bitmap\r
4584    * being written to.  When all the work is done, we can\r
4585    * copy it to the real DC (the screen).  This avoids\r
4586    * the problems with flickering.\r
4587    */\r
4588   GetClientRect(hwndMain, &Rect);\r
4589   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4590                                         Rect.bottom-Rect.top+1);\r
4591   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4592   if (!appData.monoMode) {\r
4593     SelectPalette(hdcmem, hPal, FALSE);\r
4594   }\r
4595 \r
4596   /* Create clips for dragging */\r
4597   if (!fullrepaint) {\r
4598     if (dragInfo.from.x >= 0) {\r
4599       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4600       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4601     }\r
4602     if (dragInfo.start.x >= 0) {\r
4603       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4604       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4605     }\r
4606     if (dragInfo.pos.x >= 0) {\r
4607       x = dragInfo.pos.x - squareSize / 2;\r
4608       y = dragInfo.pos.y - squareSize / 2;\r
4609       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4610     }\r
4611     if (dragInfo.lastpos.x >= 0) {\r
4612       x = dragInfo.lastpos.x - squareSize / 2;\r
4613       y = dragInfo.lastpos.y - squareSize / 2;\r
4614       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4615     }\r
4616   }\r
4617 \r
4618   /* Are we animating a move?  \r
4619    * If so, \r
4620    *   - remove the piece from the board (temporarely)\r
4621    *   - calculate the clipping region\r
4622    */\r
4623   if (!fullrepaint) {\r
4624     if (animInfo.piece != EmptySquare) {\r
4625       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4626       x = boardRect.left + animInfo.lastpos.x;\r
4627       y = boardRect.top + animInfo.lastpos.y;\r
4628       x2 = boardRect.left + animInfo.pos.x;\r
4629       y2 = boardRect.top + animInfo.pos.y;\r
4630       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4631       /* Slight kludge.  The real problem is that after AnimateMove is\r
4632          done, the position on the screen does not match lastDrawn.\r
4633          This currently causes trouble only on e.p. captures in\r
4634          atomic, where the piece moves to an empty square and then\r
4635          explodes.  The old and new positions both had an empty square\r
4636          at the destination, but animation has drawn a piece there and\r
4637          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4638       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4639     }\r
4640   }\r
4641 \r
4642   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4643   if (num_clips == 0)\r
4644     fullrepaint = TRUE;\r
4645 \r
4646   /* Set clipping on the memory DC */\r
4647   if (!fullrepaint) {\r
4648     SelectClipRgn(hdcmem, clips[0]);\r
4649     for (x = 1; x < num_clips; x++) {\r
4650       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4651         abort();  // this should never ever happen!\r
4652     }\r
4653   }\r
4654 \r
4655   /* Do all the drawing to the memory DC */\r
4656   if(explodeInfo.radius) { // [HGM] atomic\r
4657         HBRUSH oldBrush;\r
4658         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4659         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4660         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4661         x += squareSize/2;\r
4662         y += squareSize/2;\r
4663         if(!fullrepaint) {\r
4664           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4665           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4666         }\r
4667         DrawGridOnDC(hdcmem);\r
4668         DrawHighlightsOnDC(hdcmem);\r
4669         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4670         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4671         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4672         SelectObject(hdcmem, oldBrush);\r
4673   } else {\r
4674     DrawGridOnDC(hdcmem);\r
4675     DrawHighlightsOnDC(hdcmem);\r
4676     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4677   }\r
4678   if(logoHeight) {\r
4679         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4680         if(appData.autoLogo) {\r
4681           \r
4682           switch(gameMode) { // pick logos based on game mode\r
4683             case IcsObserving:\r
4684                 whiteLogo = second.programLogo; // ICS logo\r
4685                 blackLogo = second.programLogo;\r
4686             default:\r
4687                 break;\r
4688             case IcsPlayingWhite:\r
4689                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4690                 blackLogo = second.programLogo; // ICS logo\r
4691                 break;\r
4692             case IcsPlayingBlack:\r
4693                 whiteLogo = second.programLogo; // ICS logo\r
4694                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4695                 break;\r
4696             case TwoMachinesPlay:\r
4697                 if(first.twoMachinesColor[0] == 'b') {\r
4698                     whiteLogo = second.programLogo;\r
4699                     blackLogo = first.programLogo;\r
4700                 }\r
4701                 break;\r
4702             case MachinePlaysWhite:\r
4703                 blackLogo = userLogo;\r
4704                 break;\r
4705             case MachinePlaysBlack:\r
4706                 whiteLogo = userLogo;\r
4707                 blackLogo = first.programLogo;\r
4708           }\r
4709         }\r
4710         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4711         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4712   }\r
4713 \r
4714   if( appData.highlightMoveWithArrow ) {\r
4715     DrawArrowHighlight(hdcmem);\r
4716   }\r
4717 \r
4718   DrawCoordsOnDC(hdcmem);\r
4719 \r
4720   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4721                  /* to make sure lastDrawn contains what is actually drawn */\r
4722 \r
4723   /* Put the dragged piece back into place and draw it (out of place!) */\r
4724     if (dragged_piece != EmptySquare) {\r
4725     /* [HGM] or restack */\r
4726     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4727                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4728     else\r
4729     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4730                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4731     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4732     x = dragInfo.pos.x - squareSize / 2;\r
4733     y = dragInfo.pos.y - squareSize / 2;\r
4734     DrawPieceOnDC(hdcmem, dragged_piece,\r
4735                   ((int) dragged_piece < (int) BlackPawn), \r
4736                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4737   }   \r
4738   \r
4739   /* Put the animated piece back into place and draw it */\r
4740   if (animInfo.piece != EmptySquare) {\r
4741     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4742     x = boardRect.left + animInfo.pos.x;\r
4743     y = boardRect.top + animInfo.pos.y;\r
4744     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4745                   ((int) animInfo.piece < (int) BlackPawn),\r
4746                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4747   }\r
4748 \r
4749   /* Release the bufferBitmap by selecting in the old bitmap \r
4750    * and delete the memory DC\r
4751    */\r
4752   SelectObject(hdcmem, oldBitmap);\r
4753   DeleteDC(hdcmem);\r
4754 \r
4755   /* Set clipping on the target DC */\r
4756   if (!fullrepaint) {\r
4757     SelectClipRgn(hdc, clips[0]);\r
4758     for (x = 1; x < num_clips; x++) {\r
4759       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4760         abort();   // this should never ever happen!\r
4761     } \r
4762   }\r
4763 \r
4764   /* Copy the new bitmap onto the screen in one go.\r
4765    * This way we avoid any flickering\r
4766    */\r
4767   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4768   BitBlt(hdc, boardRect.left, boardRect.top,\r
4769          boardRect.right - boardRect.left,\r
4770          boardRect.bottom - boardRect.top,\r
4771          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4772   if(saveDiagFlag) { \r
4773     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4774     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4775 \r
4776     GetObject(bufferBitmap, sizeof(b), &b);\r
4777     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4778         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4779         bih.biWidth = b.bmWidth;\r
4780         bih.biHeight = b.bmHeight;\r
4781         bih.biPlanes = 1;\r
4782         bih.biBitCount = b.bmBitsPixel;\r
4783         bih.biCompression = 0;\r
4784         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4785         bih.biXPelsPerMeter = 0;\r
4786         bih.biYPelsPerMeter = 0;\r
4787         bih.biClrUsed = 0;\r
4788         bih.biClrImportant = 0;\r
4789 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4790 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4791         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4792 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4793 \r
4794 #if 1\r
4795         wb = b.bmWidthBytes;\r
4796         // count colors\r
4797         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4798                 int k = ((int*) pData)[i];\r
4799                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4800                 if(j >= 16) break;\r
4801                 color[j] = k;\r
4802                 if(j >= nrColors) nrColors = j+1;\r
4803         }\r
4804         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4805                 INT p = 0;\r
4806                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4807                     for(w=0; w<(wb>>2); w+=2) {\r
4808                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4809                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4810                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4811                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4812                         pData[p++] = m | j<<4;\r
4813                     }\r
4814                     while(p&3) pData[p++] = 0;\r
4815                 }\r
4816                 fac = 3;\r
4817                 wb = ((wb+31)>>5)<<2;\r
4818         }\r
4819         // write BITMAPFILEHEADER\r
4820         fprintf(diagFile, "BM");\r
4821         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4822         fputDW(diagFile, 0);\r
4823         fputDW(diagFile, 0x36 + (fac?64:0));\r
4824         // write BITMAPINFOHEADER\r
4825         fputDW(diagFile, 40);\r
4826         fputDW(diagFile, b.bmWidth);\r
4827         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4828         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4829         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4830         fputDW(diagFile, 0);\r
4831         fputDW(diagFile, 0);\r
4832         fputDW(diagFile, 0);\r
4833         fputDW(diagFile, 0);\r
4834         fputDW(diagFile, 0);\r
4835         fputDW(diagFile, 0);\r
4836         // write color table\r
4837         if(fac)\r
4838         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4839         // write bitmap data\r
4840         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4841                 fputc(pData[i], diagFile);\r
4842 #endif\r
4843      }\r
4844   }\r
4845 \r
4846   SelectObject(tmphdc, oldBitmap);\r
4847 \r
4848   /* Massive cleanup */\r
4849   for (x = 0; x < num_clips; x++)\r
4850     DeleteObject(clips[x]);\r
4851 \r
4852   DeleteDC(tmphdc);\r
4853   DeleteObject(bufferBitmap);\r
4854 \r
4855   if (releaseDC) \r
4856     ReleaseDC(hwndMain, hdc);\r
4857   \r
4858   if (lastDrawnFlipView != flipView) {\r
4859     if (flipView)\r
4860       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4861     else\r
4862       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4863   }\r
4864 \r
4865 /*  CopyBoard(lastDrawn, board);*/\r
4866   lastDrawnHighlight = highlightInfo;\r
4867   lastDrawnPremove   = premoveHighlightInfo;\r
4868   lastDrawnFlipView = flipView;\r
4869   lastDrawnValid = 1;\r
4870 }\r
4871 \r
4872 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4873 int\r
4874 SaveDiagram(f)\r
4875      FILE *f;\r
4876 {\r
4877     saveDiagFlag = 1; diagFile = f;\r
4878     HDCDrawPosition(NULL, TRUE, NULL);\r
4879 \r
4880     saveDiagFlag = 0;\r
4881 \r
4882 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4883     \r
4884     fclose(f);\r
4885     return TRUE;\r
4886 }\r
4887 \r
4888 \r
4889 /*---------------------------------------------------------------------------*\\r
4890 | CLIENT PAINT PROCEDURE\r
4891 |   This is the main event-handler for the WM_PAINT message.\r
4892 |\r
4893 \*---------------------------------------------------------------------------*/\r
4894 VOID\r
4895 PaintProc(HWND hwnd)\r
4896 {\r
4897   HDC         hdc;\r
4898   PAINTSTRUCT ps;\r
4899   HFONT       oldFont;\r
4900 \r
4901   if((hdc = BeginPaint(hwnd, &ps))) {\r
4902     if (IsIconic(hwnd)) {\r
4903       DrawIcon(hdc, 2, 2, iconCurrent);\r
4904     } else {\r
4905       if (!appData.monoMode) {\r
4906         SelectPalette(hdc, hPal, FALSE);\r
4907         RealizePalette(hdc);\r
4908       }\r
4909       HDCDrawPosition(hdc, 1, NULL);\r
4910       oldFont =\r
4911         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4912       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4913                  ETO_CLIPPED|ETO_OPAQUE,\r
4914                  &messageRect, messageText, strlen(messageText), NULL);\r
4915       SelectObject(hdc, oldFont);\r
4916       DisplayBothClocks();\r
4917     }\r
4918     EndPaint(hwnd,&ps);\r
4919   }\r
4920 \r
4921   return;\r
4922 }\r
4923 \r
4924 \r
4925 /*\r
4926  * If the user selects on a border boundary, return -1; if off the board,\r
4927  *   return -2.  Otherwise map the event coordinate to the square.\r
4928  * The offset boardRect.left or boardRect.top must already have been\r
4929  *   subtracted from x.\r
4930  */\r
4931 int\r
4932 EventToSquare(int x)\r
4933 {\r
4934   if (x <= 0)\r
4935     return -2;\r
4936   if (x < lineGap)\r
4937     return -1;\r
4938   x -= lineGap;\r
4939   if ((x % (squareSize + lineGap)) >= squareSize)\r
4940     return -1;\r
4941   x /= (squareSize + lineGap);\r
4942   if (x >= BOARD_SIZE)\r
4943     return -2;\r
4944   return x;\r
4945 }\r
4946 \r
4947 typedef struct {\r
4948   char piece;\r
4949   int command;\r
4950   char* name;\r
4951 } DropEnable;\r
4952 \r
4953 DropEnable dropEnables[] = {\r
4954   { 'P', DP_Pawn, "Pawn" },\r
4955   { 'N', DP_Knight, "Knight" },\r
4956   { 'B', DP_Bishop, "Bishop" },\r
4957   { 'R', DP_Rook, "Rook" },\r
4958   { 'Q', DP_Queen, "Queen" },\r
4959 };\r
4960 \r
4961 VOID\r
4962 SetupDropMenu(HMENU hmenu)\r
4963 {\r
4964   int i, count, enable;\r
4965   char *p;\r
4966   extern char white_holding[], black_holding[];\r
4967   char item[MSG_SIZ];\r
4968 \r
4969   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4970     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4971                dropEnables[i].piece);\r
4972     count = 0;\r
4973     while (p && *p++ == dropEnables[i].piece) count++;\r
4974     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4975     enable = count > 0 || !appData.testLegality\r
4976       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4977                       && !appData.icsActive);\r
4978     ModifyMenu(hmenu, dropEnables[i].command,\r
4979                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4980                dropEnables[i].command, item);\r
4981   }\r
4982 }\r
4983 \r
4984 /* Event handler for mouse messages */\r
4985 VOID\r
4986 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4987 {\r
4988   int x, y;\r
4989   POINT pt;\r
4990   static int recursive = 0;\r
4991   HMENU hmenu;\r
4992 //  BOOLEAN needsRedraw = FALSE;\r
4993   BOOLEAN saveAnimate;\r
4994   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4995   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4996   ChessMove moveType;\r
4997 \r
4998   if (recursive) {\r
4999     if (message == WM_MBUTTONUP) {\r
5000       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5001          to the middle button: we simulate pressing the left button too!\r
5002          */\r
5003       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5004       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5005     }\r
5006     return;\r
5007   }\r
5008   recursive++;\r
5009   \r
5010   pt.x = LOWORD(lParam);\r
5011   pt.y = HIWORD(lParam);\r
5012   x = EventToSquare(pt.x - boardRect.left);\r
5013   y = EventToSquare(pt.y - boardRect.top);\r
5014   if (!flipView && y >= 0) {\r
5015     y = BOARD_HEIGHT - 1 - y;\r
5016   }\r
5017   if (flipView && x >= 0) {\r
5018     x = BOARD_WIDTH - 1 - x;\r
5019   }\r
5020 \r
5021   switch (message) {\r
5022   case WM_LBUTTONDOWN:\r
5023     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5024         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5025         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5026         if(gameInfo.holdingsWidth && \r
5027                 (WhiteOnMove(currentMove) \r
5028                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5029                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5030             // click in right holdings, for determining promotion piece\r
5031             ChessSquare p = boards[currentMove][y][x];\r
5032             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5033             if(p != EmptySquare) {\r
5034                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5035                 fromX = fromY = -1;\r
5036                 break;\r
5037             }\r
5038         }\r
5039         DrawPosition(FALSE, boards[currentMove]);\r
5040         break;\r
5041     }\r
5042     ErrorPopDown();\r
5043     sameAgain = FALSE;\r
5044     if (y == -2) {\r
5045       /* Downclick vertically off board; check if on clock */\r
5046       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5047         if (gameMode == EditPosition) {\r
5048           SetWhiteToPlayEvent();\r
5049         } else if (gameMode == IcsPlayingBlack ||\r
5050                    gameMode == MachinePlaysWhite) {\r
5051           CallFlagEvent();\r
5052         } else if (gameMode == EditGame) {\r
5053           AdjustClock(flipClock, -1);\r
5054         }\r
5055       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5056         if (gameMode == EditPosition) {\r
5057           SetBlackToPlayEvent();\r
5058         } else if (gameMode == IcsPlayingWhite ||\r
5059                    gameMode == MachinePlaysBlack) {\r
5060           CallFlagEvent();\r
5061         } else if (gameMode == EditGame) {\r
5062           AdjustClock(!flipClock, -1);\r
5063         }\r
5064       }\r
5065       if (!appData.highlightLastMove) {\r
5066         ClearHighlights();\r
5067         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5068       }\r
5069       fromX = fromY = -1;\r
5070       dragInfo.start.x = dragInfo.start.y = -1;\r
5071       dragInfo.from = dragInfo.start;\r
5072       break;\r
5073     } else if (x < 0 || y < 0\r
5074       /* [HGM] block clicks between board and holdings */\r
5075               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5076               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5077               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5078         /* EditPosition, empty square, or different color piece;\r
5079            click-click move is possible */\r
5080                                ) {\r
5081       break;\r
5082     } else if (fromX == x && fromY == y) {\r
5083       /* Downclick on same square again */\r
5084       ClearHighlights();\r
5085       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5086       sameAgain = TRUE;  \r
5087     } else if (fromX != -1 &&\r
5088                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5089                                                                         ) {\r
5090       /* Downclick on different square. */\r
5091       /* [HGM] if on holdings file, should count as new first click ! */\r
5092       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5093         toX = x;\r
5094         toY = y;\r
5095         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5096            to make sure move is legal before showing promotion popup */\r
5097         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5098         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5099                 fromX = fromY = -1; \r
5100                 ClearHighlights();\r
5101                 DrawPosition(FALSE, boards[currentMove]);\r
5102                 break; \r
5103         } else \r
5104         if(moveType != ImpossibleMove) {\r
5105           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5106           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5107             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5108               appData.alwaysPromoteToQueen)) {\r
5109                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5110                   if (!appData.highlightLastMove) {\r
5111                       ClearHighlights();\r
5112                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5113                   }\r
5114           } else\r
5115           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5116                   SetHighlights(fromX, fromY, toX, toY);\r
5117                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5118                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5119                      If promotion to Q is legal, all are legal! */\r
5120                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5121                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5122                     // kludge to temporarily execute move on display, wthout promotng yet\r
5123                     promotionChoice = TRUE;\r
5124                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5125                     boards[currentMove][toY][toX] = p;\r
5126                     DrawPosition(FALSE, boards[currentMove]);\r
5127                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5128                     boards[currentMove][toY][toX] = q;\r
5129                   } else\r
5130                   PromotionPopup(hwnd);\r
5131           } else {       /* not a promotion */\r
5132              if (appData.animate || appData.highlightLastMove) {\r
5133                  SetHighlights(fromX, fromY, toX, toY);\r
5134              } else {\r
5135                  ClearHighlights();\r
5136              }\r
5137              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5138              fromX = fromY = -1;\r
5139              if (appData.animate && !appData.highlightLastMove) {\r
5140                   ClearHighlights();\r
5141                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5142              }\r
5143           }\r
5144           break;\r
5145         }\r
5146         if (gotPremove) {\r
5147             /* [HGM] it seemed that braces were missing here */\r
5148             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5149             fromX = fromY = -1;\r
5150             break;\r
5151         }\r
5152       }\r
5153       ClearHighlights();\r
5154       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5155     }\r
5156     /* First downclick, or restart on a square with same color piece */\r
5157     if (!frozen && OKToStartUserMove(x, y)) {\r
5158       fromX = x;\r
5159       fromY = y;\r
5160       dragInfo.lastpos = pt;\r
5161       dragInfo.from.x = fromX;\r
5162       dragInfo.from.y = fromY;\r
5163       dragInfo.start = dragInfo.from;\r
5164       SetCapture(hwndMain);\r
5165     } else {\r
5166       fromX = fromY = -1;\r
5167       dragInfo.start.x = dragInfo.start.y = -1;\r
5168       dragInfo.from = dragInfo.start;\r
5169       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5170     }\r
5171     break;\r
5172 \r
5173   case WM_LBUTTONUP:\r
5174     ReleaseCapture();\r
5175     if (fromX == -1) break;\r
5176     if (x == fromX && y == fromY) {\r
5177       dragInfo.from.x = dragInfo.from.y = -1;\r
5178       /* Upclick on same square */\r
5179       if (sameAgain) {\r
5180         /* Clicked same square twice: abort click-click move */\r
5181         fromX = fromY = -1;\r
5182         gotPremove = 0;\r
5183         ClearPremoveHighlights();\r
5184       } else {\r
5185         /* First square clicked: start click-click move */\r
5186         SetHighlights(fromX, fromY, -1, -1);\r
5187       }\r
5188       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5189     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5190       /* Errant click; ignore */\r
5191       break;\r
5192     } else {\r
5193       /* Finish drag move. */\r
5194     if (appData.debugMode) {\r
5195         fprintf(debugFP, "release\n");\r
5196     }\r
5197       dragInfo.from.x = dragInfo.from.y = -1;\r
5198       toX = x;\r
5199       toY = y;\r
5200       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5201       appData.animate = appData.animate && !appData.animateDragging;\r
5202       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5203       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5204                 fromX = fromY = -1; \r
5205                 ClearHighlights();\r
5206                 DrawPosition(FALSE, boards[currentMove]);\r
5207                 appData.animate = saveAnimate;\r
5208                 break; \r
5209       } else \r
5210       if(moveType != ImpossibleMove) {\r
5211           /* [HGM] use move type to determine if move is promotion.\r
5212              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5213           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5214             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5215               appData.alwaysPromoteToQueen)) \r
5216                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5217           else \r
5218           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5219                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5220                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5221                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5222                     // kludge to temporarily execute move on display, wthout promotng yet\r
5223                     promotionChoice = TRUE;\r
5224                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5225                     boards[currentMove][toY][toX] = p;\r
5226                     DrawPosition(FALSE, boards[currentMove]);\r
5227                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5228                     boards[currentMove][toY][toX] = q;\r
5229                     appData.animate = saveAnimate;\r
5230                     break;\r
5231                   } else\r
5232                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5233           } else {\r
5234             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5235                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5236                                         moveType == WhiteCapturesEnPassant || \r
5237                                         moveType == BlackCapturesEnPassant   ) )\r
5238                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5239             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5240           }\r
5241       }\r
5242       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5243       appData.animate = saveAnimate;\r
5244       fromX = fromY = -1;\r
5245       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5246         ClearHighlights();\r
5247       }\r
5248       if (appData.animate || appData.animateDragging ||\r
5249           appData.highlightDragging || gotPremove) {\r
5250         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5251       }\r
5252     }\r
5253     dragInfo.start.x = dragInfo.start.y = -1; \r
5254     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5255     break;\r
5256 \r
5257   case WM_MOUSEMOVE:\r
5258     if ((appData.animateDragging || appData.highlightDragging)\r
5259         && (wParam & MK_LBUTTON)\r
5260         && dragInfo.from.x >= 0) \r
5261     {\r
5262       BOOL full_repaint = FALSE;\r
5263 \r
5264       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5265       if (appData.animateDragging) {\r
5266         dragInfo.pos = pt;\r
5267       }\r
5268       if (appData.highlightDragging) {\r
5269         SetHighlights(fromX, fromY, x, y);\r
5270         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5271             full_repaint = TRUE;\r
5272         }\r
5273       }\r
5274       \r
5275       DrawPosition( full_repaint, NULL);\r
5276       \r
5277       dragInfo.lastpos = dragInfo.pos;\r
5278     }\r
5279     break;\r
5280 \r
5281   case WM_MOUSEWHEEL: // [DM]\r
5282     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5283        /* Mouse Wheel is being rolled forward\r
5284         * Play moves forward\r
5285         */\r
5286        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5287                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5288        /* Mouse Wheel is being rolled backward\r
5289         * Play moves backward\r
5290         */\r
5291        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5292                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5293     }\r
5294     break;\r
5295 \r
5296   case WM_MBUTTONDOWN:\r
5297   case WM_RBUTTONDOWN:\r
5298     ErrorPopDown();\r
5299     ReleaseCapture();\r
5300     fromX = fromY = -1;\r
5301     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5302     dragInfo.start.x = dragInfo.start.y = -1;\r
5303     dragInfo.from = dragInfo.start;\r
5304     dragInfo.lastpos = dragInfo.pos;\r
5305     if (appData.highlightDragging) {\r
5306       ClearHighlights();\r
5307     }\r
5308     if(y == -2) {\r
5309       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5310       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5311           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5312       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5313           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5314       }\r
5315     }\r
5316     DrawPosition(TRUE, NULL);\r
5317 \r
5318     switch (gameMode) {\r
5319     case EditPosition:\r
5320     case IcsExamining:\r
5321       if (x < 0 || y < 0) break;\r
5322       fromX = x;\r
5323       fromY = y;\r
5324       if (message == WM_MBUTTONDOWN) {\r
5325         buttonCount = 3;  /* even if system didn't think so */\r
5326         if (wParam & MK_SHIFT) \r
5327           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5328         else\r
5329           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5330       } else { /* message == WM_RBUTTONDOWN */\r
5331 #if 0\r
5332         if (buttonCount == 3) {\r
5333           if (wParam & MK_SHIFT) \r
5334             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5335           else\r
5336             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5337         } else {\r
5338           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5339         }\r
5340 #else\r
5341         /* Just have one menu, on the right button.  Windows users don't\r
5342            think to try the middle one, and sometimes other software steals\r
5343            it, or it doesn't really exist. */\r
5344         if(gameInfo.variant != VariantShogi)\r
5345             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5346         else\r
5347             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5348 #endif\r
5349       }\r
5350       break;\r
5351     case IcsPlayingWhite:\r
5352     case IcsPlayingBlack:\r
5353     case EditGame:\r
5354     case MachinePlaysWhite:\r
5355     case MachinePlaysBlack:\r
5356       if (appData.testLegality &&\r
5357           gameInfo.variant != VariantBughouse &&\r
5358           gameInfo.variant != VariantCrazyhouse) break;\r
5359       if (x < 0 || y < 0) break;\r
5360       fromX = x;\r
5361       fromY = y;\r
5362       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5363       SetupDropMenu(hmenu);\r
5364       MenuPopup(hwnd, pt, hmenu, -1);\r
5365       break;\r
5366     default:\r
5367       break;\r
5368     }\r
5369     break;\r
5370   }\r
5371 \r
5372   recursive--;\r
5373 }\r
5374 \r
5375 /* Preprocess messages for buttons in main window */\r
5376 LRESULT CALLBACK\r
5377 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5378 {\r
5379   int id = GetWindowLong(hwnd, GWL_ID);\r
5380   int i, dir;\r
5381 \r
5382   for (i=0; i<N_BUTTONS; i++) {\r
5383     if (buttonDesc[i].id == id) break;\r
5384   }\r
5385   if (i == N_BUTTONS) return 0;\r
5386   switch (message) {\r
5387   case WM_KEYDOWN:\r
5388     switch (wParam) {\r
5389     case VK_LEFT:\r
5390     case VK_RIGHT:\r
5391       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5392       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5393       return TRUE;\r
5394     }\r
5395     break;\r
5396   case WM_CHAR:\r
5397     switch (wParam) {\r
5398     case '\r':\r
5399       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5400       return TRUE;\r
5401     default:\r
5402       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5403         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5404         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5405         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5406         SetFocus(h);\r
5407         SendMessage(h, WM_CHAR, wParam, lParam);\r
5408         return TRUE;\r
5409       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5410         PopUpMoveDialog((char)wParam);\r
5411       }\r
5412       break;\r
5413     }\r
5414     break;\r
5415   }\r
5416   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5417 }\r
5418 \r
5419 /* Process messages for Promotion dialog box */\r
5420 LRESULT CALLBACK\r
5421 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5422 {\r
5423   char promoChar;\r
5424 \r
5425   switch (message) {\r
5426   case WM_INITDIALOG: /* message: initialize dialog box */\r
5427     /* Center the dialog over the application window */\r
5428     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5429     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5430       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5431        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5432                SW_SHOW : SW_HIDE);\r
5433     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5434     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5435        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5436          PieceToChar(WhiteAngel) != '~') ||\r
5437         (PieceToChar(BlackAngel) >= 'A' &&\r
5438          PieceToChar(BlackAngel) != '~')   ) ?\r
5439                SW_SHOW : SW_HIDE);\r
5440     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5441        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5442          PieceToChar(WhiteMarshall) != '~') ||\r
5443         (PieceToChar(BlackMarshall) >= 'A' &&\r
5444          PieceToChar(BlackMarshall) != '~')   ) ?\r
5445                SW_SHOW : SW_HIDE);\r
5446     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5447     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5448        gameInfo.variant != VariantShogi ?\r
5449                SW_SHOW : SW_HIDE);\r
5450     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5451        gameInfo.variant != VariantShogi ?\r
5452                SW_SHOW : SW_HIDE);\r
5453     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5454        gameInfo.variant == VariantShogi ?\r
5455                SW_SHOW : SW_HIDE);\r
5456     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5457        gameInfo.variant == VariantShogi ?\r
5458                SW_SHOW : SW_HIDE);\r
5459     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5460        gameInfo.variant == VariantSuper ?\r
5461                SW_SHOW : SW_HIDE);\r
5462     return TRUE;\r
5463 \r
5464   case WM_COMMAND: /* message: received a command */\r
5465     switch (LOWORD(wParam)) {\r
5466     case IDCANCEL:\r
5467       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5468       ClearHighlights();\r
5469       DrawPosition(FALSE, NULL);\r
5470       return TRUE;\r
5471     case PB_King:\r
5472       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5473       break;\r
5474     case PB_Queen:\r
5475       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5476       break;\r
5477     case PB_Rook:\r
5478       promoChar = PieceToChar(BlackRook);\r
5479       break;\r
5480     case PB_Bishop:\r
5481       promoChar = PieceToChar(BlackBishop);\r
5482       break;\r
5483     case PB_Chancellor:\r
5484       promoChar = PieceToChar(BlackMarshall);\r
5485       break;\r
5486     case PB_Archbishop:\r
5487       promoChar = PieceToChar(BlackAngel);\r
5488       break;\r
5489     case PB_Knight:\r
5490       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5491       break;\r
5492     default:\r
5493       return FALSE;\r
5494     }\r
5495     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5496     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5497        only show the popup when we are already sure the move is valid or\r
5498        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5499        will figure out it is a promotion from the promoChar. */\r
5500     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5501     if (!appData.highlightLastMove) {\r
5502       ClearHighlights();\r
5503       DrawPosition(FALSE, NULL);\r
5504     }\r
5505     return TRUE;\r
5506   }\r
5507   return FALSE;\r
5508 }\r
5509 \r
5510 /* Pop up promotion dialog */\r
5511 VOID\r
5512 PromotionPopup(HWND hwnd)\r
5513 {\r
5514   FARPROC lpProc;\r
5515 \r
5516   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5517   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5518     hwnd, (DLGPROC)lpProc);\r
5519   FreeProcInstance(lpProc);\r
5520 }\r
5521 \r
5522 /* Toggle ShowThinking */\r
5523 VOID\r
5524 ToggleShowThinking()\r
5525 {\r
5526   appData.showThinking = !appData.showThinking;\r
5527   ShowThinkingEvent();\r
5528 }\r
5529 \r
5530 VOID\r
5531 LoadGameDialog(HWND hwnd, char* title)\r
5532 {\r
5533   UINT number = 0;\r
5534   FILE *f;\r
5535   char fileTitle[MSG_SIZ];\r
5536   f = OpenFileDialog(hwnd, "rb", "",\r
5537                      appData.oldSaveStyle ? "gam" : "pgn",\r
5538                      GAME_FILT,\r
5539                      title, &number, fileTitle, NULL);\r
5540   if (f != NULL) {\r
5541     cmailMsgLoaded = FALSE;\r
5542     if (number == 0) {\r
5543       int error = GameListBuild(f);\r
5544       if (error) {\r
5545         DisplayError("Cannot build game list", error);\r
5546       } else if (!ListEmpty(&gameList) &&\r
5547                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5548         GameListPopUp(f, fileTitle);\r
5549         return;\r
5550       }\r
5551       GameListDestroy();\r
5552       number = 1;\r
5553     }\r
5554     LoadGame(f, number, fileTitle, FALSE);\r
5555   }\r
5556 }\r
5557 \r
5558 VOID\r
5559 ChangedConsoleFont()\r
5560 {\r
5561   CHARFORMAT cfmt;\r
5562   CHARRANGE tmpsel, sel;\r
5563   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5564   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5565   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5566   PARAFORMAT paraf;\r
5567 \r
5568   cfmt.cbSize = sizeof(CHARFORMAT);\r
5569   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5570   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5571   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5572    * size.  This was undocumented in the version of MSVC++ that I had\r
5573    * when I wrote the code, but is apparently documented now.\r
5574    */\r
5575   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5576   cfmt.bCharSet = f->lf.lfCharSet;\r
5577   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5578   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5579   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5580   /* Why are the following seemingly needed too? */\r
5581   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5582   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5583   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5584   tmpsel.cpMin = 0;\r
5585   tmpsel.cpMax = -1; /*999999?*/\r
5586   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5587   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5588   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5589    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5590    */\r
5591   paraf.cbSize = sizeof(paraf);\r
5592   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5593   paraf.dxStartIndent = 0;\r
5594   paraf.dxOffset = WRAP_INDENT;\r
5595   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5596   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5597 }\r
5598 \r
5599 /*---------------------------------------------------------------------------*\\r
5600  *\r
5601  * Window Proc for main window\r
5602  *\r
5603 \*---------------------------------------------------------------------------*/\r
5604 \r
5605 /* Process messages for main window, etc. */\r
5606 LRESULT CALLBACK\r
5607 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5608 {\r
5609   FARPROC lpProc;\r
5610   int wmId, wmEvent;\r
5611   char *defName;\r
5612   FILE *f;\r
5613   UINT number;\r
5614   char fileTitle[MSG_SIZ];\r
5615   char buf[MSG_SIZ];\r
5616   static SnapData sd;\r
5617 \r
5618   switch (message) {\r
5619 \r
5620   case WM_PAINT: /* message: repaint portion of window */\r
5621     PaintProc(hwnd);\r
5622     break;\r
5623 \r
5624   case WM_ERASEBKGND:\r
5625     if (IsIconic(hwnd)) {\r
5626       /* Cheat; change the message */\r
5627       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5628     } else {\r
5629       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5630     }\r
5631     break;\r
5632 \r
5633   case WM_LBUTTONDOWN:\r
5634   case WM_MBUTTONDOWN:\r
5635   case WM_RBUTTONDOWN:\r
5636   case WM_LBUTTONUP:\r
5637   case WM_MBUTTONUP:\r
5638   case WM_RBUTTONUP:\r
5639   case WM_MOUSEMOVE:\r
5640   case WM_MOUSEWHEEL:\r
5641     MouseEvent(hwnd, message, wParam, lParam);\r
5642     break;\r
5643 \r
5644   JAWS_KB_NAVIGATION\r
5645 \r
5646   case WM_CHAR:\r
5647     \r
5648     JAWS_ALT_INTERCEPT\r
5649 \r
5650     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5651         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5652         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5653         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5654         SetFocus(h);\r
5655         SendMessage(h, message, wParam, lParam);\r
5656     } else if(lParam != KF_REPEAT) {\r
5657         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5658                 PopUpMoveDialog((char)wParam);\r
5659         } else if((char)wParam == 003) CopyGameToClipboard();\r
5660          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5661     }\r
5662 \r
5663     break;\r
5664 \r
5665   case WM_PALETTECHANGED:\r
5666     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5667       int nnew;\r
5668       HDC hdc = GetDC(hwndMain);\r
5669       SelectPalette(hdc, hPal, TRUE);\r
5670       nnew = RealizePalette(hdc);\r
5671       if (nnew > 0) {\r
5672         paletteChanged = TRUE;\r
5673 #if 0\r
5674         UpdateColors(hdc);\r
5675 #else\r
5676         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5677 #endif\r
5678       }\r
5679       ReleaseDC(hwnd, hdc);\r
5680     }\r
5681     break;\r
5682 \r
5683   case WM_QUERYNEWPALETTE:\r
5684     if (!appData.monoMode /*&& paletteChanged*/) {\r
5685       int nnew;\r
5686       HDC hdc = GetDC(hwndMain);\r
5687       paletteChanged = FALSE;\r
5688       SelectPalette(hdc, hPal, FALSE);\r
5689       nnew = RealizePalette(hdc);\r
5690       if (nnew > 0) {\r
5691         InvalidateRect(hwnd, &boardRect, FALSE);\r
5692       }\r
5693       ReleaseDC(hwnd, hdc);\r
5694       return TRUE;\r
5695     }\r
5696     return FALSE;\r
5697 \r
5698   case WM_COMMAND: /* message: command from application menu */\r
5699     wmId    = LOWORD(wParam);\r
5700     wmEvent = HIWORD(wParam);\r
5701 \r
5702     switch (wmId) {\r
5703     case IDM_NewGame:\r
5704       ResetGameEvent();\r
5705       AnalysisPopDown();\r
5706       SAY("new game enter a move to play against the computer with white");\r
5707       break;\r
5708 \r
5709     case IDM_NewGameFRC:\r
5710       if( NewGameFRC() == 0 ) {\r
5711         ResetGameEvent();\r
5712         AnalysisPopDown();\r
5713       }\r
5714       break;\r
5715 \r
5716     case IDM_NewVariant:\r
5717       NewVariantPopup(hwnd);\r
5718       break;\r
5719 \r
5720     case IDM_LoadGame:\r
5721       LoadGameDialog(hwnd, "Load Game from File");\r
5722       break;\r
5723 \r
5724     case IDM_LoadNextGame:\r
5725       ReloadGame(1);\r
5726       break;\r
5727 \r
5728     case IDM_LoadPrevGame:\r
5729       ReloadGame(-1);\r
5730       break;\r
5731 \r
5732     case IDM_ReloadGame:\r
5733       ReloadGame(0);\r
5734       break;\r
5735 \r
5736     case IDM_LoadPosition:\r
5737       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5738         Reset(FALSE, TRUE);\r
5739       }\r
5740       number = 1;\r
5741       f = OpenFileDialog(hwnd, "rb", "",\r
5742                          appData.oldSaveStyle ? "pos" : "fen",\r
5743                          POSITION_FILT,\r
5744                          "Load Position from File", &number, fileTitle, NULL);\r
5745       if (f != NULL) {\r
5746         LoadPosition(f, number, fileTitle);\r
5747       }\r
5748       break;\r
5749 \r
5750     case IDM_LoadNextPosition:\r
5751       ReloadPosition(1);\r
5752       break;\r
5753 \r
5754     case IDM_LoadPrevPosition:\r
5755       ReloadPosition(-1);\r
5756       break;\r
5757 \r
5758     case IDM_ReloadPosition:\r
5759       ReloadPosition(0);\r
5760       break;\r
5761 \r
5762     case IDM_SaveGame:\r
5763       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5764       f = OpenFileDialog(hwnd, "a", defName,\r
5765                          appData.oldSaveStyle ? "gam" : "pgn",\r
5766                          GAME_FILT,\r
5767                          "Save Game to File", NULL, fileTitle, NULL);\r
5768       if (f != NULL) {\r
5769         SaveGame(f, 0, "");\r
5770       }\r
5771       break;\r
5772 \r
5773     case IDM_SavePosition:\r
5774       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5775       f = OpenFileDialog(hwnd, "a", defName,\r
5776                          appData.oldSaveStyle ? "pos" : "fen",\r
5777                          POSITION_FILT,\r
5778                          "Save Position to File", NULL, fileTitle, NULL);\r
5779       if (f != NULL) {\r
5780         SavePosition(f, 0, "");\r
5781       }\r
5782       break;\r
5783 \r
5784     case IDM_SaveDiagram:\r
5785       defName = "diagram";\r
5786       f = OpenFileDialog(hwnd, "wb", defName,\r
5787                          "bmp",\r
5788                          DIAGRAM_FILT,\r
5789                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5790       if (f != NULL) {\r
5791         SaveDiagram(f);\r
5792       }\r
5793       break;\r
5794 \r
5795     case IDM_CopyGame:\r
5796       CopyGameToClipboard();\r
5797       break;\r
5798 \r
5799     case IDM_PasteGame:\r
5800       PasteGameFromClipboard();\r
5801       break;\r
5802 \r
5803     case IDM_CopyGameListToClipboard:\r
5804       CopyGameListToClipboard();\r
5805       break;\r
5806 \r
5807     /* [AS] Autodetect FEN or PGN data */\r
5808     case IDM_PasteAny:\r
5809       PasteGameOrFENFromClipboard();\r
5810       break;\r
5811 \r
5812     /* [AS] Move history */\r
5813     case IDM_ShowMoveHistory:\r
5814         if( MoveHistoryIsUp() ) {\r
5815             MoveHistoryPopDown();\r
5816         }\r
5817         else {\r
5818             MoveHistoryPopUp();\r
5819         }\r
5820         break;\r
5821 \r
5822     /* [AS] Eval graph */\r
5823     case IDM_ShowEvalGraph:\r
5824         if( EvalGraphIsUp() ) {\r
5825             EvalGraphPopDown();\r
5826         }\r
5827         else {\r
5828             EvalGraphPopUp();\r
5829             SetFocus(hwndMain);\r
5830         }\r
5831         break;\r
5832 \r
5833     /* [AS] Engine output */\r
5834     case IDM_ShowEngineOutput:\r
5835         if( EngineOutputIsUp() ) {\r
5836             EngineOutputPopDown();\r
5837         }\r
5838         else {\r
5839             EngineOutputPopUp();\r
5840         }\r
5841         break;\r
5842 \r
5843     /* [AS] User adjudication */\r
5844     case IDM_UserAdjudication_White:\r
5845         UserAdjudicationEvent( +1 );\r
5846         break;\r
5847 \r
5848     case IDM_UserAdjudication_Black:\r
5849         UserAdjudicationEvent( -1 );\r
5850         break;\r
5851 \r
5852     case IDM_UserAdjudication_Draw:\r
5853         UserAdjudicationEvent( 0 );\r
5854         break;\r
5855 \r
5856     /* [AS] Game list options dialog */\r
5857     case IDM_GameListOptions:\r
5858       GameListOptions();\r
5859       break;\r
5860 \r
5861     case IDM_CopyPosition:\r
5862       CopyFENToClipboard();\r
5863       break;\r
5864 \r
5865     case IDM_PastePosition:\r
5866       PasteFENFromClipboard();\r
5867       break;\r
5868 \r
5869     case IDM_MailMove:\r
5870       MailMoveEvent();\r
5871       break;\r
5872 \r
5873     case IDM_ReloadCMailMsg:\r
5874       Reset(TRUE, TRUE);\r
5875       ReloadCmailMsgEvent(FALSE);\r
5876       break;\r
5877 \r
5878     case IDM_Minimize:\r
5879       ShowWindow(hwnd, SW_MINIMIZE);\r
5880       break;\r
5881 \r
5882     case IDM_Exit:\r
5883       ExitEvent(0);\r
5884       break;\r
5885 \r
5886     case IDM_MachineWhite:\r
5887       MachineWhiteEvent();\r
5888       /*\r
5889        * refresh the tags dialog only if it's visible\r
5890        */\r
5891       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5892           char *tags;\r
5893           tags = PGNTags(&gameInfo);\r
5894           TagsPopUp(tags, CmailMsg());\r
5895           free(tags);\r
5896       }\r
5897       SAY("computer starts playing white");\r
5898       break;\r
5899 \r
5900     case IDM_MachineBlack:\r
5901       MachineBlackEvent();\r
5902       /*\r
5903        * refresh the tags dialog only if it's visible\r
5904        */\r
5905       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5906           char *tags;\r
5907           tags = PGNTags(&gameInfo);\r
5908           TagsPopUp(tags, CmailMsg());\r
5909           free(tags);\r
5910       }\r
5911       SAY("computer starts playing black");\r
5912       break;\r
5913 \r
5914     case IDM_TwoMachines:\r
5915       TwoMachinesEvent();\r
5916       /*\r
5917        * refresh the tags dialog only if it's visible\r
5918        */\r
5919       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5920           char *tags;\r
5921           tags = PGNTags(&gameInfo);\r
5922           TagsPopUp(tags, CmailMsg());\r
5923           free(tags);\r
5924       }\r
5925       SAY("programs start playing each other");\r
5926       break;\r
5927 \r
5928     case IDM_AnalysisMode:\r
5929       if (!first.analysisSupport) {\r
5930         sprintf(buf, "%s does not support analysis", first.tidy);\r
5931         DisplayError(buf, 0);\r
5932       } else {\r
5933         SAY("analyzing current position");\r
5934         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5935         if (appData.icsActive) {\r
5936                if (gameMode != IcsObserving) {\r
5937                        sprintf(buf, "You are not observing a game");\r
5938                        DisplayError(buf, 0);\r
5939                        /* secure check */\r
5940                        if (appData.icsEngineAnalyze) {\r
5941                                if (appData.debugMode) \r
5942                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5943                                ExitAnalyzeMode();\r
5944                                ModeHighlight();\r
5945                                break;\r
5946                        }\r
5947                        break;\r
5948                } else {\r
5949                        /* if enable, user want disable icsEngineAnalyze */\r
5950                        if (appData.icsEngineAnalyze) {\r
5951                                ExitAnalyzeMode();\r
5952                                ModeHighlight();\r
5953                                break;\r
5954                        }\r
5955                        appData.icsEngineAnalyze = TRUE;\r
5956                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5957                }\r
5958         } \r
5959         if (!appData.showThinking) ToggleShowThinking();\r
5960         AnalyzeModeEvent();\r
5961       }\r
5962       break;\r
5963 \r
5964     case IDM_AnalyzeFile:\r
5965       if (!first.analysisSupport) {\r
5966         char buf[MSG_SIZ];\r
5967         sprintf(buf, "%s does not support analysis", first.tidy);\r
5968         DisplayError(buf, 0);\r
5969       } else {\r
5970         if (!appData.showThinking) ToggleShowThinking();\r
5971         AnalyzeFileEvent();\r
5972         LoadGameDialog(hwnd, "Analyze Game from File");\r
5973         AnalysisPeriodicEvent(1);\r
5974       }\r
5975       break;\r
5976 \r
5977     case IDM_IcsClient:\r
5978       IcsClientEvent();\r
5979       break;\r
5980 \r
5981     case IDM_EditGame:\r
5982       EditGameEvent();\r
5983       SAY("edit game");\r
5984       break;\r
5985 \r
5986     case IDM_EditPosition:\r
5987       EditPositionEvent();\r
5988       SAY("to set up a position type a FEN");\r
5989       break;\r
5990 \r
5991     case IDM_Training:\r
5992       TrainingEvent();\r
5993       break;\r
5994 \r
5995     case IDM_ShowGameList:\r
5996       ShowGameListProc();\r
5997       break;\r
5998 \r
5999     case IDM_EditTags:\r
6000       EditTagsProc();\r
6001       break;\r
6002 \r
6003     case IDM_EditComment:\r
6004       if (commentDialogUp && editComment) {\r
6005         CommentPopDown();\r
6006       } else {\r
6007         EditCommentEvent();\r
6008       }\r
6009       break;\r
6010 \r
6011     case IDM_Pause:\r
6012       PauseEvent();\r
6013       break;\r
6014 \r
6015     case IDM_Accept:\r
6016       AcceptEvent();\r
6017       break;\r
6018 \r
6019     case IDM_Decline:\r
6020       DeclineEvent();\r
6021       break;\r
6022 \r
6023     case IDM_Rematch:\r
6024       RematchEvent();\r
6025       break;\r
6026 \r
6027     case IDM_CallFlag:\r
6028       CallFlagEvent();\r
6029       break;\r
6030 \r
6031     case IDM_Draw:\r
6032       DrawEvent();\r
6033       break;\r
6034 \r
6035     case IDM_Adjourn:\r
6036       AdjournEvent();\r
6037       break;\r
6038 \r
6039     case IDM_Abort:\r
6040       AbortEvent();\r
6041       break;\r
6042 \r
6043     case IDM_Resign:\r
6044       ResignEvent();\r
6045       break;\r
6046 \r
6047     case IDM_StopObserving:\r
6048       StopObservingEvent();\r
6049       break;\r
6050 \r
6051     case IDM_StopExamining:\r
6052       StopExaminingEvent();\r
6053       break;\r
6054 \r
6055     case IDM_TypeInMove:\r
6056       PopUpMoveDialog('\000');\r
6057       break;\r
6058 \r
6059     case IDM_TypeInName:\r
6060       PopUpNameDialog('\000');\r
6061       break;\r
6062 \r
6063     case IDM_Backward:\r
6064       BackwardEvent();\r
6065       SetFocus(hwndMain);\r
6066       break;\r
6067 \r
6068     JAWS_MENU_ITEMS\r
6069 \r
6070     case IDM_Forward:\r
6071       ForwardEvent();\r
6072       SetFocus(hwndMain);\r
6073       break;\r
6074 \r
6075     case IDM_ToStart:\r
6076       ToStartEvent();\r
6077       SetFocus(hwndMain);\r
6078       break;\r
6079 \r
6080     case IDM_ToEnd:\r
6081       ToEndEvent();\r
6082       SetFocus(hwndMain);\r
6083       break;\r
6084 \r
6085     case IDM_Revert:\r
6086       RevertEvent();\r
6087       break;\r
6088 \r
6089     case IDM_TruncateGame:\r
6090       TruncateGameEvent();\r
6091       break;\r
6092 \r
6093     case IDM_MoveNow:\r
6094       MoveNowEvent();\r
6095       break;\r
6096 \r
6097     case IDM_RetractMove:\r
6098       RetractMoveEvent();\r
6099       break;\r
6100 \r
6101     case IDM_FlipView:\r
6102       flipView = !flipView;\r
6103       DrawPosition(FALSE, NULL);\r
6104       break;\r
6105 \r
6106     case IDM_FlipClock:\r
6107       flipClock = !flipClock;\r
6108       DisplayBothClocks();\r
6109       DrawPosition(FALSE, NULL);\r
6110       break;\r
6111 \r
6112     case IDM_GeneralOptions:\r
6113       GeneralOptionsPopup(hwnd);\r
6114       DrawPosition(TRUE, NULL);\r
6115       break;\r
6116 \r
6117     case IDM_BoardOptions:\r
6118       BoardOptionsPopup(hwnd);\r
6119       break;\r
6120 \r
6121     case IDM_EnginePlayOptions:\r
6122       EnginePlayOptionsPopup(hwnd);\r
6123       break;\r
6124 \r
6125     case IDM_OptionsUCI:\r
6126       UciOptionsPopup(hwnd);\r
6127       break;\r
6128 \r
6129     case IDM_IcsOptions:\r
6130       IcsOptionsPopup(hwnd);\r
6131       break;\r
6132 \r
6133     case IDM_Fonts:\r
6134       FontsOptionsPopup(hwnd);\r
6135       break;\r
6136 \r
6137     case IDM_Sounds:\r
6138       SoundOptionsPopup(hwnd);\r
6139       break;\r
6140 \r
6141     case IDM_CommPort:\r
6142       CommPortOptionsPopup(hwnd);\r
6143       break;\r
6144 \r
6145     case IDM_LoadOptions:\r
6146       LoadOptionsPopup(hwnd);\r
6147       break;\r
6148 \r
6149     case IDM_SaveOptions:\r
6150       SaveOptionsPopup(hwnd);\r
6151       break;\r
6152 \r
6153     case IDM_TimeControl:\r
6154       TimeControlOptionsPopup(hwnd);\r
6155       break;\r
6156 \r
6157     case IDM_SaveSettings:\r
6158       SaveSettings(settingsFileName);\r
6159       break;\r
6160 \r
6161     case IDM_SaveSettingsOnExit:\r
6162       saveSettingsOnExit = !saveSettingsOnExit;\r
6163       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6164                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6165                                          MF_CHECKED : MF_UNCHECKED));\r
6166       break;\r
6167 \r
6168     case IDM_Hint:\r
6169       HintEvent();\r
6170       break;\r
6171 \r
6172     case IDM_Book:\r
6173       BookEvent();\r
6174       break;\r
6175 \r
6176     case IDM_AboutGame:\r
6177       AboutGameEvent();\r
6178       break;\r
6179 \r
6180     case IDM_Debug:\r
6181       appData.debugMode = !appData.debugMode;\r
6182       if (appData.debugMode) {\r
6183         char dir[MSG_SIZ];\r
6184         GetCurrentDirectory(MSG_SIZ, dir);\r
6185         SetCurrentDirectory(installDir);\r
6186         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6187         SetCurrentDirectory(dir);\r
6188         setbuf(debugFP, NULL);\r
6189       } else {\r
6190         fclose(debugFP);\r
6191         debugFP = NULL;\r
6192       }\r
6193       break;\r
6194 \r
6195     case IDM_HELPCONTENTS:\r
6196       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6197           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6198           MessageBox (GetFocus(),\r
6199                     "Unable to activate help",\r
6200                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6201       }\r
6202       break;\r
6203 \r
6204     case IDM_HELPSEARCH:\r
6205         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6206             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6207         MessageBox (GetFocus(),\r
6208                     "Unable to activate help",\r
6209                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6210       }\r
6211       break;\r
6212 \r
6213     case IDM_HELPHELP:\r
6214       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6215         MessageBox (GetFocus(),\r
6216                     "Unable to activate help",\r
6217                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6218       }\r
6219       break;\r
6220 \r
6221     case IDM_ABOUT:\r
6222       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6223       DialogBox(hInst, \r
6224         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6225         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6226       FreeProcInstance(lpProc);\r
6227       break;\r
6228 \r
6229     case IDM_DirectCommand1:\r
6230       AskQuestionEvent("Direct Command",\r
6231                        "Send to chess program:", "", "1");\r
6232       break;\r
6233     case IDM_DirectCommand2:\r
6234       AskQuestionEvent("Direct Command",\r
6235                        "Send to second chess program:", "", "2");\r
6236       break;\r
6237 \r
6238     case EP_WhitePawn:\r
6239       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6240       fromX = fromY = -1;\r
6241       break;\r
6242 \r
6243     case EP_WhiteKnight:\r
6244       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6245       fromX = fromY = -1;\r
6246       break;\r
6247 \r
6248     case EP_WhiteBishop:\r
6249       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6250       fromX = fromY = -1;\r
6251       break;\r
6252 \r
6253     case EP_WhiteRook:\r
6254       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6255       fromX = fromY = -1;\r
6256       break;\r
6257 \r
6258     case EP_WhiteQueen:\r
6259       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6260       fromX = fromY = -1;\r
6261       break;\r
6262 \r
6263     case EP_WhiteFerz:\r
6264       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6265       fromX = fromY = -1;\r
6266       break;\r
6267 \r
6268     case EP_WhiteWazir:\r
6269       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6270       fromX = fromY = -1;\r
6271       break;\r
6272 \r
6273     case EP_WhiteAlfil:\r
6274       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6275       fromX = fromY = -1;\r
6276       break;\r
6277 \r
6278     case EP_WhiteCannon:\r
6279       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6280       fromX = fromY = -1;\r
6281       break;\r
6282 \r
6283     case EP_WhiteCardinal:\r
6284       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6285       fromX = fromY = -1;\r
6286       break;\r
6287 \r
6288     case EP_WhiteMarshall:\r
6289       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6290       fromX = fromY = -1;\r
6291       break;\r
6292 \r
6293     case EP_WhiteKing:\r
6294       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6295       fromX = fromY = -1;\r
6296       break;\r
6297 \r
6298     case EP_BlackPawn:\r
6299       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6300       fromX = fromY = -1;\r
6301       break;\r
6302 \r
6303     case EP_BlackKnight:\r
6304       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6305       fromX = fromY = -1;\r
6306       break;\r
6307 \r
6308     case EP_BlackBishop:\r
6309       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6310       fromX = fromY = -1;\r
6311       break;\r
6312 \r
6313     case EP_BlackRook:\r
6314       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6315       fromX = fromY = -1;\r
6316       break;\r
6317 \r
6318     case EP_BlackQueen:\r
6319       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6320       fromX = fromY = -1;\r
6321       break;\r
6322 \r
6323     case EP_BlackFerz:\r
6324       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6325       fromX = fromY = -1;\r
6326       break;\r
6327 \r
6328     case EP_BlackWazir:\r
6329       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6330       fromX = fromY = -1;\r
6331       break;\r
6332 \r
6333     case EP_BlackAlfil:\r
6334       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6335       fromX = fromY = -1;\r
6336       break;\r
6337 \r
6338     case EP_BlackCannon:\r
6339       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6340       fromX = fromY = -1;\r
6341       break;\r
6342 \r
6343     case EP_BlackCardinal:\r
6344       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6345       fromX = fromY = -1;\r
6346       break;\r
6347 \r
6348     case EP_BlackMarshall:\r
6349       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6350       fromX = fromY = -1;\r
6351       break;\r
6352 \r
6353     case EP_BlackKing:\r
6354       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6355       fromX = fromY = -1;\r
6356       break;\r
6357 \r
6358     case EP_EmptySquare:\r
6359       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6360       fromX = fromY = -1;\r
6361       break;\r
6362 \r
6363     case EP_ClearBoard:\r
6364       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6365       fromX = fromY = -1;\r
6366       break;\r
6367 \r
6368     case EP_White:\r
6369       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6370       fromX = fromY = -1;\r
6371       break;\r
6372 \r
6373     case EP_Black:\r
6374       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6375       fromX = fromY = -1;\r
6376       break;\r
6377 \r
6378     case EP_Promote:\r
6379       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6380       fromX = fromY = -1;\r
6381       break;\r
6382 \r
6383     case EP_Demote:\r
6384       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6385       fromX = fromY = -1;\r
6386       break;\r
6387 \r
6388     case DP_Pawn:\r
6389       DropMenuEvent(WhitePawn, fromX, fromY);\r
6390       fromX = fromY = -1;\r
6391       break;\r
6392 \r
6393     case DP_Knight:\r
6394       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6395       fromX = fromY = -1;\r
6396       break;\r
6397 \r
6398     case DP_Bishop:\r
6399       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6400       fromX = fromY = -1;\r
6401       break;\r
6402 \r
6403     case DP_Rook:\r
6404       DropMenuEvent(WhiteRook, fromX, fromY);\r
6405       fromX = fromY = -1;\r
6406       break;\r
6407 \r
6408     case DP_Queen:\r
6409       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6410       fromX = fromY = -1;\r
6411       break;\r
6412 \r
6413     default:\r
6414       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6415     }\r
6416     break;\r
6417 \r
6418   case WM_TIMER:\r
6419     switch (wParam) {\r
6420     case CLOCK_TIMER_ID:\r
6421       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6422       clockTimerEvent = 0;\r
6423       DecrementClocks(); /* call into back end */\r
6424       break;\r
6425     case LOAD_GAME_TIMER_ID:\r
6426       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6427       loadGameTimerEvent = 0;\r
6428       AutoPlayGameLoop(); /* call into back end */\r
6429       break;\r
6430     case ANALYSIS_TIMER_ID:\r
6431       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6432                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6433         AnalysisPeriodicEvent(0);\r
6434       } else {\r
6435         KillTimer(hwnd, analysisTimerEvent);\r
6436         analysisTimerEvent = 0;\r
6437       }\r
6438       break;\r
6439     case DELAYED_TIMER_ID:\r
6440       KillTimer(hwnd, delayedTimerEvent);\r
6441       delayedTimerEvent = 0;\r
6442       delayedTimerCallback();\r
6443       break;\r
6444     }\r
6445     break;\r
6446 \r
6447   case WM_USER_Input:\r
6448     InputEvent(hwnd, message, wParam, lParam);\r
6449     break;\r
6450 \r
6451   /* [AS] Also move "attached" child windows */\r
6452   case WM_WINDOWPOSCHANGING:\r
6453 \r
6454     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6455         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6456 \r
6457         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6458             /* Window is moving */\r
6459             RECT rcMain;\r
6460 \r
6461 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6462             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6463             rcMain.right  = boardX + winWidth;\r
6464             rcMain.top    = boardY;\r
6465             rcMain.bottom = boardY + winHeight;\r
6466             \r
6467             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6468             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6469             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6470             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6471             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6472             boardX = lpwp->x;\r
6473             boardY = lpwp->y;\r
6474         }\r
6475     }\r
6476     break;\r
6477 \r
6478   /* [AS] Snapping */\r
6479   case WM_ENTERSIZEMOVE:\r
6480     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6481     if (hwnd == hwndMain) {\r
6482       doingSizing = TRUE;\r
6483       lastSizing = 0;\r
6484     }\r
6485     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6486     break;\r
6487 \r
6488   case WM_SIZING:\r
6489     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6490     if (hwnd == hwndMain) {\r
6491       lastSizing = wParam;\r
6492     }\r
6493     break;\r
6494 \r
6495   case WM_MOVING:\r
6496     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6497       return OnMoving( &sd, hwnd, wParam, lParam );\r
6498 \r
6499   case WM_EXITSIZEMOVE:\r
6500     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6501     if (hwnd == hwndMain) {\r
6502       RECT client;\r
6503       doingSizing = FALSE;\r
6504       InvalidateRect(hwnd, &boardRect, FALSE);\r
6505       GetClientRect(hwnd, &client);\r
6506       ResizeBoard(client.right, client.bottom, lastSizing);\r
6507       lastSizing = 0;\r
6508       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6509     }\r
6510     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6511     break;\r
6512 \r
6513   case WM_DESTROY: /* message: window being destroyed */\r
6514     PostQuitMessage(0);\r
6515     break;\r
6516 \r
6517   case WM_CLOSE:\r
6518     if (hwnd == hwndMain) {\r
6519       ExitEvent(0);\r
6520     }\r
6521     break;\r
6522 \r
6523   default:      /* Passes it on if unprocessed */\r
6524     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6525   }\r
6526   return 0;\r
6527 }\r
6528 \r
6529 /*---------------------------------------------------------------------------*\\r
6530  *\r
6531  * Misc utility routines\r
6532  *\r
6533 \*---------------------------------------------------------------------------*/\r
6534 \r
6535 /*\r
6536  * Decent random number generator, at least not as bad as Windows\r
6537  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6538  */\r
6539 unsigned int randstate;\r
6540 \r
6541 int\r
6542 myrandom(void)\r
6543 {\r
6544   randstate = randstate * 1664525 + 1013904223;\r
6545   return (int) randstate & 0x7fffffff;\r
6546 }\r
6547 \r
6548 void\r
6549 mysrandom(unsigned int seed)\r
6550 {\r
6551   randstate = seed;\r
6552 }\r
6553 \r
6554 \r
6555 /* \r
6556  * returns TRUE if user selects a different color, FALSE otherwise \r
6557  */\r
6558 \r
6559 BOOL\r
6560 ChangeColor(HWND hwnd, COLORREF *which)\r
6561 {\r
6562   static BOOL firstTime = TRUE;\r
6563   static DWORD customColors[16];\r
6564   CHOOSECOLOR cc;\r
6565   COLORREF newcolor;\r
6566   int i;\r
6567   ColorClass ccl;\r
6568 \r
6569   if (firstTime) {\r
6570     /* Make initial colors in use available as custom colors */\r
6571     /* Should we put the compiled-in defaults here instead? */\r
6572     i = 0;\r
6573     customColors[i++] = lightSquareColor & 0xffffff;\r
6574     customColors[i++] = darkSquareColor & 0xffffff;\r
6575     customColors[i++] = whitePieceColor & 0xffffff;\r
6576     customColors[i++] = blackPieceColor & 0xffffff;\r
6577     customColors[i++] = highlightSquareColor & 0xffffff;\r
6578     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6579 \r
6580     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6581       customColors[i++] = textAttribs[ccl].color;\r
6582     }\r
6583     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6584     firstTime = FALSE;\r
6585   }\r
6586 \r
6587   cc.lStructSize = sizeof(cc);\r
6588   cc.hwndOwner = hwnd;\r
6589   cc.hInstance = NULL;\r
6590   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6591   cc.lpCustColors = (LPDWORD) customColors;\r
6592   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6593 \r
6594   if (!ChooseColor(&cc)) return FALSE;\r
6595 \r
6596   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6597   if (newcolor == *which) return FALSE;\r
6598   *which = newcolor;\r
6599   return TRUE;\r
6600 \r
6601   /*\r
6602   InitDrawingColors();\r
6603   InvalidateRect(hwnd, &boardRect, FALSE);\r
6604   */\r
6605 }\r
6606 \r
6607 BOOLEAN\r
6608 MyLoadSound(MySound *ms)\r
6609 {\r
6610   BOOL ok = FALSE;\r
6611   struct stat st;\r
6612   FILE *f;\r
6613 \r
6614   if (ms->data) free(ms->data);\r
6615   ms->data = NULL;\r
6616 \r
6617   switch (ms->name[0]) {\r
6618   case NULLCHAR:\r
6619     /* Silence */\r
6620     ok = TRUE;\r
6621     break;\r
6622   case '$':\r
6623     /* System sound from Control Panel.  Don't preload here. */\r
6624     ok = TRUE;\r
6625     break;\r
6626   case '!':\r
6627     if (ms->name[1] == NULLCHAR) {\r
6628       /* "!" alone = silence */\r
6629       ok = TRUE;\r
6630     } else {\r
6631       /* Builtin wave resource.  Error if not found. */\r
6632       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6633       if (h == NULL) break;\r
6634       ms->data = (void *)LoadResource(hInst, h);\r
6635       if (h == NULL) break;\r
6636       ok = TRUE;\r
6637     }\r
6638     break;\r
6639   default:\r
6640     /* .wav file.  Error if not found. */\r
6641     f = fopen(ms->name, "rb");\r
6642     if (f == NULL) break;\r
6643     if (fstat(fileno(f), &st) < 0) break;\r
6644     ms->data = malloc(st.st_size);\r
6645     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6646     fclose(f);\r
6647     ok = TRUE;\r
6648     break;\r
6649   }\r
6650   if (!ok) {\r
6651     char buf[MSG_SIZ];\r
6652     sprintf(buf, "Error loading sound %s", ms->name);\r
6653     DisplayError(buf, GetLastError());\r
6654   }\r
6655   return ok;\r
6656 }\r
6657 \r
6658 BOOLEAN\r
6659 MyPlaySound(MySound *ms)\r
6660 {\r
6661   BOOLEAN ok = FALSE;\r
6662         if(appData.debugMode) fprintf(debugFP, "make sound %s %x %d\n", ms->name, ms, ms->name[0]);\r
6663   switch (ms->name[0]) {\r
6664   case NULLCHAR:\r
6665         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6666     /* Silence */\r
6667     ok = TRUE;\r
6668     break;\r
6669   case '$':\r
6670     /* System sound from Control Panel (deprecated feature).\r
6671        "$" alone or an unset sound name gets default beep (still in use). */\r
6672     if (ms->name[1]) {\r
6673       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6674     }\r
6675     if (!ok) ok = MessageBeep(MB_OK);\r
6676     break; \r
6677   case '!':\r
6678     /* Builtin wave resource, or "!" alone for silence */\r
6679     if (ms->name[1]) {\r
6680       if (ms->data == NULL) return FALSE;\r
6681       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6682     } else {\r
6683       ok = TRUE;\r
6684     }\r
6685     break;\r
6686   default:\r
6687     /* .wav file.  Error if not found. */\r
6688     if (ms->data == NULL) return FALSE;\r
6689     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6690     break;\r
6691   }\r
6692   /* Don't print an error: this can happen innocently if the sound driver\r
6693      is busy; for instance, if another instance of WinBoard is playing\r
6694      a sound at about the same time. */\r
6695 #if 0\r
6696   if (!ok) {\r
6697     char buf[MSG_SIZ];\r
6698     sprintf(buf, "Error playing sound %s", ms->name);\r
6699     DisplayError(buf, GetLastError());\r
6700   }\r
6701 #endif\r
6702   return ok;\r
6703 }\r
6704 \r
6705 \r
6706 LRESULT CALLBACK\r
6707 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6708 {\r
6709   BOOL ok;\r
6710   OPENFILENAME *ofn;\r
6711   static UINT *number; /* gross that this is static */\r
6712 \r
6713   switch (message) {\r
6714   case WM_INITDIALOG: /* message: initialize dialog box */\r
6715     /* Center the dialog over the application window */\r
6716     ofn = (OPENFILENAME *) lParam;\r
6717     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6718       number = (UINT *) ofn->lCustData;\r
6719       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6720     } else {\r
6721       number = NULL;\r
6722     }\r
6723     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6724     return FALSE;  /* Allow for further processing */\r
6725 \r
6726   case WM_COMMAND:\r
6727     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6728       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6729     }\r
6730     return FALSE;  /* Allow for further processing */\r
6731   }\r
6732   return FALSE;\r
6733 }\r
6734 \r
6735 UINT APIENTRY\r
6736 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6737 {\r
6738   static UINT *number;\r
6739   OPENFILENAME *ofname;\r
6740   OFNOTIFY *ofnot;\r
6741   switch (uiMsg) {\r
6742   case WM_INITDIALOG:\r
6743     ofname = (OPENFILENAME *)lParam;\r
6744     number = (UINT *)(ofname->lCustData);\r
6745     break;\r
6746   case WM_NOTIFY:\r
6747     ofnot = (OFNOTIFY *)lParam;\r
6748     if (ofnot->hdr.code == CDN_FILEOK) {\r
6749       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6750     }\r
6751     break;\r
6752   }\r
6753   return 0;\r
6754 }\r
6755 \r
6756 \r
6757 FILE *\r
6758 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6759                char *nameFilt, char *dlgTitle, UINT *number,\r
6760                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6761 {\r
6762   OPENFILENAME openFileName;\r
6763   char buf1[MSG_SIZ];\r
6764   FILE *f;\r
6765 \r
6766   if (fileName == NULL) fileName = buf1;\r
6767   if (defName == NULL) {\r
6768     strcpy(fileName, "*.");\r
6769     strcat(fileName, defExt);\r
6770   } else {\r
6771     strcpy(fileName, defName);\r
6772   }\r
6773   if (fileTitle) strcpy(fileTitle, "");\r
6774   if (number) *number = 0;\r
6775 \r
6776   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6777   openFileName.hwndOwner         = hwnd;\r
6778   openFileName.hInstance         = (HANDLE) hInst;\r
6779   openFileName.lpstrFilter       = nameFilt;\r
6780   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6781   openFileName.nMaxCustFilter    = 0L;\r
6782   openFileName.nFilterIndex      = 1L;\r
6783   openFileName.lpstrFile         = fileName;\r
6784   openFileName.nMaxFile          = MSG_SIZ;\r
6785   openFileName.lpstrFileTitle    = fileTitle;\r
6786   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6787   openFileName.lpstrInitialDir   = NULL;\r
6788   openFileName.lpstrTitle        = dlgTitle;\r
6789   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6790     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6791     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6792     | (oldDialog ? 0 : OFN_EXPLORER);\r
6793   openFileName.nFileOffset       = 0;\r
6794   openFileName.nFileExtension    = 0;\r
6795   openFileName.lpstrDefExt       = defExt;\r
6796   openFileName.lCustData         = (LONG) number;\r
6797   openFileName.lpfnHook          = oldDialog ?\r
6798     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6799   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6800 \r
6801   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6802                         GetOpenFileName(&openFileName)) {\r
6803     /* open the file */\r
6804     f = fopen(openFileName.lpstrFile, write);\r
6805     if (f == NULL) {\r
6806       MessageBox(hwnd, "File open failed", NULL,\r
6807                  MB_OK|MB_ICONEXCLAMATION);\r
6808       return NULL;\r
6809     }\r
6810   } else {\r
6811     int err = CommDlgExtendedError();\r
6812     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6813     return FALSE;\r
6814   }\r
6815   return f;\r
6816 }\r
6817 \r
6818 \r
6819 \r
6820 VOID APIENTRY\r
6821 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6822 {\r
6823   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6824 \r
6825   /*\r
6826    * Get the first pop-up menu in the menu template. This is the\r
6827    * menu that TrackPopupMenu displays.\r
6828    */\r
6829   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6830 \r
6831   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6832 \r
6833   /*\r
6834    * TrackPopup uses screen coordinates, so convert the\r
6835    * coordinates of the mouse click to screen coordinates.\r
6836    */\r
6837   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6838 \r
6839   /* Draw and track the floating pop-up menu. */\r
6840   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6841                  pt.x, pt.y, 0, hwnd, NULL);\r
6842 \r
6843   /* Destroy the menu.*/\r
6844   DestroyMenu(hmenu);\r
6845 }\r
6846    \r
6847 typedef struct {\r
6848   HWND hDlg, hText;\r
6849   int sizeX, sizeY, newSizeX, newSizeY;\r
6850   HDWP hdwp;\r
6851 } ResizeEditPlusButtonsClosure;\r
6852 \r
6853 BOOL CALLBACK\r
6854 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6855 {\r
6856   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6857   RECT rect;\r
6858   POINT pt;\r
6859 \r
6860   if (hChild == cl->hText) return TRUE;\r
6861   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6862   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6863   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6864   ScreenToClient(cl->hDlg, &pt);\r
6865   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6866     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6867   return TRUE;\r
6868 }\r
6869 \r
6870 /* Resize a dialog that has a (rich) edit field filling most of\r
6871    the top, with a row of buttons below */\r
6872 VOID\r
6873 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6874 {\r
6875   RECT rectText;\r
6876   int newTextHeight, newTextWidth;\r
6877   ResizeEditPlusButtonsClosure cl;\r
6878   \r
6879   /*if (IsIconic(hDlg)) return;*/\r
6880   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6881   \r
6882   cl.hdwp = BeginDeferWindowPos(8);\r
6883 \r
6884   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6885   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6886   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6887   if (newTextHeight < 0) {\r
6888     newSizeY += -newTextHeight;\r
6889     newTextHeight = 0;\r
6890   }\r
6891   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6892     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6893 \r
6894   cl.hDlg = hDlg;\r
6895   cl.hText = hText;\r
6896   cl.sizeX = sizeX;\r
6897   cl.sizeY = sizeY;\r
6898   cl.newSizeX = newSizeX;\r
6899   cl.newSizeY = newSizeY;\r
6900   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6901 \r
6902   EndDeferWindowPos(cl.hdwp);\r
6903 }\r
6904 \r
6905 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6906 {\r
6907     RECT    rChild, rParent;\r
6908     int     wChild, hChild, wParent, hParent;\r
6909     int     wScreen, hScreen, xNew, yNew;\r
6910     HDC     hdc;\r
6911 \r
6912     /* Get the Height and Width of the child window */\r
6913     GetWindowRect (hwndChild, &rChild);\r
6914     wChild = rChild.right - rChild.left;\r
6915     hChild = rChild.bottom - rChild.top;\r
6916 \r
6917     /* Get the Height and Width of the parent window */\r
6918     GetWindowRect (hwndParent, &rParent);\r
6919     wParent = rParent.right - rParent.left;\r
6920     hParent = rParent.bottom - rParent.top;\r
6921 \r
6922     /* Get the display limits */\r
6923     hdc = GetDC (hwndChild);\r
6924     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6925     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6926     ReleaseDC(hwndChild, hdc);\r
6927 \r
6928     /* Calculate new X position, then adjust for screen */\r
6929     xNew = rParent.left + ((wParent - wChild) /2);\r
6930     if (xNew < 0) {\r
6931         xNew = 0;\r
6932     } else if ((xNew+wChild) > wScreen) {\r
6933         xNew = wScreen - wChild;\r
6934     }\r
6935 \r
6936     /* Calculate new Y position, then adjust for screen */\r
6937     if( mode == 0 ) {\r
6938         yNew = rParent.top  + ((hParent - hChild) /2);\r
6939     }\r
6940     else {\r
6941         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6942     }\r
6943 \r
6944     if (yNew < 0) {\r
6945         yNew = 0;\r
6946     } else if ((yNew+hChild) > hScreen) {\r
6947         yNew = hScreen - hChild;\r
6948     }\r
6949 \r
6950     /* Set it, and return */\r
6951     return SetWindowPos (hwndChild, NULL,\r
6952                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6953 }\r
6954 \r
6955 /* Center one window over another */\r
6956 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6957 {\r
6958     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6959 }\r
6960 \r
6961 /*---------------------------------------------------------------------------*\\r
6962  *\r
6963  * Startup Dialog functions\r
6964  *\r
6965 \*---------------------------------------------------------------------------*/\r
6966 void\r
6967 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6968 {\r
6969   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6970 \r
6971   while (*cd != NULL) {\r
6972     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6973     cd++;\r
6974   }\r
6975 }\r
6976 \r
6977 void\r
6978 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6979 {\r
6980   char buf1[ARG_MAX];\r
6981   int len;\r
6982 \r
6983   if (str[0] == '@') {\r
6984     FILE* f = fopen(str + 1, "r");\r
6985     if (f == NULL) {\r
6986       DisplayFatalError(str + 1, errno, 2);\r
6987       return;\r
6988     }\r
6989     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6990     fclose(f);\r
6991     buf1[len] = NULLCHAR;\r
6992     str = buf1;\r
6993   }\r
6994 \r
6995   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6996 \r
6997   for (;;) {\r
6998     char buf[MSG_SIZ];\r
6999     char *end = strchr(str, '\n');\r
7000     if (end == NULL) return;\r
7001     memcpy(buf, str, end - str);\r
7002     buf[end - str] = NULLCHAR;\r
7003     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7004     str = end + 1;\r
7005   }\r
7006 }\r
7007 \r
7008 void\r
7009 SetStartupDialogEnables(HWND hDlg)\r
7010 {\r
7011   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7012     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7013     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7014   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7015     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7016   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7017     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7018   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7019     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7020   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7021     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7022     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7023     IsDlgButtonChecked(hDlg, OPT_View));\r
7024 }\r
7025 \r
7026 char *\r
7027 QuoteForFilename(char *filename)\r
7028 {\r
7029   int dquote, space;\r
7030   dquote = strchr(filename, '"') != NULL;\r
7031   space = strchr(filename, ' ') != NULL;\r
7032   if (dquote || space) {\r
7033     if (dquote) {\r
7034       return "'";\r
7035     } else {\r
7036       return "\"";\r
7037     }\r
7038   } else {\r
7039     return "";\r
7040   }\r
7041 }\r
7042 \r
7043 VOID\r
7044 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7045 {\r
7046   char buf[MSG_SIZ];\r
7047   char *q;\r
7048 \r
7049   InitComboStringsFromOption(hwndCombo, nthnames);\r
7050   q = QuoteForFilename(nthcp);\r
7051   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7052   if (*nthdir != NULLCHAR) {\r
7053     q = QuoteForFilename(nthdir);\r
7054     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7055   }\r
7056   if (*nthcp == NULLCHAR) {\r
7057     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7058   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7059     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7060     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7061   }\r
7062 }\r
7063 \r
7064 LRESULT CALLBACK\r
7065 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7066 {\r
7067   char buf[MSG_SIZ];\r
7068   HANDLE hwndCombo;\r
7069   char *p;\r
7070 \r
7071   switch (message) {\r
7072   case WM_INITDIALOG:\r
7073     /* Center the dialog */\r
7074     CenterWindow (hDlg, GetDesktopWindow());\r
7075     /* Initialize the dialog items */\r
7076     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7077                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7078                   firstChessProgramNames);\r
7079     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7080                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7081                   secondChessProgramNames);\r
7082     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7083     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7084     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7085     if (*appData.icsHelper != NULLCHAR) {\r
7086       char *q = QuoteForFilename(appData.icsHelper);\r
7087       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7088     }\r
7089     if (*appData.icsHost == NULLCHAR) {\r
7090       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7091       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7092     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7093       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7094       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7095     }\r
7096 \r
7097     if (appData.icsActive) {\r
7098       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7099     }\r
7100     else if (appData.noChessProgram) {\r
7101       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7102     }\r
7103     else {\r
7104       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7105     }\r
7106 \r
7107     SetStartupDialogEnables(hDlg);\r
7108     return TRUE;\r
7109 \r
7110   case WM_COMMAND:\r
7111     switch (LOWORD(wParam)) {\r
7112     case IDOK:\r
7113       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7114         strcpy(buf, "/fcp=");\r
7115         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7116         p = buf;\r
7117         ParseArgs(StringGet, &p);\r
7118         strcpy(buf, "/scp=");\r
7119         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7120         p = buf;\r
7121         ParseArgs(StringGet, &p);\r
7122         appData.noChessProgram = FALSE;\r
7123         appData.icsActive = FALSE;\r
7124       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7125         strcpy(buf, "/ics /icshost=");\r
7126         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7127         p = buf;\r
7128         ParseArgs(StringGet, &p);\r
7129         if (appData.zippyPlay) {\r
7130           strcpy(buf, "/fcp=");\r
7131           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7132           p = buf;\r
7133           ParseArgs(StringGet, &p);\r
7134         }\r
7135       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7136         appData.noChessProgram = TRUE;\r
7137         appData.icsActive = FALSE;\r
7138       } else {\r
7139         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7140                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7141         return TRUE;\r
7142       }\r
7143       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7144         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7145         p = buf;\r
7146         ParseArgs(StringGet, &p);\r
7147       }\r
7148       EndDialog(hDlg, TRUE);\r
7149       return TRUE;\r
7150 \r
7151     case IDCANCEL:\r
7152       ExitEvent(0);\r
7153       return TRUE;\r
7154 \r
7155     case IDM_HELPCONTENTS:\r
7156       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7157         MessageBox (GetFocus(),\r
7158                     "Unable to activate help",\r
7159                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7160       }\r
7161       break;\r
7162 \r
7163     default:\r
7164       SetStartupDialogEnables(hDlg);\r
7165       break;\r
7166     }\r
7167     break;\r
7168   }\r
7169   return FALSE;\r
7170 }\r
7171 \r
7172 /*---------------------------------------------------------------------------*\\r
7173  *\r
7174  * About box dialog functions\r
7175  *\r
7176 \*---------------------------------------------------------------------------*/\r
7177 \r
7178 /* Process messages for "About" dialog box */\r
7179 LRESULT CALLBACK\r
7180 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7181 {\r
7182   switch (message) {\r
7183   case WM_INITDIALOG: /* message: initialize dialog box */\r
7184     /* Center the dialog over the application window */\r
7185     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7186     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7187     return (TRUE);\r
7188 \r
7189   case WM_COMMAND: /* message: received a command */\r
7190     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7191         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7192       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7193       return (TRUE);\r
7194     }\r
7195     break;\r
7196   }\r
7197   return (FALSE);\r
7198 }\r
7199 \r
7200 /*---------------------------------------------------------------------------*\\r
7201  *\r
7202  * Comment Dialog functions\r
7203  *\r
7204 \*---------------------------------------------------------------------------*/\r
7205 \r
7206 LRESULT CALLBACK\r
7207 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7208 {\r
7209   static HANDLE hwndText = NULL;\r
7210   int len, newSizeX, newSizeY, flags;\r
7211   static int sizeX, sizeY;\r
7212   char *str;\r
7213   RECT rect;\r
7214   MINMAXINFO *mmi;\r
7215 \r
7216   switch (message) {\r
7217   case WM_INITDIALOG: /* message: initialize dialog box */\r
7218     /* Initialize the dialog items */\r
7219     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7220     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7221     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7222     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7223     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7224     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7225     SetWindowText(hDlg, commentTitle);\r
7226     if (editComment) {\r
7227       SetFocus(hwndText);\r
7228     } else {\r
7229       SetFocus(GetDlgItem(hDlg, IDOK));\r
7230     }\r
7231     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7232                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7233                 MAKELPARAM(FALSE, 0));\r
7234     /* Size and position the dialog */\r
7235     if (!commentDialog) {\r
7236       commentDialog = hDlg;\r
7237       flags = SWP_NOZORDER;\r
7238       GetClientRect(hDlg, &rect);\r
7239       sizeX = rect.right;\r
7240       sizeY = rect.bottom;\r
7241       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7242           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7243         WINDOWPLACEMENT wp;\r
7244         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7245         wp.length = sizeof(WINDOWPLACEMENT);\r
7246         wp.flags = 0;\r
7247         wp.showCmd = SW_SHOW;\r
7248         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7249         wp.rcNormalPosition.left = commentX;\r
7250         wp.rcNormalPosition.right = commentX + commentW;\r
7251         wp.rcNormalPosition.top = commentY;\r
7252         wp.rcNormalPosition.bottom = commentY + commentH;\r
7253         SetWindowPlacement(hDlg, &wp);\r
7254 \r
7255         GetClientRect(hDlg, &rect);\r
7256         newSizeX = rect.right;\r
7257         newSizeY = rect.bottom;\r
7258         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7259                               newSizeX, newSizeY);\r
7260         sizeX = newSizeX;\r
7261         sizeY = newSizeY;\r
7262       }\r
7263     }\r
7264     return FALSE;\r
7265 \r
7266   case WM_COMMAND: /* message: received a command */\r
7267     switch (LOWORD(wParam)) {\r
7268     case IDOK:\r
7269       if (editComment) {\r
7270         char *p, *q;\r
7271         /* Read changed options from the dialog box */\r
7272         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7273         len = GetWindowTextLength(hwndText);\r
7274         str = (char *) malloc(len + 1);\r
7275         GetWindowText(hwndText, str, len + 1);\r
7276         p = q = str;\r
7277         while (*q) {\r
7278           if (*q == '\r')\r
7279             q++;\r
7280           else\r
7281             *p++ = *q++;\r
7282         }\r
7283         *p = NULLCHAR;\r
7284         ReplaceComment(commentIndex, str);\r
7285         free(str);\r
7286       }\r
7287       CommentPopDown();\r
7288       return TRUE;\r
7289 \r
7290     case IDCANCEL:\r
7291     case OPT_CancelComment:\r
7292       CommentPopDown();\r
7293       return TRUE;\r
7294 \r
7295     case OPT_ClearComment:\r
7296       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7297       break;\r
7298 \r
7299     case OPT_EditComment:\r
7300       EditCommentEvent();\r
7301       return TRUE;\r
7302 \r
7303     default:\r
7304       break;\r
7305     }\r
7306     break;\r
7307 \r
7308   case WM_SIZE:\r
7309     newSizeX = LOWORD(lParam);\r
7310     newSizeY = HIWORD(lParam);\r
7311     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7312     sizeX = newSizeX;\r
7313     sizeY = newSizeY;\r
7314     break;\r
7315 \r
7316   case WM_GETMINMAXINFO:\r
7317     /* Prevent resizing window too small */\r
7318     mmi = (MINMAXINFO *) lParam;\r
7319     mmi->ptMinTrackSize.x = 100;\r
7320     mmi->ptMinTrackSize.y = 100;\r
7321     break;\r
7322   }\r
7323   return FALSE;\r
7324 }\r
7325 \r
7326 VOID\r
7327 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7328 {\r
7329   FARPROC lpProc;\r
7330   char *p, *q;\r
7331 \r
7332   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7333 \r
7334   if (str == NULL) str = "";\r
7335   p = (char *) malloc(2 * strlen(str) + 2);\r
7336   q = p;\r
7337   while (*str) {\r
7338     if (*str == '\n') *q++ = '\r';\r
7339     *q++ = *str++;\r
7340   }\r
7341   *q = NULLCHAR;\r
7342   if (commentText != NULL) free(commentText);\r
7343 \r
7344   commentIndex = index;\r
7345   commentTitle = title;\r
7346   commentText = p;\r
7347   editComment = edit;\r
7348 \r
7349   if (commentDialog) {\r
7350     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7351     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7352   } else {\r
7353     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7354     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7355                  hwndMain, (DLGPROC)lpProc);\r
7356     FreeProcInstance(lpProc);\r
7357   }\r
7358   commentDialogUp = TRUE;\r
7359 }\r
7360 \r
7361 \r
7362 /*---------------------------------------------------------------------------*\\r
7363  *\r
7364  * Type-in move dialog functions\r
7365  * \r
7366 \*---------------------------------------------------------------------------*/\r
7367 \r
7368 LRESULT CALLBACK\r
7369 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7370 {\r
7371   char move[MSG_SIZ];\r
7372   HWND hInput;\r
7373   ChessMove moveType;\r
7374   int fromX, fromY, toX, toY;\r
7375   char promoChar;\r
7376 \r
7377   switch (message) {\r
7378   case WM_INITDIALOG:\r
7379     move[0] = (char) lParam;\r
7380     move[1] = NULLCHAR;\r
7381     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7382     hInput = GetDlgItem(hDlg, OPT_Move);\r
7383     SetWindowText(hInput, move);\r
7384     SetFocus(hInput);\r
7385     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7386     return FALSE;\r
7387 \r
7388   case WM_COMMAND:\r
7389     switch (LOWORD(wParam)) {\r
7390     case IDOK:\r
7391       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7392       { int n; Board board;\r
7393         // [HGM] FENedit\r
7394         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7395                 EditPositionPasteFEN(move);\r
7396                 EndDialog(hDlg, TRUE);\r
7397                 return TRUE;\r
7398         }\r
7399         // [HGM] movenum: allow move number to be typed in any mode\r
7400         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7401           currentMove = 2*n-1;\r
7402           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7403           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7404           EndDialog(hDlg, TRUE);\r
7405           DrawPosition(TRUE, boards[currentMove]);\r
7406           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7407           else DisplayMessage("", "");\r
7408           return TRUE;\r
7409         }\r
7410       }\r
7411       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7412         gameMode != Training) {\r
7413         DisplayMoveError("Displayed move is not current");\r
7414       } else {\r
7415 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7416         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7417           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7418         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7419         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7420           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7421           if (gameMode != Training)\r
7422               forwardMostMove = currentMove;\r
7423           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7424         } else {\r
7425           DisplayMoveError("Could not parse move");\r
7426         }\r
7427       }\r
7428       EndDialog(hDlg, TRUE);\r
7429       return TRUE;\r
7430     case IDCANCEL:\r
7431       EndDialog(hDlg, FALSE);\r
7432       return TRUE;\r
7433     default:\r
7434       break;\r
7435     }\r
7436     break;\r
7437   }\r
7438   return FALSE;\r
7439 }\r
7440 \r
7441 VOID\r
7442 PopUpMoveDialog(char firstchar)\r
7443 {\r
7444     FARPROC lpProc;\r
7445     \r
7446     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7447         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7448         gameMode == AnalyzeMode || gameMode == EditGame || \r
7449         gameMode == EditPosition || gameMode == IcsExamining ||\r
7450         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7451         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7452                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7453                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7454         gameMode == Training) {\r
7455       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7456       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7457         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7458       FreeProcInstance(lpProc);\r
7459     }\r
7460 }\r
7461 \r
7462 /*---------------------------------------------------------------------------*\\r
7463  *\r
7464  * Type-in name dialog functions\r
7465  * \r
7466 \*---------------------------------------------------------------------------*/\r
7467 \r
7468 LRESULT CALLBACK\r
7469 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7470 {\r
7471   char move[MSG_SIZ];\r
7472   HWND hInput;\r
7473 \r
7474   switch (message) {\r
7475   case WM_INITDIALOG:\r
7476     move[0] = (char) lParam;\r
7477     move[1] = NULLCHAR;\r
7478     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7479     hInput = GetDlgItem(hDlg, OPT_Name);\r
7480     SetWindowText(hInput, move);\r
7481     SetFocus(hInput);\r
7482     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7483     return FALSE;\r
7484 \r
7485   case WM_COMMAND:\r
7486     switch (LOWORD(wParam)) {\r
7487     case IDOK:\r
7488       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7489       appData.userName = strdup(move);\r
7490       SetUserLogo();\r
7491 \r
7492       EndDialog(hDlg, TRUE);\r
7493       return TRUE;\r
7494     case IDCANCEL:\r
7495       EndDialog(hDlg, FALSE);\r
7496       return TRUE;\r
7497     default:\r
7498       break;\r
7499     }\r
7500     break;\r
7501   }\r
7502   return FALSE;\r
7503 }\r
7504 \r
7505 VOID\r
7506 PopUpNameDialog(char firstchar)\r
7507 {\r
7508     FARPROC lpProc;\r
7509     \r
7510       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7511       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7512         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7513       FreeProcInstance(lpProc);\r
7514 }\r
7515 \r
7516 /*---------------------------------------------------------------------------*\\r
7517  *\r
7518  *  Error dialogs\r
7519  * \r
7520 \*---------------------------------------------------------------------------*/\r
7521 \r
7522 /* Nonmodal error box */\r
7523 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7524                              WPARAM wParam, LPARAM lParam);\r
7525 \r
7526 VOID\r
7527 ErrorPopUp(char *title, char *content)\r
7528 {\r
7529   FARPROC lpProc;\r
7530   char *p, *q;\r
7531   BOOLEAN modal = hwndMain == NULL;\r
7532 \r
7533   p = content;\r
7534   q = errorMessage;\r
7535   while (*p) {\r
7536     if (*p == '\n') {\r
7537       if (modal) {\r
7538         *q++ = ' ';\r
7539         p++;\r
7540       } else {\r
7541         *q++ = '\r';\r
7542         *q++ = *p++;\r
7543       }\r
7544     } else {\r
7545       *q++ = *p++;\r
7546     }\r
7547   }\r
7548   *q = NULLCHAR;\r
7549   strncpy(errorTitle, title, sizeof(errorTitle));\r
7550   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7551   \r
7552   if (modal) {\r
7553     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7554   } else {\r
7555     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7556     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7557                  hwndMain, (DLGPROC)lpProc);\r
7558     FreeProcInstance(lpProc);\r
7559   }\r
7560 }\r
7561 \r
7562 VOID\r
7563 ErrorPopDown()\r
7564 {\r
7565   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7566   if (errorDialog == NULL) return;\r
7567   DestroyWindow(errorDialog);\r
7568   errorDialog = NULL;\r
7569 }\r
7570 \r
7571 LRESULT CALLBACK\r
7572 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7573 {\r
7574   HANDLE hwndText;\r
7575   RECT rChild;\r
7576 \r
7577   switch (message) {\r
7578   case WM_INITDIALOG:\r
7579     GetWindowRect(hDlg, &rChild);\r
7580 \r
7581     /*\r
7582     SetWindowPos(hDlg, NULL, rChild.left,\r
7583       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7584       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7585     */\r
7586 \r
7587     /* \r
7588         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7589         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7590         and it doesn't work when you resize the dialog.\r
7591         For now, just give it a default position.\r
7592     */\r
7593     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7594 \r
7595     errorDialog = hDlg;\r
7596     SetWindowText(hDlg, errorTitle);\r
7597     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7598     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7599     return FALSE;\r
7600 \r
7601   case WM_COMMAND:\r
7602     switch (LOWORD(wParam)) {\r
7603     case IDOK:\r
7604     case IDCANCEL:\r
7605       if (errorDialog == hDlg) errorDialog = NULL;\r
7606       DestroyWindow(hDlg);\r
7607       return TRUE;\r
7608 \r
7609     default:\r
7610       break;\r
7611     }\r
7612     break;\r
7613   }\r
7614   return FALSE;\r
7615 }\r
7616 \r
7617 #ifdef GOTHIC\r
7618 HWND gothicDialog = NULL;\r
7619 \r
7620 LRESULT CALLBACK\r
7621 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7622 {\r
7623   HANDLE hwndText;\r
7624   RECT rChild;\r
7625   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7626 \r
7627   switch (message) {\r
7628   case WM_INITDIALOG:\r
7629     GetWindowRect(hDlg, &rChild);\r
7630 \r
7631     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7632                                                              SWP_NOZORDER);\r
7633 \r
7634     /* \r
7635         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7636         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7637         and it doesn't work when you resize the dialog.\r
7638         For now, just give it a default position.\r
7639     */\r
7640     gothicDialog = hDlg;\r
7641     SetWindowText(hDlg, errorTitle);\r
7642     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7643     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7644     return FALSE;\r
7645 \r
7646   case WM_COMMAND:\r
7647     switch (LOWORD(wParam)) {\r
7648     case IDOK:\r
7649     case IDCANCEL:\r
7650       if (errorDialog == hDlg) errorDialog = NULL;\r
7651       DestroyWindow(hDlg);\r
7652       return TRUE;\r
7653 \r
7654     default:\r
7655       break;\r
7656     }\r
7657     break;\r
7658   }\r
7659   return FALSE;\r
7660 }\r
7661 \r
7662 VOID\r
7663 GothicPopUp(char *title, VariantClass variant)\r
7664 {\r
7665   FARPROC lpProc;\r
7666   static char *lastTitle;\r
7667 \r
7668   strncpy(errorTitle, title, sizeof(errorTitle));\r
7669   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7670 \r
7671   if(lastTitle != title && gothicDialog != NULL) {\r
7672     DestroyWindow(gothicDialog);\r
7673     gothicDialog = NULL;\r
7674   }\r
7675   if(variant != VariantNormal && gothicDialog == NULL) {\r
7676     title = lastTitle;\r
7677     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7678     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7679                  hwndMain, (DLGPROC)lpProc);\r
7680     FreeProcInstance(lpProc);\r
7681   }\r
7682 }\r
7683 #endif\r
7684 \r
7685 /*---------------------------------------------------------------------------*\\r
7686  *\r
7687  *  Ics Interaction console functions\r
7688  *\r
7689 \*---------------------------------------------------------------------------*/\r
7690 \r
7691 #define HISTORY_SIZE 64\r
7692 static char *history[HISTORY_SIZE];\r
7693 int histIn = 0, histP = 0;\r
7694 \r
7695 VOID\r
7696 SaveInHistory(char *cmd)\r
7697 {\r
7698   if (history[histIn] != NULL) {\r
7699     free(history[histIn]);\r
7700     history[histIn] = NULL;\r
7701   }\r
7702   if (*cmd == NULLCHAR) return;\r
7703   history[histIn] = StrSave(cmd);\r
7704   histIn = (histIn + 1) % HISTORY_SIZE;\r
7705   if (history[histIn] != NULL) {\r
7706     free(history[histIn]);\r
7707     history[histIn] = NULL;\r
7708   }\r
7709   histP = histIn;\r
7710 }\r
7711 \r
7712 char *\r
7713 PrevInHistory(char *cmd)\r
7714 {\r
7715   int newhp;\r
7716   if (histP == histIn) {\r
7717     if (history[histIn] != NULL) free(history[histIn]);\r
7718     history[histIn] = StrSave(cmd);\r
7719   }\r
7720   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7721   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7722   histP = newhp;\r
7723   return history[histP];\r
7724 }\r
7725 \r
7726 char *\r
7727 NextInHistory()\r
7728 {\r
7729   if (histP == histIn) return NULL;\r
7730   histP = (histP + 1) % HISTORY_SIZE;\r
7731   return history[histP];\r
7732 }\r
7733 \r
7734 typedef struct {\r
7735   char *item;\r
7736   char *command;\r
7737   BOOLEAN getname;\r
7738   BOOLEAN immediate;\r
7739 } IcsTextMenuEntry;\r
7740 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7741 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7742 \r
7743 void\r
7744 ParseIcsTextMenu(char *icsTextMenuString)\r
7745 {\r
7746 //  int flags = 0;\r
7747   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7748   char *p = icsTextMenuString;\r
7749   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7750     free(e->item);\r
7751     e->item = NULL;\r
7752     if (e->command != NULL) {\r
7753       free(e->command);\r
7754       e->command = NULL;\r
7755     }\r
7756     e++;\r
7757   }\r
7758   e = icsTextMenuEntry;\r
7759   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7760     if (*p == ';' || *p == '\n') {\r
7761       e->item = strdup("-");\r
7762       e->command = NULL;\r
7763       p++;\r
7764     } else if (*p == '-') {\r
7765       e->item = strdup("-");\r
7766       e->command = NULL;\r
7767       p++;\r
7768       if (*p) p++;\r
7769     } else {\r
7770       char *q, *r, *s, *t;\r
7771       char c;\r
7772       q = strchr(p, ',');\r
7773       if (q == NULL) break;\r
7774       *q = NULLCHAR;\r
7775       r = strchr(q + 1, ',');\r
7776       if (r == NULL) break;\r
7777       *r = NULLCHAR;\r
7778       s = strchr(r + 1, ',');\r
7779       if (s == NULL) break;\r
7780       *s = NULLCHAR;\r
7781       c = ';';\r
7782       t = strchr(s + 1, c);\r
7783       if (t == NULL) {\r
7784         c = '\n';\r
7785         t = strchr(s + 1, c);\r
7786       }\r
7787       if (t != NULL) *t = NULLCHAR;\r
7788       e->item = strdup(p);\r
7789       e->command = strdup(q + 1);\r
7790       e->getname = *(r + 1) != '0';\r
7791       e->immediate = *(s + 1) != '0';\r
7792       *q = ',';\r
7793       *r = ',';\r
7794       *s = ',';\r
7795       if (t == NULL) break;\r
7796       *t = c;\r
7797       p = t + 1;\r
7798     }\r
7799     e++;\r
7800   } \r
7801 }\r
7802 \r
7803 HMENU\r
7804 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7805 {\r
7806   HMENU hmenu, h;\r
7807   int i = 0;\r
7808   hmenu = LoadMenu(hInst, "TextMenu");\r
7809   h = GetSubMenu(hmenu, 0);\r
7810   while (e->item) {\r
7811     if (strcmp(e->item, "-") == 0) {\r
7812       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7813     } else {\r
7814       if (e->item[0] == '|') {\r
7815         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7816                    IDM_CommandX + i, &e->item[1]);\r
7817       } else {\r
7818         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7819       }\r
7820     }\r
7821     e++;\r
7822     i++;\r
7823   } \r
7824   return hmenu;\r
7825 }\r
7826 \r
7827 WNDPROC consoleTextWindowProc;\r
7828 \r
7829 void\r
7830 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7831 {\r
7832   char buf[MSG_SIZ], name[MSG_SIZ];\r
7833   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7834   CHARRANGE sel;\r
7835 \r
7836   if (!getname) {\r
7837     SetWindowText(hInput, command);\r
7838     if (immediate) {\r
7839       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7840     } else {\r
7841       sel.cpMin = 999999;\r
7842       sel.cpMax = 999999;\r
7843       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7844       SetFocus(hInput);\r
7845     }\r
7846     return;\r
7847   }    \r
7848   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7849   if (sel.cpMin == sel.cpMax) {\r
7850     /* Expand to surrounding word */\r
7851     TEXTRANGE tr;\r
7852     do {\r
7853       tr.chrg.cpMax = sel.cpMin;\r
7854       tr.chrg.cpMin = --sel.cpMin;\r
7855       if (sel.cpMin < 0) break;\r
7856       tr.lpstrText = name;\r
7857       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7858     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7859     sel.cpMin++;\r
7860 \r
7861     do {\r
7862       tr.chrg.cpMin = sel.cpMax;\r
7863       tr.chrg.cpMax = ++sel.cpMax;\r
7864       tr.lpstrText = name;\r
7865       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7866     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7867     sel.cpMax--;\r
7868 \r
7869     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7870       MessageBeep(MB_ICONEXCLAMATION);\r
7871       return;\r
7872     }\r
7873     tr.chrg = sel;\r
7874     tr.lpstrText = name;\r
7875     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7876   } else {\r
7877     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7878       MessageBeep(MB_ICONEXCLAMATION);\r
7879       return;\r
7880     }\r
7881     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7882   }\r
7883   if (immediate) {\r
7884     sprintf(buf, "%s %s", command, name);\r
7885     SetWindowText(hInput, buf);\r
7886     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7887   } else {\r
7888     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7889     SetWindowText(hInput, buf);\r
7890     sel.cpMin = 999999;\r
7891     sel.cpMax = 999999;\r
7892     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7893     SetFocus(hInput);\r
7894   }\r
7895 }\r
7896 \r
7897 LRESULT CALLBACK \r
7898 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7899 {\r
7900   HWND hInput;\r
7901   CHARRANGE sel;\r
7902 \r
7903   switch (message) {\r
7904   case WM_KEYDOWN:\r
7905     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7906     switch (wParam) {\r
7907     case VK_PRIOR:\r
7908       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7909       return 0;\r
7910     case VK_NEXT:\r
7911       sel.cpMin = 999999;\r
7912       sel.cpMax = 999999;\r
7913       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7914       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7915       return 0;\r
7916     }\r
7917     break;\r
7918   case WM_CHAR:\r
7919    if(wParam != '\022') {\r
7920     if (wParam == '\t') {\r
7921       if (GetKeyState(VK_SHIFT) < 0) {\r
7922         /* shifted */\r
7923         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7924         if (buttonDesc[0].hwnd) {\r
7925           SetFocus(buttonDesc[0].hwnd);\r
7926         } else {\r
7927           SetFocus(hwndMain);\r
7928         }\r
7929       } else {\r
7930         /* unshifted */\r
7931         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7932       }\r
7933     } else {\r
7934       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7935       JAWS_DELETE( SetFocus(hInput); )\r
7936       SendMessage(hInput, message, wParam, lParam);\r
7937     }\r
7938     return 0;\r
7939    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7940   case WM_RBUTTONUP:\r
7941     if (GetKeyState(VK_SHIFT) & ~1) {\r
7942       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7943         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7944     } else {\r
7945       POINT pt;\r
7946       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7947       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7948       if (sel.cpMin == sel.cpMax) {\r
7949         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7950         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7951       }\r
7952       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7953         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7954       }\r
7955       pt.x = LOWORD(lParam);\r
7956       pt.y = HIWORD(lParam);\r
7957       MenuPopup(hwnd, pt, hmenu, -1);\r
7958     }\r
7959     return 0;\r
7960   case WM_PASTE:\r
7961     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7962     SetFocus(hInput);\r
7963     return SendMessage(hInput, message, wParam, lParam);\r
7964   case WM_MBUTTONDOWN:\r
7965     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7966   case WM_RBUTTONDOWN:\r
7967     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7968       /* Move selection here if it was empty */\r
7969       POINT pt;\r
7970       pt.x = LOWORD(lParam);\r
7971       pt.y = HIWORD(lParam);\r
7972       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7973       if (sel.cpMin == sel.cpMax) {\r
7974         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7975         sel.cpMax = sel.cpMin;\r
7976         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7977       }\r
7978       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7979     }\r
7980     return 0;\r
7981   case WM_COMMAND:\r
7982     switch (LOWORD(wParam)) {\r
7983     case IDM_QuickPaste:\r
7984       {\r
7985         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7986         if (sel.cpMin == sel.cpMax) {\r
7987           MessageBeep(MB_ICONEXCLAMATION);\r
7988           return 0;\r
7989         }\r
7990         SendMessage(hwnd, WM_COPY, 0, 0);\r
7991         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7992         SendMessage(hInput, WM_PASTE, 0, 0);\r
7993         SetFocus(hInput);\r
7994         return 0;\r
7995       }\r
7996     case IDM_Cut:\r
7997       SendMessage(hwnd, WM_CUT, 0, 0);\r
7998       return 0;\r
7999     case IDM_Paste:\r
8000       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8001       return 0;\r
8002     case IDM_Copy:\r
8003       SendMessage(hwnd, WM_COPY, 0, 0);\r
8004       return 0;\r
8005     default:\r
8006       {\r
8007         int i = LOWORD(wParam) - IDM_CommandX;\r
8008         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8009             icsTextMenuEntry[i].command != NULL) {\r
8010           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8011                    icsTextMenuEntry[i].getname,\r
8012                    icsTextMenuEntry[i].immediate);\r
8013           return 0;\r
8014         }\r
8015       }\r
8016       break;\r
8017     }\r
8018     break;\r
8019   }\r
8020   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8021 }\r
8022 \r
8023 WNDPROC consoleInputWindowProc;\r
8024 \r
8025 LRESULT CALLBACK\r
8026 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8027 {\r
8028   char buf[MSG_SIZ];\r
8029   char *p;\r
8030   static BOOL sendNextChar = FALSE;\r
8031   static BOOL quoteNextChar = FALSE;\r
8032   InputSource *is = consoleInputSource;\r
8033   CHARFORMAT cf;\r
8034   CHARRANGE sel;\r
8035 \r
8036   switch (message) {\r
8037   case WM_CHAR:\r
8038     if (!appData.localLineEditing || sendNextChar) {\r
8039       is->buf[0] = (CHAR) wParam;\r
8040       is->count = 1;\r
8041       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8042       sendNextChar = FALSE;\r
8043       return 0;\r
8044     }\r
8045     if (quoteNextChar) {\r
8046       buf[0] = (char) wParam;\r
8047       buf[1] = NULLCHAR;\r
8048       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8049       quoteNextChar = FALSE;\r
8050       return 0;\r
8051     }\r
8052     switch (wParam) {\r
8053     case '\r':   /* Enter key */\r
8054       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8055       if (consoleEcho) SaveInHistory(is->buf);\r
8056       is->buf[is->count++] = '\n';\r
8057       is->buf[is->count] = NULLCHAR;\r
8058       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8059       if (consoleEcho) {\r
8060         ConsoleOutput(is->buf, is->count, TRUE);\r
8061       } else if (appData.localLineEditing) {\r
8062         ConsoleOutput("\n", 1, TRUE);\r
8063       }\r
8064       /* fall thru */\r
8065     case '\033': /* Escape key */\r
8066       SetWindowText(hwnd, "");\r
8067       cf.cbSize = sizeof(CHARFORMAT);\r
8068       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8069       if (consoleEcho) {\r
8070         cf.crTextColor = textAttribs[ColorNormal].color;\r
8071       } else {\r
8072         cf.crTextColor = COLOR_ECHOOFF;\r
8073       }\r
8074       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8075       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8076       return 0;\r
8077     case '\t':   /* Tab key */\r
8078       if (GetKeyState(VK_SHIFT) < 0) {\r
8079         /* shifted */\r
8080         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8081       } else {\r
8082         /* unshifted */\r
8083         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8084         if (buttonDesc[0].hwnd) {\r
8085           SetFocus(buttonDesc[0].hwnd);\r
8086         } else {\r
8087           SetFocus(hwndMain);\r
8088         }\r
8089       }\r
8090       return 0;\r
8091     case '\023': /* Ctrl+S */\r
8092       sendNextChar = TRUE;\r
8093       return 0;\r
8094     case '\021': /* Ctrl+Q */\r
8095       quoteNextChar = TRUE;\r
8096       return 0;\r
8097     JAWS_REPLAY\r
8098     default:\r
8099       break;\r
8100     }\r
8101     break;\r
8102   case WM_KEYDOWN:\r
8103     switch (wParam) {\r
8104     case VK_UP:\r
8105       GetWindowText(hwnd, buf, MSG_SIZ);\r
8106       p = PrevInHistory(buf);\r
8107       if (p != NULL) {\r
8108         SetWindowText(hwnd, p);\r
8109         sel.cpMin = 999999;\r
8110         sel.cpMax = 999999;\r
8111         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8112         return 0;\r
8113       }\r
8114       break;\r
8115     case VK_DOWN:\r
8116       p = NextInHistory();\r
8117       if (p != NULL) {\r
8118         SetWindowText(hwnd, p);\r
8119         sel.cpMin = 999999;\r
8120         sel.cpMax = 999999;\r
8121         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8122         return 0;\r
8123       }\r
8124       break;\r
8125     case VK_HOME:\r
8126     case VK_END:\r
8127       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8128       /* fall thru */\r
8129     case VK_PRIOR:\r
8130     case VK_NEXT:\r
8131       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8132       return 0;\r
8133     }\r
8134     break;\r
8135   case WM_MBUTTONDOWN:\r
8136     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8137       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8138     break;\r
8139   case WM_RBUTTONUP:\r
8140     if (GetKeyState(VK_SHIFT) & ~1) {\r
8141       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8142         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8143     } else {\r
8144       POINT pt;\r
8145       HMENU hmenu;\r
8146       hmenu = LoadMenu(hInst, "InputMenu");\r
8147       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8148       if (sel.cpMin == sel.cpMax) {\r
8149         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8150         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8151       }\r
8152       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8153         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8154       }\r
8155       pt.x = LOWORD(lParam);\r
8156       pt.y = HIWORD(lParam);\r
8157       MenuPopup(hwnd, pt, hmenu, -1);\r
8158     }\r
8159     return 0;\r
8160   case WM_COMMAND:\r
8161     switch (LOWORD(wParam)) { \r
8162     case IDM_Undo:\r
8163       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8164       return 0;\r
8165     case IDM_SelectAll:\r
8166       sel.cpMin = 0;\r
8167       sel.cpMax = -1; /*999999?*/\r
8168       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8169       return 0;\r
8170     case IDM_Cut:\r
8171       SendMessage(hwnd, WM_CUT, 0, 0);\r
8172       return 0;\r
8173     case IDM_Paste:\r
8174       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8175       return 0;\r
8176     case IDM_Copy:\r
8177       SendMessage(hwnd, WM_COPY, 0, 0);\r
8178       return 0;\r
8179     }\r
8180     break;\r
8181   }\r
8182   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8183 }\r
8184 \r
8185 #define CO_MAX  100000\r
8186 #define CO_TRIM   1000\r
8187 \r
8188 LRESULT CALLBACK\r
8189 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8190 {\r
8191   static SnapData sd;\r
8192   static HWND hText, hInput /*, hFocus*/;\r
8193 //  InputSource *is = consoleInputSource;\r
8194   RECT rect;\r
8195   static int sizeX, sizeY;\r
8196   int newSizeX, newSizeY;\r
8197   MINMAXINFO *mmi;\r
8198 \r
8199   switch (message) {\r
8200   case WM_INITDIALOG: /* message: initialize dialog box */\r
8201     hwndConsole = hDlg;\r
8202     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8203     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8204     SetFocus(hInput);\r
8205     consoleTextWindowProc = (WNDPROC)\r
8206       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8207     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8208     consoleInputWindowProc = (WNDPROC)\r
8209       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8210     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8211     Colorize(ColorNormal, TRUE);\r
8212     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8213     ChangedConsoleFont();\r
8214     GetClientRect(hDlg, &rect);\r
8215     sizeX = rect.right;\r
8216     sizeY = rect.bottom;\r
8217     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8218         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8219       WINDOWPLACEMENT wp;\r
8220       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8221       wp.length = sizeof(WINDOWPLACEMENT);\r
8222       wp.flags = 0;\r
8223       wp.showCmd = SW_SHOW;\r
8224       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8225       wp.rcNormalPosition.left = wpConsole.x;\r
8226       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8227       wp.rcNormalPosition.top = wpConsole.y;\r
8228       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8229       SetWindowPlacement(hDlg, &wp);\r
8230     }\r
8231 #if 1\r
8232    // [HGM] Chessknight's change 2004-07-13\r
8233    else { /* Determine Defaults */\r
8234        WINDOWPLACEMENT wp;\r
8235        wpConsole.x = winWidth + 1;\r
8236        wpConsole.y = boardY;\r
8237        wpConsole.width = screenWidth -  winWidth;\r
8238        wpConsole.height = winHeight;\r
8239        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8240        wp.length = sizeof(WINDOWPLACEMENT);\r
8241        wp.flags = 0;\r
8242        wp.showCmd = SW_SHOW;\r
8243        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8244        wp.rcNormalPosition.left = wpConsole.x;\r
8245        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8246        wp.rcNormalPosition.top = wpConsole.y;\r
8247        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8248        SetWindowPlacement(hDlg, &wp);\r
8249     }\r
8250 #endif\r
8251     return FALSE;\r
8252 \r
8253   case WM_SETFOCUS:\r
8254     SetFocus(hInput);\r
8255     return 0;\r
8256 \r
8257   case WM_CLOSE:\r
8258     ExitEvent(0);\r
8259     /* not reached */\r
8260     break;\r
8261 \r
8262   case WM_SIZE:\r
8263     if (IsIconic(hDlg)) break;\r
8264     newSizeX = LOWORD(lParam);\r
8265     newSizeY = HIWORD(lParam);\r
8266     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8267       RECT rectText, rectInput;\r
8268       POINT pt;\r
8269       int newTextHeight, newTextWidth;\r
8270       GetWindowRect(hText, &rectText);\r
8271       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8272       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8273       if (newTextHeight < 0) {\r
8274         newSizeY += -newTextHeight;\r
8275         newTextHeight = 0;\r
8276       }\r
8277       SetWindowPos(hText, NULL, 0, 0,\r
8278         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8279       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8280       pt.x = rectInput.left;\r
8281       pt.y = rectInput.top + newSizeY - sizeY;\r
8282       ScreenToClient(hDlg, &pt);\r
8283       SetWindowPos(hInput, NULL, \r
8284         pt.x, pt.y, /* needs client coords */   \r
8285         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8286         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8287     }\r
8288     sizeX = newSizeX;\r
8289     sizeY = newSizeY;\r
8290     break;\r
8291 \r
8292   case WM_GETMINMAXINFO:\r
8293     /* Prevent resizing window too small */\r
8294     mmi = (MINMAXINFO *) lParam;\r
8295     mmi->ptMinTrackSize.x = 100;\r
8296     mmi->ptMinTrackSize.y = 100;\r
8297     break;\r
8298 \r
8299   /* [AS] Snapping */\r
8300   case WM_ENTERSIZEMOVE:\r
8301     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8302 \r
8303   case WM_SIZING:\r
8304     return OnSizing( &sd, hDlg, wParam, lParam );\r
8305 \r
8306   case WM_MOVING:\r
8307     return OnMoving( &sd, hDlg, wParam, lParam );\r
8308 \r
8309   case WM_EXITSIZEMOVE:\r
8310     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8311   }\r
8312 \r
8313   return DefWindowProc(hDlg, message, wParam, lParam);\r
8314 }\r
8315 \r
8316 \r
8317 VOID\r
8318 ConsoleCreate()\r
8319 {\r
8320   HWND hCons;\r
8321   if (hwndConsole) return;\r
8322   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8323   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8324 }\r
8325 \r
8326 \r
8327 VOID\r
8328 ConsoleOutput(char* data, int length, int forceVisible)\r
8329 {\r
8330   HWND hText;\r
8331   int trim, exlen;\r
8332   char *p, *q;\r
8333   char buf[CO_MAX+1];\r
8334   POINT pEnd;\r
8335   RECT rect;\r
8336   static int delayLF = 0;\r
8337   CHARRANGE savesel, sel;\r
8338 \r
8339   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8340   p = data;\r
8341   q = buf;\r
8342   if (delayLF) {\r
8343     *q++ = '\r';\r
8344     *q++ = '\n';\r
8345     delayLF = 0;\r
8346   }\r
8347   while (length--) {\r
8348     if (*p == '\n') {\r
8349       if (*++p) {\r
8350         *q++ = '\r';\r
8351         *q++ = '\n';\r
8352       } else {\r
8353         delayLF = 1;\r
8354       }\r
8355     } else if (*p == '\007') {\r
8356        MyPlaySound(&sounds[(int)SoundBell]);\r
8357        p++;\r
8358     } else {\r
8359       *q++ = *p++;\r
8360     }\r
8361   }\r
8362   *q = NULLCHAR;\r
8363   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8364   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8365   /* Save current selection */\r
8366   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8367   exlen = GetWindowTextLength(hText);\r
8368   /* Find out whether current end of text is visible */\r
8369   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8370   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8371   /* Trim existing text if it's too long */\r
8372   if (exlen + (q - buf) > CO_MAX) {\r
8373     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8374     sel.cpMin = 0;\r
8375     sel.cpMax = trim;\r
8376     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8377     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8378     exlen -= trim;\r
8379     savesel.cpMin -= trim;\r
8380     savesel.cpMax -= trim;\r
8381     if (exlen < 0) exlen = 0;\r
8382     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8383     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8384   }\r
8385   /* Append the new text */\r
8386   sel.cpMin = exlen;\r
8387   sel.cpMax = exlen;\r
8388   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8389   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8390   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8391   if (forceVisible || exlen == 0 ||\r
8392       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8393        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8394     /* Scroll to make new end of text visible if old end of text\r
8395        was visible or new text is an echo of user typein */\r
8396     sel.cpMin = 9999999;\r
8397     sel.cpMax = 9999999;\r
8398     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8399     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8400     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8401     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8402   }\r
8403   if (savesel.cpMax == exlen || forceVisible) {\r
8404     /* Move insert point to new end of text if it was at the old\r
8405        end of text or if the new text is an echo of user typein */\r
8406     sel.cpMin = 9999999;\r
8407     sel.cpMax = 9999999;\r
8408     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8409   } else {\r
8410     /* Restore previous selection */\r
8411     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8412   }\r
8413   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8414 }\r
8415 \r
8416 /*---------*/\r
8417 \r
8418 \r
8419 void\r
8420 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8421 {\r
8422   char buf[100];\r
8423   char *str;\r
8424   COLORREF oldFg, oldBg;\r
8425   HFONT oldFont;\r
8426   RECT rect;\r
8427 \r
8428   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8429 \r
8430   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8431   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8432   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8433 \r
8434   rect.left = x;\r
8435   rect.right = x + squareSize;\r
8436   rect.top  = y;\r
8437   rect.bottom = y + squareSize;\r
8438   str = buf;\r
8439 \r
8440   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8441                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8442              y, ETO_CLIPPED|ETO_OPAQUE,\r
8443              &rect, str, strlen(str), NULL);\r
8444 \r
8445   (void) SetTextColor(hdc, oldFg);\r
8446   (void) SetBkColor(hdc, oldBg);\r
8447   (void) SelectObject(hdc, oldFont);\r
8448 }\r
8449 \r
8450 void\r
8451 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8452               RECT *rect, char *color, char *flagFell)\r
8453 {\r
8454   char buf[100];\r
8455   char *str;\r
8456   COLORREF oldFg, oldBg;\r
8457   HFONT oldFont;\r
8458 \r
8459   if (appData.clockMode) {\r
8460     if (tinyLayout)\r
8461       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8462     else\r
8463       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8464     str = buf;\r
8465   } else {\r
8466     str = color;\r
8467   }\r
8468 \r
8469   if (highlight) {\r
8470     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8471     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8472   } else {\r
8473     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8474     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8475   }\r
8476   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8477 \r
8478   JAWS_SILENCE\r
8479 \r
8480   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8481              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8482              rect, str, strlen(str), NULL);\r
8483   if(logoHeight > 0 && appData.clockMode) {\r
8484       RECT r;\r
8485       sprintf(buf, "%s %s", buf+7, flagFell);\r
8486       r.top = rect->top + logoHeight/2;\r
8487       r.left = rect->left;\r
8488       r.right = rect->right;\r
8489       r.bottom = rect->bottom;\r
8490       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8491                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8492                  &r, str, strlen(str), NULL);\r
8493   }\r
8494   (void) SetTextColor(hdc, oldFg);\r
8495   (void) SetBkColor(hdc, oldBg);\r
8496   (void) SelectObject(hdc, oldFont);\r
8497 }\r
8498 \r
8499 \r
8500 int\r
8501 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8502            OVERLAPPED *ovl)\r
8503 {\r
8504   int ok, err;\r
8505 \r
8506   /* [AS]  */\r
8507   if( count <= 0 ) {\r
8508     if (appData.debugMode) {\r
8509       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8510     }\r
8511 \r
8512     return ERROR_INVALID_USER_BUFFER;\r
8513   }\r
8514 \r
8515   ResetEvent(ovl->hEvent);\r
8516   ovl->Offset = ovl->OffsetHigh = 0;\r
8517   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8518   if (ok) {\r
8519     err = NO_ERROR;\r
8520   } else {\r
8521     err = GetLastError();\r
8522     if (err == ERROR_IO_PENDING) {\r
8523       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8524       if (ok)\r
8525         err = NO_ERROR;\r
8526       else\r
8527         err = GetLastError();\r
8528     }\r
8529   }\r
8530   return err;\r
8531 }\r
8532 \r
8533 int\r
8534 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8535             OVERLAPPED *ovl)\r
8536 {\r
8537   int ok, err;\r
8538 \r
8539   ResetEvent(ovl->hEvent);\r
8540   ovl->Offset = ovl->OffsetHigh = 0;\r
8541   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8542   if (ok) {\r
8543     err = NO_ERROR;\r
8544   } else {\r
8545     err = GetLastError();\r
8546     if (err == ERROR_IO_PENDING) {\r
8547       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8548       if (ok)\r
8549         err = NO_ERROR;\r
8550       else\r
8551         err = GetLastError();\r
8552     }\r
8553   }\r
8554   return err;\r
8555 }\r
8556 \r
8557 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8558 void CheckForInputBufferFull( InputSource * is )\r
8559 {\r
8560     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8561         /* Look for end of line */\r
8562         char * p = is->buf;\r
8563         \r
8564         while( p < is->next && *p != '\n' ) {\r
8565             p++;\r
8566         }\r
8567 \r
8568         if( p >= is->next ) {\r
8569             if (appData.debugMode) {\r
8570                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8571             }\r
8572 \r
8573             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8574             is->count = (DWORD) -1;\r
8575             is->next = is->buf;\r
8576         }\r
8577     }\r
8578 }\r
8579 \r
8580 DWORD\r
8581 InputThread(LPVOID arg)\r
8582 {\r
8583   InputSource *is;\r
8584   OVERLAPPED ovl;\r
8585 \r
8586   is = (InputSource *) arg;\r
8587   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8588   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8589   while (is->hThread != NULL) {\r
8590     is->error = DoReadFile(is->hFile, is->next,\r
8591                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8592                            &is->count, &ovl);\r
8593     if (is->error == NO_ERROR) {\r
8594       is->next += is->count;\r
8595     } else {\r
8596       if (is->error == ERROR_BROKEN_PIPE) {\r
8597         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8598         is->count = 0;\r
8599       } else {\r
8600         is->count = (DWORD) -1;\r
8601         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8602         break; \r
8603       }\r
8604     }\r
8605 \r
8606     CheckForInputBufferFull( is );\r
8607 \r
8608     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8609 \r
8610     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8611 \r
8612     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8613   }\r
8614 \r
8615   CloseHandle(ovl.hEvent);\r
8616   CloseHandle(is->hFile);\r
8617 \r
8618   if (appData.debugMode) {\r
8619     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8620   }\r
8621 \r
8622   return 0;\r
8623 }\r
8624 \r
8625 \r
8626 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8627 DWORD\r
8628 NonOvlInputThread(LPVOID arg)\r
8629 {\r
8630   InputSource *is;\r
8631   char *p, *q;\r
8632   int i;\r
8633   char prev;\r
8634 \r
8635   is = (InputSource *) arg;\r
8636   while (is->hThread != NULL) {\r
8637     is->error = ReadFile(is->hFile, is->next,\r
8638                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8639                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8640     if (is->error == NO_ERROR) {\r
8641       /* Change CRLF to LF */\r
8642       if (is->next > is->buf) {\r
8643         p = is->next - 1;\r
8644         i = is->count + 1;\r
8645       } else {\r
8646         p = is->next;\r
8647         i = is->count;\r
8648       }\r
8649       q = p;\r
8650       prev = NULLCHAR;\r
8651       while (i > 0) {\r
8652         if (prev == '\r' && *p == '\n') {\r
8653           *(q-1) = '\n';\r
8654           is->count--;\r
8655         } else { \r
8656           *q++ = *p;\r
8657         }\r
8658         prev = *p++;\r
8659         i--;\r
8660       }\r
8661       *q = NULLCHAR;\r
8662       is->next = q;\r
8663     } else {\r
8664       if (is->error == ERROR_BROKEN_PIPE) {\r
8665         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8666         is->count = 0; \r
8667       } else {\r
8668         is->count = (DWORD) -1;\r
8669       }\r
8670     }\r
8671 \r
8672     CheckForInputBufferFull( is );\r
8673 \r
8674     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8675 \r
8676     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8677 \r
8678     if (is->count < 0) break;  /* Quit on error */\r
8679   }\r
8680   CloseHandle(is->hFile);\r
8681   return 0;\r
8682 }\r
8683 \r
8684 DWORD\r
8685 SocketInputThread(LPVOID arg)\r
8686 {\r
8687   InputSource *is;\r
8688 \r
8689   is = (InputSource *) arg;\r
8690   while (is->hThread != NULL) {\r
8691     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8692     if ((int)is->count == SOCKET_ERROR) {\r
8693       is->count = (DWORD) -1;\r
8694       is->error = WSAGetLastError();\r
8695     } else {\r
8696       is->error = NO_ERROR;\r
8697       is->next += is->count;\r
8698       if (is->count == 0 && is->second == is) {\r
8699         /* End of file on stderr; quit with no message */\r
8700         break;\r
8701       }\r
8702     }\r
8703     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8704 \r
8705     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8706 \r
8707     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8708   }\r
8709   return 0;\r
8710 }\r
8711 \r
8712 VOID\r
8713 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8714 {\r
8715   InputSource *is;\r
8716 \r
8717   is = (InputSource *) lParam;\r
8718   if (is->lineByLine) {\r
8719     /* Feed in lines one by one */\r
8720     char *p = is->buf;\r
8721     char *q = p;\r
8722     while (q < is->next) {\r
8723       if (*q++ == '\n') {\r
8724         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8725         p = q;\r
8726       }\r
8727     }\r
8728     \r
8729     /* Move any partial line to the start of the buffer */\r
8730     q = is->buf;\r
8731     while (p < is->next) {\r
8732       *q++ = *p++;\r
8733     }\r
8734     is->next = q;\r
8735 \r
8736     if (is->error != NO_ERROR || is->count == 0) {\r
8737       /* Notify backend of the error.  Note: If there was a partial\r
8738          line at the end, it is not flushed through. */\r
8739       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8740     }\r
8741   } else {\r
8742     /* Feed in the whole chunk of input at once */\r
8743     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8744     is->next = is->buf;\r
8745   }\r
8746 }\r
8747 \r
8748 /*---------------------------------------------------------------------------*\\r
8749  *\r
8750  *  Menu enables. Used when setting various modes.\r
8751  *\r
8752 \*---------------------------------------------------------------------------*/\r
8753 \r
8754 typedef struct {\r
8755   int item;\r
8756   int flags;\r
8757 } Enables;\r
8758 \r
8759 VOID\r
8760 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8761 {\r
8762   while (enab->item > 0) {\r
8763     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8764     enab++;\r
8765   }\r
8766 }\r
8767 \r
8768 Enables gnuEnables[] = {\r
8769   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8770   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8771   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8772   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8773   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8774   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8775   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8779   { -1, -1 }\r
8780 };\r
8781 \r
8782 Enables icsEnables[] = {\r
8783   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8789   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8795   { -1, -1 }\r
8796 };\r
8797 \r
8798 #ifdef ZIPPY\r
8799 Enables zippyEnables[] = {\r
8800   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8801   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8802   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8803   { -1, -1 }\r
8804 };\r
8805 #endif\r
8806 \r
8807 Enables ncpEnables[] = {\r
8808   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8809   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8810   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8811   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8812   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8813   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8814   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8816   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8817   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8823   { -1, -1 }\r
8824 };\r
8825 \r
8826 Enables trainingOnEnables[] = {\r
8827   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8835   { -1, -1 }\r
8836 };\r
8837 \r
8838 Enables trainingOffEnables[] = {\r
8839   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8840   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8841   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8842   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8843   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8844   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8845   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8847   { -1, -1 }\r
8848 };\r
8849 \r
8850 /* These modify either ncpEnables or gnuEnables */\r
8851 Enables cmailEnables[] = {\r
8852   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8853   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8854   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8855   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8856   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8857   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8858   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8859   { -1, -1 }\r
8860 };\r
8861 \r
8862 Enables machineThinkingEnables[] = {\r
8863   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8866   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8868   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8869   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8878   { -1, -1 }\r
8879 };\r
8880 \r
8881 Enables userThinkingEnables[] = {\r
8882   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8883   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8884   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8885   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8887   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8888   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8893   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8897   { -1, -1 }\r
8898 };\r
8899 \r
8900 /*---------------------------------------------------------------------------*\\r
8901  *\r
8902  *  Front-end interface functions exported by XBoard.\r
8903  *  Functions appear in same order as prototypes in frontend.h.\r
8904  * \r
8905 \*---------------------------------------------------------------------------*/\r
8906 VOID\r
8907 ModeHighlight()\r
8908 {\r
8909   static UINT prevChecked = 0;\r
8910   static int prevPausing = 0;\r
8911   UINT nowChecked;\r
8912 \r
8913   if (pausing != prevPausing) {\r
8914     prevPausing = pausing;\r
8915     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8916                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8917     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8918   }\r
8919 \r
8920   switch (gameMode) {\r
8921   case BeginningOfGame:\r
8922     if (appData.icsActive)\r
8923       nowChecked = IDM_IcsClient;\r
8924     else if (appData.noChessProgram)\r
8925       nowChecked = IDM_EditGame;\r
8926     else\r
8927       nowChecked = IDM_MachineBlack;\r
8928     break;\r
8929   case MachinePlaysBlack:\r
8930     nowChecked = IDM_MachineBlack;\r
8931     break;\r
8932   case MachinePlaysWhite:\r
8933     nowChecked = IDM_MachineWhite;\r
8934     break;\r
8935   case TwoMachinesPlay:\r
8936     nowChecked = IDM_TwoMachines;\r
8937     break;\r
8938   case AnalyzeMode:\r
8939     nowChecked = IDM_AnalysisMode;\r
8940     break;\r
8941   case AnalyzeFile:\r
8942     nowChecked = IDM_AnalyzeFile;\r
8943     break;\r
8944   case EditGame:\r
8945     nowChecked = IDM_EditGame;\r
8946     break;\r
8947   case PlayFromGameFile:\r
8948     nowChecked = IDM_LoadGame;\r
8949     break;\r
8950   case EditPosition:\r
8951     nowChecked = IDM_EditPosition;\r
8952     break;\r
8953   case Training:\r
8954     nowChecked = IDM_Training;\r
8955     break;\r
8956   case IcsPlayingWhite:\r
8957   case IcsPlayingBlack:\r
8958   case IcsObserving:\r
8959   case IcsIdle:\r
8960     nowChecked = IDM_IcsClient;\r
8961     break;\r
8962   default:\r
8963   case EndOfGame:\r
8964     nowChecked = 0;\r
8965     break;\r
8966   }\r
8967   if (prevChecked != 0)\r
8968     (void) CheckMenuItem(GetMenu(hwndMain),\r
8969                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8970   if (nowChecked != 0)\r
8971     (void) CheckMenuItem(GetMenu(hwndMain),\r
8972                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8973 \r
8974   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8975     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8976                           MF_BYCOMMAND|MF_ENABLED);\r
8977   } else {\r
8978     (void) EnableMenuItem(GetMenu(hwndMain), \r
8979                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8980   }\r
8981 \r
8982   prevChecked = nowChecked;\r
8983 \r
8984   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8985   if (appData.icsActive) {\r
8986        if (appData.icsEngineAnalyze) {\r
8987                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8988                        MF_BYCOMMAND|MF_CHECKED);\r
8989        } else {\r
8990                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8991                        MF_BYCOMMAND|MF_UNCHECKED);\r
8992        }\r
8993   }\r
8994 }\r
8995 \r
8996 VOID\r
8997 SetICSMode()\r
8998 {\r
8999   HMENU hmenu = GetMenu(hwndMain);\r
9000   SetMenuEnables(hmenu, icsEnables);\r
9001   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9002     MF_BYPOSITION|MF_ENABLED);\r
9003 #ifdef ZIPPY\r
9004   if (appData.zippyPlay) {\r
9005     SetMenuEnables(hmenu, zippyEnables);\r
9006     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9007          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9008           MF_BYCOMMAND|MF_ENABLED);\r
9009   }\r
9010 #endif\r
9011 }\r
9012 \r
9013 VOID\r
9014 SetGNUMode()\r
9015 {\r
9016   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9017 }\r
9018 \r
9019 VOID\r
9020 SetNCPMode()\r
9021 {\r
9022   HMENU hmenu = GetMenu(hwndMain);\r
9023   SetMenuEnables(hmenu, ncpEnables);\r
9024   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9025     MF_BYPOSITION|MF_GRAYED);\r
9026     DrawMenuBar(hwndMain);\r
9027 }\r
9028 \r
9029 VOID\r
9030 SetCmailMode()\r
9031 {\r
9032   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9033 }\r
9034 \r
9035 VOID \r
9036 SetTrainingModeOn()\r
9037 {\r
9038   int i;\r
9039   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9040   for (i = 0; i < N_BUTTONS; i++) {\r
9041     if (buttonDesc[i].hwnd != NULL)\r
9042       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9043   }\r
9044   CommentPopDown();\r
9045 }\r
9046 \r
9047 VOID SetTrainingModeOff()\r
9048 {\r
9049   int i;\r
9050   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9051   for (i = 0; i < N_BUTTONS; i++) {\r
9052     if (buttonDesc[i].hwnd != NULL)\r
9053       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9054   }\r
9055 }\r
9056 \r
9057 \r
9058 VOID\r
9059 SetUserThinkingEnables()\r
9060 {\r
9061   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9062 }\r
9063 \r
9064 VOID\r
9065 SetMachineThinkingEnables()\r
9066 {\r
9067   HMENU hMenu = GetMenu(hwndMain);\r
9068   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9069 \r
9070   SetMenuEnables(hMenu, machineThinkingEnables);\r
9071 \r
9072   if (gameMode == MachinePlaysBlack) {\r
9073     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9074   } else if (gameMode == MachinePlaysWhite) {\r
9075     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9076   } else if (gameMode == TwoMachinesPlay) {\r
9077     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9078   }\r
9079 }\r
9080 \r
9081 \r
9082 VOID\r
9083 DisplayTitle(char *str)\r
9084 {\r
9085   char title[MSG_SIZ], *host;\r
9086   if (str[0] != NULLCHAR) {\r
9087     strcpy(title, str);\r
9088   } else if (appData.icsActive) {\r
9089     if (appData.icsCommPort[0] != NULLCHAR)\r
9090       host = "ICS";\r
9091     else \r
9092       host = appData.icsHost;\r
9093     sprintf(title, "%s: %s", szTitle, host);\r
9094   } else if (appData.noChessProgram) {\r
9095     strcpy(title, szTitle);\r
9096   } else {\r
9097     strcpy(title, szTitle);\r
9098     strcat(title, ": ");\r
9099     strcat(title, first.tidy);\r
9100   }\r
9101   SetWindowText(hwndMain, title);\r
9102 }\r
9103 \r
9104 \r
9105 VOID\r
9106 DisplayMessage(char *str1, char *str2)\r
9107 {\r
9108   HDC hdc;\r
9109   HFONT oldFont;\r
9110   int remain = MESSAGE_TEXT_MAX - 1;\r
9111   int len;\r
9112 \r
9113   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9114   messageText[0] = NULLCHAR;\r
9115   if (*str1) {\r
9116     len = strlen(str1);\r
9117     if (len > remain) len = remain;\r
9118     strncpy(messageText, str1, len);\r
9119     messageText[len] = NULLCHAR;\r
9120     remain -= len;\r
9121   }\r
9122   if (*str2 && remain >= 2) {\r
9123     if (*str1) {\r
9124       strcat(messageText, "  ");\r
9125       remain -= 2;\r
9126     }\r
9127     len = strlen(str2);\r
9128     if (len > remain) len = remain;\r
9129     strncat(messageText, str2, len);\r
9130   }\r
9131   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9132 \r
9133   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9134 \r
9135   SAYMACHINEMOVE();\r
9136 \r
9137   hdc = GetDC(hwndMain);\r
9138   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9139   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9140              &messageRect, messageText, strlen(messageText), NULL);\r
9141   (void) SelectObject(hdc, oldFont);\r
9142   (void) ReleaseDC(hwndMain, hdc);\r
9143 }\r
9144 \r
9145 VOID\r
9146 DisplayError(char *str, int error)\r
9147 {\r
9148   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9149   int len;\r
9150 \r
9151   if (error == 0) {\r
9152     strcpy(buf, str);\r
9153   } else {\r
9154     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9155                         NULL, error, LANG_NEUTRAL,\r
9156                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9157     if (len > 0) {\r
9158       sprintf(buf, "%s:\n%s", str, buf2);\r
9159     } else {\r
9160       ErrorMap *em = errmap;\r
9161       while (em->err != 0 && em->err != error) em++;\r
9162       if (em->err != 0) {\r
9163         sprintf(buf, "%s:\n%s", str, em->msg);\r
9164       } else {\r
9165         sprintf(buf, "%s:\nError code %d", str, error);\r
9166       }\r
9167     }\r
9168   }\r
9169   \r
9170   ErrorPopUp("Error", buf);\r
9171 }\r
9172 \r
9173 \r
9174 VOID\r
9175 DisplayMoveError(char *str)\r
9176 {\r
9177   fromX = fromY = -1;\r
9178   ClearHighlights();\r
9179   DrawPosition(FALSE, NULL);\r
9180   if (appData.popupMoveErrors) {\r
9181     ErrorPopUp("Error", str);\r
9182   } else {\r
9183     DisplayMessage(str, "");\r
9184     moveErrorMessageUp = TRUE;\r
9185   }\r
9186 }\r
9187 \r
9188 VOID\r
9189 DisplayFatalError(char *str, int error, int exitStatus)\r
9190 {\r
9191   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9192   int len;\r
9193   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9194 \r
9195   if (error != 0) {\r
9196     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9197                         NULL, error, LANG_NEUTRAL,\r
9198                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9199     if (len > 0) {\r
9200       sprintf(buf, "%s:\n%s", str, buf2);\r
9201     } else {\r
9202       ErrorMap *em = errmap;\r
9203       while (em->err != 0 && em->err != error) em++;\r
9204       if (em->err != 0) {\r
9205         sprintf(buf, "%s:\n%s", str, em->msg);\r
9206       } else {\r
9207         sprintf(buf, "%s:\nError code %d", str, error);\r
9208       }\r
9209     }\r
9210     str = buf;\r
9211   }\r
9212   if (appData.debugMode) {\r
9213     fprintf(debugFP, "%s: %s\n", label, str);\r
9214   }\r
9215   if (appData.popupExitMessage) {\r
9216     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9217                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9218   }\r
9219   ExitEvent(exitStatus);\r
9220 }\r
9221 \r
9222 \r
9223 VOID\r
9224 DisplayInformation(char *str)\r
9225 {\r
9226   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9227 }\r
9228 \r
9229 \r
9230 VOID\r
9231 DisplayNote(char *str)\r
9232 {\r
9233   ErrorPopUp("Note", str);\r
9234 }\r
9235 \r
9236 \r
9237 typedef struct {\r
9238   char *title, *question, *replyPrefix;\r
9239   ProcRef pr;\r
9240 } QuestionParams;\r
9241 \r
9242 LRESULT CALLBACK\r
9243 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9244 {\r
9245   static QuestionParams *qp;\r
9246   char reply[MSG_SIZ];\r
9247   int len, err;\r
9248 \r
9249   switch (message) {\r
9250   case WM_INITDIALOG:\r
9251     qp = (QuestionParams *) lParam;\r
9252     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9253     SetWindowText(hDlg, qp->title);\r
9254     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9255     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9256     return FALSE;\r
9257 \r
9258   case WM_COMMAND:\r
9259     switch (LOWORD(wParam)) {\r
9260     case IDOK:\r
9261       strcpy(reply, qp->replyPrefix);\r
9262       if (*reply) strcat(reply, " ");\r
9263       len = strlen(reply);\r
9264       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9265       strcat(reply, "\n");\r
9266       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9267       EndDialog(hDlg, TRUE);\r
9268       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9269       return TRUE;\r
9270     case IDCANCEL:\r
9271       EndDialog(hDlg, FALSE);\r
9272       return TRUE;\r
9273     default:\r
9274       break;\r
9275     }\r
9276     break;\r
9277   }\r
9278   return FALSE;\r
9279 }\r
9280 \r
9281 VOID\r
9282 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9283 {\r
9284     QuestionParams qp;\r
9285     FARPROC lpProc;\r
9286     \r
9287     qp.title = title;\r
9288     qp.question = question;\r
9289     qp.replyPrefix = replyPrefix;\r
9290     qp.pr = pr;\r
9291     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9292     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9293       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9294     FreeProcInstance(lpProc);\r
9295 }\r
9296 \r
9297 /* [AS] Pick FRC position */\r
9298 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9299 {\r
9300     static int * lpIndexFRC;\r
9301     BOOL index_is_ok;\r
9302     char buf[16];\r
9303 \r
9304     switch( message )\r
9305     {\r
9306     case WM_INITDIALOG:\r
9307         lpIndexFRC = (int *) lParam;\r
9308 \r
9309         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9310 \r
9311         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9312         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9313         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9314         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9315 \r
9316         break;\r
9317 \r
9318     case WM_COMMAND:\r
9319         switch( LOWORD(wParam) ) {\r
9320         case IDOK:\r
9321             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9322             EndDialog( hDlg, 0 );\r
9323             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9324             return TRUE;\r
9325         case IDCANCEL:\r
9326             EndDialog( hDlg, 1 );   \r
9327             return TRUE;\r
9328         case IDC_NFG_Edit:\r
9329             if( HIWORD(wParam) == EN_CHANGE ) {\r
9330                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9331 \r
9332                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9333             }\r
9334             return TRUE;\r
9335         case IDC_NFG_Random:\r
9336             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9337             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9338             return TRUE;\r
9339         }\r
9340 \r
9341         break;\r
9342     }\r
9343 \r
9344     return FALSE;\r
9345 }\r
9346 \r
9347 int NewGameFRC()\r
9348 {\r
9349     int result;\r
9350     int index = appData.defaultFrcPosition;\r
9351     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9352 \r
9353     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9354 \r
9355     if( result == 0 ) {\r
9356         appData.defaultFrcPosition = index;\r
9357     }\r
9358 \r
9359     return result;\r
9360 }\r
9361 \r
9362 /* [AS] Game list options */\r
9363 typedef struct {\r
9364     char id;\r
9365     char * name;\r
9366 } GLT_Item;\r
9367 \r
9368 static GLT_Item GLT_ItemInfo[] = {\r
9369     { GLT_EVENT,      "Event" },\r
9370     { GLT_SITE,       "Site" },\r
9371     { GLT_DATE,       "Date" },\r
9372     { GLT_ROUND,      "Round" },\r
9373     { GLT_PLAYERS,    "Players" },\r
9374     { GLT_RESULT,     "Result" },\r
9375     { GLT_WHITE_ELO,  "White Rating" },\r
9376     { GLT_BLACK_ELO,  "Black Rating" },\r
9377     { GLT_TIME_CONTROL,"Time Control" },\r
9378     { GLT_VARIANT,    "Variant" },\r
9379     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9380     { 0, 0 }\r
9381 };\r
9382 \r
9383 const char * GLT_FindItem( char id )\r
9384 {\r
9385     const char * result = 0;\r
9386 \r
9387     GLT_Item * list = GLT_ItemInfo;\r
9388 \r
9389     while( list->id != 0 ) {\r
9390         if( list->id == id ) {\r
9391             result = list->name;\r
9392             break;\r
9393         }\r
9394 \r
9395         list++;\r
9396     }\r
9397 \r
9398     return result;\r
9399 }\r
9400 \r
9401 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9402 {\r
9403     const char * name = GLT_FindItem( id );\r
9404 \r
9405     if( name != 0 ) {\r
9406         if( index >= 0 ) {\r
9407             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9408         }\r
9409         else {\r
9410             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9411         }\r
9412     }\r
9413 }\r
9414 \r
9415 void GLT_TagsToList( HWND hDlg, char * tags )\r
9416 {\r
9417     char * pc = tags;\r
9418 \r
9419     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9420 \r
9421     while( *pc ) {\r
9422         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9423         pc++;\r
9424     }\r
9425 \r
9426     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9427 \r
9428     pc = GLT_ALL_TAGS;\r
9429 \r
9430     while( *pc ) {\r
9431         if( strchr( tags, *pc ) == 0 ) {\r
9432             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9433         }\r
9434         pc++;\r
9435     }\r
9436 \r
9437     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9438 }\r
9439 \r
9440 char GLT_ListItemToTag( HWND hDlg, int index )\r
9441 {\r
9442     char result = '\0';\r
9443     char name[128];\r
9444 \r
9445     GLT_Item * list = GLT_ItemInfo;\r
9446 \r
9447     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9448         while( list->id != 0 ) {\r
9449             if( strcmp( list->name, name ) == 0 ) {\r
9450                 result = list->id;\r
9451                 break;\r
9452             }\r
9453 \r
9454             list++;\r
9455         }\r
9456     }\r
9457 \r
9458     return result;\r
9459 }\r
9460 \r
9461 void GLT_MoveSelection( HWND hDlg, int delta )\r
9462 {\r
9463     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9464     int idx2 = idx1 + delta;\r
9465     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9466 \r
9467     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9468         char buf[128];\r
9469 \r
9470         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9471         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9472         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9473         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9474     }\r
9475 }\r
9476 \r
9477 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9478 {\r
9479     static char glt[64];\r
9480     static char * lpUserGLT;\r
9481 \r
9482     switch( message )\r
9483     {\r
9484     case WM_INITDIALOG:\r
9485         lpUserGLT = (char *) lParam;\r
9486         \r
9487         strcpy( glt, lpUserGLT );\r
9488 \r
9489         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9490 \r
9491         /* Initialize list */\r
9492         GLT_TagsToList( hDlg, glt );\r
9493 \r
9494         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9495 \r
9496         break;\r
9497 \r
9498     case WM_COMMAND:\r
9499         switch( LOWORD(wParam) ) {\r
9500         case IDOK:\r
9501             {\r
9502                 char * pc = lpUserGLT;\r
9503                 int idx = 0;\r
9504 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9505                 char id;\r
9506 \r
9507                 do {\r
9508                     id = GLT_ListItemToTag( hDlg, idx );\r
9509 \r
9510                     *pc++ = id;\r
9511                     idx++;\r
9512                 } while( id != '\0' );\r
9513             }\r
9514             EndDialog( hDlg, 0 );\r
9515             return TRUE;\r
9516         case IDCANCEL:\r
9517             EndDialog( hDlg, 1 );\r
9518             return TRUE;\r
9519 \r
9520         case IDC_GLT_Default:\r
9521             strcpy( glt, GLT_DEFAULT_TAGS );\r
9522             GLT_TagsToList( hDlg, glt );\r
9523             return TRUE;\r
9524 \r
9525         case IDC_GLT_Restore:\r
9526             strcpy( glt, lpUserGLT );\r
9527             GLT_TagsToList( hDlg, glt );\r
9528             return TRUE;\r
9529 \r
9530         case IDC_GLT_Up:\r
9531             GLT_MoveSelection( hDlg, -1 );\r
9532             return TRUE;\r
9533 \r
9534         case IDC_GLT_Down:\r
9535             GLT_MoveSelection( hDlg, +1 );\r
9536             return TRUE;\r
9537         }\r
9538 \r
9539         break;\r
9540     }\r
9541 \r
9542     return FALSE;\r
9543 }\r
9544 \r
9545 int GameListOptions()\r
9546 {\r
9547     char glt[64];\r
9548     int result;\r
9549     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9550 \r
9551     strcpy( glt, appData.gameListTags );\r
9552 \r
9553     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9554 \r
9555     if( result == 0 ) {\r
9556         /* [AS] Memory leak here! */\r
9557         appData.gameListTags = strdup( glt ); \r
9558     }\r
9559 \r
9560     return result;\r
9561 }\r
9562 \r
9563 \r
9564 VOID\r
9565 DisplayIcsInteractionTitle(char *str)\r
9566 {\r
9567   char consoleTitle[MSG_SIZ];\r
9568 \r
9569   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9570   SetWindowText(hwndConsole, consoleTitle);\r
9571 }\r
9572 \r
9573 void\r
9574 DrawPosition(int fullRedraw, Board board)\r
9575 {\r
9576   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9577 }\r
9578 \r
9579 \r
9580 VOID\r
9581 ResetFrontEnd()\r
9582 {\r
9583   fromX = fromY = -1;\r
9584   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9585     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9586     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9587     dragInfo.lastpos = dragInfo.pos;\r
9588     dragInfo.start.x = dragInfo.start.y = -1;\r
9589     dragInfo.from = dragInfo.start;\r
9590     ReleaseCapture();\r
9591     DrawPosition(TRUE, NULL);\r
9592   }\r
9593 }\r
9594 \r
9595 \r
9596 VOID\r
9597 CommentPopUp(char *title, char *str)\r
9598 {\r
9599   HWND hwnd = GetActiveWindow();\r
9600   EitherCommentPopUp(0, title, str, FALSE);\r
9601   SetActiveWindow(hwnd);\r
9602 }\r
9603 \r
9604 VOID\r
9605 CommentPopDown(void)\r
9606 {\r
9607   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9608   if (commentDialog) {\r
9609     ShowWindow(commentDialog, SW_HIDE);\r
9610   }\r
9611   commentDialogUp = FALSE;\r
9612 }\r
9613 \r
9614 VOID\r
9615 EditCommentPopUp(int index, char *title, char *str)\r
9616 {\r
9617   EitherCommentPopUp(index, title, str, TRUE);\r
9618 }\r
9619 \r
9620 \r
9621 VOID\r
9622 RingBell()\r
9623 {\r
9624   MyPlaySound(&sounds[(int)SoundMove]);\r
9625 }\r
9626 \r
9627 VOID PlayIcsWinSound()\r
9628 {\r
9629   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9630 }\r
9631 \r
9632 VOID PlayIcsLossSound()\r
9633 {\r
9634   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9635 }\r
9636 \r
9637 VOID PlayIcsDrawSound()\r
9638 {\r
9639   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9640 }\r
9641 \r
9642 VOID PlayIcsUnfinishedSound()\r
9643 {\r
9644   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9645 }\r
9646 \r
9647 VOID\r
9648 PlayAlarmSound()\r
9649 {\r
9650   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9651 }\r
9652 \r
9653 \r
9654 VOID\r
9655 EchoOn()\r
9656 {\r
9657   HWND hInput;\r
9658   consoleEcho = TRUE;\r
9659   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9660   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9661   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9662 }\r
9663 \r
9664 \r
9665 VOID\r
9666 EchoOff()\r
9667 {\r
9668   CHARFORMAT cf;\r
9669   HWND hInput;\r
9670   consoleEcho = FALSE;\r
9671   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9672   /* This works OK: set text and background both to the same color */\r
9673   cf = consoleCF;\r
9674   cf.crTextColor = COLOR_ECHOOFF;\r
9675   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9676   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9677 }\r
9678 \r
9679 /* No Raw()...? */\r
9680 \r
9681 void Colorize(ColorClass cc, int continuation)\r
9682 {\r
9683   currentColorClass = cc;\r
9684   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9685   consoleCF.crTextColor = textAttribs[cc].color;\r
9686   consoleCF.dwEffects = textAttribs[cc].effects;\r
9687   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9688 }\r
9689 \r
9690 char *\r
9691 UserName()\r
9692 {\r
9693   static char buf[MSG_SIZ];\r
9694   DWORD bufsiz = MSG_SIZ;\r
9695 \r
9696   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9697         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9698   }\r
9699   if (!GetUserName(buf, &bufsiz)) {\r
9700     /*DisplayError("Error getting user name", GetLastError());*/\r
9701     strcpy(buf, "User");\r
9702   }\r
9703   return buf;\r
9704 }\r
9705 \r
9706 char *\r
9707 HostName()\r
9708 {\r
9709   static char buf[MSG_SIZ];\r
9710   DWORD bufsiz = MSG_SIZ;\r
9711 \r
9712   if (!GetComputerName(buf, &bufsiz)) {\r
9713     /*DisplayError("Error getting host name", GetLastError());*/\r
9714     strcpy(buf, "Unknown");\r
9715   }\r
9716   return buf;\r
9717 }\r
9718 \r
9719 \r
9720 int\r
9721 ClockTimerRunning()\r
9722 {\r
9723   return clockTimerEvent != 0;\r
9724 }\r
9725 \r
9726 int\r
9727 StopClockTimer()\r
9728 {\r
9729   if (clockTimerEvent == 0) return FALSE;\r
9730   KillTimer(hwndMain, clockTimerEvent);\r
9731   clockTimerEvent = 0;\r
9732   return TRUE;\r
9733 }\r
9734 \r
9735 void\r
9736 StartClockTimer(long millisec)\r
9737 {\r
9738   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9739                              (UINT) millisec, NULL);\r
9740 }\r
9741 \r
9742 void\r
9743 DisplayWhiteClock(long timeRemaining, int highlight)\r
9744 {\r
9745   HDC hdc;\r
9746   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9747 \r
9748   if(appData.noGUI) return;\r
9749   hdc = GetDC(hwndMain);\r
9750   if (!IsIconic(hwndMain)) {\r
9751     DisplayAClock(hdc, timeRemaining, highlight, \r
9752                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9753   }\r
9754   if (highlight && iconCurrent == iconBlack) {\r
9755     iconCurrent = iconWhite;\r
9756     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9757     if (IsIconic(hwndMain)) {\r
9758       DrawIcon(hdc, 2, 2, iconCurrent);\r
9759     }\r
9760   }\r
9761   (void) ReleaseDC(hwndMain, hdc);\r
9762   if (hwndConsole)\r
9763     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9764 }\r
9765 \r
9766 void\r
9767 DisplayBlackClock(long timeRemaining, int highlight)\r
9768 {\r
9769   HDC hdc;\r
9770   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9771 \r
9772   if(appData.noGUI) return;\r
9773   hdc = GetDC(hwndMain);\r
9774   if (!IsIconic(hwndMain)) {\r
9775     DisplayAClock(hdc, timeRemaining, highlight, \r
9776                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9777   }\r
9778   if (highlight && iconCurrent == iconWhite) {\r
9779     iconCurrent = iconBlack;\r
9780     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9781     if (IsIconic(hwndMain)) {\r
9782       DrawIcon(hdc, 2, 2, iconCurrent);\r
9783     }\r
9784   }\r
9785   (void) ReleaseDC(hwndMain, hdc);\r
9786   if (hwndConsole)\r
9787     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9788 }\r
9789 \r
9790 \r
9791 int\r
9792 LoadGameTimerRunning()\r
9793 {\r
9794   return loadGameTimerEvent != 0;\r
9795 }\r
9796 \r
9797 int\r
9798 StopLoadGameTimer()\r
9799 {\r
9800   if (loadGameTimerEvent == 0) return FALSE;\r
9801   KillTimer(hwndMain, loadGameTimerEvent);\r
9802   loadGameTimerEvent = 0;\r
9803   return TRUE;\r
9804 }\r
9805 \r
9806 void\r
9807 StartLoadGameTimer(long millisec)\r
9808 {\r
9809   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9810                                 (UINT) millisec, NULL);\r
9811 }\r
9812 \r
9813 void\r
9814 AutoSaveGame()\r
9815 {\r
9816   char *defName;\r
9817   FILE *f;\r
9818   char fileTitle[MSG_SIZ];\r
9819 \r
9820   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9821   f = OpenFileDialog(hwndMain, "a", defName,\r
9822                      appData.oldSaveStyle ? "gam" : "pgn",\r
9823                      GAME_FILT, \r
9824                      "Save Game to File", NULL, fileTitle, NULL);\r
9825   if (f != NULL) {\r
9826     SaveGame(f, 0, "");\r
9827     fclose(f);\r
9828   }\r
9829 }\r
9830 \r
9831 \r
9832 void\r
9833 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9834 {\r
9835   if (delayedTimerEvent != 0) {\r
9836     if (appData.debugMode) {\r
9837       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9838     }\r
9839     KillTimer(hwndMain, delayedTimerEvent);\r
9840     delayedTimerEvent = 0;\r
9841     delayedTimerCallback();\r
9842   }\r
9843   delayedTimerCallback = cb;\r
9844   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9845                                 (UINT) millisec, NULL);\r
9846 }\r
9847 \r
9848 DelayedEventCallback\r
9849 GetDelayedEvent()\r
9850 {\r
9851   if (delayedTimerEvent) {\r
9852     return delayedTimerCallback;\r
9853   } else {\r
9854     return NULL;\r
9855   }\r
9856 }\r
9857 \r
9858 void\r
9859 CancelDelayedEvent()\r
9860 {\r
9861   if (delayedTimerEvent) {\r
9862     KillTimer(hwndMain, delayedTimerEvent);\r
9863     delayedTimerEvent = 0;\r
9864   }\r
9865 }\r
9866 \r
9867 DWORD GetWin32Priority(int nice)\r
9868 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9869 /*\r
9870 REALTIME_PRIORITY_CLASS     0x00000100\r
9871 HIGH_PRIORITY_CLASS         0x00000080\r
9872 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9873 NORMAL_PRIORITY_CLASS       0x00000020\r
9874 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9875 IDLE_PRIORITY_CLASS         0x00000040\r
9876 */\r
9877         if (nice < -15) return 0x00000080;\r
9878         if (nice < 0)   return 0x00008000;\r
9879         if (nice == 0)  return 0x00000020;\r
9880         if (nice < 15)  return 0x00004000;\r
9881         return 0x00000040;\r
9882 }\r
9883 \r
9884 /* Start a child process running the given program.\r
9885    The process's standard output can be read from "from", and its\r
9886    standard input can be written to "to".\r
9887    Exit with fatal error if anything goes wrong.\r
9888    Returns an opaque pointer that can be used to destroy the process\r
9889    later.\r
9890 */\r
9891 int\r
9892 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9893 {\r
9894 #define BUFSIZE 4096\r
9895 \r
9896   HANDLE hChildStdinRd, hChildStdinWr,\r
9897     hChildStdoutRd, hChildStdoutWr;\r
9898   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9899   SECURITY_ATTRIBUTES saAttr;\r
9900   BOOL fSuccess;\r
9901   PROCESS_INFORMATION piProcInfo;\r
9902   STARTUPINFO siStartInfo;\r
9903   ChildProc *cp;\r
9904   char buf[MSG_SIZ];\r
9905   DWORD err;\r
9906 \r
9907   if (appData.debugMode) {\r
9908     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9909   }\r
9910 \r
9911   *pr = NoProc;\r
9912 \r
9913   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9914   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9915   saAttr.bInheritHandle = TRUE;\r
9916   saAttr.lpSecurityDescriptor = NULL;\r
9917 \r
9918   /*\r
9919    * The steps for redirecting child's STDOUT:\r
9920    *     1. Create anonymous pipe to be STDOUT for child.\r
9921    *     2. Create a noninheritable duplicate of read handle,\r
9922    *         and close the inheritable read handle.\r
9923    */\r
9924 \r
9925   /* Create a pipe for the child's STDOUT. */\r
9926   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9927     return GetLastError();\r
9928   }\r
9929 \r
9930   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9931   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9932                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9933                              FALSE,     /* not inherited */\r
9934                              DUPLICATE_SAME_ACCESS);\r
9935   if (! fSuccess) {\r
9936     return GetLastError();\r
9937   }\r
9938   CloseHandle(hChildStdoutRd);\r
9939 \r
9940   /*\r
9941    * The steps for redirecting child's STDIN:\r
9942    *     1. Create anonymous pipe to be STDIN for child.\r
9943    *     2. Create a noninheritable duplicate of write handle,\r
9944    *         and close the inheritable write handle.\r
9945    */\r
9946 \r
9947   /* Create a pipe for the child's STDIN. */\r
9948   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9949     return GetLastError();\r
9950   }\r
9951 \r
9952   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9953   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9954                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9955                              FALSE,     /* not inherited */\r
9956                              DUPLICATE_SAME_ACCESS);\r
9957   if (! fSuccess) {\r
9958     return GetLastError();\r
9959   }\r
9960   CloseHandle(hChildStdinWr);\r
9961 \r
9962   /* Arrange to (1) look in dir for the child .exe file, and\r
9963    * (2) have dir be the child's working directory.  Interpret\r
9964    * dir relative to the directory WinBoard loaded from. */\r
9965   GetCurrentDirectory(MSG_SIZ, buf);\r
9966   SetCurrentDirectory(installDir);\r
9967   SetCurrentDirectory(dir);\r
9968 \r
9969   /* Now create the child process. */\r
9970 \r
9971   siStartInfo.cb = sizeof(STARTUPINFO);\r
9972   siStartInfo.lpReserved = NULL;\r
9973   siStartInfo.lpDesktop = NULL;\r
9974   siStartInfo.lpTitle = NULL;\r
9975   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9976   siStartInfo.cbReserved2 = 0;\r
9977   siStartInfo.lpReserved2 = NULL;\r
9978   siStartInfo.hStdInput = hChildStdinRd;\r
9979   siStartInfo.hStdOutput = hChildStdoutWr;\r
9980   siStartInfo.hStdError = hChildStdoutWr;\r
9981 \r
9982   fSuccess = CreateProcess(NULL,\r
9983                            cmdLine,        /* command line */\r
9984                            NULL,           /* process security attributes */\r
9985                            NULL,           /* primary thread security attrs */\r
9986                            TRUE,           /* handles are inherited */\r
9987                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9988                            NULL,           /* use parent's environment */\r
9989                            NULL,\r
9990                            &siStartInfo, /* STARTUPINFO pointer */\r
9991                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9992 \r
9993   err = GetLastError();\r
9994   SetCurrentDirectory(buf); /* return to prev directory */\r
9995   if (! fSuccess) {\r
9996     return err;\r
9997   }\r
9998 \r
9999   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10000     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10001     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10002   }\r
10003 \r
10004   /* Close the handles we don't need in the parent */\r
10005   CloseHandle(piProcInfo.hThread);\r
10006   CloseHandle(hChildStdinRd);\r
10007   CloseHandle(hChildStdoutWr);\r
10008 \r
10009   /* Prepare return value */\r
10010   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10011   cp->kind = CPReal;\r
10012   cp->hProcess = piProcInfo.hProcess;\r
10013   cp->pid = piProcInfo.dwProcessId;\r
10014   cp->hFrom = hChildStdoutRdDup;\r
10015   cp->hTo = hChildStdinWrDup;\r
10016 \r
10017   *pr = (void *) cp;\r
10018 \r
10019   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10020      2000 where engines sometimes don't see the initial command(s)\r
10021      from WinBoard and hang.  I don't understand how that can happen,\r
10022      but the Sleep is harmless, so I've put it in.  Others have also\r
10023      reported what may be the same problem, so hopefully this will fix\r
10024      it for them too.  */\r
10025   Sleep(500);\r
10026 \r
10027   return NO_ERROR;\r
10028 }\r
10029 \r
10030 \r
10031 void\r
10032 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10033 {\r
10034   ChildProc *cp; int result;\r
10035 \r
10036   cp = (ChildProc *) pr;\r
10037   if (cp == NULL) return;\r
10038 \r
10039   switch (cp->kind) {\r
10040   case CPReal:\r
10041     /* TerminateProcess is considered harmful, so... */\r
10042     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10043     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10044     /* The following doesn't work because the chess program\r
10045        doesn't "have the same console" as WinBoard.  Maybe\r
10046        we could arrange for this even though neither WinBoard\r
10047        nor the chess program uses a console for stdio? */\r
10048     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10049 \r
10050     /* [AS] Special termination modes for misbehaving programs... */\r
10051     if( signal == 9 ) { \r
10052         result = TerminateProcess( cp->hProcess, 0 );\r
10053 \r
10054         if ( appData.debugMode) {\r
10055             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10056         }\r
10057     }\r
10058     else if( signal == 10 ) {\r
10059         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10060 \r
10061         if( dw != WAIT_OBJECT_0 ) {\r
10062             result = TerminateProcess( cp->hProcess, 0 );\r
10063 \r
10064             if ( appData.debugMode) {\r
10065                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10066             }\r
10067 \r
10068         }\r
10069     }\r
10070 \r
10071     CloseHandle(cp->hProcess);\r
10072     break;\r
10073 \r
10074   case CPComm:\r
10075     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10076     break;\r
10077 \r
10078   case CPSock:\r
10079     closesocket(cp->sock);\r
10080     WSACleanup();\r
10081     break;\r
10082 \r
10083   case CPRcmd:\r
10084     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10085     closesocket(cp->sock);\r
10086     closesocket(cp->sock2);\r
10087     WSACleanup();\r
10088     break;\r
10089   }\r
10090   free(cp);\r
10091 }\r
10092 \r
10093 void\r
10094 InterruptChildProcess(ProcRef pr)\r
10095 {\r
10096   ChildProc *cp;\r
10097 \r
10098   cp = (ChildProc *) pr;\r
10099   if (cp == NULL) return;\r
10100   switch (cp->kind) {\r
10101   case CPReal:\r
10102     /* The following doesn't work because the chess program\r
10103        doesn't "have the same console" as WinBoard.  Maybe\r
10104        we could arrange for this even though neither WinBoard\r
10105        nor the chess program uses a console for stdio */\r
10106     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10107     break;\r
10108 \r
10109   case CPComm:\r
10110   case CPSock:\r
10111     /* Can't interrupt */\r
10112     break;\r
10113 \r
10114   case CPRcmd:\r
10115     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10116     break;\r
10117   }\r
10118 }\r
10119 \r
10120 \r
10121 int\r
10122 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10123 {\r
10124   char cmdLine[MSG_SIZ];\r
10125 \r
10126   if (port[0] == NULLCHAR) {\r
10127     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10128   } else {\r
10129     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10130   }\r
10131   return StartChildProcess(cmdLine, "", pr);\r
10132 }\r
10133 \r
10134 \r
10135 /* Code to open TCP sockets */\r
10136 \r
10137 int\r
10138 OpenTCP(char *host, char *port, ProcRef *pr)\r
10139 {\r
10140   ChildProc *cp;\r
10141   int err;\r
10142   SOCKET s;\r
10143   struct sockaddr_in sa, mysa;\r
10144   struct hostent FAR *hp;\r
10145   unsigned short uport;\r
10146   WORD wVersionRequested;\r
10147   WSADATA wsaData;\r
10148 \r
10149   /* Initialize socket DLL */\r
10150   wVersionRequested = MAKEWORD(1, 1);\r
10151   err = WSAStartup(wVersionRequested, &wsaData);\r
10152   if (err != 0) return err;\r
10153 \r
10154   /* Make socket */\r
10155   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10156     err = WSAGetLastError();\r
10157     WSACleanup();\r
10158     return err;\r
10159   }\r
10160 \r
10161   /* Bind local address using (mostly) don't-care values.\r
10162    */\r
10163   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10164   mysa.sin_family = AF_INET;\r
10165   mysa.sin_addr.s_addr = INADDR_ANY;\r
10166   uport = (unsigned short) 0;\r
10167   mysa.sin_port = htons(uport);\r
10168   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10169       == SOCKET_ERROR) {\r
10170     err = WSAGetLastError();\r
10171     WSACleanup();\r
10172     return err;\r
10173   }\r
10174 \r
10175   /* Resolve remote host name */\r
10176   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10177   if (!(hp = gethostbyname(host))) {\r
10178     unsigned int b0, b1, b2, b3;\r
10179 \r
10180     err = WSAGetLastError();\r
10181 \r
10182     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10183       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10184       hp->h_addrtype = AF_INET;\r
10185       hp->h_length = 4;\r
10186       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10187       hp->h_addr_list[0] = (char *) malloc(4);\r
10188       hp->h_addr_list[0][0] = (char) b0;\r
10189       hp->h_addr_list[0][1] = (char) b1;\r
10190       hp->h_addr_list[0][2] = (char) b2;\r
10191       hp->h_addr_list[0][3] = (char) b3;\r
10192     } else {\r
10193       WSACleanup();\r
10194       return err;\r
10195     }\r
10196   }\r
10197   sa.sin_family = hp->h_addrtype;\r
10198   uport = (unsigned short) atoi(port);\r
10199   sa.sin_port = htons(uport);\r
10200   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10201 \r
10202   /* Make connection */\r
10203   if (connect(s, (struct sockaddr *) &sa,\r
10204               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10205     err = WSAGetLastError();\r
10206     WSACleanup();\r
10207     return err;\r
10208   }\r
10209 \r
10210   /* Prepare return value */\r
10211   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10212   cp->kind = CPSock;\r
10213   cp->sock = s;\r
10214   *pr = (ProcRef *) cp;\r
10215 \r
10216   return NO_ERROR;\r
10217 }\r
10218 \r
10219 int\r
10220 OpenCommPort(char *name, ProcRef *pr)\r
10221 {\r
10222   HANDLE h;\r
10223   COMMTIMEOUTS ct;\r
10224   ChildProc *cp;\r
10225   char fullname[MSG_SIZ];\r
10226 \r
10227   if (*name != '\\')\r
10228     sprintf(fullname, "\\\\.\\%s", name);\r
10229   else\r
10230     strcpy(fullname, name);\r
10231 \r
10232   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10233                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10234   if (h == (HANDLE) -1) {\r
10235     return GetLastError();\r
10236   }\r
10237   hCommPort = h;\r
10238 \r
10239   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10240 \r
10241   /* Accumulate characters until a 100ms pause, then parse */\r
10242   ct.ReadIntervalTimeout = 100;\r
10243   ct.ReadTotalTimeoutMultiplier = 0;\r
10244   ct.ReadTotalTimeoutConstant = 0;\r
10245   ct.WriteTotalTimeoutMultiplier = 0;\r
10246   ct.WriteTotalTimeoutConstant = 0;\r
10247   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10248 \r
10249   /* Prepare return value */\r
10250   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10251   cp->kind = CPComm;\r
10252   cp->hFrom = h;\r
10253   cp->hTo = h;\r
10254   *pr = (ProcRef *) cp;\r
10255 \r
10256   return NO_ERROR;\r
10257 }\r
10258 \r
10259 int\r
10260 OpenLoopback(ProcRef *pr)\r
10261 {\r
10262   DisplayFatalError("Not implemented", 0, 1);\r
10263   return NO_ERROR;\r
10264 }\r
10265 \r
10266 \r
10267 int\r
10268 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10269 {\r
10270   ChildProc *cp;\r
10271   int err;\r
10272   SOCKET s, s2, s3;\r
10273   struct sockaddr_in sa, mysa;\r
10274   struct hostent FAR *hp;\r
10275   unsigned short uport;\r
10276   WORD wVersionRequested;\r
10277   WSADATA wsaData;\r
10278   int fromPort;\r
10279   char stderrPortStr[MSG_SIZ];\r
10280 \r
10281   /* Initialize socket DLL */\r
10282   wVersionRequested = MAKEWORD(1, 1);\r
10283   err = WSAStartup(wVersionRequested, &wsaData);\r
10284   if (err != 0) return err;\r
10285 \r
10286   /* Resolve remote host name */\r
10287   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10288   if (!(hp = gethostbyname(host))) {\r
10289     unsigned int b0, b1, b2, b3;\r
10290 \r
10291     err = WSAGetLastError();\r
10292 \r
10293     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10294       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10295       hp->h_addrtype = AF_INET;\r
10296       hp->h_length = 4;\r
10297       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10298       hp->h_addr_list[0] = (char *) malloc(4);\r
10299       hp->h_addr_list[0][0] = (char) b0;\r
10300       hp->h_addr_list[0][1] = (char) b1;\r
10301       hp->h_addr_list[0][2] = (char) b2;\r
10302       hp->h_addr_list[0][3] = (char) b3;\r
10303     } else {\r
10304       WSACleanup();\r
10305       return err;\r
10306     }\r
10307   }\r
10308   sa.sin_family = hp->h_addrtype;\r
10309   uport = (unsigned short) 514;\r
10310   sa.sin_port = htons(uport);\r
10311   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10312 \r
10313   /* Bind local socket to unused "privileged" port address\r
10314    */\r
10315   s = INVALID_SOCKET;\r
10316   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10317   mysa.sin_family = AF_INET;\r
10318   mysa.sin_addr.s_addr = INADDR_ANY;\r
10319   for (fromPort = 1023;; fromPort--) {\r
10320     if (fromPort < 0) {\r
10321       WSACleanup();\r
10322       return WSAEADDRINUSE;\r
10323     }\r
10324     if (s == INVALID_SOCKET) {\r
10325       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10326         err = WSAGetLastError();\r
10327         WSACleanup();\r
10328         return err;\r
10329       }\r
10330     }\r
10331     uport = (unsigned short) fromPort;\r
10332     mysa.sin_port = htons(uport);\r
10333     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10334         == SOCKET_ERROR) {\r
10335       err = WSAGetLastError();\r
10336       if (err == WSAEADDRINUSE) continue;\r
10337       WSACleanup();\r
10338       return err;\r
10339     }\r
10340     if (connect(s, (struct sockaddr *) &sa,\r
10341       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10342       err = WSAGetLastError();\r
10343       if (err == WSAEADDRINUSE) {\r
10344         closesocket(s);\r
10345         s = -1;\r
10346         continue;\r
10347       }\r
10348       WSACleanup();\r
10349       return err;\r
10350     }\r
10351     break;\r
10352   }\r
10353 \r
10354   /* Bind stderr local socket to unused "privileged" port address\r
10355    */\r
10356   s2 = INVALID_SOCKET;\r
10357   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10358   mysa.sin_family = AF_INET;\r
10359   mysa.sin_addr.s_addr = INADDR_ANY;\r
10360   for (fromPort = 1023;; fromPort--) {\r
10361     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10362     if (fromPort < 0) {\r
10363       (void) closesocket(s);\r
10364       WSACleanup();\r
10365       return WSAEADDRINUSE;\r
10366     }\r
10367     if (s2 == INVALID_SOCKET) {\r
10368       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10369         err = WSAGetLastError();\r
10370         closesocket(s);\r
10371         WSACleanup();\r
10372         return err;\r
10373       }\r
10374     }\r
10375     uport = (unsigned short) fromPort;\r
10376     mysa.sin_port = htons(uport);\r
10377     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10378         == SOCKET_ERROR) {\r
10379       err = WSAGetLastError();\r
10380       if (err == WSAEADDRINUSE) continue;\r
10381       (void) closesocket(s);\r
10382       WSACleanup();\r
10383       return err;\r
10384     }\r
10385     if (listen(s2, 1) == SOCKET_ERROR) {\r
10386       err = WSAGetLastError();\r
10387       if (err == WSAEADDRINUSE) {\r
10388         closesocket(s2);\r
10389         s2 = INVALID_SOCKET;\r
10390         continue;\r
10391       }\r
10392       (void) closesocket(s);\r
10393       (void) closesocket(s2);\r
10394       WSACleanup();\r
10395       return err;\r
10396     }\r
10397     break;\r
10398   }\r
10399   prevStderrPort = fromPort; // remember port used\r
10400   sprintf(stderrPortStr, "%d", fromPort);\r
10401 \r
10402   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10403     err = WSAGetLastError();\r
10404     (void) closesocket(s);\r
10405     (void) closesocket(s2);\r
10406     WSACleanup();\r
10407     return err;\r
10408   }\r
10409 \r
10410   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10411     err = WSAGetLastError();\r
10412     (void) closesocket(s);\r
10413     (void) closesocket(s2);\r
10414     WSACleanup();\r
10415     return err;\r
10416   }\r
10417   if (*user == NULLCHAR) user = UserName();\r
10418   if (send(s, user, strlen(user) + 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   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10426     err = WSAGetLastError();\r
10427     (void) closesocket(s);\r
10428     (void) closesocket(s2);\r
10429     WSACleanup();\r
10430     return err;\r
10431   }\r
10432 \r
10433   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10434     err = WSAGetLastError();\r
10435     (void) closesocket(s);\r
10436     (void) closesocket(s2);\r
10437     WSACleanup();\r
10438     return err;\r
10439   }\r
10440   (void) closesocket(s2);  /* Stop listening */\r
10441 \r
10442   /* Prepare return value */\r
10443   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10444   cp->kind = CPRcmd;\r
10445   cp->sock = s;\r
10446   cp->sock2 = s3;\r
10447   *pr = (ProcRef *) cp;\r
10448 \r
10449   return NO_ERROR;\r
10450 }\r
10451 \r
10452 \r
10453 InputSourceRef\r
10454 AddInputSource(ProcRef pr, int lineByLine,\r
10455                InputCallback func, VOIDSTAR closure)\r
10456 {\r
10457   InputSource *is, *is2 = NULL;\r
10458   ChildProc *cp = (ChildProc *) pr;\r
10459 \r
10460   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10461   is->lineByLine = lineByLine;\r
10462   is->func = func;\r
10463   is->closure = closure;\r
10464   is->second = NULL;\r
10465   is->next = is->buf;\r
10466   if (pr == NoProc) {\r
10467     is->kind = CPReal;\r
10468     consoleInputSource = is;\r
10469   } else {\r
10470     is->kind = cp->kind;\r
10471     /* \r
10472         [AS] Try to avoid a race condition if the thread is given control too early:\r
10473         we create all threads suspended so that the is->hThread variable can be\r
10474         safely assigned, then let the threads start with ResumeThread.\r
10475     */\r
10476     switch (cp->kind) {\r
10477     case CPReal:\r
10478       is->hFile = cp->hFrom;\r
10479       cp->hFrom = NULL; /* now owned by InputThread */\r
10480       is->hThread =\r
10481         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10482                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10483       break;\r
10484 \r
10485     case CPComm:\r
10486       is->hFile = cp->hFrom;\r
10487       cp->hFrom = NULL; /* now owned by InputThread */\r
10488       is->hThread =\r
10489         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10490                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10491       break;\r
10492 \r
10493     case CPSock:\r
10494       is->sock = cp->sock;\r
10495       is->hThread =\r
10496         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10497                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10498       break;\r
10499 \r
10500     case CPRcmd:\r
10501       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10502       *is2 = *is;\r
10503       is->sock = cp->sock;\r
10504       is->second = is2;\r
10505       is2->sock = cp->sock2;\r
10506       is2->second = is2;\r
10507       is->hThread =\r
10508         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10509                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10510       is2->hThread =\r
10511         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10512                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10513       break;\r
10514     }\r
10515 \r
10516     if( is->hThread != NULL ) {\r
10517         ResumeThread( is->hThread );\r
10518     }\r
10519 \r
10520     if( is2 != NULL && is2->hThread != NULL ) {\r
10521         ResumeThread( is2->hThread );\r
10522     }\r
10523   }\r
10524 \r
10525   return (InputSourceRef) is;\r
10526 }\r
10527 \r
10528 void\r
10529 RemoveInputSource(InputSourceRef isr)\r
10530 {\r
10531   InputSource *is;\r
10532 \r
10533   is = (InputSource *) isr;\r
10534   is->hThread = NULL;  /* tell thread to stop */\r
10535   CloseHandle(is->hThread);\r
10536   if (is->second != NULL) {\r
10537     is->second->hThread = NULL;\r
10538     CloseHandle(is->second->hThread);\r
10539   }\r
10540 }\r
10541 \r
10542 \r
10543 int\r
10544 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10545 {\r
10546   DWORD dOutCount;\r
10547   int outCount = SOCKET_ERROR;\r
10548   ChildProc *cp = (ChildProc *) pr;\r
10549   static OVERLAPPED ovl;\r
10550 \r
10551   if (pr == NoProc) {\r
10552     ConsoleOutput(message, count, FALSE);\r
10553     return count;\r
10554   } \r
10555 \r
10556   if (ovl.hEvent == NULL) {\r
10557     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10558   }\r
10559   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10560 \r
10561   switch (cp->kind) {\r
10562   case CPSock:\r
10563   case CPRcmd:\r
10564     outCount = send(cp->sock, message, count, 0);\r
10565     if (outCount == SOCKET_ERROR) {\r
10566       *outError = WSAGetLastError();\r
10567     } else {\r
10568       *outError = NO_ERROR;\r
10569     }\r
10570     break;\r
10571 \r
10572   case CPReal:\r
10573     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10574                   &dOutCount, NULL)) {\r
10575       *outError = NO_ERROR;\r
10576       outCount = (int) dOutCount;\r
10577     } else {\r
10578       *outError = GetLastError();\r
10579     }\r
10580     break;\r
10581 \r
10582   case CPComm:\r
10583     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10584                             &dOutCount, &ovl);\r
10585     if (*outError == NO_ERROR) {\r
10586       outCount = (int) dOutCount;\r
10587     }\r
10588     break;\r
10589   }\r
10590   return outCount;\r
10591 }\r
10592 \r
10593 int\r
10594 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10595                        long msdelay)\r
10596 {\r
10597   /* Ignore delay, not implemented for WinBoard */\r
10598   return OutputToProcess(pr, message, count, outError);\r
10599 }\r
10600 \r
10601 \r
10602 void\r
10603 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10604                         char *buf, int count, int error)\r
10605 {\r
10606   DisplayFatalError("Not implemented", 0, 1);\r
10607 }\r
10608 \r
10609 /* see wgamelist.c for Game List functions */\r
10610 /* see wedittags.c for Edit Tags functions */\r
10611 \r
10612 \r
10613 VOID\r
10614 ICSInitScript()\r
10615 {\r
10616   FILE *f;\r
10617   char buf[MSG_SIZ];\r
10618   char *dummy;\r
10619 \r
10620   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10621     f = fopen(buf, "r");\r
10622     if (f != NULL) {\r
10623       ProcessICSInitScript(f);\r
10624       fclose(f);\r
10625     }\r
10626   }\r
10627 }\r
10628 \r
10629 \r
10630 VOID\r
10631 StartAnalysisClock()\r
10632 {\r
10633   if (analysisTimerEvent) return;\r
10634   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10635                                         (UINT) 2000, NULL);\r
10636 }\r
10637 \r
10638 LRESULT CALLBACK\r
10639 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10640 {\r
10641   static HANDLE hwndText;\r
10642   RECT rect;\r
10643   static int sizeX, sizeY;\r
10644   int newSizeX, newSizeY, flags;\r
10645   MINMAXINFO *mmi;\r
10646 \r
10647   switch (message) {\r
10648   case WM_INITDIALOG: /* message: initialize dialog box */\r
10649     /* Initialize the dialog items */\r
10650     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10651     SetWindowText(hDlg, analysisTitle);\r
10652     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10653     /* Size and position the dialog */\r
10654     if (!analysisDialog) {\r
10655       analysisDialog = hDlg;\r
10656       flags = SWP_NOZORDER;\r
10657       GetClientRect(hDlg, &rect);\r
10658       sizeX = rect.right;\r
10659       sizeY = rect.bottom;\r
10660       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10661           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10662         WINDOWPLACEMENT wp;\r
10663         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10664         wp.length = sizeof(WINDOWPLACEMENT);\r
10665         wp.flags = 0;\r
10666         wp.showCmd = SW_SHOW;\r
10667         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10668         wp.rcNormalPosition.left = analysisX;\r
10669         wp.rcNormalPosition.right = analysisX + analysisW;\r
10670         wp.rcNormalPosition.top = analysisY;\r
10671         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10672         SetWindowPlacement(hDlg, &wp);\r
10673 \r
10674         GetClientRect(hDlg, &rect);\r
10675         newSizeX = rect.right;\r
10676         newSizeY = rect.bottom;\r
10677         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10678                               newSizeX, newSizeY);\r
10679         sizeX = newSizeX;\r
10680         sizeY = newSizeY;\r
10681       }\r
10682     }\r
10683     return FALSE;\r
10684 \r
10685   case WM_COMMAND: /* message: received a command */\r
10686     switch (LOWORD(wParam)) {\r
10687     case IDCANCEL:\r
10688       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10689           ExitAnalyzeMode();\r
10690           ModeHighlight();\r
10691           return TRUE;\r
10692       }\r
10693       EditGameEvent();\r
10694       return TRUE;\r
10695     default:\r
10696       break;\r
10697     }\r
10698     break;\r
10699 \r
10700   case WM_SIZE:\r
10701     newSizeX = LOWORD(lParam);\r
10702     newSizeY = HIWORD(lParam);\r
10703     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10704     sizeX = newSizeX;\r
10705     sizeY = newSizeY;\r
10706     break;\r
10707 \r
10708   case WM_GETMINMAXINFO:\r
10709     /* Prevent resizing window too small */\r
10710     mmi = (MINMAXINFO *) lParam;\r
10711     mmi->ptMinTrackSize.x = 100;\r
10712     mmi->ptMinTrackSize.y = 100;\r
10713     break;\r
10714   }\r
10715   return FALSE;\r
10716 }\r
10717 \r
10718 VOID\r
10719 AnalysisPopUp(char* title, char* str)\r
10720 {\r
10721   FARPROC lpProc;\r
10722   char *p, *q;\r
10723 \r
10724   /* [AS] */\r
10725   EngineOutputPopUp();\r
10726   return;\r
10727 \r
10728   if (str == NULL) str = "";\r
10729   p = (char *) malloc(2 * strlen(str) + 2);\r
10730   q = p;\r
10731   while (*str) {\r
10732     if (*str == '\n') *q++ = '\r';\r
10733     *q++ = *str++;\r
10734   }\r
10735   *q = NULLCHAR;\r
10736   if (analysisText != NULL) free(analysisText);\r
10737   analysisText = p;\r
10738 \r
10739   if (analysisDialog) {\r
10740     SetWindowText(analysisDialog, title);\r
10741     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10742     ShowWindow(analysisDialog, SW_SHOW);\r
10743   } else {\r
10744     analysisTitle = title;\r
10745     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10746     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10747                  hwndMain, (DLGPROC)lpProc);\r
10748     FreeProcInstance(lpProc);\r
10749   }\r
10750   analysisDialogUp = TRUE;  \r
10751 }\r
10752 \r
10753 VOID\r
10754 AnalysisPopDown()\r
10755 {\r
10756   if (analysisDialog) {\r
10757     ShowWindow(analysisDialog, SW_HIDE);\r
10758   }\r
10759   analysisDialogUp = FALSE;  \r
10760 }\r
10761 \r
10762 \r
10763 VOID\r
10764 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10765 {\r
10766   highlightInfo.sq[0].x = fromX;\r
10767   highlightInfo.sq[0].y = fromY;\r
10768   highlightInfo.sq[1].x = toX;\r
10769   highlightInfo.sq[1].y = toY;\r
10770 }\r
10771 \r
10772 VOID\r
10773 ClearHighlights()\r
10774 {\r
10775   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10776     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10777 }\r
10778 \r
10779 VOID\r
10780 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10781 {\r
10782   premoveHighlightInfo.sq[0].x = fromX;\r
10783   premoveHighlightInfo.sq[0].y = fromY;\r
10784   premoveHighlightInfo.sq[1].x = toX;\r
10785   premoveHighlightInfo.sq[1].y = toY;\r
10786 }\r
10787 \r
10788 VOID\r
10789 ClearPremoveHighlights()\r
10790 {\r
10791   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10792     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10793 }\r
10794 \r
10795 VOID\r
10796 ShutDownFrontEnd()\r
10797 {\r
10798   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10799   DeleteClipboardTempFiles();\r
10800 }\r
10801 \r
10802 void\r
10803 BoardToTop()\r
10804 {\r
10805     if (IsIconic(hwndMain))\r
10806       ShowWindow(hwndMain, SW_RESTORE);\r
10807 \r
10808     SetActiveWindow(hwndMain);\r
10809 }\r
10810 \r
10811 /*\r
10812  * Prototypes for animation support routines\r
10813  */\r
10814 static void ScreenSquare(int column, int row, POINT * pt);\r
10815 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10816      POINT frames[], int * nFrames);\r
10817 \r
10818 \r
10819 void\r
10820 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10821 {       // [HGM] atomic: animate blast wave\r
10822         int i;\r
10823 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10824         explodeInfo.fromX = fromX;\r
10825         explodeInfo.fromY = fromY;\r
10826         explodeInfo.toX = toX;\r
10827         explodeInfo.toY = toY;\r
10828         for(i=1; i<nFrames; i++) {\r
10829             explodeInfo.radius = (i*180)/(nFrames-1);\r
10830             DrawPosition(FALSE, NULL);\r
10831             Sleep(appData.animSpeed);\r
10832         }\r
10833         explodeInfo.radius = 0;\r
10834         DrawPosition(TRUE, NULL);\r
10835 }\r
10836 \r
10837 #define kFactor 4\r
10838 \r
10839 void\r
10840 AnimateMove(board, fromX, fromY, toX, toY)\r
10841      Board board;\r
10842      int fromX;\r
10843      int fromY;\r
10844      int toX;\r
10845      int toY;\r
10846 {\r
10847   ChessSquare piece;\r
10848   POINT start, finish, mid;\r
10849   POINT frames[kFactor * 2 + 1];\r
10850   int nFrames, n;\r
10851 \r
10852   if (!appData.animate) return;\r
10853   if (doingSizing) return;\r
10854   if (fromY < 0 || fromX < 0) return;\r
10855   piece = board[fromY][fromX];\r
10856   if (piece >= EmptySquare) return;\r
10857 \r
10858   ScreenSquare(fromX, fromY, &start);\r
10859   ScreenSquare(toX, toY, &finish);\r
10860 \r
10861   /* All pieces except knights move in straight line */\r
10862   if (piece != WhiteKnight && piece != BlackKnight) {\r
10863     mid.x = start.x + (finish.x - start.x) / 2;\r
10864     mid.y = start.y + (finish.y - start.y) / 2;\r
10865   } else {\r
10866     /* Knight: make diagonal movement then straight */\r
10867     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10868        mid.x = start.x + (finish.x - start.x) / 2;\r
10869        mid.y = finish.y;\r
10870      } else {\r
10871        mid.x = finish.x;\r
10872        mid.y = start.y + (finish.y - start.y) / 2;\r
10873      }\r
10874   }\r
10875   \r
10876   /* Don't use as many frames for very short moves */\r
10877   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10878     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10879   else\r
10880     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10881 \r
10882   animInfo.from.x = fromX;\r
10883   animInfo.from.y = fromY;\r
10884   animInfo.to.x = toX;\r
10885   animInfo.to.y = toY;\r
10886   animInfo.lastpos = start;\r
10887   animInfo.piece = piece;\r
10888   for (n = 0; n < nFrames; n++) {\r
10889     animInfo.pos = frames[n];\r
10890     DrawPosition(FALSE, NULL);\r
10891     animInfo.lastpos = animInfo.pos;\r
10892     Sleep(appData.animSpeed);\r
10893   }\r
10894   animInfo.pos = finish;\r
10895   DrawPosition(FALSE, NULL);\r
10896   animInfo.piece = EmptySquare;\r
10897   if(gameInfo.variant == VariantAtomic && \r
10898      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10899         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10900 }\r
10901 \r
10902 /*      Convert board position to corner of screen rect and color       */\r
10903 \r
10904 static void\r
10905 ScreenSquare(column, row, pt)\r
10906      int column; int row; POINT * pt;\r
10907 {\r
10908   if (flipView) {\r
10909     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10910     pt->y = lineGap + row * (squareSize + lineGap);\r
10911   } else {\r
10912     pt->x = lineGap + column * (squareSize + lineGap);\r
10913     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10914   }\r
10915 }\r
10916 \r
10917 /*      Generate a series of frame coords from start->mid->finish.\r
10918         The movement rate doubles until the half way point is\r
10919         reached, then halves back down to the final destination,\r
10920         which gives a nice slow in/out effect. The algorithmn\r
10921         may seem to generate too many intermediates for short\r
10922         moves, but remember that the purpose is to attract the\r
10923         viewers attention to the piece about to be moved and\r
10924         then to where it ends up. Too few frames would be less\r
10925         noticeable.                                             */\r
10926 \r
10927 static void\r
10928 Tween(start, mid, finish, factor, frames, nFrames)\r
10929      POINT * start; POINT * mid;\r
10930      POINT * finish; int factor;\r
10931      POINT frames[]; int * nFrames;\r
10932 {\r
10933   int n, fraction = 1, count = 0;\r
10934 \r
10935   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10936   for (n = 0; n < factor; n++)\r
10937     fraction *= 2;\r
10938   for (n = 0; n < factor; n++) {\r
10939     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10940     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10941     count ++;\r
10942     fraction = fraction / 2;\r
10943   }\r
10944   \r
10945   /* Midpoint */\r
10946   frames[count] = *mid;\r
10947   count ++;\r
10948   \r
10949   /* Slow out, stepping 1/2, then 1/4, ... */\r
10950   fraction = 2;\r
10951   for (n = 0; n < factor; n++) {\r
10952     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10953     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10954     count ++;\r
10955     fraction = fraction * 2;\r
10956   }\r
10957   *nFrames = count;\r
10958 }\r
10959 \r
10960 void\r
10961 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10962 {\r
10963 #if 0\r
10964     char buf[256];\r
10965 \r
10966     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10967         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10968 \r
10969     OutputDebugString( buf );\r
10970 #endif\r
10971 \r
10972     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10973 \r
10974     EvalGraphSet( first, last, current, pvInfoList );\r
10975 }\r
10976 \r
10977 void SetProgramStats( FrontEndProgramStats * stats )\r
10978 {\r
10979 #if 0\r
10980     char buf[1024];\r
10981 \r
10982     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10983         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10984 \r
10985     OutputDebugString( buf );\r
10986 #endif\r
10987 \r
10988     EngineOutputUpdate( stats );\r
10989 }\r