Enhancement of the way -autoLogo decides which logos to display
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
107 \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[] = "ICS 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, consoleX, consoleY, consoleW, consoleH;\r
156 static int squareSize, lineGap, minorSize;\r
157 static int winWidth, winHeight;\r
158 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
159 static int logoHeight = 0;\r
160 static char messageText[MESSAGE_TEXT_MAX];\r
161 static int clockTimerEvent = 0;\r
162 static int loadGameTimerEvent = 0;\r
163 static int analysisTimerEvent = 0;\r
164 static DelayedEventCallback delayedTimerCallback;\r
165 static int delayedTimerEvent = 0;\r
166 static int buttonCount = 2;\r
167 char *icsTextMenuString;\r
168 char *icsNames;\r
169 char *firstChessProgramNames;\r
170 char *secondChessProgramNames;\r
171 \r
172 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
173 \r
174 #define PALETTESIZE 256\r
175 \r
176 HINSTANCE hInst;          /* current instance */\r
177 HWND hwndMain = NULL;        /* root window*/\r
178 HWND hwndConsole = NULL;\r
179 BOOLEAN alwaysOnTop = FALSE;\r
180 RECT boardRect;\r
181 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
182   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
183 HPALETTE hPal;\r
184 ColorClass currentColorClass;\r
185 \r
186 HWND hCommPort = NULL;    /* currently open comm port */\r
187 static HWND hwndPause;    /* pause button */\r
188 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
189 static HBRUSH lightSquareBrush, darkSquareBrush,\r
190   blackSquareBrush, /* [HGM] for band between board and holdings */\r
191   explodeBrush,     /* [HGM] atomic */\r
192   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
193 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
194 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
195 static HPEN gridPen = NULL;\r
196 static HPEN highlightPen = NULL;\r
197 static HPEN premovePen = NULL;\r
198 static NPLOGPALETTE pLogPal;\r
199 static BOOL paletteChanged = FALSE;\r
200 static HICON iconWhite, iconBlack, iconCurrent;\r
201 static int doingSizing = FALSE;\r
202 static int lastSizing = 0;\r
203 static int prevStderrPort;\r
204 static HBITMAP userLogo;\r
205 \r
206 /* [AS] Support for background textures */\r
207 #define BACK_TEXTURE_MODE_DISABLED      0\r
208 #define BACK_TEXTURE_MODE_PLAIN         1\r
209 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
210 \r
211 static HBITMAP liteBackTexture = NULL;\r
212 static HBITMAP darkBackTexture = NULL;\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int backTextureSquareSize = 0;\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
217 \r
218 #if __GNUC__ && !defined(_winmajor)\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
220 #else\r
221 #define oldDialog (_winmajor < 4)\r
222 #endif\r
223 \r
224 char *defaultTextAttribs[] = \r
225 {\r
226   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
227   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
228   COLOR_NONE\r
229 };\r
230 \r
231 typedef struct {\r
232   char *name;\r
233   int squareSize;\r
234   int lineGap;\r
235   int smallLayout;\r
236   int tinyLayout;\r
237   int cliWidth, cliHeight;\r
238 } SizeInfo;\r
239 \r
240 SizeInfo sizeInfo[] = \r
241 {\r
242   { "tiny",     21, 0, 1, 1, 0, 0 },\r
243   { "teeny",    25, 1, 1, 1, 0, 0 },\r
244   { "dinky",    29, 1, 1, 1, 0, 0 },\r
245   { "petite",   33, 1, 1, 1, 0, 0 },\r
246   { "slim",     37, 2, 1, 0, 0, 0 },\r
247   { "small",    40, 2, 1, 0, 0, 0 },\r
248   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
249   { "middling", 49, 2, 0, 0, 0, 0 },\r
250   { "average",  54, 2, 0, 0, 0, 0 },\r
251   { "moderate", 58, 3, 0, 0, 0, 0 },\r
252   { "medium",   64, 3, 0, 0, 0, 0 },\r
253   { "bulky",    72, 3, 0, 0, 0, 0 },\r
254   { "large",    80, 3, 0, 0, 0, 0 },\r
255   { "big",      87, 3, 0, 0, 0, 0 },\r
256   { "huge",     95, 3, 0, 0, 0, 0 },\r
257   { "giant",    108, 3, 0, 0, 0, 0 },\r
258   { "colossal", 116, 4, 0, 0, 0, 0 },\r
259   { "titanic",  129, 4, 0, 0, 0, 0 },\r
260   { NULL, 0, 0, 0, 0, 0, 0 }\r
261 };\r
262 \r
263 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
264 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
265 {\r
266   { 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
267   { 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
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284 };\r
285 \r
286 MyFont *font[NUM_SIZES][NUM_FONTS];\r
287 \r
288 typedef struct {\r
289   char *label;\r
290   int id;\r
291   HWND hwnd;\r
292   WNDPROC wndproc;\r
293 } MyButtonDesc;\r
294 \r
295 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
296 #define N_BUTTONS 5\r
297 \r
298 MyButtonDesc buttonDesc[N_BUTTONS] =\r
299 {\r
300   {"<<", IDM_ToStart, NULL, NULL},\r
301   {"<", IDM_Backward, NULL, NULL},\r
302   {"P", IDM_Pause, NULL, NULL},\r
303   {">", IDM_Forward, NULL, NULL},\r
304   {">>", IDM_ToEnd, NULL, NULL},\r
305 };\r
306 \r
307 int tinyLayout = 0, smallLayout = 0;\r
308 #define MENU_BAR_ITEMS 6\r
309 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
310   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
311   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
312 };\r
313 \r
314 \r
315 MySound sounds[(int)NSoundClasses];\r
316 MyTextAttribs textAttribs[(int)NColorClasses];\r
317 \r
318 MyColorizeAttribs colorizeAttribs[] = {\r
319   { (COLORREF)0, 0, "Shout Text" },\r
320   { (COLORREF)0, 0, "SShout/CShout" },\r
321   { (COLORREF)0, 0, "Channel 1 Text" },\r
322   { (COLORREF)0, 0, "Channel Text" },\r
323   { (COLORREF)0, 0, "Kibitz Text" },\r
324   { (COLORREF)0, 0, "Tell Text" },\r
325   { (COLORREF)0, 0, "Challenge Text" },\r
326   { (COLORREF)0, 0, "Request Text" },\r
327   { (COLORREF)0, 0, "Seek Text" },\r
328   { (COLORREF)0, 0, "Normal Text" },\r
329   { (COLORREF)0, 0, "None" }\r
330 };\r
331 \r
332 \r
333 \r
334 static char *commentTitle;\r
335 static char *commentText;\r
336 static int commentIndex;\r
337 static Boolean editComment = FALSE;\r
338 HWND commentDialog = NULL;\r
339 BOOLEAN commentDialogUp = FALSE;\r
340 static int commentX, commentY, commentH, commentW;\r
341 \r
342 static char *analysisTitle;\r
343 static char *analysisText;\r
344 HWND analysisDialog = NULL;\r
345 BOOLEAN analysisDialogUp = FALSE;\r
346 static int analysisX, analysisY, analysisH, analysisW;\r
347 \r
348 char errorTitle[MSG_SIZ];\r
349 char errorMessage[2*MSG_SIZ];\r
350 HWND errorDialog = NULL;\r
351 BOOLEAN moveErrorMessageUp = FALSE;\r
352 BOOLEAN consoleEcho = TRUE;\r
353 CHARFORMAT consoleCF;\r
354 COLORREF consoleBackgroundColor;\r
355 \r
356 char *programVersion;\r
357 \r
358 #define CPReal 1\r
359 #define CPComm 2\r
360 #define CPSock 3\r
361 #define CPRcmd 4\r
362 typedef int CPKind;\r
363 \r
364 typedef struct {\r
365   CPKind kind;\r
366   HANDLE hProcess;\r
367   DWORD pid;\r
368   HANDLE hTo;\r
369   HANDLE hFrom;\r
370   SOCKET sock;\r
371   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
372 } ChildProc;\r
373 \r
374 #define INPUT_SOURCE_BUF_SIZE 4096\r
375 \r
376 typedef struct _InputSource {\r
377   CPKind kind;\r
378   HANDLE hFile;\r
379   SOCKET sock;\r
380   int lineByLine;\r
381   HANDLE hThread;\r
382   DWORD id;\r
383   char buf[INPUT_SOURCE_BUF_SIZE];\r
384   char *next;\r
385   DWORD count;\r
386   int error;\r
387   InputCallback func;\r
388   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
389   VOIDSTAR closure;\r
390 } InputSource;\r
391 \r
392 InputSource *consoleInputSource;\r
393 \r
394 DCB dcb;\r
395 \r
396 /* forward */\r
397 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
398 VOID ConsoleCreate();\r
399 LRESULT CALLBACK\r
400   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
401 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
402 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
403 VOID ParseCommSettings(char *arg, DCB *dcb);\r
404 LRESULT CALLBACK\r
405   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
406 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
407 void ParseIcsTextMenu(char *icsTextMenuString);\r
408 VOID PopUpMoveDialog(char firstchar);\r
409 VOID PopUpNameDialog(char firstchar);\r
410 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
411 \r
412 /* [AS] */\r
413 int NewGameFRC();\r
414 int GameListOptions();\r
415 \r
416 HWND moveHistoryDialog = NULL;\r
417 BOOLEAN moveHistoryDialogUp = FALSE;\r
418 \r
419 WindowPlacement wpMoveHistory;\r
420 \r
421 HWND evalGraphDialog = NULL;\r
422 BOOLEAN evalGraphDialogUp = FALSE;\r
423 \r
424 WindowPlacement wpEvalGraph;\r
425 \r
426 HWND engineOutputDialog = NULL;\r
427 BOOLEAN engineOutputDialogUp = FALSE;\r
428 \r
429 WindowPlacement wpEngineOutput;\r
430 \r
431 VOID MoveHistoryPopUp();\r
432 VOID MoveHistoryPopDown();\r
433 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
434 BOOL MoveHistoryIsUp();\r
435 \r
436 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
437 VOID EvalGraphPopUp();\r
438 VOID EvalGraphPopDown();\r
439 BOOL EvalGraphIsUp();\r
440 \r
441 VOID EngineOutputPopUp();\r
442 VOID EngineOutputPopDown();\r
443 BOOL EngineOutputIsUp();\r
444 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
445 \r
446 VOID GothicPopUp(char *title, VariantClass variant);\r
447 /*\r
448  * Setting "frozen" should disable all user input other than deleting\r
449  * the window.  We do this while engines are initializing themselves.\r
450  */\r
451 static int frozen = 0;\r
452 static int oldMenuItemState[MENU_BAR_ITEMS];\r
453 void FreezeUI()\r
454 {\r
455   HMENU hmenu;\r
456   int i;\r
457 \r
458   if (frozen) return;\r
459   frozen = 1;\r
460   hmenu = GetMenu(hwndMain);\r
461   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
462     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
463   }\r
464   DrawMenuBar(hwndMain);\r
465 }\r
466 \r
467 /* Undo a FreezeUI */\r
468 void ThawUI()\r
469 {\r
470   HMENU hmenu;\r
471   int i;\r
472 \r
473   if (!frozen) return;\r
474   frozen = 0;\r
475   hmenu = GetMenu(hwndMain);\r
476   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
477     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
478   }\r
479   DrawMenuBar(hwndMain);\r
480 }\r
481 \r
482 /*---------------------------------------------------------------------------*\\r
483  *\r
484  * WinMain\r
485  *\r
486 \*---------------------------------------------------------------------------*/\r
487 \r
488 int APIENTRY\r
489 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
490         LPSTR lpCmdLine, int nCmdShow)\r
491 {\r
492   MSG msg;\r
493   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
494 //  INITCOMMONCONTROLSEX ex;\r
495 \r
496   debugFP = stderr;\r
497 \r
498   LoadLibrary("RICHED32.DLL");\r
499   consoleCF.cbSize = sizeof(CHARFORMAT);\r
500 \r
501   if (!InitApplication(hInstance)) {\r
502     return (FALSE);\r
503   }\r
504   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
505     return (FALSE);\r
506   }\r
507 \r
508 //  InitCommonControlsEx(&ex);\r
509   InitCommonControls();\r
510 \r
511   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
512   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
513   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
514 \r
515   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
516 \r
517   while (GetMessage(&msg, /* message structure */\r
518                     NULL, /* handle of window receiving the message */\r
519                     0,    /* lowest message to examine */\r
520                     0))   /* highest message to examine */\r
521     {\r
522       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
523           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
524           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
525           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
526           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
527           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
528           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
529           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
530           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
531           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
532         TranslateMessage(&msg); /* Translates virtual key codes */\r
533         DispatchMessage(&msg);  /* Dispatches message to window */\r
534       }\r
535     }\r
536 \r
537 \r
538   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
539 }\r
540 \r
541 /*---------------------------------------------------------------------------*\\r
542  *\r
543  * Initialization functions\r
544  *\r
545 \*---------------------------------------------------------------------------*/\r
546 \r
547 void\r
548 SetUserLogo()\r
549 {   // update user logo if necessary\r
550     static char oldUserName[MSG_SIZ], *curName;\r
551 \r
552     if(appData.autoLogo) {\r
553           curName = UserName();\r
554           if(strcmp(curName, oldUserName)) {\r
555                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
556                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
557                 strcpy(oldUserName, curName);\r
558           }\r
559     }\r
560 }\r
561 \r
562 BOOL\r
563 InitApplication(HINSTANCE hInstance)\r
564 {\r
565   WNDCLASS wc;\r
566 \r
567   /* Fill in window class structure with parameters that describe the */\r
568   /* main window. */\r
569 \r
570   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
571   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
572   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
573   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
574   wc.hInstance     = hInstance;         /* Owner of this class */\r
575   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
576   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
577   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
578   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
579   wc.lpszClassName = szAppName;                 /* Name to register as */\r
580 \r
581   /* Register the window class and return success/failure code. */\r
582   if (!RegisterClass(&wc)) return FALSE;\r
583 \r
584   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
585   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
586   wc.cbClsExtra    = 0;\r
587   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
588   wc.hInstance     = hInstance;\r
589   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
590   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
591   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
592   wc.lpszMenuName  = NULL;\r
593   wc.lpszClassName = szConsoleName;\r
594 \r
595   if (!RegisterClass(&wc)) return FALSE;\r
596   return TRUE;\r
597 }\r
598 \r
599 \r
600 /* Set by InitInstance, used by EnsureOnScreen */\r
601 int screenHeight, screenWidth;\r
602 \r
603 void\r
604 EnsureOnScreen(int *x, int *y)\r
605 {\r
606 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
607   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
608   if (*x > screenWidth - 32) *x = 0;\r
609   if (*y > screenHeight - 32) *y = 0;\r
610   if (*x < 0) *x = 0;\r
611   if (*y < 0) *y = 0;\r
612 //  if (*x < 10) *x = 10;\r
613 //  if (*y < gap) *y = gap;\r
614 }\r
615 \r
616 BOOL\r
617 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
618 {\r
619   HWND hwnd; /* Main window handle. */\r
620   int ibs;\r
621   WINDOWPLACEMENT wp;\r
622   char *filepart;\r
623 \r
624   hInst = hInstance;    /* Store instance handle in our global variable */\r
625 \r
626   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
627     *filepart = NULLCHAR;\r
628   } else {\r
629     GetCurrentDirectory(MSG_SIZ, installDir);\r
630   }\r
631   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
632   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
633   if (appData.debugMode) {\r
634     debugFP = fopen(appData.nameOfDebugFile, "w");\r
635     setbuf(debugFP, NULL);\r
636   }\r
637 \r
638   InitBackEnd1();\r
639 \r
640 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
641 //  InitEngineUCI( installDir, &second );\r
642 \r
643   /* Create a main window for this application instance. */\r
644   hwnd = CreateWindow(szAppName, szTitle,\r
645                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
646                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
647                       NULL, NULL, hInstance, NULL);\r
648   hwndMain = hwnd;\r
649 \r
650   /* If window could not be created, return "failure" */\r
651   if (!hwnd) {\r
652     return (FALSE);\r
653   }\r
654 \r
655   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
656   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
657       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
658 \r
659       if (first.programLogo == NULL && appData.debugMode) {\r
660           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
661       }\r
662   } else if(appData.autoLogo) {\r
663       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
664         char buf[MSG_SIZ];\r
665         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
666         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
667       }\r
668   }\r
669 \r
670   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
671       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
672 \r
673       if (second.programLogo == NULL && appData.debugMode) {\r
674           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
675       }\r
676   } else if(appData.autoLogo) {\r
677       char buf[MSG_SIZ];\r
678       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
679         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
680         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
681       } else\r
682       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
683         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
684         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
685       }\r
686   }\r
687 \r
688   SetUserLogo();\r
689 \r
690   iconWhite = LoadIcon(hInstance, "icon_white");\r
691   iconBlack = LoadIcon(hInstance, "icon_black");\r
692   iconCurrent = iconWhite;\r
693   InitDrawingColors();\r
694   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
695   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
696   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
697     /* Compute window size for each board size, and use the largest\r
698        size that fits on this screen as the default. */\r
699     InitDrawingSizes((BoardSize)ibs, 0);\r
700     if (boardSize == (BoardSize)-1 &&\r
701         winHeight <= screenHeight\r
702            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
703         && winWidth <= screenWidth) {\r
704       boardSize = (BoardSize)ibs;\r
705     }\r
706   }\r
707 \r
708   InitDrawingSizes(boardSize, 0);\r
709   InitMenuChecks();\r
710   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
711 \r
712   /* [AS] Load textures if specified */\r
713   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
714   \r
715   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
716       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
717       liteBackTextureMode = appData.liteBackTextureMode;\r
718 \r
719       if (liteBackTexture == NULL && appData.debugMode) {\r
720           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
721       }\r
722   }\r
723   \r
724   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
725       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
726       darkBackTextureMode = appData.darkBackTextureMode;\r
727 \r
728       if (darkBackTexture == NULL && appData.debugMode) {\r
729           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
730       }\r
731   }\r
732 \r
733   mysrandom( (unsigned) time(NULL) );\r
734 \r
735   /* [AS] Restore layout */\r
736   if( wpMoveHistory.visible ) {\r
737       MoveHistoryPopUp();\r
738   }\r
739 \r
740   if( wpEvalGraph.visible ) {\r
741       EvalGraphPopUp();\r
742   }\r
743 \r
744   if( wpEngineOutput.visible ) {\r
745       EngineOutputPopUp();\r
746   }\r
747 \r
748   InitBackEnd2();\r
749 \r
750   /* Make the window visible; update its client area; and return "success" */\r
751   EnsureOnScreen(&boardX, &boardY);\r
752   wp.length = sizeof(WINDOWPLACEMENT);\r
753   wp.flags = 0;\r
754   wp.showCmd = nCmdShow;\r
755   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
756   wp.rcNormalPosition.left = boardX;\r
757   wp.rcNormalPosition.right = boardX + winWidth;\r
758   wp.rcNormalPosition.top = boardY;\r
759   wp.rcNormalPosition.bottom = boardY + winHeight;\r
760   SetWindowPlacement(hwndMain, &wp);\r
761 \r
762   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
763                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
764 \r
765 #if 0\r
766   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
767   if( gameInfo.variant != VariantFischeRandom ) {\r
768       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
769   }\r
770 #endif\r
771   if (hwndConsole) {\r
772 #if AOT_CONSOLE\r
773     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
774                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
775 #endif\r
776     ShowWindow(hwndConsole, nCmdShow);\r
777   }\r
778   UpdateWindow(hwnd);\r
779 \r
780   return TRUE;\r
781 \r
782 }\r
783 \r
784 \r
785 typedef enum {\r
786   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
787   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
788   ArgSettingsFilename\r
789 } ArgType;\r
790 \r
791 typedef struct {\r
792   char *argName;\r
793   ArgType argType;\r
794   /***\r
795   union {\r
796     String *pString;       // ArgString\r
797     int *pInt;             // ArgInt\r
798     float *pFloat;         // ArgFloat\r
799     Boolean *pBoolean;     // ArgBoolean\r
800     COLORREF *pColor;      // ArgColor\r
801     ColorClass cc;         // ArgAttribs\r
802     String *pFilename;     // ArgFilename\r
803     BoardSize *pBoardSize; // ArgBoardSize\r
804     int whichFont;         // ArgFont\r
805     DCB *pDCB;             // ArgCommSettings\r
806     String *pFilename;     // ArgSettingsFilename\r
807   } argLoc;\r
808   ***/\r
809   LPVOID argLoc;\r
810   BOOL save;\r
811 } ArgDescriptor;\r
812 \r
813 int junk;\r
814 ArgDescriptor argDescriptors[] = {\r
815   /* positional arguments */\r
816   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
817   { "", ArgNone, NULL },\r
818   /* keyword arguments */\r
819   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
820   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
821   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
822   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
823   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
824   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
825   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
826   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
827   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
828   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
829   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
830   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
831   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
832   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
833   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
834   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
835   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
836   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
837     FALSE },\r
838   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
839     FALSE },\r
840   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
841     FALSE },\r
842   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
843   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
844     FALSE },\r
845   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
846   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
847   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
848   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
849   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
850   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
851   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
852   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
853   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
854   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
855   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
856   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
857   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
858   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
859   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
860   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
861   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
862   /*!!bitmapDirectory?*/\r
863   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
864   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
865   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
866   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
867   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
868   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
869   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
870   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
871   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
872   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
873   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
874   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
875   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
876   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
877   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
878   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
879   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
880   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
881   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
882   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
883   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
884   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
885   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
886   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
887   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
888   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
889   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
890   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
891   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
892   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
893   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
894   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
895   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
896   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
897   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
898   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
899   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
900   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
901   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
902   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
903   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
904   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
905   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
906   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
907   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
908   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
909   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
910   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
911   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
912   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
913   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
914   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
915   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
916   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
917   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
918   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
919   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
920   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
921   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
922   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
923   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
924   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
925   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
926   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
927   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
928   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
929   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
930   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
931   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
932   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
933   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
934   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
935   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
936   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
937   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
938   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
939   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
940   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
941   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
942   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
943   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
944   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
945   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
946   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
947   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
948   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
949   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
950   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
951   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
952   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
953   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
954   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
955   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
956   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
957     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
958   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
959   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
960   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
961   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
962   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
963   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
964   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
965   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
966     TRUE }, /* must come after all fonts */\r
967   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
968   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
969     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
970   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
971   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
972   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
973   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
974   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
975   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
976   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
977   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
978   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
979   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
980   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
981   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
982   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
983   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
984   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
985   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
986   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
987   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
988   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
989   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
990   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
991   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
992   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
993   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
994   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
995   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
996   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
997   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
998 #if 0\r
999   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1000   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1001 #endif\r
1002   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1003   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1004   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1005   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1006   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1007   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1008   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1009   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1010   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1011   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1012   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1013   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1014   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1015   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1016   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1017   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1018   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1019   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1020   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1021   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1022   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1023   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1024   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1025   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1026   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1027   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1028   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1029   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1030   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1031   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1032   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1033   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1034   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1035   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1036   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1037   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1038   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1039   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1040   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1041   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1042   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1043   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1044   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1045   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1046   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1047   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1048   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1049   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1050   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1051   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1052   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1053   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1054   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1055   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1056   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1057   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1058   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1059   { "highlightLastMove", ArgBoolean,\r
1060     (LPVOID) &appData.highlightLastMove, TRUE },\r
1061   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1062   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1063   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1064   { "highlightDragging", ArgBoolean,\r
1065     (LPVOID) &appData.highlightDragging, TRUE },\r
1066   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1067   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1068   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1069   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1070   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1071   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1072   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1073   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1074   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1075   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1076   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1077   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1078   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1079   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1080   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1081   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1082   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1083   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1084   { "soundShout", ArgFilename,\r
1085     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1086   { "soundSShout", ArgFilename,\r
1087     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1088   { "soundChannel1", ArgFilename,\r
1089     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1090   { "soundChannel", ArgFilename,\r
1091     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1092   { "soundKibitz", ArgFilename,\r
1093     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1094   { "soundTell", ArgFilename,\r
1095     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1096   { "soundChallenge", ArgFilename,\r
1097     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1098   { "soundRequest", ArgFilename,\r
1099     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1100   { "soundSeek", ArgFilename,\r
1101     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1102   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1103   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1104   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1105   { "soundIcsLoss", ArgFilename, \r
1106     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1107   { "soundIcsDraw", ArgFilename, \r
1108     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1109   { "soundIcsUnfinished", ArgFilename, \r
1110     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1111   { "soundIcsAlarm", ArgFilename, \r
1112     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1113   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1114   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1115   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1116   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1117   { "reuseChessPrograms", ArgBoolean,\r
1118     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1119   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1120   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1121   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1122   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1123   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1124   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1125   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1126   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1127   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1128   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1129   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1130   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1131   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1132   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1133   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1134   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1135   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1136   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1137   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1138   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1139   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1140   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1141   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1142   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1143   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1144   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1145   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1146   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1147   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1148   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1149   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1150   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1151   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1152   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1153   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1154   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1155   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1156     TRUE },\r
1157   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1158     TRUE },\r
1159   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1160   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1161   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1162   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1163   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1164   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1165   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1166   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1167   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1168   /* [AS] New features */\r
1169   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1170   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1171   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1172   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1173   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1174   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1175   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1176   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1177   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1178   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1179   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1180   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1181   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1182   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1183   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1184   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1185   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1186   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1187   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1188   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1189   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1190   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1191   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1192   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1193   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1194   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1195   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1196   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1197   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1198   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1199   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1200   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1201   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1202   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1203   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1204   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1205   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1206   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1207   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1208   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1209   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1210   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1211   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1212   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1213   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1214   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1215   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1216   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1217   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1218   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1219 \r
1220   /* [AS] Layout stuff */\r
1221   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1222   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1223   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1224   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1225   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1226 \r
1227   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1228   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1229   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1230   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1231   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1232 \r
1233   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1234   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1235   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1236   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1237   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1238 \r
1239   /* [HGM] board-size, adjudication and misc. options */\r
1240   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1241   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1242   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1243   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1244   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1245   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1246   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1247   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1248   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1249   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1250   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1251   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1252   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1253   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1254   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1255   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1256   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1257   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1258   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1259   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1260   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1261   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1262   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1263   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1264   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1265   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1266   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1267   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1268   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1269   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1270   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1271 \r
1272 #ifdef ZIPPY\r
1273   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1274   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1275   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1276   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1277   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1278   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1279   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1280   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1281   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1282   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1283   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1284   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1285   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1286     FALSE },\r
1287   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1288   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1289   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1290   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1291   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1292   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1293   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1294     FALSE },\r
1295   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1296   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1297   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1298   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1299   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1300   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1301   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1302   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1303   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1304   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1305   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1306   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1307   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1308   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1309   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1310   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1311   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1312   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1313   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1314 #endif\r
1315   /* [HGM] options for broadcasting and time odds */\r
1316   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1317   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1318   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1319   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1320   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1321   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1322   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1323   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1324   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1325   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1326   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1327   { NULL, ArgNone, NULL, FALSE }\r
1328 };\r
1329 \r
1330 \r
1331 /* Kludge for indirection files on command line */\r
1332 char* lastIndirectionFilename;\r
1333 ArgDescriptor argDescriptorIndirection =\r
1334 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1335 \r
1336 \r
1337 VOID\r
1338 ExitArgError(char *msg, char *badArg)\r
1339 {\r
1340   char buf[MSG_SIZ];\r
1341 \r
1342   sprintf(buf, "%s %s", msg, badArg);\r
1343   DisplayFatalError(buf, 0, 2);\r
1344   exit(2);\r
1345 }\r
1346 \r
1347 /* Command line font name parser.  NULL name means do nothing.\r
1348    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1349    For backward compatibility, syntax without the colon is also\r
1350    accepted, but font names with digits in them won't work in that case.\r
1351 */\r
1352 VOID\r
1353 ParseFontName(char *name, MyFontParams *mfp)\r
1354 {\r
1355   char *p, *q;\r
1356   if (name == NULL) return;\r
1357   p = name;\r
1358   q = strchr(p, ':');\r
1359   if (q) {\r
1360     if (q - p >= sizeof(mfp->faceName))\r
1361       ExitArgError("Font name too long:", name);\r
1362     memcpy(mfp->faceName, p, q - p);\r
1363     mfp->faceName[q - p] = NULLCHAR;\r
1364     p = q + 1;\r
1365   } else {\r
1366     q = mfp->faceName;\r
1367     while (*p && !isdigit(*p)) {\r
1368       *q++ = *p++;\r
1369       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1370         ExitArgError("Font name too long:", name);\r
1371     }\r
1372     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1373     *q = NULLCHAR;\r
1374   }\r
1375   if (!*p) ExitArgError("Font point size missing:", name);\r
1376   mfp->pointSize = (float) atof(p);\r
1377   mfp->bold = (strchr(p, 'b') != NULL);\r
1378   mfp->italic = (strchr(p, 'i') != NULL);\r
1379   mfp->underline = (strchr(p, 'u') != NULL);\r
1380   mfp->strikeout = (strchr(p, 's') != NULL);\r
1381 }\r
1382 \r
1383 /* Color name parser.\r
1384    X version accepts X color names, but this one\r
1385    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1386 COLORREF\r
1387 ParseColorName(char *name)\r
1388 {\r
1389   int red, green, blue, count;\r
1390   char buf[MSG_SIZ];\r
1391 \r
1392   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1393   if (count != 3) {\r
1394     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1395       &red, &green, &blue);\r
1396   }\r
1397   if (count != 3) {\r
1398     sprintf(buf, "Can't parse color name %s", name);\r
1399     DisplayError(buf, 0);\r
1400     return RGB(0, 0, 0);\r
1401   }\r
1402   return PALETTERGB(red, green, blue);\r
1403 }\r
1404 \r
1405 \r
1406 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1407 {\r
1408   char *e = argValue;\r
1409   int eff = 0;\r
1410 \r
1411   while (*e) {\r
1412     if (*e == 'b')      eff |= CFE_BOLD;\r
1413     else if (*e == 'i') eff |= CFE_ITALIC;\r
1414     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1415     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1416     else if (*e == '#' || isdigit(*e)) break;\r
1417     e++;\r
1418   }\r
1419   *effects = eff;\r
1420   *color   = ParseColorName(e);\r
1421 }\r
1422 \r
1423 \r
1424 BoardSize\r
1425 ParseBoardSize(char *name)\r
1426 {\r
1427   BoardSize bs = SizeTiny;\r
1428   while (sizeInfo[bs].name != NULL) {\r
1429     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1430     bs++;\r
1431   }\r
1432   ExitArgError("Unrecognized board size value", name);\r
1433   return bs; /* not reached */\r
1434 }\r
1435 \r
1436 \r
1437 char\r
1438 StringGet(void *getClosure)\r
1439 {\r
1440   char **p = (char **) getClosure;\r
1441   return *((*p)++);\r
1442 }\r
1443 \r
1444 char\r
1445 FileGet(void *getClosure)\r
1446 {\r
1447   int c;\r
1448   FILE* f = (FILE*) getClosure;\r
1449 \r
1450   c = getc(f);\r
1451   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1452   if (c == EOF)\r
1453     return NULLCHAR;\r
1454   else\r
1455     return (char) c;\r
1456 }\r
1457 \r
1458 /* Parse settings file named "name". If file found, return the\r
1459    full name in fullname and return TRUE; else return FALSE */\r
1460 BOOLEAN\r
1461 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1462 {\r
1463   char *dummy;\r
1464   FILE *f;\r
1465   int ok; char buf[MSG_SIZ];\r
1466 \r
1467   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1468   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1469     sprintf(buf, "%s.ini", name);\r
1470     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1471   }\r
1472   if (ok) {\r
1473     f = fopen(fullname, "r");\r
1474     if (f != NULL) {\r
1475       ParseArgs(FileGet, f);\r
1476       fclose(f);\r
1477       return TRUE;\r
1478     }\r
1479   }\r
1480   return FALSE;\r
1481 }\r
1482 \r
1483 VOID\r
1484 ParseArgs(GetFunc get, void *cl)\r
1485 {\r
1486   char argName[ARG_MAX];\r
1487   char argValue[ARG_MAX];\r
1488   ArgDescriptor *ad;\r
1489   char start;\r
1490   char *q;\r
1491   int i, octval;\r
1492   char ch;\r
1493   int posarg = 0;\r
1494 \r
1495   ch = get(cl);\r
1496   for (;;) {\r
1497     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1498     if (ch == NULLCHAR) break;\r
1499     if (ch == ';') {\r
1500       /* Comment to end of line */\r
1501       ch = get(cl);\r
1502       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1503       continue;\r
1504     } else if (ch == '/' || ch == '-') {\r
1505       /* Switch */\r
1506       q = argName;\r
1507       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1508              ch != '\n' && ch != '\t') {\r
1509         *q++ = ch;\r
1510         ch = get(cl);\r
1511       }\r
1512       *q = NULLCHAR;\r
1513 \r
1514       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1515         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1516 \r
1517       if (ad->argName == NULL)\r
1518         ExitArgError("Unrecognized argument", argName);\r
1519 \r
1520     } else if (ch == '@') {\r
1521       /* Indirection file */\r
1522       ad = &argDescriptorIndirection;\r
1523       ch = get(cl);\r
1524     } else {\r
1525       /* Positional argument */\r
1526       ad = &argDescriptors[posarg++];\r
1527       strcpy(argName, ad->argName);\r
1528     }\r
1529 \r
1530     if (ad->argType == ArgTrue) {\r
1531       *(Boolean *) ad->argLoc = TRUE;\r
1532       continue;\r
1533     }\r
1534     if (ad->argType == ArgFalse) {\r
1535       *(Boolean *) ad->argLoc = FALSE;\r
1536       continue;\r
1537     }\r
1538 \r
1539     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1540     if (ch == NULLCHAR || ch == '\n') {\r
1541       ExitArgError("No value provided for argument", argName);\r
1542     }\r
1543     q = argValue;\r
1544     if (ch == '{') {\r
1545       // Quoting with { }.  No characters have to (or can) be escaped.\r
1546       // Thus the string cannot contain a '}' character.\r
1547       start = ch;\r
1548       ch = get(cl);\r
1549       while (start) {\r
1550         switch (ch) {\r
1551         case NULLCHAR:\r
1552           start = NULLCHAR;\r
1553           break;\r
1554           \r
1555         case '}':\r
1556           ch = get(cl);\r
1557           start = NULLCHAR;\r
1558           break;\r
1559 \r
1560         default:\r
1561           *q++ = ch;\r
1562           ch = get(cl);\r
1563           break;\r
1564         }\r
1565       }   \r
1566     } else if (ch == '\'' || ch == '"') {\r
1567       // Quoting with ' ' or " ", with \ as escape character.\r
1568       // Inconvenient for long strings that may contain Windows filenames.\r
1569       start = ch;\r
1570       ch = get(cl);\r
1571       while (start) {\r
1572         switch (ch) {\r
1573         case NULLCHAR:\r
1574           start = NULLCHAR;\r
1575           break;\r
1576 \r
1577         default:\r
1578         not_special:\r
1579           *q++ = ch;\r
1580           ch = get(cl);\r
1581           break;\r
1582 \r
1583         case '\'':\r
1584         case '\"':\r
1585           if (ch == start) {\r
1586             ch = get(cl);\r
1587             start = NULLCHAR;\r
1588             break;\r
1589           } else {\r
1590             goto not_special;\r
1591           }\r
1592 \r
1593         case '\\':\r
1594           if (ad->argType == ArgFilename\r
1595               || ad->argType == ArgSettingsFilename) {\r
1596               goto not_special;\r
1597           }\r
1598           ch = get(cl);\r
1599           switch (ch) {\r
1600           case NULLCHAR:\r
1601             ExitArgError("Incomplete \\ escape in value for", argName);\r
1602             break;\r
1603           case 'n':\r
1604             *q++ = '\n';\r
1605             ch = get(cl);\r
1606             break;\r
1607           case 'r':\r
1608             *q++ = '\r';\r
1609             ch = get(cl);\r
1610             break;\r
1611           case 't':\r
1612             *q++ = '\t';\r
1613             ch = get(cl);\r
1614             break;\r
1615           case 'b':\r
1616             *q++ = '\b';\r
1617             ch = get(cl);\r
1618             break;\r
1619           case 'f':\r
1620             *q++ = '\f';\r
1621             ch = get(cl);\r
1622             break;\r
1623           default:\r
1624             octval = 0;\r
1625             for (i = 0; i < 3; i++) {\r
1626               if (ch >= '0' && ch <= '7') {\r
1627                 octval = octval*8 + (ch - '0');\r
1628                 ch = get(cl);\r
1629               } else {\r
1630                 break;\r
1631               }\r
1632             }\r
1633             if (i > 0) {\r
1634               *q++ = (char) octval;\r
1635             } else {\r
1636               *q++ = ch;\r
1637               ch = get(cl);\r
1638             }\r
1639             break;\r
1640           }\r
1641           break;\r
1642         }\r
1643       }\r
1644     } else {\r
1645       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1646         *q++ = ch;\r
1647         ch = get(cl);\r
1648       }\r
1649     }\r
1650     *q = NULLCHAR;\r
1651 \r
1652     switch (ad->argType) {\r
1653     case ArgInt:\r
1654       *(int *) ad->argLoc = atoi(argValue);\r
1655       break;\r
1656 \r
1657     case ArgFloat:\r
1658       *(float *) ad->argLoc = (float) atof(argValue);\r
1659       break;\r
1660 \r
1661     case ArgString:\r
1662     case ArgFilename:\r
1663       *(char **) ad->argLoc = strdup(argValue);\r
1664       break;\r
1665 \r
1666     case ArgSettingsFilename:\r
1667       {\r
1668         char fullname[MSG_SIZ];\r
1669         if (ParseSettingsFile(argValue, fullname)) {\r
1670           if (ad->argLoc != NULL) {\r
1671             *(char **) ad->argLoc = strdup(fullname);\r
1672           }\r
1673         } else {\r
1674           if (ad->argLoc != NULL) {\r
1675           } else {\r
1676             ExitArgError("Failed to open indirection file", argValue);\r
1677           }\r
1678         }\r
1679       }\r
1680       break;\r
1681 \r
1682     case ArgBoolean:\r
1683       switch (argValue[0]) {\r
1684       case 't':\r
1685       case 'T':\r
1686         *(Boolean *) ad->argLoc = TRUE;\r
1687         break;\r
1688       case 'f':\r
1689       case 'F':\r
1690         *(Boolean *) ad->argLoc = FALSE;\r
1691         break;\r
1692       default:\r
1693         ExitArgError("Unrecognized boolean argument value", argValue);\r
1694         break;\r
1695       }\r
1696       break;\r
1697 \r
1698     case ArgColor:\r
1699       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1700       break;\r
1701 \r
1702     case ArgAttribs: {\r
1703       ColorClass cc = (ColorClass)ad->argLoc;\r
1704       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1705       }\r
1706       break;\r
1707       \r
1708     case ArgBoardSize:\r
1709       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1710       break;\r
1711 \r
1712     case ArgFont:\r
1713       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1714       break;\r
1715 \r
1716     case ArgCommSettings:\r
1717       ParseCommSettings(argValue, &dcb);\r
1718       break;\r
1719 \r
1720     case ArgNone:\r
1721       ExitArgError("Unrecognized argument", argValue);\r
1722       break;\r
1723     case ArgTrue:\r
1724     case ArgFalse: ;\r
1725     }\r
1726   }\r
1727 }\r
1728 \r
1729 VOID\r
1730 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1731 {\r
1732   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1733   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1734   DeleteDC(hdc);\r
1735   lf->lfWidth = 0;\r
1736   lf->lfEscapement = 0;\r
1737   lf->lfOrientation = 0;\r
1738   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1739   lf->lfItalic = mfp->italic;\r
1740   lf->lfUnderline = mfp->underline;\r
1741   lf->lfStrikeOut = mfp->strikeout;\r
1742   lf->lfCharSet = DEFAULT_CHARSET;\r
1743   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1744   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1745   lf->lfQuality = DEFAULT_QUALITY;\r
1746   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1747   strcpy(lf->lfFaceName, mfp->faceName);\r
1748 }\r
1749 \r
1750 VOID\r
1751 CreateFontInMF(MyFont *mf)\r
1752 {\r
1753   LFfromMFP(&mf->lf, &mf->mfp);\r
1754   if (mf->hf) DeleteObject(mf->hf);\r
1755   mf->hf = CreateFontIndirect(&mf->lf);\r
1756 }\r
1757 \r
1758 VOID\r
1759 SetDefaultTextAttribs()\r
1760 {\r
1761   ColorClass cc;\r
1762   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1763     ParseAttribs(&textAttribs[cc].color, \r
1764                  &textAttribs[cc].effects, \r
1765                  defaultTextAttribs[cc]);\r
1766   }\r
1767 }\r
1768 \r
1769 VOID\r
1770 SetDefaultSounds()\r
1771 {\r
1772   ColorClass cc;\r
1773   SoundClass sc;\r
1774   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1775     textAttribs[cc].sound.name = strdup("");\r
1776     textAttribs[cc].sound.data = NULL;\r
1777   }\r
1778   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1779     sounds[sc].name = strdup("");\r
1780     sounds[sc].data = NULL;\r
1781   }\r
1782   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1783 }\r
1784 \r
1785 VOID\r
1786 LoadAllSounds()\r
1787 {\r
1788   ColorClass cc;\r
1789   SoundClass sc;\r
1790   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1791     MyLoadSound(&textAttribs[cc].sound);\r
1792   }\r
1793   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1794     MyLoadSound(&sounds[sc]);\r
1795   }\r
1796 }\r
1797 \r
1798 VOID\r
1799 InitAppData(LPSTR lpCmdLine)\r
1800 {\r
1801   int i, j;\r
1802   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1803   char *dummy, *p;\r
1804 \r
1805   programName = szAppName;\r
1806 \r
1807   /* Initialize to defaults */\r
1808   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1809   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1810   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1811   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1812   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1813   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1814   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1815   SetDefaultTextAttribs();\r
1816   SetDefaultSounds();\r
1817   appData.movesPerSession = MOVES_PER_SESSION;\r
1818   appData.initString = INIT_STRING;\r
1819   appData.secondInitString = INIT_STRING;\r
1820   appData.firstComputerString = COMPUTER_STRING;\r
1821   appData.secondComputerString = COMPUTER_STRING;\r
1822   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1823   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1824   appData.firstPlaysBlack = FALSE;\r
1825   appData.noChessProgram = FALSE;\r
1826   chessProgram = FALSE;\r
1827   appData.firstHost = FIRST_HOST;\r
1828   appData.secondHost = SECOND_HOST;\r
1829   appData.firstDirectory = FIRST_DIRECTORY;\r
1830   appData.secondDirectory = SECOND_DIRECTORY;\r
1831   appData.bitmapDirectory = "";\r
1832   appData.remoteShell = REMOTE_SHELL;\r
1833   appData.remoteUser = "";\r
1834   appData.timeDelay = TIME_DELAY;\r
1835   appData.timeControl = TIME_CONTROL;\r
1836   appData.timeIncrement = TIME_INCREMENT;\r
1837   appData.icsActive = FALSE;\r
1838   appData.icsHost = "";\r
1839   appData.icsPort = ICS_PORT;\r
1840   appData.icsCommPort = ICS_COMM_PORT;\r
1841   appData.icsLogon = ICS_LOGON;\r
1842   appData.icsHelper = "";\r
1843   appData.useTelnet = FALSE;\r
1844   appData.telnetProgram = TELNET_PROGRAM;\r
1845   appData.gateway = "";\r
1846   appData.loadGameFile = "";\r
1847   appData.loadGameIndex = 0;\r
1848   appData.saveGameFile = "";\r
1849   appData.autoSaveGames = FALSE;\r
1850   appData.loadPositionFile = "";\r
1851   appData.loadPositionIndex = 1;\r
1852   appData.savePositionFile = "";\r
1853   appData.matchMode = FALSE;\r
1854   appData.matchGames = 0;\r
1855   appData.monoMode = FALSE;\r
1856   appData.debugMode = FALSE;\r
1857   appData.clockMode = TRUE;\r
1858   boardSize = (BoardSize) -1; /* determine by screen size */\r
1859   appData.Iconic = FALSE; /*unused*/\r
1860   appData.searchTime = "";\r
1861   appData.searchDepth = 0;\r
1862   appData.showCoords = FALSE;\r
1863   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1864   appData.autoCallFlag = FALSE;\r
1865   appData.flipView = FALSE;\r
1866   appData.autoFlipView = TRUE;\r
1867   appData.cmailGameName = "";\r
1868   appData.alwaysPromoteToQueen = FALSE;\r
1869   appData.oldSaveStyle = FALSE;\r
1870   appData.quietPlay = FALSE;\r
1871   appData.showThinking = FALSE;\r
1872   appData.ponderNextMove = TRUE;\r
1873   appData.periodicUpdates = TRUE;\r
1874   appData.popupExitMessage = TRUE;\r
1875   appData.popupMoveErrors = FALSE;\r
1876   appData.autoObserve = FALSE;\r
1877   appData.autoComment = FALSE;\r
1878   appData.animate = TRUE;\r
1879   appData.animSpeed = 10;\r
1880   appData.animateDragging = TRUE;\r
1881   appData.highlightLastMove = TRUE;\r
1882   appData.getMoveList = TRUE;\r
1883   appData.testLegality = TRUE;\r
1884   appData.premove = TRUE;\r
1885   appData.premoveWhite = FALSE;\r
1886   appData.premoveWhiteText = "";\r
1887   appData.premoveBlack = FALSE;\r
1888   appData.premoveBlackText = "";\r
1889   appData.icsAlarm = TRUE;\r
1890   appData.icsAlarmTime = 5000;\r
1891   appData.autoRaiseBoard = TRUE;\r
1892   appData.localLineEditing = TRUE;\r
1893   appData.colorize = TRUE;\r
1894   appData.reuseFirst = TRUE;\r
1895   appData.reuseSecond = TRUE;\r
1896   appData.blindfold = FALSE;\r
1897   appData.icsEngineAnalyze = FALSE;\r
1898   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1899   dcb.DCBlength = sizeof(DCB);\r
1900   dcb.BaudRate = 9600;\r
1901   dcb.fBinary = TRUE;\r
1902   dcb.fParity = FALSE;\r
1903   dcb.fOutxCtsFlow = FALSE;\r
1904   dcb.fOutxDsrFlow = FALSE;\r
1905   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1906   dcb.fDsrSensitivity = FALSE;\r
1907   dcb.fTXContinueOnXoff = TRUE;\r
1908   dcb.fOutX = FALSE;\r
1909   dcb.fInX = FALSE;\r
1910   dcb.fNull = FALSE;\r
1911   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1912   dcb.fAbortOnError = FALSE;\r
1913   dcb.ByteSize = 7;\r
1914   dcb.Parity = SPACEPARITY;\r
1915   dcb.StopBits = ONESTOPBIT;\r
1916   settingsFileName = SETTINGS_FILE;\r
1917   saveSettingsOnExit = TRUE;\r
1918   boardX = CW_USEDEFAULT;\r
1919   boardY = CW_USEDEFAULT;\r
1920   consoleX = CW_USEDEFAULT; \r
1921   consoleY = CW_USEDEFAULT; \r
1922   consoleW = CW_USEDEFAULT;\r
1923   consoleH = CW_USEDEFAULT;\r
1924   analysisX = CW_USEDEFAULT; \r
1925   analysisY = CW_USEDEFAULT; \r
1926   analysisW = CW_USEDEFAULT;\r
1927   analysisH = CW_USEDEFAULT;\r
1928   commentX = CW_USEDEFAULT; \r
1929   commentY = CW_USEDEFAULT; \r
1930   commentW = CW_USEDEFAULT;\r
1931   commentH = CW_USEDEFAULT;\r
1932   editTagsX = CW_USEDEFAULT; \r
1933   editTagsY = CW_USEDEFAULT; \r
1934   editTagsW = CW_USEDEFAULT;\r
1935   editTagsH = CW_USEDEFAULT;\r
1936   gameListX = CW_USEDEFAULT; \r
1937   gameListY = CW_USEDEFAULT; \r
1938   gameListW = CW_USEDEFAULT;\r
1939   gameListH = CW_USEDEFAULT;\r
1940   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1941   icsNames = ICS_NAMES;\r
1942   firstChessProgramNames = FCP_NAMES;\r
1943   secondChessProgramNames = SCP_NAMES;\r
1944   appData.initialMode = "";\r
1945   appData.variant = "normal";\r
1946   appData.firstProtocolVersion = PROTOVER;\r
1947   appData.secondProtocolVersion = PROTOVER;\r
1948   appData.showButtonBar = TRUE;\r
1949 \r
1950    /* [AS] New properties (see comments in header file) */\r
1951   appData.firstScoreIsAbsolute = FALSE;\r
1952   appData.secondScoreIsAbsolute = FALSE;\r
1953   appData.saveExtendedInfoInPGN = FALSE;\r
1954   appData.hideThinkingFromHuman = FALSE;\r
1955   appData.liteBackTextureFile = "";\r
1956   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1957   appData.darkBackTextureFile = "";\r
1958   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1959   appData.renderPiecesWithFont = "";\r
1960   appData.fontToPieceTable = "";\r
1961   appData.fontBackColorWhite = 0;\r
1962   appData.fontForeColorWhite = 0;\r
1963   appData.fontBackColorBlack = 0;\r
1964   appData.fontForeColorBlack = 0;\r
1965   appData.fontPieceSize = 80;\r
1966   appData.overrideLineGap = 1;\r
1967   appData.adjudicateLossThreshold = 0;\r
1968   appData.delayBeforeQuit = 0;\r
1969   appData.delayAfterQuit = 0;\r
1970   appData.nameOfDebugFile = "winboard.debug";\r
1971   appData.pgnEventHeader = "Computer Chess Game";\r
1972   appData.defaultFrcPosition = -1;\r
1973   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1974   appData.saveOutOfBookInfo = TRUE;\r
1975   appData.showEvalInMoveHistory = TRUE;\r
1976   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1977   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1978   appData.highlightMoveWithArrow = FALSE;\r
1979   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1980   appData.useStickyWindows = TRUE;\r
1981   appData.adjudicateDrawMoves = 0;\r
1982   appData.autoDisplayComment = TRUE;\r
1983   appData.autoDisplayTags = TRUE;\r
1984   appData.firstIsUCI = FALSE;\r
1985   appData.secondIsUCI = FALSE;\r
1986   appData.firstHasOwnBookUCI = TRUE;\r
1987   appData.secondHasOwnBookUCI = TRUE;\r
1988   appData.polyglotDir = "";\r
1989   appData.usePolyglotBook = FALSE;\r
1990   appData.polyglotBook = "";\r
1991   appData.defaultHashSize = 64;\r
1992   appData.defaultCacheSizeEGTB = 4;\r
1993   appData.defaultPathEGTB = "c:\\egtb";\r
1994   appData.firstOptions = "";\r
1995   appData.secondOptions = "";\r
1996 \r
1997   InitWindowPlacement( &wpMoveHistory );\r
1998   InitWindowPlacement( &wpEvalGraph );\r
1999   InitWindowPlacement( &wpEngineOutput );\r
2000 \r
2001   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2002   appData.NrFiles      = -1;\r
2003   appData.NrRanks      = -1;\r
2004   appData.holdingsSize = -1;\r
2005   appData.testClaims   = FALSE;\r
2006   appData.checkMates   = FALSE;\r
2007   appData.materialDraws= FALSE;\r
2008   appData.trivialDraws = FALSE;\r
2009   appData.ruleMoves    = 51;\r
2010   appData.drawRepeats  = 6;\r
2011   appData.matchPause   = 10000;\r
2012   appData.alphaRank    = FALSE;\r
2013   appData.allWhite     = FALSE;\r
2014   appData.upsideDown   = FALSE;\r
2015   appData.serverPause  = 15;\r
2016   appData.serverMovesName   = NULL;\r
2017   appData.suppressLoadMoves = FALSE;\r
2018   appData.firstTimeOdds  = 1;\r
2019   appData.secondTimeOdds = 1;\r
2020   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2021   appData.secondAccumulateTC = 1;\r
2022   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2023   appData.secondNPS = -1;\r
2024   appData.engineComments = 1;\r
2025   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2026   appData.egtFormats = "";\r
2027 \r
2028 #ifdef ZIPPY\r
2029   appData.zippyTalk = ZIPPY_TALK;\r
2030   appData.zippyPlay = ZIPPY_PLAY;\r
2031   appData.zippyLines = ZIPPY_LINES;\r
2032   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2033   appData.zippyPassword = ZIPPY_PASSWORD;\r
2034   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2035   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2036   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2037   appData.zippyUseI = ZIPPY_USE_I;\r
2038   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2039   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2040   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2041   appData.zippyGameStart = ZIPPY_GAME_START;\r
2042   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2043   appData.zippyAbort = ZIPPY_ABORT;\r
2044   appData.zippyVariants = ZIPPY_VARIANTS;\r
2045   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2046   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2047 #endif\r
2048 \r
2049   /* Point font array elements to structures and\r
2050      parse default font names */\r
2051   for (i=0; i<NUM_FONTS; i++) {\r
2052     for (j=0; j<NUM_SIZES; j++) {\r
2053       font[j][i] = &fontRec[j][i];\r
2054       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2055     }\r
2056   }\r
2057   \r
2058   /* Parse default settings file if any */\r
2059   if (ParseSettingsFile(settingsFileName, buf)) {\r
2060     settingsFileName = strdup(buf);\r
2061   }\r
2062 \r
2063   /* Parse command line */\r
2064   ParseArgs(StringGet, &lpCmdLine);\r
2065 \r
2066   /* [HGM] make sure board size is acceptable */\r
2067   if(appData.NrFiles > BOARD_SIZE ||\r
2068      appData.NrRanks > BOARD_SIZE   )\r
2069       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2070 \r
2071   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2072    * with options from the command line, we now make an even higher priority\r
2073    * overrule by WB options attached to the engine command line. This so that\r
2074    * tournament managers can use WB options (such as /timeOdds) that follow\r
2075    * the engines.\r
2076    */\r
2077   if(appData.firstChessProgram != NULL) {\r
2078       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2079       static char *f = "first";\r
2080       char buf[MSG_SIZ], *q = buf;\r
2081       if(p != NULL) { // engine command line contains WinBoard options\r
2082           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2083           ParseArgs(StringGet, &q);\r
2084           p[-1] = 0; // cut them offengine command line\r
2085       }\r
2086   }\r
2087   // now do same for second chess program\r
2088   if(appData.secondChessProgram != NULL) {\r
2089       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2090       static char *s = "second";\r
2091       char buf[MSG_SIZ], *q = buf;\r
2092       if(p != NULL) { // engine command line contains WinBoard options\r
2093           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2094           ParseArgs(StringGet, &q);\r
2095           p[-1] = 0; // cut them offengine command line\r
2096       }\r
2097   }\r
2098 \r
2099 \r
2100   /* Propagate options that affect others */\r
2101   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2102   if (appData.icsActive || appData.noChessProgram) {\r
2103      chessProgram = FALSE;  /* not local chess program mode */\r
2104   }\r
2105 \r
2106   /* Open startup dialog if needed */\r
2107   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2108       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2109       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2110                         *appData.secondChessProgram == NULLCHAR))) {\r
2111     FARPROC lpProc;\r
2112     \r
2113     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2114     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2115     FreeProcInstance(lpProc);\r
2116   }\r
2117 \r
2118   /* Make sure save files land in the right (?) directory */\r
2119   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2120     appData.saveGameFile = strdup(buf);\r
2121   }\r
2122   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2123     appData.savePositionFile = strdup(buf);\r
2124   }\r
2125 \r
2126   /* Finish initialization for fonts and sounds */\r
2127   for (i=0; i<NUM_FONTS; i++) {\r
2128     for (j=0; j<NUM_SIZES; j++) {\r
2129       CreateFontInMF(font[j][i]);\r
2130     }\r
2131   }\r
2132   /* xboard, and older WinBoards, controlled the move sound with the\r
2133      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2134      always turn the option on (so that the backend will call us),\r
2135      then let the user turn the sound off by setting it to silence if\r
2136      desired.  To accommodate old winboard.ini files saved by old\r
2137      versions of WinBoard, we also turn off the sound if the option\r
2138      was initially set to false. */\r
2139   if (!appData.ringBellAfterMoves) {\r
2140     sounds[(int)SoundMove].name = strdup("");\r
2141     appData.ringBellAfterMoves = TRUE;\r
2142   }\r
2143   GetCurrentDirectory(MSG_SIZ, currDir);\r
2144   SetCurrentDirectory(installDir);\r
2145   LoadAllSounds();\r
2146   SetCurrentDirectory(currDir);\r
2147 \r
2148   p = icsTextMenuString;\r
2149   if (p[0] == '@') {\r
2150     FILE* f = fopen(p + 1, "r");\r
2151     if (f == NULL) {\r
2152       DisplayFatalError(p + 1, errno, 2);\r
2153       return;\r
2154     }\r
2155     i = fread(buf, 1, sizeof(buf)-1, f);\r
2156     fclose(f);\r
2157     buf[i] = NULLCHAR;\r
2158     p = buf;\r
2159   }\r
2160   ParseIcsTextMenu(strdup(p));\r
2161 }\r
2162 \r
2163 \r
2164 VOID\r
2165 InitMenuChecks()\r
2166 {\r
2167   HMENU hmenu = GetMenu(hwndMain);\r
2168 \r
2169   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2170                         MF_BYCOMMAND|((appData.icsActive &&\r
2171                                        *appData.icsCommPort != NULLCHAR) ?\r
2172                                       MF_ENABLED : MF_GRAYED));\r
2173   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2174                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2175                                      MF_CHECKED : MF_UNCHECKED));\r
2176 }\r
2177 \r
2178 \r
2179 VOID\r
2180 SaveSettings(char* name)\r
2181 {\r
2182   FILE *f;\r
2183   ArgDescriptor *ad;\r
2184   WINDOWPLACEMENT wp;\r
2185   char dir[MSG_SIZ];\r
2186 \r
2187   if (!hwndMain) return;\r
2188 \r
2189   GetCurrentDirectory(MSG_SIZ, dir);\r
2190   SetCurrentDirectory(installDir);\r
2191   f = fopen(name, "w");\r
2192   SetCurrentDirectory(dir);\r
2193   if (f == NULL) {\r
2194     DisplayError(name, errno);\r
2195     return;\r
2196   }\r
2197   fprintf(f, ";\n");\r
2198   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2199   fprintf(f, ";\n");\r
2200   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2201   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2202   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2203   fprintf(f, ";\n");\r
2204 \r
2205   wp.length = sizeof(WINDOWPLACEMENT);\r
2206   GetWindowPlacement(hwndMain, &wp);\r
2207   boardX = wp.rcNormalPosition.left;\r
2208   boardY = wp.rcNormalPosition.top;\r
2209 \r
2210   if (hwndConsole) {\r
2211     GetWindowPlacement(hwndConsole, &wp);\r
2212     consoleX = wp.rcNormalPosition.left;\r
2213     consoleY = wp.rcNormalPosition.top;\r
2214     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2215     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2216   }\r
2217 \r
2218   if (analysisDialog) {\r
2219     GetWindowPlacement(analysisDialog, &wp);\r
2220     analysisX = wp.rcNormalPosition.left;\r
2221     analysisY = wp.rcNormalPosition.top;\r
2222     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2223     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2224   }\r
2225 \r
2226   if (commentDialog) {\r
2227     GetWindowPlacement(commentDialog, &wp);\r
2228     commentX = wp.rcNormalPosition.left;\r
2229     commentY = wp.rcNormalPosition.top;\r
2230     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2231     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2232   }\r
2233 \r
2234   if (editTagsDialog) {\r
2235     GetWindowPlacement(editTagsDialog, &wp);\r
2236     editTagsX = wp.rcNormalPosition.left;\r
2237     editTagsY = wp.rcNormalPosition.top;\r
2238     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2239     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2240   }\r
2241 \r
2242   if (gameListDialog) {\r
2243     GetWindowPlacement(gameListDialog, &wp);\r
2244     gameListX = wp.rcNormalPosition.left;\r
2245     gameListY = wp.rcNormalPosition.top;\r
2246     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2247     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2248   }\r
2249 \r
2250   /* [AS] Move history */\r
2251   wpMoveHistory.visible = MoveHistoryIsUp();\r
2252   \r
2253   if( moveHistoryDialog ) {\r
2254     GetWindowPlacement(moveHistoryDialog, &wp);\r
2255     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2256     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2257     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2258     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2259   }\r
2260 \r
2261   /* [AS] Eval graph */\r
2262   wpEvalGraph.visible = EvalGraphIsUp();\r
2263 \r
2264   if( evalGraphDialog ) {\r
2265     GetWindowPlacement(evalGraphDialog, &wp);\r
2266     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2267     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2268     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2269     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2270   }\r
2271 \r
2272   /* [AS] Engine output */\r
2273   wpEngineOutput.visible = EngineOutputIsUp();\r
2274 \r
2275   if( engineOutputDialog ) {\r
2276     GetWindowPlacement(engineOutputDialog, &wp);\r
2277     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2278     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2279     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2280     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2281   }\r
2282 \r
2283   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2284     if (!ad->save) continue;\r
2285     switch (ad->argType) {\r
2286     case ArgString:\r
2287       {\r
2288         char *p = *(char **)ad->argLoc;\r
2289         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2290           /* Quote multiline values or \-containing values\r
2291              with { } if possible */\r
2292           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2293         } else {\r
2294           /* Else quote with " " */\r
2295           fprintf(f, "/%s=\"", ad->argName);\r
2296           while (*p) {\r
2297             if (*p == '\n') fprintf(f, "\n");\r
2298             else if (*p == '\r') fprintf(f, "\\r");\r
2299             else if (*p == '\t') fprintf(f, "\\t");\r
2300             else if (*p == '\b') fprintf(f, "\\b");\r
2301             else if (*p == '\f') fprintf(f, "\\f");\r
2302             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2303             else if (*p == '\"') fprintf(f, "\\\"");\r
2304             else if (*p == '\\') fprintf(f, "\\\\");\r
2305             else putc(*p, f);\r
2306             p++;\r
2307           }\r
2308           fprintf(f, "\"\n");\r
2309         }\r
2310       }\r
2311       break;\r
2312     case ArgInt:\r
2313       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2314       break;\r
2315     case ArgFloat:\r
2316       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2317       break;\r
2318     case ArgBoolean:\r
2319       fprintf(f, "/%s=%s\n", ad->argName, \r
2320         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2321       break;\r
2322     case ArgTrue:\r
2323       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2324       break;\r
2325     case ArgFalse:\r
2326       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2327       break;\r
2328     case ArgColor:\r
2329       {\r
2330         COLORREF color = *(COLORREF *)ad->argLoc;\r
2331         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2332           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2333       }\r
2334       break;\r
2335     case ArgAttribs:\r
2336       {\r
2337         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2338         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2339           (ta->effects & CFE_BOLD) ? "b" : "",\r
2340           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2341           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2342           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2343           (ta->effects) ? " " : "",\r
2344           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2345       }\r
2346       break;\r
2347     case ArgFilename:\r
2348       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2349         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2350       } else {\r
2351         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2352       }\r
2353       break;\r
2354     case ArgBoardSize:\r
2355       fprintf(f, "/%s=%s\n", ad->argName,\r
2356               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2357       break;\r
2358     case ArgFont:\r
2359       {\r
2360         int bs;\r
2361         for (bs=0; bs<NUM_SIZES; bs++) {\r
2362           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2363           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2364           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2365             ad->argName, mfp->faceName, mfp->pointSize,\r
2366             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2367             mfp->bold ? "b" : "",\r
2368             mfp->italic ? "i" : "",\r
2369             mfp->underline ? "u" : "",\r
2370             mfp->strikeout ? "s" : "");\r
2371         }\r
2372       }\r
2373       break;\r
2374     case ArgCommSettings:\r
2375       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2376     case ArgNone:\r
2377     case ArgSettingsFilename: ;\r
2378     }\r
2379   }\r
2380   fclose(f);\r
2381 }\r
2382 \r
2383 \r
2384 \r
2385 /*---------------------------------------------------------------------------*\\r
2386  *\r
2387  * GDI board drawing routines\r
2388  *\r
2389 \*---------------------------------------------------------------------------*/\r
2390 \r
2391 /* [AS] Draw square using background texture */\r
2392 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2393 {\r
2394     XFORM   x;\r
2395 \r
2396     if( mode == 0 ) {\r
2397         return; /* Should never happen! */\r
2398     }\r
2399 \r
2400     SetGraphicsMode( dst, GM_ADVANCED );\r
2401 \r
2402     switch( mode ) {\r
2403     case 1:\r
2404         /* Identity */\r
2405         break;\r
2406     case 2:\r
2407         /* X reflection */\r
2408         x.eM11 = -1.0;\r
2409         x.eM12 = 0;\r
2410         x.eM21 = 0;\r
2411         x.eM22 = 1.0;\r
2412         x.eDx = (FLOAT) dw + dx - 1;\r
2413         x.eDy = 0;\r
2414         dx = 0;\r
2415         SetWorldTransform( dst, &x );\r
2416         break;\r
2417     case 3:\r
2418         /* Y reflection */\r
2419         x.eM11 = 1.0;\r
2420         x.eM12 = 0;\r
2421         x.eM21 = 0;\r
2422         x.eM22 = -1.0;\r
2423         x.eDx = 0;\r
2424         x.eDy = (FLOAT) dh + dy - 1;\r
2425         dy = 0;\r
2426         SetWorldTransform( dst, &x );\r
2427         break;\r
2428     case 4:\r
2429         /* X/Y flip */\r
2430         x.eM11 = 0;\r
2431         x.eM12 = 1.0;\r
2432         x.eM21 = 1.0;\r
2433         x.eM22 = 0;\r
2434         x.eDx = (FLOAT) dx;\r
2435         x.eDy = (FLOAT) dy;\r
2436         dx = 0;\r
2437         dy = 0;\r
2438         SetWorldTransform( dst, &x );\r
2439         break;\r
2440     }\r
2441 \r
2442     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2443 \r
2444     x.eM11 = 1.0;\r
2445     x.eM12 = 0;\r
2446     x.eM21 = 0;\r
2447     x.eM22 = 1.0;\r
2448     x.eDx = 0;\r
2449     x.eDy = 0;\r
2450     SetWorldTransform( dst, &x );\r
2451 \r
2452     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2453 }\r
2454 \r
2455 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2456 enum {\r
2457     PM_WP = (int) WhitePawn, \r
2458     PM_WN = (int) WhiteKnight, \r
2459     PM_WB = (int) WhiteBishop, \r
2460     PM_WR = (int) WhiteRook, \r
2461     PM_WQ = (int) WhiteQueen, \r
2462     PM_WF = (int) WhiteFerz, \r
2463     PM_WW = (int) WhiteWazir, \r
2464     PM_WE = (int) WhiteAlfil, \r
2465     PM_WM = (int) WhiteMan, \r
2466     PM_WO = (int) WhiteCannon, \r
2467     PM_WU = (int) WhiteUnicorn, \r
2468     PM_WH = (int) WhiteNightrider, \r
2469     PM_WA = (int) WhiteAngel, \r
2470     PM_WC = (int) WhiteMarshall, \r
2471     PM_WAB = (int) WhiteCardinal, \r
2472     PM_WD = (int) WhiteDragon, \r
2473     PM_WL = (int) WhiteLance, \r
2474     PM_WS = (int) WhiteCobra, \r
2475     PM_WV = (int) WhiteFalcon, \r
2476     PM_WSG = (int) WhiteSilver, \r
2477     PM_WG = (int) WhiteGrasshopper, \r
2478     PM_WK = (int) WhiteKing,\r
2479     PM_BP = (int) BlackPawn, \r
2480     PM_BN = (int) BlackKnight, \r
2481     PM_BB = (int) BlackBishop, \r
2482     PM_BR = (int) BlackRook, \r
2483     PM_BQ = (int) BlackQueen, \r
2484     PM_BF = (int) BlackFerz, \r
2485     PM_BW = (int) BlackWazir, \r
2486     PM_BE = (int) BlackAlfil, \r
2487     PM_BM = (int) BlackMan,\r
2488     PM_BO = (int) BlackCannon, \r
2489     PM_BU = (int) BlackUnicorn, \r
2490     PM_BH = (int) BlackNightrider, \r
2491     PM_BA = (int) BlackAngel, \r
2492     PM_BC = (int) BlackMarshall, \r
2493     PM_BG = (int) BlackGrasshopper, \r
2494     PM_BAB = (int) BlackCardinal,\r
2495     PM_BD = (int) BlackDragon,\r
2496     PM_BL = (int) BlackLance,\r
2497     PM_BS = (int) BlackCobra,\r
2498     PM_BV = (int) BlackFalcon,\r
2499     PM_BSG = (int) BlackSilver,\r
2500     PM_BK = (int) BlackKing\r
2501 };\r
2502 \r
2503 static HFONT hPieceFont = NULL;\r
2504 static HBITMAP hPieceMask[(int) EmptySquare];\r
2505 static HBITMAP hPieceFace[(int) EmptySquare];\r
2506 static int fontBitmapSquareSize = 0;\r
2507 static char pieceToFontChar[(int) EmptySquare] =\r
2508                               { 'p', 'n', 'b', 'r', 'q', \r
2509                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2510                       'k', 'o', 'm', 'v', 't', 'w', \r
2511                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2512                                                               'l' };\r
2513 \r
2514 extern BOOL SetCharTable( char *table, const char * map );\r
2515 /* [HGM] moved to backend.c */\r
2516 \r
2517 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2518 {\r
2519     HBRUSH hbrush;\r
2520     BYTE r1 = GetRValue( color );\r
2521     BYTE g1 = GetGValue( color );\r
2522     BYTE b1 = GetBValue( color );\r
2523     BYTE r2 = r1 / 2;\r
2524     BYTE g2 = g1 / 2;\r
2525     BYTE b2 = b1 / 2;\r
2526     RECT rc;\r
2527 \r
2528     /* Create a uniform background first */\r
2529     hbrush = CreateSolidBrush( color );\r
2530     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2531     FillRect( hdc, &rc, hbrush );\r
2532     DeleteObject( hbrush );\r
2533     \r
2534     if( mode == 1 ) {\r
2535         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2536         int steps = squareSize / 2;\r
2537         int i;\r
2538 \r
2539         for( i=0; i<steps; i++ ) {\r
2540             BYTE r = r1 - (r1-r2) * i / steps;\r
2541             BYTE g = g1 - (g1-g2) * i / steps;\r
2542             BYTE b = b1 - (b1-b2) * i / steps;\r
2543 \r
2544             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2545             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2546             FillRect( hdc, &rc, hbrush );\r
2547             DeleteObject(hbrush);\r
2548         }\r
2549     }\r
2550     else if( mode == 2 ) {\r
2551         /* Diagonal gradient, good more or less for every piece */\r
2552         POINT triangle[3];\r
2553         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2554         HBRUSH hbrush_old;\r
2555         int steps = squareSize;\r
2556         int i;\r
2557 \r
2558         triangle[0].x = squareSize - steps;\r
2559         triangle[0].y = squareSize;\r
2560         triangle[1].x = squareSize;\r
2561         triangle[1].y = squareSize;\r
2562         triangle[2].x = squareSize;\r
2563         triangle[2].y = squareSize - steps;\r
2564 \r
2565         for( i=0; i<steps; i++ ) {\r
2566             BYTE r = r1 - (r1-r2) * i / steps;\r
2567             BYTE g = g1 - (g1-g2) * i / steps;\r
2568             BYTE b = b1 - (b1-b2) * i / steps;\r
2569 \r
2570             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2571             hbrush_old = SelectObject( hdc, hbrush );\r
2572             Polygon( hdc, triangle, 3 );\r
2573             SelectObject( hdc, hbrush_old );\r
2574             DeleteObject(hbrush);\r
2575             triangle[0].x++;\r
2576             triangle[2].y++;\r
2577         }\r
2578 \r
2579         SelectObject( hdc, hpen );\r
2580     }\r
2581 }\r
2582 \r
2583 /*\r
2584     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2585     seems to work ok. The main problem here is to find the "inside" of a chess\r
2586     piece: follow the steps as explained below.\r
2587 */\r
2588 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2589 {\r
2590     HBITMAP hbm;\r
2591     HBITMAP hbm_old;\r
2592     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2593     RECT rc;\r
2594     SIZE sz;\r
2595     POINT pt;\r
2596     int backColor = whitePieceColor; \r
2597     int foreColor = blackPieceColor;\r
2598     \r
2599     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2600         backColor = appData.fontBackColorWhite;\r
2601         foreColor = appData.fontForeColorWhite;\r
2602     }\r
2603     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2604         backColor = appData.fontBackColorBlack;\r
2605         foreColor = appData.fontForeColorBlack;\r
2606     }\r
2607 \r
2608     /* Mask */\r
2609     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2610 \r
2611     hbm_old = SelectObject( hdc, hbm );\r
2612 \r
2613     rc.left = 0;\r
2614     rc.top = 0;\r
2615     rc.right = squareSize;\r
2616     rc.bottom = squareSize;\r
2617 \r
2618     /* Step 1: background is now black */\r
2619     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2620 \r
2621     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2622 \r
2623     pt.x = (squareSize - sz.cx) / 2;\r
2624     pt.y = (squareSize - sz.cy) / 2;\r
2625 \r
2626     SetBkMode( hdc, TRANSPARENT );\r
2627     SetTextColor( hdc, chroma );\r
2628     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2629     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2630 \r
2631     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2632     /* Step 3: the area outside the piece is filled with white */\r
2633 //    FloodFill( hdc, 0, 0, chroma );\r
2634     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2635     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2636     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2637     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2638     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2639     /* \r
2640         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2641         but if the start point is not inside the piece we're lost!\r
2642         There should be a better way to do this... if we could create a region or path\r
2643         from the fill operation we would be fine for example.\r
2644     */\r
2645 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2646     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2647 \r
2648     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2649         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2650         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2651 \r
2652         SelectObject( dc2, bm2 );\r
2653         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2654         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2655         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2656         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2657         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2658 \r
2659         DeleteDC( dc2 );\r
2660         DeleteObject( bm2 );\r
2661     }\r
2662 \r
2663     SetTextColor( hdc, 0 );\r
2664     /* \r
2665         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2666         draw the piece again in black for safety.\r
2667     */\r
2668     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2669 \r
2670     SelectObject( hdc, hbm_old );\r
2671 \r
2672     if( hPieceMask[index] != NULL ) {\r
2673         DeleteObject( hPieceMask[index] );\r
2674     }\r
2675 \r
2676     hPieceMask[index] = hbm;\r
2677 \r
2678     /* Face */\r
2679     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2680 \r
2681     SelectObject( hdc, hbm );\r
2682 \r
2683     {\r
2684         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2685         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2686         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2687 \r
2688         SelectObject( dc1, hPieceMask[index] );\r
2689         SelectObject( dc2, bm2 );\r
2690         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2691         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2692         \r
2693         /* \r
2694             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2695             the piece background and deletes (makes transparent) the rest.\r
2696             Thanks to that mask, we are free to paint the background with the greates\r
2697             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2698             We use this, to make gradients and give the pieces a "roundish" look.\r
2699         */\r
2700         SetPieceBackground( hdc, backColor, 2 );\r
2701         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2702 \r
2703         DeleteDC( dc2 );\r
2704         DeleteDC( dc1 );\r
2705         DeleteObject( bm2 );\r
2706     }\r
2707 \r
2708     SetTextColor( hdc, foreColor );\r
2709     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2710 \r
2711     SelectObject( hdc, hbm_old );\r
2712 \r
2713     if( hPieceFace[index] != NULL ) {\r
2714         DeleteObject( hPieceFace[index] );\r
2715     }\r
2716 \r
2717     hPieceFace[index] = hbm;\r
2718 }\r
2719 \r
2720 static int TranslatePieceToFontPiece( int piece )\r
2721 {\r
2722     switch( piece ) {\r
2723     case BlackPawn:\r
2724         return PM_BP;\r
2725     case BlackKnight:\r
2726         return PM_BN;\r
2727     case BlackBishop:\r
2728         return PM_BB;\r
2729     case BlackRook:\r
2730         return PM_BR;\r
2731     case BlackQueen:\r
2732         return PM_BQ;\r
2733     case BlackKing:\r
2734         return PM_BK;\r
2735     case WhitePawn:\r
2736         return PM_WP;\r
2737     case WhiteKnight:\r
2738         return PM_WN;\r
2739     case WhiteBishop:\r
2740         return PM_WB;\r
2741     case WhiteRook:\r
2742         return PM_WR;\r
2743     case WhiteQueen:\r
2744         return PM_WQ;\r
2745     case WhiteKing:\r
2746         return PM_WK;\r
2747 \r
2748     case BlackAngel:\r
2749         return PM_BA;\r
2750     case BlackMarshall:\r
2751         return PM_BC;\r
2752     case BlackFerz:\r
2753         return PM_BF;\r
2754     case BlackNightrider:\r
2755         return PM_BH;\r
2756     case BlackAlfil:\r
2757         return PM_BE;\r
2758     case BlackWazir:\r
2759         return PM_BW;\r
2760     case BlackUnicorn:\r
2761         return PM_BU;\r
2762     case BlackCannon:\r
2763         return PM_BO;\r
2764     case BlackGrasshopper:\r
2765         return PM_BG;\r
2766     case BlackMan:\r
2767         return PM_BM;\r
2768     case BlackSilver:\r
2769         return PM_BSG;\r
2770     case BlackLance:\r
2771         return PM_BL;\r
2772     case BlackFalcon:\r
2773         return PM_BV;\r
2774     case BlackCobra:\r
2775         return PM_BS;\r
2776     case BlackCardinal:\r
2777         return PM_BAB;\r
2778     case BlackDragon:\r
2779         return PM_BD;\r
2780 \r
2781     case WhiteAngel:\r
2782         return PM_WA;\r
2783     case WhiteMarshall:\r
2784         return PM_WC;\r
2785     case WhiteFerz:\r
2786         return PM_WF;\r
2787     case WhiteNightrider:\r
2788         return PM_WH;\r
2789     case WhiteAlfil:\r
2790         return PM_WE;\r
2791     case WhiteWazir:\r
2792         return PM_WW;\r
2793     case WhiteUnicorn:\r
2794         return PM_WU;\r
2795     case WhiteCannon:\r
2796         return PM_WO;\r
2797     case WhiteGrasshopper:\r
2798         return PM_WG;\r
2799     case WhiteMan:\r
2800         return PM_WM;\r
2801     case WhiteSilver:\r
2802         return PM_WSG;\r
2803     case WhiteLance:\r
2804         return PM_WL;\r
2805     case WhiteFalcon:\r
2806         return PM_WV;\r
2807     case WhiteCobra:\r
2808         return PM_WS;\r
2809     case WhiteCardinal:\r
2810         return PM_WAB;\r
2811     case WhiteDragon:\r
2812         return PM_WD;\r
2813     }\r
2814 \r
2815     return 0;\r
2816 }\r
2817 \r
2818 void CreatePiecesFromFont()\r
2819 {\r
2820     LOGFONT lf;\r
2821     HDC hdc_window = NULL;\r
2822     HDC hdc = NULL;\r
2823     HFONT hfont_old;\r
2824     int fontHeight;\r
2825     int i;\r
2826 \r
2827     if( fontBitmapSquareSize < 0 ) {\r
2828         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2829         return;\r
2830     }\r
2831 \r
2832     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2833         fontBitmapSquareSize = -1;\r
2834         return;\r
2835     }\r
2836 \r
2837     if( fontBitmapSquareSize != squareSize ) {\r
2838         hdc_window = GetDC( hwndMain );\r
2839         hdc = CreateCompatibleDC( hdc_window );\r
2840 \r
2841         if( hPieceFont != NULL ) {\r
2842             DeleteObject( hPieceFont );\r
2843         }\r
2844         else {\r
2845             for( i=0; i<=(int)BlackKing; i++ ) {\r
2846                 hPieceMask[i] = NULL;\r
2847                 hPieceFace[i] = NULL;\r
2848             }\r
2849         }\r
2850 \r
2851         fontHeight = 75;\r
2852 \r
2853         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2854             fontHeight = appData.fontPieceSize;\r
2855         }\r
2856 \r
2857         fontHeight = (fontHeight * squareSize) / 100;\r
2858 \r
2859         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2860         lf.lfWidth = 0;\r
2861         lf.lfEscapement = 0;\r
2862         lf.lfOrientation = 0;\r
2863         lf.lfWeight = FW_NORMAL;\r
2864         lf.lfItalic = 0;\r
2865         lf.lfUnderline = 0;\r
2866         lf.lfStrikeOut = 0;\r
2867         lf.lfCharSet = DEFAULT_CHARSET;\r
2868         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2869         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2870         lf.lfQuality = PROOF_QUALITY;\r
2871         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2872         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2873         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2874 \r
2875         hPieceFont = CreateFontIndirect( &lf );\r
2876 \r
2877         if( hPieceFont == NULL ) {\r
2878             fontBitmapSquareSize = -2;\r
2879         }\r
2880         else {\r
2881             /* Setup font-to-piece character table */\r
2882             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2883                 /* No (or wrong) global settings, try to detect the font */\r
2884                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2885                     /* Alpha */\r
2886                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2887                 }\r
2888                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2889                     /* DiagramTT* family */\r
2890                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2891                 }\r
2892                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2893                     /* Fairy symbols */\r
2894                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2895                 }\r
2896                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2897                     /* Good Companion (Some characters get warped as literal :-( */\r
2898                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2899                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2900                     SetCharTable(pieceToFontChar, s);\r
2901                 }\r
2902                 else {\r
2903                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2904                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2905                 }\r
2906             }\r
2907 \r
2908             /* Create bitmaps */\r
2909             hfont_old = SelectObject( hdc, hPieceFont );\r
2910 #if 0\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2923 \r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2931             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2932             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2933             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2934             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2935             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2936             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2937             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2938             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2939             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2940             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2941             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2942             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2943             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2944             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2945             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2946             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2947             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2948             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2949             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2950             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2951             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2952             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2953             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2954             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2955             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2956 #else\r
2957             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2958                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2959                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2960 #endif\r
2961             SelectObject( hdc, hfont_old );\r
2962 \r
2963             fontBitmapSquareSize = squareSize;\r
2964         }\r
2965     }\r
2966 \r
2967     if( hdc != NULL ) {\r
2968         DeleteDC( hdc );\r
2969     }\r
2970 \r
2971     if( hdc_window != NULL ) {\r
2972         ReleaseDC( hwndMain, hdc_window );\r
2973     }\r
2974 }\r
2975 \r
2976 HBITMAP\r
2977 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2978 {\r
2979   char name[128];\r
2980 \r
2981   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2982   if (gameInfo.event &&\r
2983       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2984       strcmp(name, "k80s") == 0) {\r
2985     strcpy(name, "tim");\r
2986   }\r
2987   return LoadBitmap(hinst, name);\r
2988 }\r
2989 \r
2990 \r
2991 /* Insert a color into the program's logical palette\r
2992    structure.  This code assumes the given color is\r
2993    the result of the RGB or PALETTERGB macro, and it\r
2994    knows how those macros work (which is documented).\r
2995 */\r
2996 VOID\r
2997 InsertInPalette(COLORREF color)\r
2998 {\r
2999   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3000 \r
3001   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3002     DisplayFatalError("Too many colors", 0, 1);\r
3003     pLogPal->palNumEntries--;\r
3004     return;\r
3005   }\r
3006 \r
3007   pe->peFlags = (char) 0;\r
3008   pe->peRed = (char) (0xFF & color);\r
3009   pe->peGreen = (char) (0xFF & (color >> 8));\r
3010   pe->peBlue = (char) (0xFF & (color >> 16));\r
3011   return;\r
3012 }\r
3013 \r
3014 \r
3015 VOID\r
3016 InitDrawingColors()\r
3017 {\r
3018   if (pLogPal == NULL) {\r
3019     /* Allocate enough memory for a logical palette with\r
3020      * PALETTESIZE entries and set the size and version fields\r
3021      * of the logical palette structure.\r
3022      */\r
3023     pLogPal = (NPLOGPALETTE)\r
3024       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3025                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3026     pLogPal->palVersion    = 0x300;\r
3027   }\r
3028   pLogPal->palNumEntries = 0;\r
3029 \r
3030   InsertInPalette(lightSquareColor);\r
3031   InsertInPalette(darkSquareColor);\r
3032   InsertInPalette(whitePieceColor);\r
3033   InsertInPalette(blackPieceColor);\r
3034   InsertInPalette(highlightSquareColor);\r
3035   InsertInPalette(premoveHighlightColor);\r
3036 \r
3037   /*  create a logical color palette according the information\r
3038    *  in the LOGPALETTE structure.\r
3039    */\r
3040   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3041 \r
3042   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3043   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3044   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3045   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3046   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3047   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3048   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3049   /* [AS] Force rendering of the font-based pieces */\r
3050   if( fontBitmapSquareSize > 0 ) {\r
3051     fontBitmapSquareSize = 0;\r
3052   }\r
3053 }\r
3054 \r
3055 \r
3056 int\r
3057 BoardWidth(int boardSize, int n)\r
3058 { /* [HGM] argument n added to allow different width and height */\r
3059   int lineGap = sizeInfo[boardSize].lineGap;\r
3060 \r
3061   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3062       lineGap = appData.overrideLineGap;\r
3063   }\r
3064 \r
3065   return (n + 1) * lineGap +\r
3066           n * sizeInfo[boardSize].squareSize;\r
3067 }\r
3068 \r
3069 /* Respond to board resize by dragging edge */\r
3070 VOID\r
3071 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3072 {\r
3073   BoardSize newSize = NUM_SIZES - 1;\r
3074   static int recurse = 0;\r
3075   if (IsIconic(hwndMain)) return;\r
3076   if (recurse > 0) return;\r
3077   recurse++;\r
3078   while (newSize > 0) {\r
3079         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3080         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3081            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3082     newSize--;\r
3083   } \r
3084   boardSize = newSize;\r
3085   InitDrawingSizes(boardSize, flags);\r
3086   recurse--;\r
3087 }\r
3088 \r
3089 \r
3090 \r
3091 VOID\r
3092 InitDrawingSizes(BoardSize boardSize, int flags)\r
3093 {\r
3094   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3095   ChessSquare piece;\r
3096   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3097   HDC hdc;\r
3098   SIZE clockSize, messageSize;\r
3099   HFONT oldFont;\r
3100   char buf[MSG_SIZ];\r
3101   char *str;\r
3102   HMENU hmenu = GetMenu(hwndMain);\r
3103   RECT crect, wrect;\r
3104   int offby;\r
3105   LOGBRUSH logbrush;\r
3106 \r
3107   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3108   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3109 \r
3110   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3111   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3112 \r
3113   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3114   smallLayout = sizeInfo[boardSize].smallLayout;\r
3115   squareSize = sizeInfo[boardSize].squareSize;\r
3116   lineGap = sizeInfo[boardSize].lineGap;\r
3117   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3118 \r
3119   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3120       lineGap = appData.overrideLineGap;\r
3121   }\r
3122 \r
3123   if (tinyLayout != oldTinyLayout) {\r
3124     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3125     if (tinyLayout) {\r
3126       style &= ~WS_SYSMENU;\r
3127       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3128                  "&Minimize\tCtrl+F4");\r
3129     } else {\r
3130       style |= WS_SYSMENU;\r
3131       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3132     }\r
3133     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3134 \r
3135     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3136       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3137         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3138     }\r
3139     DrawMenuBar(hwndMain);\r
3140   }\r
3141 \r
3142   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3143   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3144 \r
3145   /* Get text area sizes */\r
3146   hdc = GetDC(hwndMain);\r
3147   if (appData.clockMode) {\r
3148     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3149   } else {\r
3150     sprintf(buf, "White");\r
3151   }\r
3152   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3153   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3154   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3155   str = "We only care about the height here";\r
3156   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3157   SelectObject(hdc, oldFont);\r
3158   ReleaseDC(hwndMain, hdc);\r
3159 \r
3160   /* Compute where everything goes */\r
3161   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3162         /* [HGM] logo: if either logo is on, reserve space for it */\r
3163         logoHeight =  2*clockSize.cy;\r
3164         leftLogoRect.left   = OUTER_MARGIN;\r
3165         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3166         leftLogoRect.top    = OUTER_MARGIN;\r
3167         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3168 \r
3169         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3170         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3171         rightLogoRect.top    = OUTER_MARGIN;\r
3172         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3173 \r
3174 \r
3175     whiteRect.left = leftLogoRect.right;\r
3176     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3177     whiteRect.top = OUTER_MARGIN;\r
3178     whiteRect.bottom = whiteRect.top + logoHeight;\r
3179 \r
3180     blackRect.right = rightLogoRect.left;\r
3181     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3182     blackRect.top = whiteRect.top;\r
3183     blackRect.bottom = whiteRect.bottom;\r
3184   } else {\r
3185     whiteRect.left = OUTER_MARGIN;\r
3186     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3187     whiteRect.top = OUTER_MARGIN;\r
3188     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3189 \r
3190     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3191     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3192     blackRect.top = whiteRect.top;\r
3193     blackRect.bottom = whiteRect.bottom;\r
3194   }\r
3195 \r
3196   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3197   if (appData.showButtonBar) {\r
3198     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3199       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3200   } else {\r
3201     messageRect.right = OUTER_MARGIN + boardWidth;\r
3202   }\r
3203   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3204   messageRect.bottom = messageRect.top + messageSize.cy;\r
3205 \r
3206   boardRect.left = OUTER_MARGIN;\r
3207   boardRect.right = boardRect.left + boardWidth;\r
3208   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3209   boardRect.bottom = boardRect.top + boardHeight;\r
3210 \r
3211   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3212   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3213   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3214   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3215   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3216     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3217   GetWindowRect(hwndMain, &wrect);\r
3218   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3219                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3220   /* compensate if menu bar wrapped */\r
3221   GetClientRect(hwndMain, &crect);\r
3222   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3223   winHeight += offby;\r
3224   switch (flags) {\r
3225   case WMSZ_TOPLEFT:\r
3226     SetWindowPos(hwndMain, NULL, \r
3227                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3228                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3229     break;\r
3230 \r
3231   case WMSZ_TOPRIGHT:\r
3232   case WMSZ_TOP:\r
3233     SetWindowPos(hwndMain, NULL, \r
3234                  wrect.left, wrect.bottom - winHeight, \r
3235                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3236     break;\r
3237 \r
3238   case WMSZ_BOTTOMLEFT:\r
3239   case WMSZ_LEFT:\r
3240     SetWindowPos(hwndMain, NULL, \r
3241                  wrect.right - winWidth, wrect.top, \r
3242                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3243     break;\r
3244 \r
3245   case WMSZ_BOTTOMRIGHT:\r
3246   case WMSZ_BOTTOM:\r
3247   case WMSZ_RIGHT:\r
3248   default:\r
3249     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3250                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3251     break;\r
3252   }\r
3253 \r
3254   hwndPause = NULL;\r
3255   for (i = 0; i < N_BUTTONS; i++) {\r
3256     if (buttonDesc[i].hwnd != NULL) {\r
3257       DestroyWindow(buttonDesc[i].hwnd);\r
3258       buttonDesc[i].hwnd = NULL;\r
3259     }\r
3260     if (appData.showButtonBar) {\r
3261       buttonDesc[i].hwnd =\r
3262         CreateWindow("BUTTON", buttonDesc[i].label,\r
3263                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3264                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3265                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3266                      (HMENU) buttonDesc[i].id,\r
3267                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3268       if (tinyLayout) {\r
3269         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3270                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3271                     MAKELPARAM(FALSE, 0));\r
3272       }\r
3273       if (buttonDesc[i].id == IDM_Pause)\r
3274         hwndPause = buttonDesc[i].hwnd;\r
3275       buttonDesc[i].wndproc = (WNDPROC)\r
3276         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3277     }\r
3278   }\r
3279   if (gridPen != NULL) DeleteObject(gridPen);\r
3280   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3281   if (premovePen != NULL) DeleteObject(premovePen);\r
3282   if (lineGap != 0) {\r
3283     logbrush.lbStyle = BS_SOLID;\r
3284     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3285     gridPen =\r
3286       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3287                    lineGap, &logbrush, 0, NULL);\r
3288     logbrush.lbColor = highlightSquareColor;\r
3289     highlightPen =\r
3290       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3291                    lineGap, &logbrush, 0, NULL);\r
3292 \r
3293     logbrush.lbColor = premoveHighlightColor; \r
3294     premovePen =\r
3295       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3296                    lineGap, &logbrush, 0, NULL);\r
3297 \r
3298     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3299     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3300       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3301       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3302         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3303       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3304         BOARD_WIDTH * (squareSize + lineGap);\r
3305       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3306     }\r
3307     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3308       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3309       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3310         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3311         lineGap / 2 + (i * (squareSize + lineGap));\r
3312       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3313         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3314       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3315     }\r
3316   }\r
3317 \r
3318   /* [HGM] Licensing requirement */\r
3319 #ifdef GOTHIC\r
3320   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3321 #endif\r
3322 #ifdef FALCON\r
3323   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3324 #endif\r
3325   GothicPopUp( "", VariantNormal);\r
3326 \r
3327 \r
3328 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3329   oldBoardSize = boardSize;\r
3330   oldTinyLayout = tinyLayout;\r
3331 \r
3332   /* Load piece bitmaps for this board size */\r
3333   for (i=0; i<=2; i++) {\r
3334     for (piece = WhitePawn;\r
3335          (int) piece < (int) BlackPawn;\r
3336          piece = (ChessSquare) ((int) piece + 1)) {\r
3337       if (pieceBitmap[i][piece] != NULL)\r
3338         DeleteObject(pieceBitmap[i][piece]);\r
3339     }\r
3340   }\r
3341 \r
3342   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3343   // Orthodox Chess pieces\r
3344   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3345   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3346   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3347   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3348   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3349   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3350   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3351   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3352   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3353   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3354   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3355   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3356   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3357   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3358   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3359   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3360     // in Shogi, Hijack the unused Queen for Lance\r
3361     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3362     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3363     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3364   } else {\r
3365     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3366     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3367     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3368   }\r
3369 \r
3370   if(squareSize <= 72 && squareSize >= 33) { \r
3371     /* A & C are available in most sizes now */\r
3372     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3373       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3374       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3375       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3376       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3377       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3378       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3379       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3380       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3381       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3382       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3383       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3384       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3385     } else { // Smirf-like\r
3386       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3387       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3388       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3389     }\r
3390     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3391       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3392       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3393       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3394     } else { // WinBoard standard\r
3395       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3396       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3397       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3398     }\r
3399   }\r
3400 \r
3401 \r
3402   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3403     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3404     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3405     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3406     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3407     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3408     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3409     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3410     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3411     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3412     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3413     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3414     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3415     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3416     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3417     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3418     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3419     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3420     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3421     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3422     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3423     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3424     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3425     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3426     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3427     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3428     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3429     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3430     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3431     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3432     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3433 \r
3434     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3435       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3436       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3437       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3438       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3439       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3440       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3441       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3442       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3443       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3444       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3445       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3446       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3447     } else {\r
3448       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3449       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3450       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3451       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3452       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3453       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3454       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3455       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3456       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3457       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3458       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3459       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3460     }\r
3461 \r
3462   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3463     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3464     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3465     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3466     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3467     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3468     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3469     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3470     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3471     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3472     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3473     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3474     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3475     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3476     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3477   }\r
3478 \r
3479 \r
3480   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3481   /* special Shogi support in this size */\r
3482   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3483       for (piece = WhitePawn;\r
3484            (int) piece < (int) BlackPawn;\r
3485            piece = (ChessSquare) ((int) piece + 1)) {\r
3486         if (pieceBitmap[i][piece] != NULL)\r
3487           DeleteObject(pieceBitmap[i][piece]);\r
3488       }\r
3489     }\r
3490   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3491   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3492   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3493   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3494   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3495   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3496   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3497   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3498   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3499   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3500   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3501   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3502   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3503   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3504   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3505   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3506   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3507   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3508   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3509   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3510   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3511   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3512   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3513   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3514   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3515   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3516   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3517   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3518   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3519   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3520   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3521   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3522   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3523   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3524   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3525   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3526   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3527   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3528   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3529   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3530   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3531   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3532   minorSize = 0;\r
3533   }\r
3534 }\r
3535 \r
3536 HBITMAP\r
3537 PieceBitmap(ChessSquare p, int kind)\r
3538 {\r
3539   if ((int) p >= (int) BlackPawn)\r
3540     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3541 \r
3542   return pieceBitmap[kind][(int) p];\r
3543 }\r
3544 \r
3545 /***************************************************************/\r
3546 \r
3547 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3548 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3549 /*\r
3550 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3551 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3552 */\r
3553 \r
3554 VOID\r
3555 SquareToPos(int row, int column, int * x, int * y)\r
3556 {\r
3557   if (flipView) {\r
3558     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3559     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3560   } else {\r
3561     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3562     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3563   }\r
3564 }\r
3565 \r
3566 VOID\r
3567 DrawCoordsOnDC(HDC hdc)\r
3568 {\r
3569   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
3570   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
3571   char str[2] = { NULLCHAR, NULLCHAR };\r
3572   int oldMode, oldAlign, x, y, start, i;\r
3573   HFONT oldFont;\r
3574   HBRUSH oldBrush;\r
3575 \r
3576   if (!appData.showCoords)\r
3577     return;\r
3578 \r
3579   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3580 \r
3581   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3582   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3583   oldAlign = GetTextAlign(hdc);\r
3584   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3585 \r
3586   y = boardRect.top + lineGap;\r
3587   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3588 \r
3589   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3590   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3591     str[0] = files[start + i];\r
3592     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3593     y += squareSize + lineGap;\r
3594   }\r
3595 \r
3596   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3597 \r
3598   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3599   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3600     str[0] = ranks[start + i];\r
3601     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3602     x += squareSize + lineGap;\r
3603   }    \r
3604 \r
3605   SelectObject(hdc, oldBrush);\r
3606   SetBkMode(hdc, oldMode);\r
3607   SetTextAlign(hdc, oldAlign);\r
3608   SelectObject(hdc, oldFont);\r
3609 }\r
3610 \r
3611 VOID\r
3612 DrawGridOnDC(HDC hdc)\r
3613 {\r
3614   HPEN oldPen;\r
3615  \r
3616   if (lineGap != 0) {\r
3617     oldPen = SelectObject(hdc, gridPen);\r
3618     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3619     SelectObject(hdc, oldPen);\r
3620   }\r
3621 }\r
3622 \r
3623 #define HIGHLIGHT_PEN 0\r
3624 #define PREMOVE_PEN   1\r
3625 \r
3626 VOID\r
3627 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3628 {\r
3629   int x1, y1;\r
3630   HPEN oldPen, hPen;\r
3631   if (lineGap == 0) return;\r
3632   if (flipView) {\r
3633     x1 = boardRect.left +\r
3634       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3635     y1 = boardRect.top +\r
3636       lineGap/2 + y * (squareSize + lineGap);\r
3637   } else {\r
3638     x1 = boardRect.left +\r
3639       lineGap/2 + x * (squareSize + lineGap);\r
3640     y1 = boardRect.top +\r
3641       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3642   }\r
3643   hPen = pen ? premovePen : highlightPen;\r
3644   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3645   MoveToEx(hdc, x1, y1, NULL);\r
3646   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3647   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3648   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3649   LineTo(hdc, x1, y1);\r
3650   SelectObject(hdc, oldPen);\r
3651 }\r
3652 \r
3653 VOID\r
3654 DrawHighlightsOnDC(HDC hdc)\r
3655 {\r
3656   int i;\r
3657   for (i=0; i<2; i++) {\r
3658     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3659       DrawHighlightOnDC(hdc, TRUE,\r
3660                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3661                         HIGHLIGHT_PEN);\r
3662   }\r
3663   for (i=0; i<2; i++) {\r
3664     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3665         premoveHighlightInfo.sq[i].y >= 0) {\r
3666         DrawHighlightOnDC(hdc, TRUE,\r
3667                           premoveHighlightInfo.sq[i].x, \r
3668                           premoveHighlightInfo.sq[i].y,\r
3669                           PREMOVE_PEN);\r
3670     }\r
3671   }\r
3672 }\r
3673 \r
3674 /* Note: sqcolor is used only in monoMode */\r
3675 /* Note that this code is largely duplicated in woptions.c,\r
3676    function DrawSampleSquare, so that needs to be updated too */\r
3677 VOID\r
3678 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3679 {\r
3680   HBITMAP oldBitmap;\r
3681   HBRUSH oldBrush;\r
3682   int tmpSize;\r
3683 \r
3684   if (appData.blindfold) return;\r
3685 \r
3686   /* [AS] Use font-based pieces if needed */\r
3687   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3688     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3689     CreatePiecesFromFont();\r
3690 \r
3691     if( fontBitmapSquareSize == squareSize ) {\r
3692         int index = TranslatePieceToFontPiece(piece);\r
3693 \r
3694         SelectObject( tmphdc, hPieceMask[ index ] );\r
3695 \r
3696         BitBlt( hdc,\r
3697             x, y,\r
3698             squareSize, squareSize,\r
3699             tmphdc,\r
3700             0, 0,\r
3701             SRCAND );\r
3702 \r
3703         SelectObject( tmphdc, hPieceFace[ index ] );\r
3704 \r
3705         BitBlt( hdc,\r
3706             x, y,\r
3707             squareSize, squareSize,\r
3708             tmphdc,\r
3709             0, 0,\r
3710             SRCPAINT );\r
3711 \r
3712         return;\r
3713     }\r
3714   }\r
3715 \r
3716   if (appData.monoMode) {\r
3717     SelectObject(tmphdc, PieceBitmap(piece, \r
3718       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3719     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3720            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3721   } else {\r
3722     tmpSize = squareSize;\r
3723     if(minorSize &&\r
3724         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3725          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3726       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3727       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3728       x += (squareSize - minorSize)>>1;\r
3729       y += squareSize - minorSize - 2;\r
3730       tmpSize = minorSize;\r
3731     }\r
3732     if (color || appData.allWhite ) {\r
3733       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3734       if( color )\r
3735               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3736       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3737       if(appData.upsideDown && color==flipView)\r
3738         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3739       else\r
3740         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3741 #if 0\r
3742       /* Use black piece color for outline of white pieces */\r
3743       /* Not sure this looks really good (though xboard does it).\r
3744          Maybe better to have another selectable color, default black */\r
3745       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3746       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3747       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3748 #else\r
3749       /* Use black for outline of white pieces */\r
3750       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3751       if(appData.upsideDown && color==flipView)\r
3752         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3753       else\r
3754         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3755 #endif\r
3756     } else {\r
3757 #if 0\r
3758       /* Use white piece color for details of black pieces */\r
3759       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3760          WHITE_PIECE ones aren't always the right shape. */\r
3761       /* Not sure this looks really good (though xboard does it).\r
3762          Maybe better to have another selectable color, default medium gray? */\r
3763       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3764       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3765       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3766       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3767       SelectObject(hdc, blackPieceBrush);\r
3768       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3769 #else\r
3770       /* Use square color for details of black pieces */\r
3771       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3772       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3773       if(appData.upsideDown && !flipView)\r
3774         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3775       else\r
3776         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3777 #endif\r
3778     }\r
3779     SelectObject(hdc, oldBrush);\r
3780     SelectObject(tmphdc, oldBitmap);\r
3781   }\r
3782 }\r
3783 \r
3784 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3785 int GetBackTextureMode( int algo )\r
3786 {\r
3787     int result = BACK_TEXTURE_MODE_DISABLED;\r
3788 \r
3789     switch( algo ) \r
3790     {\r
3791         case BACK_TEXTURE_MODE_PLAIN:\r
3792             result = 1; /* Always use identity map */\r
3793             break;\r
3794         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3795             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3796             break;\r
3797     }\r
3798 \r
3799     return result;\r
3800 }\r
3801 \r
3802 /* \r
3803     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3804     to handle redraws cleanly (as random numbers would always be different).\r
3805 */\r
3806 VOID RebuildTextureSquareInfo()\r
3807 {\r
3808     BITMAP bi;\r
3809     int lite_w = 0;\r
3810     int lite_h = 0;\r
3811     int dark_w = 0;\r
3812     int dark_h = 0;\r
3813     int row;\r
3814     int col;\r
3815 \r
3816     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3817 \r
3818     if( liteBackTexture != NULL ) {\r
3819         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3820             lite_w = bi.bmWidth;\r
3821             lite_h = bi.bmHeight;\r
3822         }\r
3823     }\r
3824 \r
3825     if( darkBackTexture != NULL ) {\r
3826         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3827             dark_w = bi.bmWidth;\r
3828             dark_h = bi.bmHeight;\r
3829         }\r
3830     }\r
3831 \r
3832     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3833         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3834             if( (col + row) & 1 ) {\r
3835                 /* Lite square */\r
3836                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3837                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3838                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3839                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3840                 }\r
3841             }\r
3842             else {\r
3843                 /* Dark square */\r
3844                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3845                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3846                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3847                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3848                 }\r
3849             }\r
3850         }\r
3851     }\r
3852 }\r
3853 \r
3854 /* [AS] Arrow highlighting support */\r
3855 \r
3856 static int A_WIDTH = 5; /* Width of arrow body */\r
3857 \r
3858 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3859 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3860 \r
3861 static double Sqr( double x )\r
3862 {\r
3863     return x*x;\r
3864 }\r
3865 \r
3866 static int Round( double x )\r
3867 {\r
3868     return (int) (x + 0.5);\r
3869 }\r
3870 \r
3871 /* Draw an arrow between two points using current settings */\r
3872 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3873 {\r
3874     POINT arrow[7];\r
3875     double dx, dy, j, k, x, y;\r
3876 \r
3877     if( d_x == s_x ) {\r
3878         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3879 \r
3880         arrow[0].x = s_x + A_WIDTH;\r
3881         arrow[0].y = s_y;\r
3882 \r
3883         arrow[1].x = s_x + A_WIDTH;\r
3884         arrow[1].y = d_y - h;\r
3885 \r
3886         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3887         arrow[2].y = d_y - h;\r
3888 \r
3889         arrow[3].x = d_x;\r
3890         arrow[3].y = d_y;\r
3891 \r
3892         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3893         arrow[4].y = d_y - h;\r
3894 \r
3895         arrow[5].x = s_x - A_WIDTH;\r
3896         arrow[5].y = d_y - h;\r
3897 \r
3898         arrow[6].x = s_x - A_WIDTH;\r
3899         arrow[6].y = s_y;\r
3900     }\r
3901     else if( d_y == s_y ) {\r
3902         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3903 \r
3904         arrow[0].x = s_x;\r
3905         arrow[0].y = s_y + A_WIDTH;\r
3906 \r
3907         arrow[1].x = d_x - w;\r
3908         arrow[1].y = s_y + A_WIDTH;\r
3909 \r
3910         arrow[2].x = d_x - w;\r
3911         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3912 \r
3913         arrow[3].x = d_x;\r
3914         arrow[3].y = d_y;\r
3915 \r
3916         arrow[4].x = d_x - w;\r
3917         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3918 \r
3919         arrow[5].x = d_x - w;\r
3920         arrow[5].y = s_y - A_WIDTH;\r
3921 \r
3922         arrow[6].x = s_x;\r
3923         arrow[6].y = s_y - A_WIDTH;\r
3924     }\r
3925     else {\r
3926         /* [AS] Needed a lot of paper for this! :-) */\r
3927         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3928         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3929   \r
3930         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3931 \r
3932         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3933 \r
3934         x = s_x;\r
3935         y = s_y;\r
3936 \r
3937         arrow[0].x = Round(x - j);\r
3938         arrow[0].y = Round(y + j*dx);\r
3939 \r
3940         arrow[1].x = Round(x + j);\r
3941         arrow[1].y = Round(y - j*dx);\r
3942 \r
3943         if( d_x > s_x ) {\r
3944             x = (double) d_x - k;\r
3945             y = (double) d_y - k*dy;\r
3946         }\r
3947         else {\r
3948             x = (double) d_x + k;\r
3949             y = (double) d_y + k*dy;\r
3950         }\r
3951 \r
3952         arrow[2].x = Round(x + j);\r
3953         arrow[2].y = Round(y - j*dx);\r
3954 \r
3955         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3956         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3957 \r
3958         arrow[4].x = d_x;\r
3959         arrow[4].y = d_y;\r
3960 \r
3961         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3962         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3963 \r
3964         arrow[6].x = Round(x - j);\r
3965         arrow[6].y = Round(y + j*dx);\r
3966     }\r
3967 \r
3968     Polygon( hdc, arrow, 7 );\r
3969 }\r
3970 \r
3971 /* [AS] Draw an arrow between two squares */\r
3972 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3973 {\r
3974     int s_x, s_y, d_x, d_y;\r
3975     HPEN hpen;\r
3976     HPEN holdpen;\r
3977     HBRUSH hbrush;\r
3978     HBRUSH holdbrush;\r
3979     LOGBRUSH stLB;\r
3980 \r
3981     if( s_col == d_col && s_row == d_row ) {\r
3982         return;\r
3983     }\r
3984 \r
3985     /* Get source and destination points */\r
3986     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3987     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3988 \r
3989     if( d_y > s_y ) {\r
3990         d_y += squareSize / 4;\r
3991     }\r
3992     else if( d_y < s_y ) {\r
3993         d_y += 3 * squareSize / 4;\r
3994     }\r
3995     else {\r
3996         d_y += squareSize / 2;\r
3997     }\r
3998 \r
3999     if( d_x > s_x ) {\r
4000         d_x += squareSize / 4;\r
4001     }\r
4002     else if( d_x < s_x ) {\r
4003         d_x += 3 * squareSize / 4;\r
4004     }\r
4005     else {\r
4006         d_x += squareSize / 2;\r
4007     }\r
4008 \r
4009     s_x += squareSize / 2;\r
4010     s_y += squareSize / 2;\r
4011 \r
4012     /* Adjust width */\r
4013     A_WIDTH = squareSize / 14;\r
4014 \r
4015     /* Draw */\r
4016     stLB.lbStyle = BS_SOLID;\r
4017     stLB.lbColor = appData.highlightArrowColor;\r
4018     stLB.lbHatch = 0;\r
4019 \r
4020     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4021     holdpen = SelectObject( hdc, hpen );\r
4022     hbrush = CreateBrushIndirect( &stLB );\r
4023     holdbrush = SelectObject( hdc, hbrush );\r
4024 \r
4025     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4026 \r
4027     SelectObject( hdc, holdpen );\r
4028     SelectObject( hdc, holdbrush );\r
4029     DeleteObject( hpen );\r
4030     DeleteObject( hbrush );\r
4031 }\r
4032 \r
4033 BOOL HasHighlightInfo()\r
4034 {\r
4035     BOOL result = FALSE;\r
4036 \r
4037     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4038         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4039     {\r
4040         result = TRUE;\r
4041     }\r
4042 \r
4043     return result;\r
4044 }\r
4045 \r
4046 BOOL IsDrawArrowEnabled()\r
4047 {\r
4048     BOOL result = FALSE;\r
4049 \r
4050     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4051         result = TRUE;\r
4052     }\r
4053 \r
4054     return result;\r
4055 }\r
4056 \r
4057 VOID DrawArrowHighlight( HDC hdc )\r
4058 {\r
4059     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4060         DrawArrowBetweenSquares( hdc,\r
4061             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4062             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4063     }\r
4064 }\r
4065 \r
4066 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4067 {\r
4068     HRGN result = NULL;\r
4069 \r
4070     if( HasHighlightInfo() ) {\r
4071         int x1, y1, x2, y2;\r
4072         int sx, sy, dx, dy;\r
4073 \r
4074         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4075         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4076 \r
4077         sx = MIN( x1, x2 );\r
4078         sy = MIN( y1, y2 );\r
4079         dx = MAX( x1, x2 ) + squareSize;\r
4080         dy = MAX( y1, y2 ) + squareSize;\r
4081 \r
4082         result = CreateRectRgn( sx, sy, dx, dy );\r
4083     }\r
4084 \r
4085     return result;\r
4086 }\r
4087 \r
4088 /*\r
4089     Warning: this function modifies the behavior of several other functions. \r
4090     \r
4091     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4092     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4093     repaint is scattered all over the place, which is not good for features such as\r
4094     "arrow highlighting" that require a full repaint of the board.\r
4095 \r
4096     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4097     user interaction, when speed is not so important) but especially to avoid errors\r
4098     in the displayed graphics.\r
4099 \r
4100     In such patched places, I always try refer to this function so there is a single\r
4101     place to maintain knowledge.\r
4102     \r
4103     To restore the original behavior, just return FALSE unconditionally.\r
4104 */\r
4105 BOOL IsFullRepaintPreferrable()\r
4106 {\r
4107     BOOL result = FALSE;\r
4108 \r
4109     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4110         /* Arrow may appear on the board */\r
4111         result = TRUE;\r
4112     }\r
4113 \r
4114     return result;\r
4115 }\r
4116 \r
4117 /* \r
4118     This function is called by DrawPosition to know whether a full repaint must\r
4119     be forced or not.\r
4120 \r
4121     Only DrawPosition may directly call this function, which makes use of \r
4122     some state information. Other function should call DrawPosition specifying \r
4123     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4124 */\r
4125 BOOL DrawPositionNeedsFullRepaint()\r
4126 {\r
4127     BOOL result = FALSE;\r
4128 \r
4129     /* \r
4130         Probably a slightly better policy would be to trigger a full repaint\r
4131         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4132         but animation is fast enough that it's difficult to notice.\r
4133     */\r
4134     if( animInfo.piece == EmptySquare ) {\r
4135         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4136             result = TRUE;\r
4137         }\r
4138     }\r
4139 \r
4140     return result;\r
4141 }\r
4142 \r
4143 VOID\r
4144 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4145 {\r
4146   int row, column, x, y, square_color, piece_color;\r
4147   ChessSquare piece;\r
4148   HBRUSH oldBrush;\r
4149   HDC texture_hdc = NULL;\r
4150 \r
4151   /* [AS] Initialize background textures if needed */\r
4152   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4153       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4154       if( backTextureSquareSize != squareSize \r
4155        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4156           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4157           backTextureSquareSize = squareSize;\r
4158           RebuildTextureSquareInfo();\r
4159       }\r
4160 \r
4161       texture_hdc = CreateCompatibleDC( hdc );\r
4162   }\r
4163 \r
4164   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4165     for (column = 0; column < BOARD_WIDTH; column++) {\r
4166   \r
4167       SquareToPos(row, column, &x, &y);\r
4168 \r
4169       piece = board[row][column];\r
4170 \r
4171       square_color = ((column + row) % 2) == 1;\r
4172       if( gameInfo.variant == VariantXiangqi ) {\r
4173           square_color = !InPalace(row, column);\r
4174           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4175           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4176       }\r
4177       piece_color = (int) piece < (int) BlackPawn;\r
4178 \r
4179 \r
4180       /* [HGM] holdings file: light square or black */\r
4181       if(column == BOARD_LEFT-2) {\r
4182             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4183                 square_color = 1;\r
4184             else {\r
4185                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4186                 continue;\r
4187             }\r
4188       } else\r
4189       if(column == BOARD_RGHT + 1 ) {\r
4190             if( row < gameInfo.holdingsSize )\r
4191                 square_color = 1;\r
4192             else {\r
4193                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4194                 continue;\r
4195             }\r
4196       }\r
4197       if(column == BOARD_LEFT-1 ) /* left align */\r
4198             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4199       else if( column == BOARD_RGHT) /* right align */\r
4200             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4201       else\r
4202       if (appData.monoMode) {\r
4203         if (piece == EmptySquare) {\r
4204           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4205                  square_color ? WHITENESS : BLACKNESS);\r
4206         } else {\r
4207           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4208         }\r
4209       } \r
4210       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4211           /* [AS] Draw the square using a texture bitmap */\r
4212           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4213           int r = row, c = column; // [HGM] do not flip board in flipView\r
4214           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4215 \r
4216           DrawTile( x, y, \r
4217               squareSize, squareSize, \r
4218               hdc, \r
4219               texture_hdc,\r
4220               backTextureSquareInfo[r][c].mode,\r
4221               backTextureSquareInfo[r][c].x,\r
4222               backTextureSquareInfo[r][c].y );\r
4223 \r
4224           SelectObject( texture_hdc, hbm );\r
4225 \r
4226           if (piece != EmptySquare) {\r
4227               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4228           }\r
4229       }\r
4230       else {\r
4231         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4232 \r
4233         oldBrush = SelectObject(hdc, brush );\r
4234         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4235         SelectObject(hdc, oldBrush);\r
4236         if (piece != EmptySquare)\r
4237           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4238       }\r
4239     }\r
4240   }\r
4241 \r
4242   if( texture_hdc != NULL ) {\r
4243     DeleteDC( texture_hdc );\r
4244   }\r
4245 }\r
4246 \r
4247 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4248 void fputDW(FILE *f, int x)\r
4249 {\r
4250         fputc(x     & 255, f);\r
4251         fputc(x>>8  & 255, f);\r
4252         fputc(x>>16 & 255, f);\r
4253         fputc(x>>24 & 255, f);\r
4254 }\r
4255 \r
4256 #define MAX_CLIPS 200   /* more than enough */\r
4257 \r
4258 VOID\r
4259 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4260 {\r
4261 //  HBITMAP bufferBitmap;\r
4262   BITMAP bi;\r
4263 //  RECT Rect;\r
4264   HDC tmphdc;\r
4265   HBITMAP hbm;\r
4266   int w = 100, h = 50;\r
4267 \r
4268   if(logo == NULL) return;\r
4269 //  GetClientRect(hwndMain, &Rect);\r
4270 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4271 //                                      Rect.bottom-Rect.top+1);\r
4272   tmphdc = CreateCompatibleDC(hdc);\r
4273   hbm = SelectObject(tmphdc, logo);\r
4274   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4275             w = bi.bmWidth;\r
4276             h = bi.bmHeight;\r
4277   }\r
4278   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4279                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4280   SelectObject(tmphdc, hbm);\r
4281   DeleteDC(tmphdc);\r
4282 }\r
4283 \r
4284 VOID\r
4285 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4286 {\r
4287   static Board lastReq, lastDrawn;\r
4288   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4289   static int lastDrawnFlipView = 0;\r
4290   static int lastReqValid = 0, lastDrawnValid = 0;\r
4291   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4292   HDC tmphdc;\r
4293   HDC hdcmem;\r
4294   HBITMAP bufferBitmap;\r
4295   HBITMAP oldBitmap;\r
4296   RECT Rect;\r
4297   HRGN clips[MAX_CLIPS];\r
4298   ChessSquare dragged_piece = EmptySquare;\r
4299 \r
4300   /* I'm undecided on this - this function figures out whether a full\r
4301    * repaint is necessary on its own, so there's no real reason to have the\r
4302    * caller tell it that.  I think this can safely be set to FALSE - but\r
4303    * if we trust the callers not to request full repaints unnessesarily, then\r
4304    * we could skip some clipping work.  In other words, only request a full\r
4305    * redraw when the majority of pieces have changed positions (ie. flip, \r
4306    * gamestart and similar)  --Hawk\r
4307    */\r
4308   Boolean fullrepaint = repaint;\r
4309 \r
4310   if( DrawPositionNeedsFullRepaint() ) {\r
4311       fullrepaint = TRUE;\r
4312   }\r
4313 \r
4314 #if 0\r
4315   if( fullrepaint ) {\r
4316       static int repaint_count = 0;\r
4317       char buf[128];\r
4318 \r
4319       repaint_count++;\r
4320       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4321       OutputDebugString( buf );\r
4322   }\r
4323 #endif\r
4324 \r
4325   if (board == NULL) {\r
4326     if (!lastReqValid) {\r
4327       return;\r
4328     }\r
4329     board = lastReq;\r
4330   } else {\r
4331     CopyBoard(lastReq, board);\r
4332     lastReqValid = 1;\r
4333   }\r
4334 \r
4335   if (doingSizing) {\r
4336     return;\r
4337   }\r
4338 \r
4339   if (IsIconic(hwndMain)) {\r
4340     return;\r
4341   }\r
4342 \r
4343   if (hdc == NULL) {\r
4344     hdc = GetDC(hwndMain);\r
4345     if (!appData.monoMode) {\r
4346       SelectPalette(hdc, hPal, FALSE);\r
4347       RealizePalette(hdc);\r
4348     }\r
4349     releaseDC = TRUE;\r
4350   } else {\r
4351     releaseDC = FALSE;\r
4352   }\r
4353 \r
4354 #if 0\r
4355   fprintf(debugFP, "*******************************\n"\r
4356                    "repaint = %s\n"\r
4357                    "dragInfo.from (%d,%d)\n"\r
4358                    "dragInfo.start (%d,%d)\n"\r
4359                    "dragInfo.pos (%d,%d)\n"\r
4360                    "dragInfo.lastpos (%d,%d)\n", \r
4361                     repaint ? "TRUE" : "FALSE",\r
4362                     dragInfo.from.x, dragInfo.from.y, \r
4363                     dragInfo.start.x, dragInfo.start.y,\r
4364                     dragInfo.pos.x, dragInfo.pos.y,\r
4365                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4366   fprintf(debugFP, "prev:  ");\r
4367   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4368     for (column = 0; column < BOARD_WIDTH; column++) {\r
4369       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4370     }\r
4371   }\r
4372   fprintf(debugFP, "\n");\r
4373   fprintf(debugFP, "board: ");\r
4374   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4375     for (column = 0; column < BOARD_WIDTH; column++) {\r
4376       fprintf(debugFP, "%d ", board[row][column]);\r
4377     }\r
4378   }\r
4379   fprintf(debugFP, "\n");\r
4380   fflush(debugFP);\r
4381 #endif\r
4382 \r
4383   /* Create some work-DCs */\r
4384   hdcmem = CreateCompatibleDC(hdc);\r
4385   tmphdc = CreateCompatibleDC(hdc);\r
4386 \r
4387   /* If dragging is in progress, we temporarely remove the piece */\r
4388   /* [HGM] or temporarily decrease count if stacked              */\r
4389   /*       !! Moved to before board compare !!                   */\r
4390   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4391     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4392     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4393             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4394         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4395     } else \r
4396     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4397             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4398         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4399     } else \r
4400         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4401   }\r
4402 \r
4403   /* Figure out which squares need updating by comparing the \r
4404    * newest board with the last drawn board and checking if\r
4405    * flipping has changed.\r
4406    */\r
4407   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4408     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4409       for (column = 0; column < BOARD_WIDTH; column++) {\r
4410         if (lastDrawn[row][column] != board[row][column]) {\r
4411           SquareToPos(row, column, &x, &y);\r
4412           clips[num_clips++] =\r
4413             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4414         }\r
4415       }\r
4416     }\r
4417     for (i=0; i<2; i++) {\r
4418       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4419           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4420         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4421             lastDrawnHighlight.sq[i].y >= 0) {\r
4422           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4423                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4424           clips[num_clips++] =\r
4425             CreateRectRgn(x - lineGap, y - lineGap, \r
4426                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4427         }\r
4428         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4429           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4430           clips[num_clips++] =\r
4431             CreateRectRgn(x - lineGap, y - lineGap, \r
4432                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4433         }\r
4434       }\r
4435     }\r
4436     for (i=0; i<2; i++) {\r
4437       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4438           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4439         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4440             lastDrawnPremove.sq[i].y >= 0) {\r
4441           SquareToPos(lastDrawnPremove.sq[i].y,\r
4442                       lastDrawnPremove.sq[i].x, &x, &y);\r
4443           clips[num_clips++] =\r
4444             CreateRectRgn(x - lineGap, y - lineGap, \r
4445                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4446         }\r
4447         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4448             premoveHighlightInfo.sq[i].y >= 0) {\r
4449           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4450                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4451           clips[num_clips++] =\r
4452             CreateRectRgn(x - lineGap, y - lineGap, \r
4453                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4454         }\r
4455       }\r
4456     }\r
4457   } else {\r
4458     fullrepaint = TRUE;\r
4459   }\r
4460 \r
4461   /* Create a buffer bitmap - this is the actual bitmap\r
4462    * being written to.  When all the work is done, we can\r
4463    * copy it to the real DC (the screen).  This avoids\r
4464    * the problems with flickering.\r
4465    */\r
4466   GetClientRect(hwndMain, &Rect);\r
4467   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4468                                         Rect.bottom-Rect.top+1);\r
4469   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4470   if (!appData.monoMode) {\r
4471     SelectPalette(hdcmem, hPal, FALSE);\r
4472   }\r
4473 \r
4474   /* Create clips for dragging */\r
4475   if (!fullrepaint) {\r
4476     if (dragInfo.from.x >= 0) {\r
4477       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4478       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4479     }\r
4480     if (dragInfo.start.x >= 0) {\r
4481       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4482       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4483     }\r
4484     if (dragInfo.pos.x >= 0) {\r
4485       x = dragInfo.pos.x - squareSize / 2;\r
4486       y = dragInfo.pos.y - squareSize / 2;\r
4487       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4488     }\r
4489     if (dragInfo.lastpos.x >= 0) {\r
4490       x = dragInfo.lastpos.x - squareSize / 2;\r
4491       y = dragInfo.lastpos.y - squareSize / 2;\r
4492       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4493     }\r
4494   }\r
4495 \r
4496   /* Are we animating a move?  \r
4497    * If so, \r
4498    *   - remove the piece from the board (temporarely)\r
4499    *   - calculate the clipping region\r
4500    */\r
4501   if (!fullrepaint) {\r
4502     if (animInfo.piece != EmptySquare) {\r
4503       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4504       x = boardRect.left + animInfo.lastpos.x;\r
4505       y = boardRect.top + animInfo.lastpos.y;\r
4506       x2 = boardRect.left + animInfo.pos.x;\r
4507       y2 = boardRect.top + animInfo.pos.y;\r
4508       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4509       /* Slight kludge.  The real problem is that after AnimateMove is\r
4510          done, the position on the screen does not match lastDrawn.\r
4511          This currently causes trouble only on e.p. captures in\r
4512          atomic, where the piece moves to an empty square and then\r
4513          explodes.  The old and new positions both had an empty square\r
4514          at the destination, but animation has drawn a piece there and\r
4515          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4516       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4517     }\r
4518   }\r
4519 \r
4520   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4521   if (num_clips == 0)\r
4522     fullrepaint = TRUE;\r
4523 \r
4524   /* Set clipping on the memory DC */\r
4525   if (!fullrepaint) {\r
4526     SelectClipRgn(hdcmem, clips[0]);\r
4527     for (x = 1; x < num_clips; x++) {\r
4528       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4529         abort();  // this should never ever happen!\r
4530     }\r
4531   }\r
4532 \r
4533   /* Do all the drawing to the memory DC */\r
4534   if(explodeInfo.radius) { // [HGM] atomic\r
4535         HBRUSH oldBrush;\r
4536         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4537         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4538         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4539         x += squareSize/2;\r
4540         y += squareSize/2;\r
4541         if(!fullrepaint) {\r
4542           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4543           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4544         }\r
4545         DrawGridOnDC(hdcmem);\r
4546         DrawHighlightsOnDC(hdcmem);\r
4547         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4548         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4549         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4550         SelectObject(hdcmem, oldBrush);\r
4551   } else {\r
4552     DrawGridOnDC(hdcmem);\r
4553     DrawHighlightsOnDC(hdcmem);\r
4554     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4555   }\r
4556   if(logoHeight) {\r
4557         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4558         if(appData.autoLogo) {\r
4559           \r
4560           switch(gameMode) { // pick logos based on game mode\r
4561             case IcsObserving:\r
4562                 whiteLogo = second.programLogo; // ICS logo\r
4563                 blackLogo = second.programLogo;\r
4564             default:\r
4565                 break;\r
4566             case IcsPlayingWhite:\r
4567                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4568                 blackLogo = second.programLogo; // ICS logo\r
4569                 break;\r
4570             case IcsPlayingBlack:\r
4571                 whiteLogo = second.programLogo; // ICS logo\r
4572                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4573                 break;\r
4574             case TwoMachinesPlay:\r
4575                 if(first.twoMachinesColor[0] == 'b') {\r
4576                     whiteLogo = second.programLogo;\r
4577                     blackLogo = first.programLogo;\r
4578                 }\r
4579                 break;\r
4580             case MachinePlaysWhite:\r
4581                 blackLogo = userLogo;\r
4582                 break;\r
4583             case MachinePlaysBlack:\r
4584                 whiteLogo = userLogo;\r
4585                 blackLogo = first.programLogo;\r
4586           }\r
4587         }\r
4588         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4589         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4590   }\r
4591 \r
4592   if( appData.highlightMoveWithArrow ) {\r
4593     DrawArrowHighlight(hdcmem);\r
4594   }\r
4595 \r
4596   DrawCoordsOnDC(hdcmem);\r
4597 \r
4598   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4599                  /* to make sure lastDrawn contains what is actually drawn */\r
4600 \r
4601   /* Put the dragged piece back into place and draw it (out of place!) */\r
4602     if (dragged_piece != EmptySquare) {\r
4603     /* [HGM] or restack */\r
4604     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4605                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4606     else\r
4607     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4608                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4609     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4610     x = dragInfo.pos.x - squareSize / 2;\r
4611     y = dragInfo.pos.y - squareSize / 2;\r
4612     DrawPieceOnDC(hdcmem, dragged_piece,\r
4613                   ((int) dragged_piece < (int) BlackPawn), \r
4614                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4615   }   \r
4616   \r
4617   /* Put the animated piece back into place and draw it */\r
4618   if (animInfo.piece != EmptySquare) {\r
4619     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4620     x = boardRect.left + animInfo.pos.x;\r
4621     y = boardRect.top + animInfo.pos.y;\r
4622     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4623                   ((int) animInfo.piece < (int) BlackPawn),\r
4624                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4625   }\r
4626 \r
4627   /* Release the bufferBitmap by selecting in the old bitmap \r
4628    * and delete the memory DC\r
4629    */\r
4630   SelectObject(hdcmem, oldBitmap);\r
4631   DeleteDC(hdcmem);\r
4632 \r
4633   /* Set clipping on the target DC */\r
4634   if (!fullrepaint) {\r
4635     SelectClipRgn(hdc, clips[0]);\r
4636     for (x = 1; x < num_clips; x++) {\r
4637       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4638         abort();   // this should never ever happen!\r
4639     } \r
4640   }\r
4641 \r
4642   /* Copy the new bitmap onto the screen in one go.\r
4643    * This way we avoid any flickering\r
4644    */\r
4645   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4646   BitBlt(hdc, boardRect.left, boardRect.top,\r
4647          boardRect.right - boardRect.left,\r
4648          boardRect.bottom - boardRect.top,\r
4649          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4650   if(saveDiagFlag) { \r
4651     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4652     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4653 \r
4654     GetObject(bufferBitmap, sizeof(b), &b);\r
4655     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4656         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4657         bih.biWidth = b.bmWidth;\r
4658         bih.biHeight = b.bmHeight;\r
4659         bih.biPlanes = 1;\r
4660         bih.biBitCount = b.bmBitsPixel;\r
4661         bih.biCompression = 0;\r
4662         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4663         bih.biXPelsPerMeter = 0;\r
4664         bih.biYPelsPerMeter = 0;\r
4665         bih.biClrUsed = 0;\r
4666         bih.biClrImportant = 0;\r
4667 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4668 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4669         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4670 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4671 \r
4672 #if 1\r
4673         wb = b.bmWidthBytes;\r
4674         // count colors\r
4675         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4676                 int k = ((int*) pData)[i];\r
4677                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4678                 if(j >= 16) break;\r
4679                 color[j] = k;\r
4680                 if(j >= nrColors) nrColors = j+1;\r
4681         }\r
4682         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4683                 INT p = 0;\r
4684                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4685                     for(w=0; w<(wb>>2); w+=2) {\r
4686                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4687                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4688                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4689                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4690                         pData[p++] = m | j<<4;\r
4691                     }\r
4692                     while(p&3) pData[p++] = 0;\r
4693                 }\r
4694                 fac = 3;\r
4695                 wb = ((wb+31)>>5)<<2;\r
4696         }\r
4697         // write BITMAPFILEHEADER\r
4698         fprintf(diagFile, "BM");\r
4699         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4700         fputDW(diagFile, 0);\r
4701         fputDW(diagFile, 0x36 + (fac?64:0));\r
4702         // write BITMAPINFOHEADER\r
4703         fputDW(diagFile, 40);\r
4704         fputDW(diagFile, b.bmWidth);\r
4705         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4706         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4707         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4708         fputDW(diagFile, 0);\r
4709         fputDW(diagFile, 0);\r
4710         fputDW(diagFile, 0);\r
4711         fputDW(diagFile, 0);\r
4712         fputDW(diagFile, 0);\r
4713         fputDW(diagFile, 0);\r
4714         // write color table\r
4715         if(fac)\r
4716         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4717         // write bitmap data\r
4718         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4719                 fputc(pData[i], diagFile);\r
4720 #endif\r
4721      }\r
4722   }\r
4723 \r
4724   SelectObject(tmphdc, oldBitmap);\r
4725 \r
4726   /* Massive cleanup */\r
4727   for (x = 0; x < num_clips; x++)\r
4728     DeleteObject(clips[x]);\r
4729 \r
4730   DeleteDC(tmphdc);\r
4731   DeleteObject(bufferBitmap);\r
4732 \r
4733   if (releaseDC) \r
4734     ReleaseDC(hwndMain, hdc);\r
4735   \r
4736   if (lastDrawnFlipView != flipView) {\r
4737     if (flipView)\r
4738       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4739     else\r
4740       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4741   }\r
4742 \r
4743 /*  CopyBoard(lastDrawn, board);*/\r
4744   lastDrawnHighlight = highlightInfo;\r
4745   lastDrawnPremove   = premoveHighlightInfo;\r
4746   lastDrawnFlipView = flipView;\r
4747   lastDrawnValid = 1;\r
4748 }\r
4749 \r
4750 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4751 int\r
4752 SaveDiagram(f)\r
4753      FILE *f;\r
4754 {\r
4755     saveDiagFlag = 1; diagFile = f;\r
4756     HDCDrawPosition(NULL, TRUE, NULL);\r
4757 \r
4758     saveDiagFlag = 0;\r
4759 \r
4760 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4761     \r
4762     fclose(f);\r
4763     return TRUE;\r
4764 }\r
4765 \r
4766 \r
4767 /*---------------------------------------------------------------------------*\\r
4768 | CLIENT PAINT PROCEDURE\r
4769 |   This is the main event-handler for the WM_PAINT message.\r
4770 |\r
4771 \*---------------------------------------------------------------------------*/\r
4772 VOID\r
4773 PaintProc(HWND hwnd)\r
4774 {\r
4775   HDC         hdc;\r
4776   PAINTSTRUCT ps;\r
4777   HFONT       oldFont;\r
4778 \r
4779   if((hdc = BeginPaint(hwnd, &ps))) {\r
4780     if (IsIconic(hwnd)) {\r
4781       DrawIcon(hdc, 2, 2, iconCurrent);\r
4782     } else {\r
4783       if (!appData.monoMode) {\r
4784         SelectPalette(hdc, hPal, FALSE);\r
4785         RealizePalette(hdc);\r
4786       }\r
4787       HDCDrawPosition(hdc, 1, NULL);\r
4788       oldFont =\r
4789         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4790       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4791                  ETO_CLIPPED|ETO_OPAQUE,\r
4792                  &messageRect, messageText, strlen(messageText), NULL);\r
4793       SelectObject(hdc, oldFont);\r
4794       DisplayBothClocks();\r
4795     }\r
4796     EndPaint(hwnd,&ps);\r
4797   }\r
4798 \r
4799   return;\r
4800 }\r
4801 \r
4802 \r
4803 /*\r
4804  * If the user selects on a border boundary, return -1; if off the board,\r
4805  *   return -2.  Otherwise map the event coordinate to the square.\r
4806  * The offset boardRect.left or boardRect.top must already have been\r
4807  *   subtracted from x.\r
4808  */\r
4809 int\r
4810 EventToSquare(int x)\r
4811 {\r
4812   if (x <= 0)\r
4813     return -2;\r
4814   if (x < lineGap)\r
4815     return -1;\r
4816   x -= lineGap;\r
4817   if ((x % (squareSize + lineGap)) >= squareSize)\r
4818     return -1;\r
4819   x /= (squareSize + lineGap);\r
4820   if (x >= BOARD_SIZE)\r
4821     return -2;\r
4822   return x;\r
4823 }\r
4824 \r
4825 typedef struct {\r
4826   char piece;\r
4827   int command;\r
4828   char* name;\r
4829 } DropEnable;\r
4830 \r
4831 DropEnable dropEnables[] = {\r
4832   { 'P', DP_Pawn, "Pawn" },\r
4833   { 'N', DP_Knight, "Knight" },\r
4834   { 'B', DP_Bishop, "Bishop" },\r
4835   { 'R', DP_Rook, "Rook" },\r
4836   { 'Q', DP_Queen, "Queen" },\r
4837 };\r
4838 \r
4839 VOID\r
4840 SetupDropMenu(HMENU hmenu)\r
4841 {\r
4842   int i, count, enable;\r
4843   char *p;\r
4844   extern char white_holding[], black_holding[];\r
4845   char item[MSG_SIZ];\r
4846 \r
4847   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4848     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4849                dropEnables[i].piece);\r
4850     count = 0;\r
4851     while (p && *p++ == dropEnables[i].piece) count++;\r
4852     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4853     enable = count > 0 || !appData.testLegality\r
4854       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4855                       && !appData.icsActive);\r
4856     ModifyMenu(hmenu, dropEnables[i].command,\r
4857                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4858                dropEnables[i].command, item);\r
4859   }\r
4860 }\r
4861 \r
4862 static int fromX = -1, fromY = -1, toX, toY;\r
4863 \r
4864 /* Event handler for mouse messages */\r
4865 VOID\r
4866 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4867 {\r
4868   int x, y;\r
4869   POINT pt;\r
4870   static int recursive = 0;\r
4871   HMENU hmenu;\r
4872 //  BOOLEAN needsRedraw = FALSE;\r
4873   BOOLEAN saveAnimate;\r
4874   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4875   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4876   ChessMove moveType;\r
4877 \r
4878   if (recursive) {\r
4879     if (message == WM_MBUTTONUP) {\r
4880       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4881          to the middle button: we simulate pressing the left button too!\r
4882          */\r
4883       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4884       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4885     }\r
4886     return;\r
4887   }\r
4888   recursive++;\r
4889   \r
4890   pt.x = LOWORD(lParam);\r
4891   pt.y = HIWORD(lParam);\r
4892   x = EventToSquare(pt.x - boardRect.left);\r
4893   y = EventToSquare(pt.y - boardRect.top);\r
4894   if (!flipView && y >= 0) {\r
4895     y = BOARD_HEIGHT - 1 - y;\r
4896   }\r
4897   if (flipView && x >= 0) {\r
4898     x = BOARD_WIDTH - 1 - x;\r
4899   }\r
4900 \r
4901   switch (message) {\r
4902   case WM_LBUTTONDOWN:\r
4903     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4904         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4905         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4906         if(gameInfo.holdingsWidth && \r
4907                 (WhiteOnMove(currentMove) \r
4908                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4909                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4910             // click in right holdings, for determining promotion piece\r
4911             ChessSquare p = boards[currentMove][y][x];\r
4912             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4913             if(p != EmptySquare) {\r
4914                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4915                 fromX = fromY = -1;\r
4916                 break;\r
4917             }\r
4918         }\r
4919         DrawPosition(FALSE, boards[currentMove]);\r
4920         break;\r
4921     }\r
4922     ErrorPopDown();\r
4923     sameAgain = FALSE;\r
4924     if (y == -2) {\r
4925       /* Downclick vertically off board; check if on clock */\r
4926       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4927         if (gameMode == EditPosition) {\r
4928           SetWhiteToPlayEvent();\r
4929         } else if (gameMode == IcsPlayingBlack ||\r
4930                    gameMode == MachinePlaysWhite) {\r
4931           CallFlagEvent();\r
4932         } else if (gameMode == EditGame) {\r
4933           AdjustClock(flipClock, -1);\r
4934         }\r
4935       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4936         if (gameMode == EditPosition) {\r
4937           SetBlackToPlayEvent();\r
4938         } else if (gameMode == IcsPlayingWhite ||\r
4939                    gameMode == MachinePlaysBlack) {\r
4940           CallFlagEvent();\r
4941         } else if (gameMode == EditGame) {\r
4942           AdjustClock(!flipClock, -1);\r
4943         }\r
4944       }\r
4945       if (!appData.highlightLastMove) {\r
4946         ClearHighlights();\r
4947         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4948       }\r
4949       fromX = fromY = -1;\r
4950       dragInfo.start.x = dragInfo.start.y = -1;\r
4951       dragInfo.from = dragInfo.start;\r
4952       break;\r
4953     } else if (x < 0 || y < 0\r
4954       /* [HGM] block clicks between board and holdings */\r
4955               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4956               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4957               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4958         /* EditPosition, empty square, or different color piece;\r
4959            click-click move is possible */\r
4960                                ) {\r
4961       break;\r
4962     } else if (fromX == x && fromY == y) {\r
4963       /* Downclick on same square again */\r
4964       ClearHighlights();\r
4965       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4966       sameAgain = TRUE;  \r
4967     } else if (fromX != -1 &&\r
4968                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4969                                                                         ) {\r
4970       /* Downclick on different square. */\r
4971       /* [HGM] if on holdings file, should count as new first click ! */\r
4972       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4973         toX = x;\r
4974         toY = y;\r
4975         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4976            to make sure move is legal before showing promotion popup */\r
4977         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4978         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4979                 fromX = fromY = -1; \r
4980                 ClearHighlights();\r
4981                 DrawPosition(FALSE, boards[currentMove]);\r
4982                 break; \r
4983         } else \r
4984         if(moveType != ImpossibleMove) {\r
4985           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4986           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4987             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4988               appData.alwaysPromoteToQueen)) {\r
4989                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4990                   if (!appData.highlightLastMove) {\r
4991                       ClearHighlights();\r
4992                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4993                   }\r
4994           } else\r
4995           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4996                   SetHighlights(fromX, fromY, toX, toY);\r
4997                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4998                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4999                      If promotion to Q is legal, all are legal! */\r
5000                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5001                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5002                     // kludge to temporarily execute move on display, wthout promotng yet\r
5003                     promotionChoice = TRUE;\r
5004                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5005                     boards[currentMove][toY][toX] = p;\r
5006                     DrawPosition(FALSE, boards[currentMove]);\r
5007                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5008                     boards[currentMove][toY][toX] = q;\r
5009                   } else\r
5010                   PromotionPopup(hwnd);\r
5011           } else {       /* not a promotion */\r
5012              if (appData.animate || appData.highlightLastMove) {\r
5013                  SetHighlights(fromX, fromY, toX, toY);\r
5014              } else {\r
5015                  ClearHighlights();\r
5016              }\r
5017              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5018              fromX = fromY = -1;\r
5019              if (appData.animate && !appData.highlightLastMove) {\r
5020                   ClearHighlights();\r
5021                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5022              }\r
5023           }\r
5024           break;\r
5025         }\r
5026         if (gotPremove) {\r
5027             /* [HGM] it seemed that braces were missing here */\r
5028             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5029             fromX = fromY = -1;\r
5030             break;\r
5031         }\r
5032       }\r
5033       ClearHighlights();\r
5034       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5035     }\r
5036     /* First downclick, or restart on a square with same color piece */\r
5037     if (!frozen && OKToStartUserMove(x, y)) {\r
5038       fromX = x;\r
5039       fromY = y;\r
5040       dragInfo.lastpos = pt;\r
5041       dragInfo.from.x = fromX;\r
5042       dragInfo.from.y = fromY;\r
5043       dragInfo.start = dragInfo.from;\r
5044       SetCapture(hwndMain);\r
5045     } else {\r
5046       fromX = fromY = -1;\r
5047       dragInfo.start.x = dragInfo.start.y = -1;\r
5048       dragInfo.from = dragInfo.start;\r
5049       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5050     }\r
5051     break;\r
5052 \r
5053   case WM_LBUTTONUP:\r
5054     ReleaseCapture();\r
5055     if (fromX == -1) break;\r
5056     if (x == fromX && y == fromY) {\r
5057       dragInfo.from.x = dragInfo.from.y = -1;\r
5058       /* Upclick on same square */\r
5059       if (sameAgain) {\r
5060         /* Clicked same square twice: abort click-click move */\r
5061         fromX = fromY = -1;\r
5062         gotPremove = 0;\r
5063         ClearPremoveHighlights();\r
5064       } else {\r
5065         /* First square clicked: start click-click move */\r
5066         SetHighlights(fromX, fromY, -1, -1);\r
5067       }\r
5068       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5069     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5070       /* Errant click; ignore */\r
5071       break;\r
5072     } else {\r
5073       /* Finish drag move. */\r
5074     if (appData.debugMode) {\r
5075         fprintf(debugFP, "release\n");\r
5076     }\r
5077       dragInfo.from.x = dragInfo.from.y = -1;\r
5078       toX = x;\r
5079       toY = y;\r
5080       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5081       appData.animate = appData.animate && !appData.animateDragging;\r
5082       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5083       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5084                 fromX = fromY = -1; \r
5085                 ClearHighlights();\r
5086                 DrawPosition(FALSE, boards[currentMove]);\r
5087                 break; \r
5088       } else \r
5089       if(moveType != ImpossibleMove) {\r
5090           /* [HGM] use move type to determine if move is promotion.\r
5091              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5092           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5093             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5094               appData.alwaysPromoteToQueen)) \r
5095                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5096           else \r
5097           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5098                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5099                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5100                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5101                     // kludge to temporarily execute move on display, wthout promotng yet\r
5102                     promotionChoice = TRUE;\r
5103                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5104                     boards[currentMove][toY][toX] = p;\r
5105                     DrawPosition(FALSE, boards[currentMove]);\r
5106                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5107                     boards[currentMove][toY][toX] = q;\r
5108                     break;\r
5109                   } else\r
5110                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5111           } else {\r
5112             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5113                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5114                                         moveType == WhiteCapturesEnPassant || \r
5115                                         moveType == BlackCapturesEnPassant   ) )\r
5116                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5117             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5118           }\r
5119       }\r
5120       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5121       appData.animate = saveAnimate;\r
5122       fromX = fromY = -1;\r
5123       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5124         ClearHighlights();\r
5125       }\r
5126       if (appData.animate || appData.animateDragging ||\r
5127           appData.highlightDragging || gotPremove) {\r
5128         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5129       }\r
5130     }\r
5131     dragInfo.start.x = dragInfo.start.y = -1; \r
5132     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5133     break;\r
5134 \r
5135   case WM_MOUSEMOVE:\r
5136     if ((appData.animateDragging || appData.highlightDragging)\r
5137         && (wParam & MK_LBUTTON)\r
5138         && dragInfo.from.x >= 0) \r
5139     {\r
5140       BOOL full_repaint = FALSE;\r
5141 \r
5142       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5143       if (appData.animateDragging) {\r
5144         dragInfo.pos = pt;\r
5145       }\r
5146       if (appData.highlightDragging) {\r
5147         SetHighlights(fromX, fromY, x, y);\r
5148         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5149             full_repaint = TRUE;\r
5150         }\r
5151       }\r
5152       \r
5153       DrawPosition( full_repaint, NULL);\r
5154       \r
5155       dragInfo.lastpos = dragInfo.pos;\r
5156     }\r
5157     break;\r
5158 \r
5159   case WM_MOUSEWHEEL: // [DM]\r
5160     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5161        /* Mouse Wheel is being rolled forward\r
5162         * Play moves forward\r
5163         */\r
5164        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5165                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5166        /* Mouse Wheel is being rolled backward\r
5167         * Play moves backward\r
5168         */\r
5169        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5170                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5171     }\r
5172     break;\r
5173 \r
5174   case WM_MBUTTONDOWN:\r
5175   case WM_RBUTTONDOWN:\r
5176     ErrorPopDown();\r
5177     ReleaseCapture();\r
5178     fromX = fromY = -1;\r
5179     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5180     dragInfo.start.x = dragInfo.start.y = -1;\r
5181     dragInfo.from = dragInfo.start;\r
5182     dragInfo.lastpos = dragInfo.pos;\r
5183     if (appData.highlightDragging) {\r
5184       ClearHighlights();\r
5185     }\r
5186     if(y == -2) {\r
5187       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5188       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5189           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5190       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5191           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5192       }\r
5193     }\r
5194     DrawPosition(TRUE, NULL);\r
5195 \r
5196     switch (gameMode) {\r
5197     case EditPosition:\r
5198     case IcsExamining:\r
5199       if (x < 0 || y < 0) break;\r
5200       fromX = x;\r
5201       fromY = y;\r
5202       if (message == WM_MBUTTONDOWN) {\r
5203         buttonCount = 3;  /* even if system didn't think so */\r
5204         if (wParam & MK_SHIFT) \r
5205           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5206         else\r
5207           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5208       } else { /* message == WM_RBUTTONDOWN */\r
5209 #if 0\r
5210         if (buttonCount == 3) {\r
5211           if (wParam & MK_SHIFT) \r
5212             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5213           else\r
5214             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5215         } else {\r
5216           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5217         }\r
5218 #else\r
5219         /* Just have one menu, on the right button.  Windows users don't\r
5220            think to try the middle one, and sometimes other software steals\r
5221            it, or it doesn't really exist. */\r
5222         if(gameInfo.variant != VariantShogi)\r
5223             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5224         else\r
5225             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5226 #endif\r
5227       }\r
5228       break;\r
5229     case IcsPlayingWhite:\r
5230     case IcsPlayingBlack:\r
5231     case EditGame:\r
5232     case MachinePlaysWhite:\r
5233     case MachinePlaysBlack:\r
5234       if (appData.testLegality &&\r
5235           gameInfo.variant != VariantBughouse &&\r
5236           gameInfo.variant != VariantCrazyhouse) break;\r
5237       if (x < 0 || y < 0) break;\r
5238       fromX = x;\r
5239       fromY = y;\r
5240       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5241       SetupDropMenu(hmenu);\r
5242       MenuPopup(hwnd, pt, hmenu, -1);\r
5243       break;\r
5244     default:\r
5245       break;\r
5246     }\r
5247     break;\r
5248   }\r
5249 \r
5250   recursive--;\r
5251 }\r
5252 \r
5253 /* Preprocess messages for buttons in main window */\r
5254 LRESULT CALLBACK\r
5255 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5256 {\r
5257   int id = GetWindowLong(hwnd, GWL_ID);\r
5258   int i, dir;\r
5259 \r
5260   for (i=0; i<N_BUTTONS; i++) {\r
5261     if (buttonDesc[i].id == id) break;\r
5262   }\r
5263   if (i == N_BUTTONS) return 0;\r
5264   switch (message) {\r
5265   case WM_KEYDOWN:\r
5266     switch (wParam) {\r
5267     case VK_LEFT:\r
5268     case VK_RIGHT:\r
5269       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5270       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5271       return TRUE;\r
5272     }\r
5273     break;\r
5274   case WM_CHAR:\r
5275     switch (wParam) {\r
5276     case '\r':\r
5277       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5278       return TRUE;\r
5279     case '\t':\r
5280       if (appData.icsActive) {\r
5281         if (GetKeyState(VK_SHIFT) < 0) {\r
5282           /* shifted */\r
5283           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5284           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5285           SetFocus(h);\r
5286         } else {\r
5287           /* unshifted */\r
5288           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5289           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5290           SetFocus(h);\r
5291         }\r
5292         return TRUE;\r
5293       }\r
5294       break;\r
5295     default:\r
5296       if (appData.icsActive) {\r
5297         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5298         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5299         SetFocus(h);\r
5300         SendMessage(h, WM_CHAR, wParam, lParam);\r
5301         return TRUE;\r
5302       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5303         PopUpMoveDialog((char)wParam);\r
5304       }\r
5305       break;\r
5306     }\r
5307     break;\r
5308   }\r
5309   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5310 }\r
5311 \r
5312 /* Process messages for Promotion dialog box */\r
5313 LRESULT CALLBACK\r
5314 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5315 {\r
5316   char promoChar;\r
5317 \r
5318   switch (message) {\r
5319   case WM_INITDIALOG: /* message: initialize dialog box */\r
5320     /* Center the dialog over the application window */\r
5321     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5322     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5323       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5324        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5325                SW_SHOW : SW_HIDE);\r
5326     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5327     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5328        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5329          PieceToChar(WhiteAngel) != '~') ||\r
5330         (PieceToChar(BlackAngel) >= 'A' &&\r
5331          PieceToChar(BlackAngel) != '~')   ) ?\r
5332                SW_SHOW : SW_HIDE);\r
5333     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5334        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5335          PieceToChar(WhiteMarshall) != '~') ||\r
5336         (PieceToChar(BlackMarshall) >= 'A' &&\r
5337          PieceToChar(BlackMarshall) != '~')   ) ?\r
5338                SW_SHOW : SW_HIDE);\r
5339     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5340     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5341        gameInfo.variant != VariantShogi ?\r
5342                SW_SHOW : SW_HIDE);\r
5343     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5344        gameInfo.variant != VariantShogi ?\r
5345                SW_SHOW : SW_HIDE);\r
5346     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5347        gameInfo.variant == VariantShogi ?\r
5348                SW_SHOW : SW_HIDE);\r
5349     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5350        gameInfo.variant == VariantShogi ?\r
5351                SW_SHOW : SW_HIDE);\r
5352     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5353        gameInfo.variant == VariantSuper ?\r
5354                SW_SHOW : SW_HIDE);\r
5355     return TRUE;\r
5356 \r
5357   case WM_COMMAND: /* message: received a command */\r
5358     switch (LOWORD(wParam)) {\r
5359     case IDCANCEL:\r
5360       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5361       ClearHighlights();\r
5362       DrawPosition(FALSE, NULL);\r
5363       return TRUE;\r
5364     case PB_King:\r
5365       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5366       break;\r
5367     case PB_Queen:\r
5368       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5369       break;\r
5370     case PB_Rook:\r
5371       promoChar = PieceToChar(BlackRook);\r
5372       break;\r
5373     case PB_Bishop:\r
5374       promoChar = PieceToChar(BlackBishop);\r
5375       break;\r
5376     case PB_Chancellor:\r
5377       promoChar = PieceToChar(BlackMarshall);\r
5378       break;\r
5379     case PB_Archbishop:\r
5380       promoChar = PieceToChar(BlackAngel);\r
5381       break;\r
5382     case PB_Knight:\r
5383       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5384       break;\r
5385     default:\r
5386       return FALSE;\r
5387     }\r
5388     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5389     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5390        only show the popup when we are already sure the move is valid or\r
5391        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5392        will figure out it is a promotion from the promoChar. */\r
5393     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5394     if (!appData.highlightLastMove) {\r
5395       ClearHighlights();\r
5396       DrawPosition(FALSE, NULL);\r
5397     }\r
5398     return TRUE;\r
5399   }\r
5400   return FALSE;\r
5401 }\r
5402 \r
5403 /* Pop up promotion dialog */\r
5404 VOID\r
5405 PromotionPopup(HWND hwnd)\r
5406 {\r
5407   FARPROC lpProc;\r
5408 \r
5409   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5410   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5411     hwnd, (DLGPROC)lpProc);\r
5412   FreeProcInstance(lpProc);\r
5413 }\r
5414 \r
5415 /* Toggle ShowThinking */\r
5416 VOID\r
5417 ToggleShowThinking()\r
5418 {\r
5419   appData.showThinking = !appData.showThinking;\r
5420   ShowThinkingEvent();\r
5421 }\r
5422 \r
5423 VOID\r
5424 LoadGameDialog(HWND hwnd, char* title)\r
5425 {\r
5426   UINT number = 0;\r
5427   FILE *f;\r
5428   char fileTitle[MSG_SIZ];\r
5429   f = OpenFileDialog(hwnd, "rb", "",\r
5430                      appData.oldSaveStyle ? "gam" : "pgn",\r
5431                      GAME_FILT,\r
5432                      title, &number, fileTitle, NULL);\r
5433   if (f != NULL) {\r
5434     cmailMsgLoaded = FALSE;\r
5435     if (number == 0) {\r
5436       int error = GameListBuild(f);\r
5437       if (error) {\r
5438         DisplayError("Cannot build game list", error);\r
5439       } else if (!ListEmpty(&gameList) &&\r
5440                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5441         GameListPopUp(f, fileTitle);\r
5442         return;\r
5443       }\r
5444       GameListDestroy();\r
5445       number = 1;\r
5446     }\r
5447     LoadGame(f, number, fileTitle, FALSE);\r
5448   }\r
5449 }\r
5450 \r
5451 VOID\r
5452 ChangedConsoleFont()\r
5453 {\r
5454   CHARFORMAT cfmt;\r
5455   CHARRANGE tmpsel, sel;\r
5456   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5457   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5458   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5459   PARAFORMAT paraf;\r
5460 \r
5461   cfmt.cbSize = sizeof(CHARFORMAT);\r
5462   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5463   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5464   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5465    * size.  This was undocumented in the version of MSVC++ that I had\r
5466    * when I wrote the code, but is apparently documented now.\r
5467    */\r
5468   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5469   cfmt.bCharSet = f->lf.lfCharSet;\r
5470   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5471   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5472   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5473   /* Why are the following seemingly needed too? */\r
5474   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5475   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5476   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5477   tmpsel.cpMin = 0;\r
5478   tmpsel.cpMax = -1; /*999999?*/\r
5479   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5480   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5481   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5482    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5483    */\r
5484   paraf.cbSize = sizeof(paraf);\r
5485   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5486   paraf.dxStartIndent = 0;\r
5487   paraf.dxOffset = WRAP_INDENT;\r
5488   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5489   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5490 }\r
5491 \r
5492 /*---------------------------------------------------------------------------*\\r
5493  *\r
5494  * Window Proc for main window\r
5495  *\r
5496 \*---------------------------------------------------------------------------*/\r
5497 \r
5498 /* Process messages for main window, etc. */\r
5499 LRESULT CALLBACK\r
5500 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5501 {\r
5502   FARPROC lpProc;\r
5503   int wmId, wmEvent;\r
5504   char *defName;\r
5505   FILE *f;\r
5506   UINT number;\r
5507   char fileTitle[MSG_SIZ];\r
5508   char buf[MSG_SIZ];\r
5509   static SnapData sd;\r
5510 \r
5511   switch (message) {\r
5512 \r
5513   case WM_PAINT: /* message: repaint portion of window */\r
5514     PaintProc(hwnd);\r
5515     break;\r
5516 \r
5517   case WM_ERASEBKGND:\r
5518     if (IsIconic(hwnd)) {\r
5519       /* Cheat; change the message */\r
5520       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5521     } else {\r
5522       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5523     }\r
5524     break;\r
5525 \r
5526   case WM_LBUTTONDOWN:\r
5527   case WM_MBUTTONDOWN:\r
5528   case WM_RBUTTONDOWN:\r
5529   case WM_LBUTTONUP:\r
5530   case WM_MBUTTONUP:\r
5531   case WM_RBUTTONUP:\r
5532   case WM_MOUSEMOVE:\r
5533   case WM_MOUSEWHEEL:\r
5534     MouseEvent(hwnd, message, wParam, lParam);\r
5535     break;\r
5536 \r
5537   case WM_CHAR:\r
5538     \r
5539     if (appData.icsActive) {\r
5540       if (wParam == '\t') {\r
5541         if (GetKeyState(VK_SHIFT) < 0) {\r
5542           /* shifted */\r
5543           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5544           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5545           SetFocus(h);\r
5546         } else {\r
5547           /* unshifted */\r
5548           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5549           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5550           SetFocus(h);\r
5551         }\r
5552       } else {\r
5553         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5554         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5555         SetFocus(h);\r
5556         SendMessage(h, message, wParam, lParam);\r
5557       }\r
5558     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5559       PopUpMoveDialog((char)wParam);\r
5560     }\r
5561     break;\r
5562 \r
5563   case WM_PALETTECHANGED:\r
5564     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5565       int nnew;\r
5566       HDC hdc = GetDC(hwndMain);\r
5567       SelectPalette(hdc, hPal, TRUE);\r
5568       nnew = RealizePalette(hdc);\r
5569       if (nnew > 0) {\r
5570         paletteChanged = TRUE;\r
5571 #if 0\r
5572         UpdateColors(hdc);\r
5573 #else\r
5574         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5575 #endif\r
5576       }\r
5577       ReleaseDC(hwnd, hdc);\r
5578     }\r
5579     break;\r
5580 \r
5581   case WM_QUERYNEWPALETTE:\r
5582     if (!appData.monoMode /*&& paletteChanged*/) {\r
5583       int nnew;\r
5584       HDC hdc = GetDC(hwndMain);\r
5585       paletteChanged = FALSE;\r
5586       SelectPalette(hdc, hPal, FALSE);\r
5587       nnew = RealizePalette(hdc);\r
5588       if (nnew > 0) {\r
5589         InvalidateRect(hwnd, &boardRect, FALSE);\r
5590       }\r
5591       ReleaseDC(hwnd, hdc);\r
5592       return TRUE;\r
5593     }\r
5594     return FALSE;\r
5595 \r
5596   case WM_COMMAND: /* message: command from application menu */\r
5597     wmId    = LOWORD(wParam);\r
5598     wmEvent = HIWORD(wParam);\r
5599 \r
5600     switch (wmId) {\r
5601     case IDM_NewGame:\r
5602       ResetGameEvent();\r
5603       AnalysisPopDown();\r
5604       break;\r
5605 \r
5606     case IDM_NewGameFRC:\r
5607       if( NewGameFRC() == 0 ) {\r
5608         ResetGameEvent();\r
5609         AnalysisPopDown();\r
5610       }\r
5611       break;\r
5612 \r
5613     case IDM_NewVariant:\r
5614       NewVariantPopup(hwnd);\r
5615       break;\r
5616 \r
5617     case IDM_LoadGame:\r
5618       LoadGameDialog(hwnd, "Load Game from File");\r
5619       break;\r
5620 \r
5621     case IDM_LoadNextGame:\r
5622       ReloadGame(1);\r
5623       break;\r
5624 \r
5625     case IDM_LoadPrevGame:\r
5626       ReloadGame(-1);\r
5627       break;\r
5628 \r
5629     case IDM_ReloadGame:\r
5630       ReloadGame(0);\r
5631       break;\r
5632 \r
5633     case IDM_LoadPosition:\r
5634       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5635         Reset(FALSE, TRUE);\r
5636       }\r
5637       number = 1;\r
5638       f = OpenFileDialog(hwnd, "rb", "",\r
5639                          appData.oldSaveStyle ? "pos" : "fen",\r
5640                          POSITION_FILT,\r
5641                          "Load Position from File", &number, fileTitle, NULL);\r
5642       if (f != NULL) {\r
5643         LoadPosition(f, number, fileTitle);\r
5644       }\r
5645       break;\r
5646 \r
5647     case IDM_LoadNextPosition:\r
5648       ReloadPosition(1);\r
5649       break;\r
5650 \r
5651     case IDM_LoadPrevPosition:\r
5652       ReloadPosition(-1);\r
5653       break;\r
5654 \r
5655     case IDM_ReloadPosition:\r
5656       ReloadPosition(0);\r
5657       break;\r
5658 \r
5659     case IDM_SaveGame:\r
5660       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5661       f = OpenFileDialog(hwnd, "a", defName,\r
5662                          appData.oldSaveStyle ? "gam" : "pgn",\r
5663                          GAME_FILT,\r
5664                          "Save Game to File", NULL, fileTitle, NULL);\r
5665       if (f != NULL) {\r
5666         SaveGame(f, 0, "");\r
5667       }\r
5668       break;\r
5669 \r
5670     case IDM_SavePosition:\r
5671       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5672       f = OpenFileDialog(hwnd, "a", defName,\r
5673                          appData.oldSaveStyle ? "pos" : "fen",\r
5674                          POSITION_FILT,\r
5675                          "Save Position to File", NULL, fileTitle, NULL);\r
5676       if (f != NULL) {\r
5677         SavePosition(f, 0, "");\r
5678       }\r
5679       break;\r
5680 \r
5681     case IDM_SaveDiagram:\r
5682       defName = "diagram";\r
5683       f = OpenFileDialog(hwnd, "wb", defName,\r
5684                          "bmp",\r
5685                          DIAGRAM_FILT,\r
5686                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5687       if (f != NULL) {\r
5688         SaveDiagram(f);\r
5689       }\r
5690       break;\r
5691 \r
5692     case IDM_CopyGame:\r
5693       CopyGameToClipboard();\r
5694       break;\r
5695 \r
5696     case IDM_PasteGame:\r
5697       PasteGameFromClipboard();\r
5698       break;\r
5699 \r
5700     case IDM_CopyGameListToClipboard:\r
5701       CopyGameListToClipboard();\r
5702       break;\r
5703 \r
5704     /* [AS] Autodetect FEN or PGN data */\r
5705     case IDM_PasteAny:\r
5706       PasteGameOrFENFromClipboard();\r
5707       break;\r
5708 \r
5709     /* [AS] Move history */\r
5710     case IDM_ShowMoveHistory:\r
5711         if( MoveHistoryIsUp() ) {\r
5712             MoveHistoryPopDown();\r
5713         }\r
5714         else {\r
5715             MoveHistoryPopUp();\r
5716         }\r
5717         break;\r
5718 \r
5719     /* [AS] Eval graph */\r
5720     case IDM_ShowEvalGraph:\r
5721         if( EvalGraphIsUp() ) {\r
5722             EvalGraphPopDown();\r
5723         }\r
5724         else {\r
5725             EvalGraphPopUp();\r
5726         }\r
5727         break;\r
5728 \r
5729     /* [AS] Engine output */\r
5730     case IDM_ShowEngineOutput:\r
5731         if( EngineOutputIsUp() ) {\r
5732             EngineOutputPopDown();\r
5733         }\r
5734         else {\r
5735             EngineOutputPopUp();\r
5736         }\r
5737         break;\r
5738 \r
5739     /* [AS] User adjudication */\r
5740     case IDM_UserAdjudication_White:\r
5741         UserAdjudicationEvent( +1 );\r
5742         break;\r
5743 \r
5744     case IDM_UserAdjudication_Black:\r
5745         UserAdjudicationEvent( -1 );\r
5746         break;\r
5747 \r
5748     case IDM_UserAdjudication_Draw:\r
5749         UserAdjudicationEvent( 0 );\r
5750         break;\r
5751 \r
5752     /* [AS] Game list options dialog */\r
5753     case IDM_GameListOptions:\r
5754       GameListOptions();\r
5755       break;\r
5756 \r
5757     case IDM_CopyPosition:\r
5758       CopyFENToClipboard();\r
5759       break;\r
5760 \r
5761     case IDM_PastePosition:\r
5762       PasteFENFromClipboard();\r
5763       break;\r
5764 \r
5765     case IDM_MailMove:\r
5766       MailMoveEvent();\r
5767       break;\r
5768 \r
5769     case IDM_ReloadCMailMsg:\r
5770       Reset(TRUE, TRUE);\r
5771       ReloadCmailMsgEvent(FALSE);\r
5772       break;\r
5773 \r
5774     case IDM_Minimize:\r
5775       ShowWindow(hwnd, SW_MINIMIZE);\r
5776       break;\r
5777 \r
5778     case IDM_Exit:\r
5779       ExitEvent(0);\r
5780       break;\r
5781 \r
5782     case IDM_MachineWhite:\r
5783       MachineWhiteEvent();\r
5784       /*\r
5785        * refresh the tags dialog only if it's visible\r
5786        */\r
5787       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5788           char *tags;\r
5789           tags = PGNTags(&gameInfo);\r
5790           TagsPopUp(tags, CmailMsg());\r
5791           free(tags);\r
5792       }\r
5793       break;\r
5794 \r
5795     case IDM_MachineBlack:\r
5796       MachineBlackEvent();\r
5797       /*\r
5798        * refresh the tags dialog only if it's visible\r
5799        */\r
5800       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5801           char *tags;\r
5802           tags = PGNTags(&gameInfo);\r
5803           TagsPopUp(tags, CmailMsg());\r
5804           free(tags);\r
5805       }\r
5806       break;\r
5807 \r
5808     case IDM_TwoMachines:\r
5809       TwoMachinesEvent();\r
5810       /*\r
5811        * refresh the tags dialog only if it's visible\r
5812        */\r
5813       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5814           char *tags;\r
5815           tags = PGNTags(&gameInfo);\r
5816           TagsPopUp(tags, CmailMsg());\r
5817           free(tags);\r
5818       }\r
5819       break;\r
5820 \r
5821     case IDM_AnalysisMode:\r
5822       if (!first.analysisSupport) {\r
5823         sprintf(buf, "%s does not support analysis", first.tidy);\r
5824         DisplayError(buf, 0);\r
5825       } else {\r
5826         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5827         if (appData.icsActive) {\r
5828                if (gameMode != IcsObserving) {\r
5829                        sprintf(buf, "You are not observing a game");\r
5830                        DisplayError(buf, 0);\r
5831                        /* secure check */\r
5832                        if (appData.icsEngineAnalyze) {\r
5833                                if (appData.debugMode) \r
5834                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5835                                ExitAnalyzeMode();\r
5836                                ModeHighlight();\r
5837                                break;\r
5838                        }\r
5839                        break;\r
5840                } else {\r
5841                        /* if enable, user want disable icsEngineAnalyze */\r
5842                        if (appData.icsEngineAnalyze) {\r
5843                                ExitAnalyzeMode();\r
5844                                ModeHighlight();\r
5845                                break;\r
5846                        }\r
5847                        appData.icsEngineAnalyze = TRUE;\r
5848                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5849                }\r
5850         } \r
5851         if (!appData.showThinking) ToggleShowThinking();\r
5852         AnalyzeModeEvent();\r
5853       }\r
5854       break;\r
5855 \r
5856     case IDM_AnalyzeFile:\r
5857       if (!first.analysisSupport) {\r
5858         char buf[MSG_SIZ];\r
5859         sprintf(buf, "%s does not support analysis", first.tidy);\r
5860         DisplayError(buf, 0);\r
5861       } else {\r
5862         if (!appData.showThinking) ToggleShowThinking();\r
5863         AnalyzeFileEvent();\r
5864         LoadGameDialog(hwnd, "Analyze Game from File");\r
5865         AnalysisPeriodicEvent(1);\r
5866       }\r
5867       break;\r
5868 \r
5869     case IDM_IcsClient:\r
5870       IcsClientEvent();\r
5871       break;\r
5872 \r
5873     case IDM_EditGame:\r
5874       EditGameEvent();\r
5875       break;\r
5876 \r
5877     case IDM_EditPosition:\r
5878       EditPositionEvent();\r
5879       break;\r
5880 \r
5881     case IDM_Training:\r
5882       TrainingEvent();\r
5883       break;\r
5884 \r
5885     case IDM_ShowGameList:\r
5886       ShowGameListProc();\r
5887       break;\r
5888 \r
5889     case IDM_EditTags:\r
5890       EditTagsProc();\r
5891       break;\r
5892 \r
5893     case IDM_EditComment:\r
5894       if (commentDialogUp && editComment) {\r
5895         CommentPopDown();\r
5896       } else {\r
5897         EditCommentEvent();\r
5898       }\r
5899       break;\r
5900 \r
5901     case IDM_Pause:\r
5902       PauseEvent();\r
5903       break;\r
5904 \r
5905     case IDM_Accept:\r
5906       AcceptEvent();\r
5907       break;\r
5908 \r
5909     case IDM_Decline:\r
5910       DeclineEvent();\r
5911       break;\r
5912 \r
5913     case IDM_Rematch:\r
5914       RematchEvent();\r
5915       break;\r
5916 \r
5917     case IDM_CallFlag:\r
5918       CallFlagEvent();\r
5919       break;\r
5920 \r
5921     case IDM_Draw:\r
5922       DrawEvent();\r
5923       break;\r
5924 \r
5925     case IDM_Adjourn:\r
5926       AdjournEvent();\r
5927       break;\r
5928 \r
5929     case IDM_Abort:\r
5930       AbortEvent();\r
5931       break;\r
5932 \r
5933     case IDM_Resign:\r
5934       ResignEvent();\r
5935       break;\r
5936 \r
5937     case IDM_StopObserving:\r
5938       StopObservingEvent();\r
5939       break;\r
5940 \r
5941     case IDM_StopExamining:\r
5942       StopExaminingEvent();\r
5943       break;\r
5944 \r
5945     case IDM_TypeInMove:\r
5946       PopUpMoveDialog('\000');\r
5947       break;\r
5948 \r
5949     case IDM_TypeInName:\r
5950       PopUpNameDialog('\000');\r
5951       break;\r
5952 \r
5953     case IDM_Backward:\r
5954       BackwardEvent();\r
5955       SetFocus(hwndMain);\r
5956       break;\r
5957 \r
5958     case IDM_Forward:\r
5959       ForwardEvent();\r
5960       SetFocus(hwndMain);\r
5961       break;\r
5962 \r
5963     case IDM_ToStart:\r
5964       ToStartEvent();\r
5965       SetFocus(hwndMain);\r
5966       break;\r
5967 \r
5968     case IDM_ToEnd:\r
5969       ToEndEvent();\r
5970       SetFocus(hwndMain);\r
5971       break;\r
5972 \r
5973     case IDM_Revert:\r
5974       RevertEvent();\r
5975       break;\r
5976 \r
5977     case IDM_TruncateGame:\r
5978       TruncateGameEvent();\r
5979       break;\r
5980 \r
5981     case IDM_MoveNow:\r
5982       MoveNowEvent();\r
5983       break;\r
5984 \r
5985     case IDM_RetractMove:\r
5986       RetractMoveEvent();\r
5987       break;\r
5988 \r
5989     case IDM_FlipView:\r
5990       flipView = !flipView;\r
5991       DrawPosition(FALSE, NULL);\r
5992       break;\r
5993 \r
5994     case IDM_FlipClock:\r
5995       flipClock = !flipClock;\r
5996       DisplayBothClocks();\r
5997       DrawPosition(FALSE, NULL);\r
5998       break;\r
5999 \r
6000     case IDM_GeneralOptions:\r
6001       GeneralOptionsPopup(hwnd);\r
6002       DrawPosition(TRUE, NULL);\r
6003       break;\r
6004 \r
6005     case IDM_BoardOptions:\r
6006       BoardOptionsPopup(hwnd);\r
6007       break;\r
6008 \r
6009     case IDM_EnginePlayOptions:\r
6010       EnginePlayOptionsPopup(hwnd);\r
6011       break;\r
6012 \r
6013     case IDM_OptionsUCI:\r
6014       UciOptionsPopup(hwnd);\r
6015       break;\r
6016 \r
6017     case IDM_IcsOptions:\r
6018       IcsOptionsPopup(hwnd);\r
6019       break;\r
6020 \r
6021     case IDM_Fonts:\r
6022       FontsOptionsPopup(hwnd);\r
6023       break;\r
6024 \r
6025     case IDM_Sounds:\r
6026       SoundOptionsPopup(hwnd);\r
6027       break;\r
6028 \r
6029     case IDM_CommPort:\r
6030       CommPortOptionsPopup(hwnd);\r
6031       break;\r
6032 \r
6033     case IDM_LoadOptions:\r
6034       LoadOptionsPopup(hwnd);\r
6035       break;\r
6036 \r
6037     case IDM_SaveOptions:\r
6038       SaveOptionsPopup(hwnd);\r
6039       break;\r
6040 \r
6041     case IDM_TimeControl:\r
6042       TimeControlOptionsPopup(hwnd);\r
6043       break;\r
6044 \r
6045     case IDM_SaveSettings:\r
6046       SaveSettings(settingsFileName);\r
6047       break;\r
6048 \r
6049     case IDM_SaveSettingsOnExit:\r
6050       saveSettingsOnExit = !saveSettingsOnExit;\r
6051       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6052                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6053                                          MF_CHECKED : MF_UNCHECKED));\r
6054       break;\r
6055 \r
6056     case IDM_Hint:\r
6057       HintEvent();\r
6058       break;\r
6059 \r
6060     case IDM_Book:\r
6061       BookEvent();\r
6062       break;\r
6063 \r
6064     case IDM_AboutGame:\r
6065       AboutGameEvent();\r
6066       break;\r
6067 \r
6068     case IDM_Debug:\r
6069       appData.debugMode = !appData.debugMode;\r
6070       if (appData.debugMode) {\r
6071         char dir[MSG_SIZ];\r
6072         GetCurrentDirectory(MSG_SIZ, dir);\r
6073         SetCurrentDirectory(installDir);\r
6074         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6075         SetCurrentDirectory(dir);\r
6076         setbuf(debugFP, NULL);\r
6077       } else {\r
6078         fclose(debugFP);\r
6079         debugFP = NULL;\r
6080       }\r
6081       break;\r
6082 \r
6083     case IDM_HELPCONTENTS:\r
6084       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6085         MessageBox (GetFocus(),\r
6086                     "Unable to activate help",\r
6087                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6088       }\r
6089       break;\r
6090 \r
6091     case IDM_HELPSEARCH:\r
6092       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6093         MessageBox (GetFocus(),\r
6094                     "Unable to activate help",\r
6095                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6096       }\r
6097       break;\r
6098 \r
6099     case IDM_HELPHELP:\r
6100       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6101         MessageBox (GetFocus(),\r
6102                     "Unable to activate help",\r
6103                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6104       }\r
6105       break;\r
6106 \r
6107     case IDM_ABOUT:\r
6108       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6109       DialogBox(hInst, \r
6110         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6111         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6112       FreeProcInstance(lpProc);\r
6113       break;\r
6114 \r
6115     case IDM_DirectCommand1:\r
6116       AskQuestionEvent("Direct Command",\r
6117                        "Send to chess program:", "", "1");\r
6118       break;\r
6119     case IDM_DirectCommand2:\r
6120       AskQuestionEvent("Direct Command",\r
6121                        "Send to second chess program:", "", "2");\r
6122       break;\r
6123 \r
6124     case EP_WhitePawn:\r
6125       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6126       fromX = fromY = -1;\r
6127       break;\r
6128 \r
6129     case EP_WhiteKnight:\r
6130       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6131       fromX = fromY = -1;\r
6132       break;\r
6133 \r
6134     case EP_WhiteBishop:\r
6135       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6136       fromX = fromY = -1;\r
6137       break;\r
6138 \r
6139     case EP_WhiteRook:\r
6140       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6141       fromX = fromY = -1;\r
6142       break;\r
6143 \r
6144     case EP_WhiteQueen:\r
6145       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6146       fromX = fromY = -1;\r
6147       break;\r
6148 \r
6149     case EP_WhiteFerz:\r
6150       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6151       fromX = fromY = -1;\r
6152       break;\r
6153 \r
6154     case EP_WhiteWazir:\r
6155       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6156       fromX = fromY = -1;\r
6157       break;\r
6158 \r
6159     case EP_WhiteAlfil:\r
6160       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6161       fromX = fromY = -1;\r
6162       break;\r
6163 \r
6164     case EP_WhiteCannon:\r
6165       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6166       fromX = fromY = -1;\r
6167       break;\r
6168 \r
6169     case EP_WhiteCardinal:\r
6170       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6171       fromX = fromY = -1;\r
6172       break;\r
6173 \r
6174     case EP_WhiteMarshall:\r
6175       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6176       fromX = fromY = -1;\r
6177       break;\r
6178 \r
6179     case EP_WhiteKing:\r
6180       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6181       fromX = fromY = -1;\r
6182       break;\r
6183 \r
6184     case EP_BlackPawn:\r
6185       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6186       fromX = fromY = -1;\r
6187       break;\r
6188 \r
6189     case EP_BlackKnight:\r
6190       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6191       fromX = fromY = -1;\r
6192       break;\r
6193 \r
6194     case EP_BlackBishop:\r
6195       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6196       fromX = fromY = -1;\r
6197       break;\r
6198 \r
6199     case EP_BlackRook:\r
6200       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6201       fromX = fromY = -1;\r
6202       break;\r
6203 \r
6204     case EP_BlackQueen:\r
6205       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6206       fromX = fromY = -1;\r
6207       break;\r
6208 \r
6209     case EP_BlackFerz:\r
6210       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6211       fromX = fromY = -1;\r
6212       break;\r
6213 \r
6214     case EP_BlackWazir:\r
6215       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6216       fromX = fromY = -1;\r
6217       break;\r
6218 \r
6219     case EP_BlackAlfil:\r
6220       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6221       fromX = fromY = -1;\r
6222       break;\r
6223 \r
6224     case EP_BlackCannon:\r
6225       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6226       fromX = fromY = -1;\r
6227       break;\r
6228 \r
6229     case EP_BlackCardinal:\r
6230       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6231       fromX = fromY = -1;\r
6232       break;\r
6233 \r
6234     case EP_BlackMarshall:\r
6235       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6236       fromX = fromY = -1;\r
6237       break;\r
6238 \r
6239     case EP_BlackKing:\r
6240       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6241       fromX = fromY = -1;\r
6242       break;\r
6243 \r
6244     case EP_EmptySquare:\r
6245       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6246       fromX = fromY = -1;\r
6247       break;\r
6248 \r
6249     case EP_ClearBoard:\r
6250       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6251       fromX = fromY = -1;\r
6252       break;\r
6253 \r
6254     case EP_White:\r
6255       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6256       fromX = fromY = -1;\r
6257       break;\r
6258 \r
6259     case EP_Black:\r
6260       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6261       fromX = fromY = -1;\r
6262       break;\r
6263 \r
6264     case EP_Promote:\r
6265       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6266       fromX = fromY = -1;\r
6267       break;\r
6268 \r
6269     case EP_Demote:\r
6270       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6271       fromX = fromY = -1;\r
6272       break;\r
6273 \r
6274     case DP_Pawn:\r
6275       DropMenuEvent(WhitePawn, fromX, fromY);\r
6276       fromX = fromY = -1;\r
6277       break;\r
6278 \r
6279     case DP_Knight:\r
6280       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6281       fromX = fromY = -1;\r
6282       break;\r
6283 \r
6284     case DP_Bishop:\r
6285       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6286       fromX = fromY = -1;\r
6287       break;\r
6288 \r
6289     case DP_Rook:\r
6290       DropMenuEvent(WhiteRook, fromX, fromY);\r
6291       fromX = fromY = -1;\r
6292       break;\r
6293 \r
6294     case DP_Queen:\r
6295       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6296       fromX = fromY = -1;\r
6297       break;\r
6298 \r
6299     default:\r
6300       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6301     }\r
6302     break;\r
6303 \r
6304   case WM_TIMER:\r
6305     switch (wParam) {\r
6306     case CLOCK_TIMER_ID:\r
6307       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6308       clockTimerEvent = 0;\r
6309       DecrementClocks(); /* call into back end */\r
6310       break;\r
6311     case LOAD_GAME_TIMER_ID:\r
6312       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6313       loadGameTimerEvent = 0;\r
6314       AutoPlayGameLoop(); /* call into back end */\r
6315       break;\r
6316     case ANALYSIS_TIMER_ID:\r
6317       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6318                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6319         AnalysisPeriodicEvent(0);\r
6320       } else {\r
6321         KillTimer(hwnd, analysisTimerEvent);\r
6322         analysisTimerEvent = 0;\r
6323       }\r
6324       break;\r
6325     case DELAYED_TIMER_ID:\r
6326       KillTimer(hwnd, delayedTimerEvent);\r
6327       delayedTimerEvent = 0;\r
6328       delayedTimerCallback();\r
6329       break;\r
6330     }\r
6331     break;\r
6332 \r
6333   case WM_USER_Input:\r
6334     InputEvent(hwnd, message, wParam, lParam);\r
6335     break;\r
6336 \r
6337   /* [AS] Also move "attached" child windows */\r
6338   case WM_WINDOWPOSCHANGING:\r
6339 \r
6340     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6341         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6342 \r
6343         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6344             /* Window is moving */\r
6345             RECT rcMain;\r
6346 \r
6347 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6348             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6349             rcMain.right  = boardX + winWidth;\r
6350             rcMain.top    = boardY;\r
6351             rcMain.bottom = boardY + winHeight;\r
6352             \r
6353             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6354             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6355             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6356             boardX = lpwp->x;\r
6357             boardY = lpwp->y;\r
6358         }\r
6359     }\r
6360     break;\r
6361 \r
6362   /* [AS] Snapping */\r
6363   case WM_ENTERSIZEMOVE:\r
6364     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6365     if (hwnd == hwndMain) {\r
6366       doingSizing = TRUE;\r
6367       lastSizing = 0;\r
6368     }\r
6369     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6370     break;\r
6371 \r
6372   case WM_SIZING:\r
6373     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6374     if (hwnd == hwndMain) {\r
6375       lastSizing = wParam;\r
6376     }\r
6377     break;\r
6378 \r
6379   case WM_MOVING:\r
6380     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6381       return OnMoving( &sd, hwnd, wParam, lParam );\r
6382 \r
6383   case WM_EXITSIZEMOVE:\r
6384     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6385     if (hwnd == hwndMain) {\r
6386       RECT client;\r
6387       doingSizing = FALSE;\r
6388       InvalidateRect(hwnd, &boardRect, FALSE);\r
6389       GetClientRect(hwnd, &client);\r
6390       ResizeBoard(client.right, client.bottom, lastSizing);\r
6391       lastSizing = 0;\r
6392       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6393     }\r
6394     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6395     break;\r
6396 \r
6397   case WM_DESTROY: /* message: window being destroyed */\r
6398     PostQuitMessage(0);\r
6399     break;\r
6400 \r
6401   case WM_CLOSE:\r
6402     if (hwnd == hwndMain) {\r
6403       ExitEvent(0);\r
6404     }\r
6405     break;\r
6406 \r
6407   default:      /* Passes it on if unprocessed */\r
6408     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6409   }\r
6410   return 0;\r
6411 }\r
6412 \r
6413 /*---------------------------------------------------------------------------*\\r
6414  *\r
6415  * Misc utility routines\r
6416  *\r
6417 \*---------------------------------------------------------------------------*/\r
6418 \r
6419 /*\r
6420  * Decent random number generator, at least not as bad as Windows\r
6421  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6422  */\r
6423 unsigned int randstate;\r
6424 \r
6425 int\r
6426 myrandom(void)\r
6427 {\r
6428   randstate = randstate * 1664525 + 1013904223;\r
6429   return (int) randstate & 0x7fffffff;\r
6430 }\r
6431 \r
6432 void\r
6433 mysrandom(unsigned int seed)\r
6434 {\r
6435   randstate = seed;\r
6436 }\r
6437 \r
6438 \r
6439 /* \r
6440  * returns TRUE if user selects a different color, FALSE otherwise \r
6441  */\r
6442 \r
6443 BOOL\r
6444 ChangeColor(HWND hwnd, COLORREF *which)\r
6445 {\r
6446   static BOOL firstTime = TRUE;\r
6447   static DWORD customColors[16];\r
6448   CHOOSECOLOR cc;\r
6449   COLORREF newcolor;\r
6450   int i;\r
6451   ColorClass ccl;\r
6452 \r
6453   if (firstTime) {\r
6454     /* Make initial colors in use available as custom colors */\r
6455     /* Should we put the compiled-in defaults here instead? */\r
6456     i = 0;\r
6457     customColors[i++] = lightSquareColor & 0xffffff;\r
6458     customColors[i++] = darkSquareColor & 0xffffff;\r
6459     customColors[i++] = whitePieceColor & 0xffffff;\r
6460     customColors[i++] = blackPieceColor & 0xffffff;\r
6461     customColors[i++] = highlightSquareColor & 0xffffff;\r
6462     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6463 \r
6464     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6465       customColors[i++] = textAttribs[ccl].color;\r
6466     }\r
6467     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6468     firstTime = FALSE;\r
6469   }\r
6470 \r
6471   cc.lStructSize = sizeof(cc);\r
6472   cc.hwndOwner = hwnd;\r
6473   cc.hInstance = NULL;\r
6474   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6475   cc.lpCustColors = (LPDWORD) customColors;\r
6476   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6477 \r
6478   if (!ChooseColor(&cc)) return FALSE;\r
6479 \r
6480   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6481   if (newcolor == *which) return FALSE;\r
6482   *which = newcolor;\r
6483   return TRUE;\r
6484 \r
6485   /*\r
6486   InitDrawingColors();\r
6487   InvalidateRect(hwnd, &boardRect, FALSE);\r
6488   */\r
6489 }\r
6490 \r
6491 BOOLEAN\r
6492 MyLoadSound(MySound *ms)\r
6493 {\r
6494   BOOL ok = FALSE;\r
6495   struct stat st;\r
6496   FILE *f;\r
6497 \r
6498   if (ms->data) free(ms->data);\r
6499   ms->data = NULL;\r
6500 \r
6501   switch (ms->name[0]) {\r
6502   case NULLCHAR:\r
6503     /* Silence */\r
6504     ok = TRUE;\r
6505     break;\r
6506   case '$':\r
6507     /* System sound from Control Panel.  Don't preload here. */\r
6508     ok = TRUE;\r
6509     break;\r
6510   case '!':\r
6511     if (ms->name[1] == NULLCHAR) {\r
6512       /* "!" alone = silence */\r
6513       ok = TRUE;\r
6514     } else {\r
6515       /* Builtin wave resource.  Error if not found. */\r
6516       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6517       if (h == NULL) break;\r
6518       ms->data = (void *)LoadResource(hInst, h);\r
6519       if (h == NULL) break;\r
6520       ok = TRUE;\r
6521     }\r
6522     break;\r
6523   default:\r
6524     /* .wav file.  Error if not found. */\r
6525     f = fopen(ms->name, "rb");\r
6526     if (f == NULL) break;\r
6527     if (fstat(fileno(f), &st) < 0) break;\r
6528     ms->data = malloc(st.st_size);\r
6529     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6530     fclose(f);\r
6531     ok = TRUE;\r
6532     break;\r
6533   }\r
6534   if (!ok) {\r
6535     char buf[MSG_SIZ];\r
6536     sprintf(buf, "Error loading sound %s", ms->name);\r
6537     DisplayError(buf, GetLastError());\r
6538   }\r
6539   return ok;\r
6540 }\r
6541 \r
6542 BOOLEAN\r
6543 MyPlaySound(MySound *ms)\r
6544 {\r
6545   BOOLEAN ok = FALSE;\r
6546   switch (ms->name[0]) {\r
6547   case NULLCHAR:\r
6548     /* Silence */\r
6549     ok = TRUE;\r
6550     break;\r
6551   case '$':\r
6552     /* System sound from Control Panel (deprecated feature).\r
6553        "$" alone or an unset sound name gets default beep (still in use). */\r
6554     if (ms->name[1]) {\r
6555       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6556     }\r
6557     if (!ok) ok = MessageBeep(MB_OK);\r
6558     break; \r
6559   case '!':\r
6560     /* Builtin wave resource, or "!" alone for silence */\r
6561     if (ms->name[1]) {\r
6562       if (ms->data == NULL) return FALSE;\r
6563       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6564     } else {\r
6565       ok = TRUE;\r
6566     }\r
6567     break;\r
6568   default:\r
6569     /* .wav file.  Error if not found. */\r
6570     if (ms->data == NULL) return FALSE;\r
6571     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6572     break;\r
6573   }\r
6574   /* Don't print an error: this can happen innocently if the sound driver\r
6575      is busy; for instance, if another instance of WinBoard is playing\r
6576      a sound at about the same time. */\r
6577 #if 0\r
6578   if (!ok) {\r
6579     char buf[MSG_SIZ];\r
6580     sprintf(buf, "Error playing sound %s", ms->name);\r
6581     DisplayError(buf, GetLastError());\r
6582   }\r
6583 #endif\r
6584   return ok;\r
6585 }\r
6586 \r
6587 \r
6588 LRESULT CALLBACK\r
6589 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6590 {\r
6591   BOOL ok;\r
6592   OPENFILENAME *ofn;\r
6593   static UINT *number; /* gross that this is static */\r
6594 \r
6595   switch (message) {\r
6596   case WM_INITDIALOG: /* message: initialize dialog box */\r
6597     /* Center the dialog over the application window */\r
6598     ofn = (OPENFILENAME *) lParam;\r
6599     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6600       number = (UINT *) ofn->lCustData;\r
6601       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6602     } else {\r
6603       number = NULL;\r
6604     }\r
6605     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6606     return FALSE;  /* Allow for further processing */\r
6607 \r
6608   case WM_COMMAND:\r
6609     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6610       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6611     }\r
6612     return FALSE;  /* Allow for further processing */\r
6613   }\r
6614   return FALSE;\r
6615 }\r
6616 \r
6617 UINT APIENTRY\r
6618 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6619 {\r
6620   static UINT *number;\r
6621   OPENFILENAME *ofname;\r
6622   OFNOTIFY *ofnot;\r
6623   switch (uiMsg) {\r
6624   case WM_INITDIALOG:\r
6625     ofname = (OPENFILENAME *)lParam;\r
6626     number = (UINT *)(ofname->lCustData);\r
6627     break;\r
6628   case WM_NOTIFY:\r
6629     ofnot = (OFNOTIFY *)lParam;\r
6630     if (ofnot->hdr.code == CDN_FILEOK) {\r
6631       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6632     }\r
6633     break;\r
6634   }\r
6635   return 0;\r
6636 }\r
6637 \r
6638 \r
6639 FILE *\r
6640 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6641                char *nameFilt, char *dlgTitle, UINT *number,\r
6642                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6643 {\r
6644   OPENFILENAME openFileName;\r
6645   char buf1[MSG_SIZ];\r
6646   FILE *f;\r
6647 \r
6648   if (fileName == NULL) fileName = buf1;\r
6649   if (defName == NULL) {\r
6650     strcpy(fileName, "*.");\r
6651     strcat(fileName, defExt);\r
6652   } else {\r
6653     strcpy(fileName, defName);\r
6654   }\r
6655   if (fileTitle) strcpy(fileTitle, "");\r
6656   if (number) *number = 0;\r
6657 \r
6658   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6659   openFileName.hwndOwner         = hwnd;\r
6660   openFileName.hInstance         = (HANDLE) hInst;\r
6661   openFileName.lpstrFilter       = nameFilt;\r
6662   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6663   openFileName.nMaxCustFilter    = 0L;\r
6664   openFileName.nFilterIndex      = 1L;\r
6665   openFileName.lpstrFile         = fileName;\r
6666   openFileName.nMaxFile          = MSG_SIZ;\r
6667   openFileName.lpstrFileTitle    = fileTitle;\r
6668   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6669   openFileName.lpstrInitialDir   = NULL;\r
6670   openFileName.lpstrTitle        = dlgTitle;\r
6671   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6672     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6673     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6674     | (oldDialog ? 0 : OFN_EXPLORER);\r
6675   openFileName.nFileOffset       = 0;\r
6676   openFileName.nFileExtension    = 0;\r
6677   openFileName.lpstrDefExt       = defExt;\r
6678   openFileName.lCustData         = (LONG) number;\r
6679   openFileName.lpfnHook          = oldDialog ?\r
6680     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6681   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6682 \r
6683   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6684                         GetOpenFileName(&openFileName)) {\r
6685     /* open the file */\r
6686     f = fopen(openFileName.lpstrFile, write);\r
6687     if (f == NULL) {\r
6688       MessageBox(hwnd, "File open failed", NULL,\r
6689                  MB_OK|MB_ICONEXCLAMATION);\r
6690       return NULL;\r
6691     }\r
6692   } else {\r
6693     int err = CommDlgExtendedError();\r
6694     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6695     return FALSE;\r
6696   }\r
6697   return f;\r
6698 }\r
6699 \r
6700 \r
6701 \r
6702 VOID APIENTRY\r
6703 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6704 {\r
6705   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6706 \r
6707   /*\r
6708    * Get the first pop-up menu in the menu template. This is the\r
6709    * menu that TrackPopupMenu displays.\r
6710    */\r
6711   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6712 \r
6713   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6714 \r
6715   /*\r
6716    * TrackPopup uses screen coordinates, so convert the\r
6717    * coordinates of the mouse click to screen coordinates.\r
6718    */\r
6719   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6720 \r
6721   /* Draw and track the floating pop-up menu. */\r
6722   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6723                  pt.x, pt.y, 0, hwnd, NULL);\r
6724 \r
6725   /* Destroy the menu.*/\r
6726   DestroyMenu(hmenu);\r
6727 }\r
6728    \r
6729 typedef struct {\r
6730   HWND hDlg, hText;\r
6731   int sizeX, sizeY, newSizeX, newSizeY;\r
6732   HDWP hdwp;\r
6733 } ResizeEditPlusButtonsClosure;\r
6734 \r
6735 BOOL CALLBACK\r
6736 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6737 {\r
6738   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6739   RECT rect;\r
6740   POINT pt;\r
6741 \r
6742   if (hChild == cl->hText) return TRUE;\r
6743   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6744   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6745   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6746   ScreenToClient(cl->hDlg, &pt);\r
6747   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6748     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6749   return TRUE;\r
6750 }\r
6751 \r
6752 /* Resize a dialog that has a (rich) edit field filling most of\r
6753    the top, with a row of buttons below */\r
6754 VOID\r
6755 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6756 {\r
6757   RECT rectText;\r
6758   int newTextHeight, newTextWidth;\r
6759   ResizeEditPlusButtonsClosure cl;\r
6760   \r
6761   /*if (IsIconic(hDlg)) return;*/\r
6762   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6763   \r
6764   cl.hdwp = BeginDeferWindowPos(8);\r
6765 \r
6766   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6767   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6768   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6769   if (newTextHeight < 0) {\r
6770     newSizeY += -newTextHeight;\r
6771     newTextHeight = 0;\r
6772   }\r
6773   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6774     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6775 \r
6776   cl.hDlg = hDlg;\r
6777   cl.hText = hText;\r
6778   cl.sizeX = sizeX;\r
6779   cl.sizeY = sizeY;\r
6780   cl.newSizeX = newSizeX;\r
6781   cl.newSizeY = newSizeY;\r
6782   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6783 \r
6784   EndDeferWindowPos(cl.hdwp);\r
6785 }\r
6786 \r
6787 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6788 {\r
6789     RECT    rChild, rParent;\r
6790     int     wChild, hChild, wParent, hParent;\r
6791     int     wScreen, hScreen, xNew, yNew;\r
6792     HDC     hdc;\r
6793 \r
6794     /* Get the Height and Width of the child window */\r
6795     GetWindowRect (hwndChild, &rChild);\r
6796     wChild = rChild.right - rChild.left;\r
6797     hChild = rChild.bottom - rChild.top;\r
6798 \r
6799     /* Get the Height and Width of the parent window */\r
6800     GetWindowRect (hwndParent, &rParent);\r
6801     wParent = rParent.right - rParent.left;\r
6802     hParent = rParent.bottom - rParent.top;\r
6803 \r
6804     /* Get the display limits */\r
6805     hdc = GetDC (hwndChild);\r
6806     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6807     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6808     ReleaseDC(hwndChild, hdc);\r
6809 \r
6810     /* Calculate new X position, then adjust for screen */\r
6811     xNew = rParent.left + ((wParent - wChild) /2);\r
6812     if (xNew < 0) {\r
6813         xNew = 0;\r
6814     } else if ((xNew+wChild) > wScreen) {\r
6815         xNew = wScreen - wChild;\r
6816     }\r
6817 \r
6818     /* Calculate new Y position, then adjust for screen */\r
6819     if( mode == 0 ) {\r
6820         yNew = rParent.top  + ((hParent - hChild) /2);\r
6821     }\r
6822     else {\r
6823         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6824     }\r
6825 \r
6826     if (yNew < 0) {\r
6827         yNew = 0;\r
6828     } else if ((yNew+hChild) > hScreen) {\r
6829         yNew = hScreen - hChild;\r
6830     }\r
6831 \r
6832     /* Set it, and return */\r
6833     return SetWindowPos (hwndChild, NULL,\r
6834                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6835 }\r
6836 \r
6837 /* Center one window over another */\r
6838 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6839 {\r
6840     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6841 }\r
6842 \r
6843 /*---------------------------------------------------------------------------*\\r
6844  *\r
6845  * Startup Dialog functions\r
6846  *\r
6847 \*---------------------------------------------------------------------------*/\r
6848 void\r
6849 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6850 {\r
6851   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6852 \r
6853   while (*cd != NULL) {\r
6854     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6855     cd++;\r
6856   }\r
6857 }\r
6858 \r
6859 void\r
6860 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6861 {\r
6862   char buf1[ARG_MAX];\r
6863   int len;\r
6864 \r
6865   if (str[0] == '@') {\r
6866     FILE* f = fopen(str + 1, "r");\r
6867     if (f == NULL) {\r
6868       DisplayFatalError(str + 1, errno, 2);\r
6869       return;\r
6870     }\r
6871     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6872     fclose(f);\r
6873     buf1[len] = NULLCHAR;\r
6874     str = buf1;\r
6875   }\r
6876 \r
6877   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6878 \r
6879   for (;;) {\r
6880     char buf[MSG_SIZ];\r
6881     char *end = strchr(str, '\n');\r
6882     if (end == NULL) return;\r
6883     memcpy(buf, str, end - str);\r
6884     buf[end - str] = NULLCHAR;\r
6885     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6886     str = end + 1;\r
6887   }\r
6888 }\r
6889 \r
6890 void\r
6891 SetStartupDialogEnables(HWND hDlg)\r
6892 {\r
6893   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6894     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6895     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6896   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6897     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6898   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6899     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6900   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6901     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6902   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6903     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6904     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6905     IsDlgButtonChecked(hDlg, OPT_View));\r
6906 }\r
6907 \r
6908 char *\r
6909 QuoteForFilename(char *filename)\r
6910 {\r
6911   int dquote, space;\r
6912   dquote = strchr(filename, '"') != NULL;\r
6913   space = strchr(filename, ' ') != NULL;\r
6914   if (dquote || space) {\r
6915     if (dquote) {\r
6916       return "'";\r
6917     } else {\r
6918       return "\"";\r
6919     }\r
6920   } else {\r
6921     return "";\r
6922   }\r
6923 }\r
6924 \r
6925 VOID\r
6926 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6927 {\r
6928   char buf[MSG_SIZ];\r
6929   char *q;\r
6930 \r
6931   InitComboStringsFromOption(hwndCombo, nthnames);\r
6932   q = QuoteForFilename(nthcp);\r
6933   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6934   if (*nthdir != NULLCHAR) {\r
6935     q = QuoteForFilename(nthdir);\r
6936     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6937   }\r
6938   if (*nthcp == NULLCHAR) {\r
6939     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6940   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6941     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6942     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6943   }\r
6944 }\r
6945 \r
6946 LRESULT CALLBACK\r
6947 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6948 {\r
6949   char buf[MSG_SIZ];\r
6950   HANDLE hwndCombo;\r
6951   char *p;\r
6952 \r
6953   switch (message) {\r
6954   case WM_INITDIALOG:\r
6955     /* Center the dialog */\r
6956     CenterWindow (hDlg, GetDesktopWindow());\r
6957     /* Initialize the dialog items */\r
6958     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6959                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6960                   firstChessProgramNames);\r
6961     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6962                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6963                   secondChessProgramNames);\r
6964     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6965     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6966     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6967     if (*appData.icsHelper != NULLCHAR) {\r
6968       char *q = QuoteForFilename(appData.icsHelper);\r
6969       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6970     }\r
6971     if (*appData.icsHost == NULLCHAR) {\r
6972       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6973       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6974     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6975       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6976       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6977     }\r
6978 \r
6979     if (appData.icsActive) {\r
6980       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6981     }\r
6982     else if (appData.noChessProgram) {\r
6983       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6984     }\r
6985     else {\r
6986       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6987     }\r
6988 \r
6989     SetStartupDialogEnables(hDlg);\r
6990     return TRUE;\r
6991 \r
6992   case WM_COMMAND:\r
6993     switch (LOWORD(wParam)) {\r
6994     case IDOK:\r
6995       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6996         strcpy(buf, "/fcp=");\r
6997         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6998         p = buf;\r
6999         ParseArgs(StringGet, &p);\r
7000         strcpy(buf, "/scp=");\r
7001         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7002         p = buf;\r
7003         ParseArgs(StringGet, &p);\r
7004         appData.noChessProgram = FALSE;\r
7005         appData.icsActive = FALSE;\r
7006       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7007         strcpy(buf, "/ics /icshost=");\r
7008         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7009         p = buf;\r
7010         ParseArgs(StringGet, &p);\r
7011         if (appData.zippyPlay) {\r
7012           strcpy(buf, "/fcp=");\r
7013           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7014           p = buf;\r
7015           ParseArgs(StringGet, &p);\r
7016         }\r
7017       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7018         appData.noChessProgram = TRUE;\r
7019         appData.icsActive = FALSE;\r
7020       } else {\r
7021         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7022                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7023         return TRUE;\r
7024       }\r
7025       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7026         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7027         p = buf;\r
7028         ParseArgs(StringGet, &p);\r
7029       }\r
7030       EndDialog(hDlg, TRUE);\r
7031       return TRUE;\r
7032 \r
7033     case IDCANCEL:\r
7034       ExitEvent(0);\r
7035       return TRUE;\r
7036 \r
7037     case IDM_HELPCONTENTS:\r
7038       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7039         MessageBox (GetFocus(),\r
7040                     "Unable to activate help",\r
7041                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7042       }\r
7043       break;\r
7044 \r
7045     default:\r
7046       SetStartupDialogEnables(hDlg);\r
7047       break;\r
7048     }\r
7049     break;\r
7050   }\r
7051   return FALSE;\r
7052 }\r
7053 \r
7054 /*---------------------------------------------------------------------------*\\r
7055  *\r
7056  * About box dialog functions\r
7057  *\r
7058 \*---------------------------------------------------------------------------*/\r
7059 \r
7060 /* Process messages for "About" dialog box */\r
7061 LRESULT CALLBACK\r
7062 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7063 {\r
7064   switch (message) {\r
7065   case WM_INITDIALOG: /* message: initialize dialog box */\r
7066     /* Center the dialog over the application window */\r
7067     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7068     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7069     return (TRUE);\r
7070 \r
7071   case WM_COMMAND: /* message: received a command */\r
7072     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7073         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7074       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7075       return (TRUE);\r
7076     }\r
7077     break;\r
7078   }\r
7079   return (FALSE);\r
7080 }\r
7081 \r
7082 /*---------------------------------------------------------------------------*\\r
7083  *\r
7084  * Comment Dialog functions\r
7085  *\r
7086 \*---------------------------------------------------------------------------*/\r
7087 \r
7088 LRESULT CALLBACK\r
7089 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7090 {\r
7091   static HANDLE hwndText = NULL;\r
7092   int len, newSizeX, newSizeY, flags;\r
7093   static int sizeX, sizeY;\r
7094   char *str;\r
7095   RECT rect;\r
7096   MINMAXINFO *mmi;\r
7097 \r
7098   switch (message) {\r
7099   case WM_INITDIALOG: /* message: initialize dialog box */\r
7100     /* Initialize the dialog items */\r
7101     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7102     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7103     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7104     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7105     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7106     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7107     SetWindowText(hDlg, commentTitle);\r
7108     if (editComment) {\r
7109       SetFocus(hwndText);\r
7110     } else {\r
7111       SetFocus(GetDlgItem(hDlg, IDOK));\r
7112     }\r
7113     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7114                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7115                 MAKELPARAM(FALSE, 0));\r
7116     /* Size and position the dialog */\r
7117     if (!commentDialog) {\r
7118       commentDialog = hDlg;\r
7119       flags = SWP_NOZORDER;\r
7120       GetClientRect(hDlg, &rect);\r
7121       sizeX = rect.right;\r
7122       sizeY = rect.bottom;\r
7123       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7124           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7125         WINDOWPLACEMENT wp;\r
7126         EnsureOnScreen(&commentX, &commentY);\r
7127         wp.length = sizeof(WINDOWPLACEMENT);\r
7128         wp.flags = 0;\r
7129         wp.showCmd = SW_SHOW;\r
7130         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7131         wp.rcNormalPosition.left = commentX;\r
7132         wp.rcNormalPosition.right = commentX + commentW;\r
7133         wp.rcNormalPosition.top = commentY;\r
7134         wp.rcNormalPosition.bottom = commentY + commentH;\r
7135         SetWindowPlacement(hDlg, &wp);\r
7136 \r
7137         GetClientRect(hDlg, &rect);\r
7138         newSizeX = rect.right;\r
7139         newSizeY = rect.bottom;\r
7140         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7141                               newSizeX, newSizeY);\r
7142         sizeX = newSizeX;\r
7143         sizeY = newSizeY;\r
7144       }\r
7145     }\r
7146     return FALSE;\r
7147 \r
7148   case WM_COMMAND: /* message: received a command */\r
7149     switch (LOWORD(wParam)) {\r
7150     case IDOK:\r
7151       if (editComment) {\r
7152         char *p, *q;\r
7153         /* Read changed options from the dialog box */\r
7154         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7155         len = GetWindowTextLength(hwndText);\r
7156         str = (char *) malloc(len + 1);\r
7157         GetWindowText(hwndText, str, len + 1);\r
7158         p = q = str;\r
7159         while (*q) {\r
7160           if (*q == '\r')\r
7161             q++;\r
7162           else\r
7163             *p++ = *q++;\r
7164         }\r
7165         *p = NULLCHAR;\r
7166         ReplaceComment(commentIndex, str);\r
7167         free(str);\r
7168       }\r
7169       CommentPopDown();\r
7170       return TRUE;\r
7171 \r
7172     case IDCANCEL:\r
7173     case OPT_CancelComment:\r
7174       CommentPopDown();\r
7175       return TRUE;\r
7176 \r
7177     case OPT_ClearComment:\r
7178       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7179       break;\r
7180 \r
7181     case OPT_EditComment:\r
7182       EditCommentEvent();\r
7183       return TRUE;\r
7184 \r
7185     default:\r
7186       break;\r
7187     }\r
7188     break;\r
7189 \r
7190   case WM_SIZE:\r
7191     newSizeX = LOWORD(lParam);\r
7192     newSizeY = HIWORD(lParam);\r
7193     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7194     sizeX = newSizeX;\r
7195     sizeY = newSizeY;\r
7196     break;\r
7197 \r
7198   case WM_GETMINMAXINFO:\r
7199     /* Prevent resizing window too small */\r
7200     mmi = (MINMAXINFO *) lParam;\r
7201     mmi->ptMinTrackSize.x = 100;\r
7202     mmi->ptMinTrackSize.y = 100;\r
7203     break;\r
7204   }\r
7205   return FALSE;\r
7206 }\r
7207 \r
7208 VOID\r
7209 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7210 {\r
7211   FARPROC lpProc;\r
7212   char *p, *q;\r
7213 \r
7214   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7215 \r
7216   if (str == NULL) str = "";\r
7217   p = (char *) malloc(2 * strlen(str) + 2);\r
7218   q = p;\r
7219   while (*str) {\r
7220     if (*str == '\n') *q++ = '\r';\r
7221     *q++ = *str++;\r
7222   }\r
7223   *q = NULLCHAR;\r
7224   if (commentText != NULL) free(commentText);\r
7225 \r
7226   commentIndex = index;\r
7227   commentTitle = title;\r
7228   commentText = p;\r
7229   editComment = edit;\r
7230 \r
7231   if (commentDialog) {\r
7232     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7233     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7234   } else {\r
7235     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7236     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7237                  hwndMain, (DLGPROC)lpProc);\r
7238     FreeProcInstance(lpProc);\r
7239   }\r
7240   commentDialogUp = TRUE;\r
7241 }\r
7242 \r
7243 \r
7244 /*---------------------------------------------------------------------------*\\r
7245  *\r
7246  * Type-in move dialog functions\r
7247  * \r
7248 \*---------------------------------------------------------------------------*/\r
7249 \r
7250 LRESULT CALLBACK\r
7251 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7252 {\r
7253   char move[MSG_SIZ];\r
7254   HWND hInput;\r
7255   ChessMove moveType;\r
7256   int fromX, fromY, toX, toY;\r
7257   char promoChar;\r
7258 \r
7259   switch (message) {\r
7260   case WM_INITDIALOG:\r
7261     move[0] = (char) lParam;\r
7262     move[1] = NULLCHAR;\r
7263     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7264     hInput = GetDlgItem(hDlg, OPT_Move);\r
7265     SetWindowText(hInput, move);\r
7266     SetFocus(hInput);\r
7267     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7268     return FALSE;\r
7269 \r
7270   case WM_COMMAND:\r
7271     switch (LOWORD(wParam)) {\r
7272     case IDOK:\r
7273       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7274         gameMode != Training) {\r
7275         DisplayMoveError("Displayed move is not current");\r
7276       } else {\r
7277         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7278         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7279           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7280           if (gameMode != Training)\r
7281               forwardMostMove = currentMove;\r
7282           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7283         } else {\r
7284           DisplayMoveError("Could not parse move");\r
7285         }\r
7286       }\r
7287       EndDialog(hDlg, TRUE);\r
7288       return TRUE;\r
7289     case IDCANCEL:\r
7290       EndDialog(hDlg, FALSE);\r
7291       return TRUE;\r
7292     default:\r
7293       break;\r
7294     }\r
7295     break;\r
7296   }\r
7297   return FALSE;\r
7298 }\r
7299 \r
7300 VOID\r
7301 PopUpMoveDialog(char firstchar)\r
7302 {\r
7303     FARPROC lpProc;\r
7304     \r
7305     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7306         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7307         gameMode == AnalyzeMode || gameMode == EditGame || \r
7308         gameMode == EditPosition || gameMode == IcsExamining ||\r
7309         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7310         gameMode == Training) {\r
7311       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7312       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7313         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7314       FreeProcInstance(lpProc);\r
7315     }\r
7316 }\r
7317 \r
7318 /*---------------------------------------------------------------------------*\\r
7319  *\r
7320  * Type-in name dialog functions\r
7321  * \r
7322 \*---------------------------------------------------------------------------*/\r
7323 \r
7324 LRESULT CALLBACK\r
7325 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7326 {\r
7327   char move[MSG_SIZ];\r
7328   HWND hInput;\r
7329 \r
7330   switch (message) {\r
7331   case WM_INITDIALOG:\r
7332     move[0] = (char) lParam;\r
7333     move[1] = NULLCHAR;\r
7334     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7335     hInput = GetDlgItem(hDlg, OPT_Name);\r
7336     SetWindowText(hInput, move);\r
7337     SetFocus(hInput);\r
7338     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7339     return FALSE;\r
7340 \r
7341   case WM_COMMAND:\r
7342     switch (LOWORD(wParam)) {\r
7343     case IDOK:\r
7344       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7345       appData.userName = strdup(move);\r
7346       SetUserLogo();\r
7347 \r
7348       EndDialog(hDlg, TRUE);\r
7349       return TRUE;\r
7350     case IDCANCEL:\r
7351       EndDialog(hDlg, FALSE);\r
7352       return TRUE;\r
7353     default:\r
7354       break;\r
7355     }\r
7356     break;\r
7357   }\r
7358   return FALSE;\r
7359 }\r
7360 \r
7361 VOID\r
7362 PopUpNameDialog(char firstchar)\r
7363 {\r
7364     FARPROC lpProc;\r
7365     \r
7366       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7367       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7368         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7369       FreeProcInstance(lpProc);\r
7370 }\r
7371 \r
7372 /*---------------------------------------------------------------------------*\\r
7373  *\r
7374  *  Error dialogs\r
7375  * \r
7376 \*---------------------------------------------------------------------------*/\r
7377 \r
7378 /* Nonmodal error box */\r
7379 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7380                              WPARAM wParam, LPARAM lParam);\r
7381 \r
7382 VOID\r
7383 ErrorPopUp(char *title, char *content)\r
7384 {\r
7385   FARPROC lpProc;\r
7386   char *p, *q;\r
7387   BOOLEAN modal = hwndMain == NULL;\r
7388 \r
7389   p = content;\r
7390   q = errorMessage;\r
7391   while (*p) {\r
7392     if (*p == '\n') {\r
7393       if (modal) {\r
7394         *q++ = ' ';\r
7395         p++;\r
7396       } else {\r
7397         *q++ = '\r';\r
7398         *q++ = *p++;\r
7399       }\r
7400     } else {\r
7401       *q++ = *p++;\r
7402     }\r
7403   }\r
7404   *q = NULLCHAR;\r
7405   strncpy(errorTitle, title, sizeof(errorTitle));\r
7406   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7407   \r
7408   if (modal) {\r
7409     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7410   } else {\r
7411     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7412     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7413                  hwndMain, (DLGPROC)lpProc);\r
7414     FreeProcInstance(lpProc);\r
7415   }\r
7416 }\r
7417 \r
7418 VOID\r
7419 ErrorPopDown()\r
7420 {\r
7421   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7422   if (errorDialog == NULL) return;\r
7423   DestroyWindow(errorDialog);\r
7424   errorDialog = NULL;\r
7425 }\r
7426 \r
7427 LRESULT CALLBACK\r
7428 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7429 {\r
7430   HANDLE hwndText;\r
7431   RECT rChild;\r
7432 \r
7433   switch (message) {\r
7434   case WM_INITDIALOG:\r
7435     GetWindowRect(hDlg, &rChild);\r
7436 \r
7437     /*\r
7438     SetWindowPos(hDlg, NULL, rChild.left,\r
7439       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7440       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7441     */\r
7442 \r
7443     /* \r
7444         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7445         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7446         and it doesn't work when you resize the dialog.\r
7447         For now, just give it a default position.\r
7448     */\r
7449     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7450 \r
7451     errorDialog = hDlg;\r
7452     SetWindowText(hDlg, errorTitle);\r
7453     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7454     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7455     return FALSE;\r
7456 \r
7457   case WM_COMMAND:\r
7458     switch (LOWORD(wParam)) {\r
7459     case IDOK:\r
7460     case IDCANCEL:\r
7461       if (errorDialog == hDlg) errorDialog = NULL;\r
7462       DestroyWindow(hDlg);\r
7463       return TRUE;\r
7464 \r
7465     default:\r
7466       break;\r
7467     }\r
7468     break;\r
7469   }\r
7470   return FALSE;\r
7471 }\r
7472 \r
7473 #ifdef GOTHIC\r
7474 HWND gothicDialog = NULL;\r
7475 \r
7476 LRESULT CALLBACK\r
7477 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7478 {\r
7479   HANDLE hwndText;\r
7480   RECT rChild;\r
7481   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7482 \r
7483   switch (message) {\r
7484   case WM_INITDIALOG:\r
7485     GetWindowRect(hDlg, &rChild);\r
7486 \r
7487     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7488                                                              SWP_NOZORDER);\r
7489 \r
7490     /* \r
7491         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7492         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7493         and it doesn't work when you resize the dialog.\r
7494         For now, just give it a default position.\r
7495     */\r
7496     gothicDialog = hDlg;\r
7497     SetWindowText(hDlg, errorTitle);\r
7498     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7499     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7500     return FALSE;\r
7501 \r
7502   case WM_COMMAND:\r
7503     switch (LOWORD(wParam)) {\r
7504     case IDOK:\r
7505     case IDCANCEL:\r
7506       if (errorDialog == hDlg) errorDialog = NULL;\r
7507       DestroyWindow(hDlg);\r
7508       return TRUE;\r
7509 \r
7510     default:\r
7511       break;\r
7512     }\r
7513     break;\r
7514   }\r
7515   return FALSE;\r
7516 }\r
7517 \r
7518 VOID\r
7519 GothicPopUp(char *title, VariantClass variant)\r
7520 {\r
7521   FARPROC lpProc;\r
7522   static char *lastTitle;\r
7523 \r
7524   strncpy(errorTitle, title, sizeof(errorTitle));\r
7525   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7526 \r
7527   if(lastTitle != title && gothicDialog != NULL) {\r
7528     DestroyWindow(gothicDialog);\r
7529     gothicDialog = NULL;\r
7530   }\r
7531   if(variant != VariantNormal && gothicDialog == NULL) {\r
7532     title = lastTitle;\r
7533     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7534     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7535                  hwndMain, (DLGPROC)lpProc);\r
7536     FreeProcInstance(lpProc);\r
7537   }\r
7538 }\r
7539 #endif\r
7540 \r
7541 /*---------------------------------------------------------------------------*\\r
7542  *\r
7543  *  Ics Interaction console functions\r
7544  *\r
7545 \*---------------------------------------------------------------------------*/\r
7546 \r
7547 #define HISTORY_SIZE 64\r
7548 static char *history[HISTORY_SIZE];\r
7549 int histIn = 0, histP = 0;\r
7550 \r
7551 VOID\r
7552 SaveInHistory(char *cmd)\r
7553 {\r
7554   if (history[histIn] != NULL) {\r
7555     free(history[histIn]);\r
7556     history[histIn] = NULL;\r
7557   }\r
7558   if (*cmd == NULLCHAR) return;\r
7559   history[histIn] = StrSave(cmd);\r
7560   histIn = (histIn + 1) % HISTORY_SIZE;\r
7561   if (history[histIn] != NULL) {\r
7562     free(history[histIn]);\r
7563     history[histIn] = NULL;\r
7564   }\r
7565   histP = histIn;\r
7566 }\r
7567 \r
7568 char *\r
7569 PrevInHistory(char *cmd)\r
7570 {\r
7571   int newhp;\r
7572   if (histP == histIn) {\r
7573     if (history[histIn] != NULL) free(history[histIn]);\r
7574     history[histIn] = StrSave(cmd);\r
7575   }\r
7576   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7577   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7578   histP = newhp;\r
7579   return history[histP];\r
7580 }\r
7581 \r
7582 char *\r
7583 NextInHistory()\r
7584 {\r
7585   if (histP == histIn) return NULL;\r
7586   histP = (histP + 1) % HISTORY_SIZE;\r
7587   return history[histP];\r
7588 }\r
7589 \r
7590 typedef struct {\r
7591   char *item;\r
7592   char *command;\r
7593   BOOLEAN getname;\r
7594   BOOLEAN immediate;\r
7595 } IcsTextMenuEntry;\r
7596 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7597 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7598 \r
7599 void\r
7600 ParseIcsTextMenu(char *icsTextMenuString)\r
7601 {\r
7602 //  int flags = 0;\r
7603   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7604   char *p = icsTextMenuString;\r
7605   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7606     free(e->item);\r
7607     e->item = NULL;\r
7608     if (e->command != NULL) {\r
7609       free(e->command);\r
7610       e->command = NULL;\r
7611     }\r
7612     e++;\r
7613   }\r
7614   e = icsTextMenuEntry;\r
7615   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7616     if (*p == ';' || *p == '\n') {\r
7617       e->item = strdup("-");\r
7618       e->command = NULL;\r
7619       p++;\r
7620     } else if (*p == '-') {\r
7621       e->item = strdup("-");\r
7622       e->command = NULL;\r
7623       p++;\r
7624       if (*p) p++;\r
7625     } else {\r
7626       char *q, *r, *s, *t;\r
7627       char c;\r
7628       q = strchr(p, ',');\r
7629       if (q == NULL) break;\r
7630       *q = NULLCHAR;\r
7631       r = strchr(q + 1, ',');\r
7632       if (r == NULL) break;\r
7633       *r = NULLCHAR;\r
7634       s = strchr(r + 1, ',');\r
7635       if (s == NULL) break;\r
7636       *s = NULLCHAR;\r
7637       c = ';';\r
7638       t = strchr(s + 1, c);\r
7639       if (t == NULL) {\r
7640         c = '\n';\r
7641         t = strchr(s + 1, c);\r
7642       }\r
7643       if (t != NULL) *t = NULLCHAR;\r
7644       e->item = strdup(p);\r
7645       e->command = strdup(q + 1);\r
7646       e->getname = *(r + 1) != '0';\r
7647       e->immediate = *(s + 1) != '0';\r
7648       *q = ',';\r
7649       *r = ',';\r
7650       *s = ',';\r
7651       if (t == NULL) break;\r
7652       *t = c;\r
7653       p = t + 1;\r
7654     }\r
7655     e++;\r
7656   } \r
7657 }\r
7658 \r
7659 HMENU\r
7660 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7661 {\r
7662   HMENU hmenu, h;\r
7663   int i = 0;\r
7664   hmenu = LoadMenu(hInst, "TextMenu");\r
7665   h = GetSubMenu(hmenu, 0);\r
7666   while (e->item) {\r
7667     if (strcmp(e->item, "-") == 0) {\r
7668       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7669     } else {\r
7670       if (e->item[0] == '|') {\r
7671         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7672                    IDM_CommandX + i, &e->item[1]);\r
7673       } else {\r
7674         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7675       }\r
7676     }\r
7677     e++;\r
7678     i++;\r
7679   } \r
7680   return hmenu;\r
7681 }\r
7682 \r
7683 WNDPROC consoleTextWindowProc;\r
7684 \r
7685 void\r
7686 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7687 {\r
7688   char buf[MSG_SIZ], name[MSG_SIZ];\r
7689   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7690   CHARRANGE sel;\r
7691 \r
7692   if (!getname) {\r
7693     SetWindowText(hInput, command);\r
7694     if (immediate) {\r
7695       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7696     } else {\r
7697       sel.cpMin = 999999;\r
7698       sel.cpMax = 999999;\r
7699       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7700       SetFocus(hInput);\r
7701     }\r
7702     return;\r
7703   }    \r
7704   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7705   if (sel.cpMin == sel.cpMax) {\r
7706     /* Expand to surrounding word */\r
7707     TEXTRANGE tr;\r
7708     do {\r
7709       tr.chrg.cpMax = sel.cpMin;\r
7710       tr.chrg.cpMin = --sel.cpMin;\r
7711       if (sel.cpMin < 0) break;\r
7712       tr.lpstrText = name;\r
7713       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7714     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7715     sel.cpMin++;\r
7716 \r
7717     do {\r
7718       tr.chrg.cpMin = sel.cpMax;\r
7719       tr.chrg.cpMax = ++sel.cpMax;\r
7720       tr.lpstrText = name;\r
7721       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7722     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7723     sel.cpMax--;\r
7724 \r
7725     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7726       MessageBeep(MB_ICONEXCLAMATION);\r
7727       return;\r
7728     }\r
7729     tr.chrg = sel;\r
7730     tr.lpstrText = name;\r
7731     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7732   } else {\r
7733     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7734       MessageBeep(MB_ICONEXCLAMATION);\r
7735       return;\r
7736     }\r
7737     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7738   }\r
7739   if (immediate) {\r
7740     sprintf(buf, "%s %s", command, name);\r
7741     SetWindowText(hInput, buf);\r
7742     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7743   } else {\r
7744     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7745     SetWindowText(hInput, buf);\r
7746     sel.cpMin = 999999;\r
7747     sel.cpMax = 999999;\r
7748     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7749     SetFocus(hInput);\r
7750   }\r
7751 }\r
7752 \r
7753 LRESULT CALLBACK \r
7754 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7755 {\r
7756   HWND hInput;\r
7757   CHARRANGE sel;\r
7758 \r
7759   switch (message) {\r
7760   case WM_KEYDOWN:\r
7761     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7762     switch (wParam) {\r
7763     case VK_PRIOR:\r
7764       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7765       return 0;\r
7766     case VK_NEXT:\r
7767       sel.cpMin = 999999;\r
7768       sel.cpMax = 999999;\r
7769       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7770       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7771       return 0;\r
7772     }\r
7773     break;\r
7774   case WM_CHAR:\r
7775     if (wParam == '\t') {\r
7776       if (GetKeyState(VK_SHIFT) < 0) {\r
7777         /* shifted */\r
7778         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7779         if (buttonDesc[0].hwnd) {\r
7780           SetFocus(buttonDesc[0].hwnd);\r
7781         } else {\r
7782           SetFocus(hwndMain);\r
7783         }\r
7784       } else {\r
7785         /* unshifted */\r
7786         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7787       }\r
7788     } else {\r
7789       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7790       SetFocus(hInput);\r
7791       SendMessage(hInput, message, wParam, lParam);\r
7792     }\r
7793     return 0;\r
7794   case WM_PASTE:\r
7795     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7796     SetFocus(hInput);\r
7797     return SendMessage(hInput, message, wParam, lParam);\r
7798   case WM_MBUTTONDOWN:\r
7799     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7800   case WM_RBUTTONDOWN:\r
7801     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7802       /* Move selection here if it was empty */\r
7803       POINT pt;\r
7804       pt.x = LOWORD(lParam);\r
7805       pt.y = HIWORD(lParam);\r
7806       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7807       if (sel.cpMin == sel.cpMax) {\r
7808         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7809         sel.cpMax = sel.cpMin;\r
7810         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7811       }\r
7812       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7813     }\r
7814     return 0;\r
7815   case WM_RBUTTONUP:\r
7816     if (GetKeyState(VK_SHIFT) & ~1) {\r
7817       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7818         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7819     } else {\r
7820       POINT pt;\r
7821       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7822       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7823       if (sel.cpMin == sel.cpMax) {\r
7824         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7825         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7826       }\r
7827       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7828         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7829       }\r
7830       pt.x = LOWORD(lParam);\r
7831       pt.y = HIWORD(lParam);\r
7832       MenuPopup(hwnd, pt, hmenu, -1);\r
7833     }\r
7834     return 0;\r
7835   case WM_COMMAND:\r
7836     switch (LOWORD(wParam)) {\r
7837     case IDM_QuickPaste:\r
7838       {\r
7839         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7840         if (sel.cpMin == sel.cpMax) {\r
7841           MessageBeep(MB_ICONEXCLAMATION);\r
7842           return 0;\r
7843         }\r
7844         SendMessage(hwnd, WM_COPY, 0, 0);\r
7845         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7846         SendMessage(hInput, WM_PASTE, 0, 0);\r
7847         SetFocus(hInput);\r
7848         return 0;\r
7849       }\r
7850     case IDM_Cut:\r
7851       SendMessage(hwnd, WM_CUT, 0, 0);\r
7852       return 0;\r
7853     case IDM_Paste:\r
7854       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7855       return 0;\r
7856     case IDM_Copy:\r
7857       SendMessage(hwnd, WM_COPY, 0, 0);\r
7858       return 0;\r
7859     default:\r
7860       {\r
7861         int i = LOWORD(wParam) - IDM_CommandX;\r
7862         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7863             icsTextMenuEntry[i].command != NULL) {\r
7864           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7865                    icsTextMenuEntry[i].getname,\r
7866                    icsTextMenuEntry[i].immediate);\r
7867           return 0;\r
7868         }\r
7869       }\r
7870       break;\r
7871     }\r
7872     break;\r
7873   }\r
7874   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7875 }\r
7876 \r
7877 WNDPROC consoleInputWindowProc;\r
7878 \r
7879 LRESULT CALLBACK\r
7880 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7881 {\r
7882   char buf[MSG_SIZ];\r
7883   char *p;\r
7884   static BOOL sendNextChar = FALSE;\r
7885   static BOOL quoteNextChar = FALSE;\r
7886   InputSource *is = consoleInputSource;\r
7887   CHARFORMAT cf;\r
7888   CHARRANGE sel;\r
7889 \r
7890   switch (message) {\r
7891   case WM_CHAR:\r
7892     if (!appData.localLineEditing || sendNextChar) {\r
7893       is->buf[0] = (CHAR) wParam;\r
7894       is->count = 1;\r
7895       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7896       sendNextChar = FALSE;\r
7897       return 0;\r
7898     }\r
7899     if (quoteNextChar) {\r
7900       buf[0] = (char) wParam;\r
7901       buf[1] = NULLCHAR;\r
7902       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7903       quoteNextChar = FALSE;\r
7904       return 0;\r
7905     }\r
7906     switch (wParam) {\r
7907     case '\r':   /* Enter key */\r
7908       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7909       if (consoleEcho) SaveInHistory(is->buf);\r
7910       is->buf[is->count++] = '\n';\r
7911       is->buf[is->count] = NULLCHAR;\r
7912       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7913       if (consoleEcho) {\r
7914         ConsoleOutput(is->buf, is->count, TRUE);\r
7915       } else if (appData.localLineEditing) {\r
7916         ConsoleOutput("\n", 1, TRUE);\r
7917       }\r
7918       /* fall thru */\r
7919     case '\033': /* Escape key */\r
7920       SetWindowText(hwnd, "");\r
7921       cf.cbSize = sizeof(CHARFORMAT);\r
7922       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7923       if (consoleEcho) {\r
7924         cf.crTextColor = textAttribs[ColorNormal].color;\r
7925       } else {\r
7926         cf.crTextColor = COLOR_ECHOOFF;\r
7927       }\r
7928       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7929       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7930       return 0;\r
7931     case '\t':   /* Tab key */\r
7932       if (GetKeyState(VK_SHIFT) < 0) {\r
7933         /* shifted */\r
7934         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7935       } else {\r
7936         /* unshifted */\r
7937         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7938         if (buttonDesc[0].hwnd) {\r
7939           SetFocus(buttonDesc[0].hwnd);\r
7940         } else {\r
7941           SetFocus(hwndMain);\r
7942         }\r
7943       }\r
7944       return 0;\r
7945     case '\023': /* Ctrl+S */\r
7946       sendNextChar = TRUE;\r
7947       return 0;\r
7948     case '\021': /* Ctrl+Q */\r
7949       quoteNextChar = TRUE;\r
7950       return 0;\r
7951     default:\r
7952       break;\r
7953     }\r
7954     break;\r
7955   case WM_KEYDOWN:\r
7956     switch (wParam) {\r
7957     case VK_UP:\r
7958       GetWindowText(hwnd, buf, MSG_SIZ);\r
7959       p = PrevInHistory(buf);\r
7960       if (p != NULL) {\r
7961         SetWindowText(hwnd, p);\r
7962         sel.cpMin = 999999;\r
7963         sel.cpMax = 999999;\r
7964         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7965         return 0;\r
7966       }\r
7967       break;\r
7968     case VK_DOWN:\r
7969       p = NextInHistory();\r
7970       if (p != NULL) {\r
7971         SetWindowText(hwnd, p);\r
7972         sel.cpMin = 999999;\r
7973         sel.cpMax = 999999;\r
7974         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7975         return 0;\r
7976       }\r
7977       break;\r
7978     case VK_HOME:\r
7979     case VK_END:\r
7980       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7981       /* fall thru */\r
7982     case VK_PRIOR:\r
7983     case VK_NEXT:\r
7984       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7985       return 0;\r
7986     }\r
7987     break;\r
7988   case WM_MBUTTONDOWN:\r
7989     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7990       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7991     break;\r
7992   case WM_RBUTTONUP:\r
7993     if (GetKeyState(VK_SHIFT) & ~1) {\r
7994       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7995         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7996     } else {\r
7997       POINT pt;\r
7998       HMENU hmenu;\r
7999       hmenu = LoadMenu(hInst, "InputMenu");\r
8000       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8001       if (sel.cpMin == sel.cpMax) {\r
8002         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8003         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8004       }\r
8005       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8006         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8007       }\r
8008       pt.x = LOWORD(lParam);\r
8009       pt.y = HIWORD(lParam);\r
8010       MenuPopup(hwnd, pt, hmenu, -1);\r
8011     }\r
8012     return 0;\r
8013   case WM_COMMAND:\r
8014     switch (LOWORD(wParam)) { \r
8015     case IDM_Undo:\r
8016       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8017       return 0;\r
8018     case IDM_SelectAll:\r
8019       sel.cpMin = 0;\r
8020       sel.cpMax = -1; /*999999?*/\r
8021       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8022       return 0;\r
8023     case IDM_Cut:\r
8024       SendMessage(hwnd, WM_CUT, 0, 0);\r
8025       return 0;\r
8026     case IDM_Paste:\r
8027       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8028       return 0;\r
8029     case IDM_Copy:\r
8030       SendMessage(hwnd, WM_COPY, 0, 0);\r
8031       return 0;\r
8032     }\r
8033     break;\r
8034   }\r
8035   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8036 }\r
8037 \r
8038 #define CO_MAX  100000\r
8039 #define CO_TRIM   1000\r
8040 \r
8041 LRESULT CALLBACK\r
8042 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8043 {\r
8044   static SnapData sd;\r
8045   static HWND hText, hInput /*, hFocus*/;\r
8046 //  InputSource *is = consoleInputSource;\r
8047   RECT rect;\r
8048   static int sizeX, sizeY;\r
8049   int newSizeX, newSizeY;\r
8050   MINMAXINFO *mmi;\r
8051 \r
8052   switch (message) {\r
8053   case WM_INITDIALOG: /* message: initialize dialog box */\r
8054     hwndConsole = hDlg;\r
8055     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8056     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8057     SetFocus(hInput);\r
8058     consoleTextWindowProc = (WNDPROC)\r
8059       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8060     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8061     consoleInputWindowProc = (WNDPROC)\r
8062       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8063     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8064     Colorize(ColorNormal, TRUE);\r
8065     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8066     ChangedConsoleFont();\r
8067     GetClientRect(hDlg, &rect);\r
8068     sizeX = rect.right;\r
8069     sizeY = rect.bottom;\r
8070     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8071         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8072       WINDOWPLACEMENT wp;\r
8073       EnsureOnScreen(&consoleX, &consoleY);\r
8074       wp.length = sizeof(WINDOWPLACEMENT);\r
8075       wp.flags = 0;\r
8076       wp.showCmd = SW_SHOW;\r
8077       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8078       wp.rcNormalPosition.left = consoleX;\r
8079       wp.rcNormalPosition.right = consoleX + consoleW;\r
8080       wp.rcNormalPosition.top = consoleY;\r
8081       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8082       SetWindowPlacement(hDlg, &wp);\r
8083     }\r
8084 #if 0 \r
8085    // [HGM] Chessknight's change 2004-07-13\r
8086    else { /* Determine Defaults */\r
8087        WINDOWPLACEMENT wp;\r
8088        consoleX = winWidth + 1;\r
8089        consoleY = boardY;\r
8090        consoleW = screenWidth -  winWidth;\r
8091        consoleH = winHeight;\r
8092        EnsureOnScreen(&consoleX, &consoleY);\r
8093        wp.length = sizeof(WINDOWPLACEMENT);\r
8094        wp.flags = 0;\r
8095        wp.showCmd = SW_SHOW;\r
8096        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8097        wp.rcNormalPosition.left = consoleX;\r
8098        wp.rcNormalPosition.right = consoleX + consoleW;\r
8099        wp.rcNormalPosition.top = consoleY;\r
8100        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8101        SetWindowPlacement(hDlg, &wp);\r
8102     }\r
8103 #endif\r
8104     return FALSE;\r
8105 \r
8106   case WM_SETFOCUS:\r
8107     SetFocus(hInput);\r
8108     return 0;\r
8109 \r
8110   case WM_CLOSE:\r
8111     ExitEvent(0);\r
8112     /* not reached */\r
8113     break;\r
8114 \r
8115   case WM_SIZE:\r
8116     if (IsIconic(hDlg)) break;\r
8117     newSizeX = LOWORD(lParam);\r
8118     newSizeY = HIWORD(lParam);\r
8119     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8120       RECT rectText, rectInput;\r
8121       POINT pt;\r
8122       int newTextHeight, newTextWidth;\r
8123       GetWindowRect(hText, &rectText);\r
8124       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8125       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8126       if (newTextHeight < 0) {\r
8127         newSizeY += -newTextHeight;\r
8128         newTextHeight = 0;\r
8129       }\r
8130       SetWindowPos(hText, NULL, 0, 0,\r
8131         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8132       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8133       pt.x = rectInput.left;\r
8134       pt.y = rectInput.top + newSizeY - sizeY;\r
8135       ScreenToClient(hDlg, &pt);\r
8136       SetWindowPos(hInput, NULL, \r
8137         pt.x, pt.y, /* needs client coords */   \r
8138         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8139         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8140     }\r
8141     sizeX = newSizeX;\r
8142     sizeY = newSizeY;\r
8143     break;\r
8144 \r
8145   case WM_GETMINMAXINFO:\r
8146     /* Prevent resizing window too small */\r
8147     mmi = (MINMAXINFO *) lParam;\r
8148     mmi->ptMinTrackSize.x = 100;\r
8149     mmi->ptMinTrackSize.y = 100;\r
8150     break;\r
8151 \r
8152   /* [AS] Snapping */\r
8153   case WM_ENTERSIZEMOVE:\r
8154     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8155 \r
8156   case WM_SIZING:\r
8157     return OnSizing( &sd, hDlg, wParam, lParam );\r
8158 \r
8159   case WM_MOVING:\r
8160     return OnMoving( &sd, hDlg, wParam, lParam );\r
8161 \r
8162   case WM_EXITSIZEMOVE:\r
8163     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8164   }\r
8165 \r
8166   return DefWindowProc(hDlg, message, wParam, lParam);\r
8167 }\r
8168 \r
8169 \r
8170 VOID\r
8171 ConsoleCreate()\r
8172 {\r
8173   HWND hCons;\r
8174   if (hwndConsole) return;\r
8175   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8176   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8177 }\r
8178 \r
8179 \r
8180 VOID\r
8181 ConsoleOutput(char* data, int length, int forceVisible)\r
8182 {\r
8183   HWND hText;\r
8184   int trim, exlen;\r
8185   char *p, *q;\r
8186   char buf[CO_MAX+1];\r
8187   POINT pEnd;\r
8188   RECT rect;\r
8189   static int delayLF = 0;\r
8190   CHARRANGE savesel, sel;\r
8191 \r
8192   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8193   p = data;\r
8194   q = buf;\r
8195   if (delayLF) {\r
8196     *q++ = '\r';\r
8197     *q++ = '\n';\r
8198     delayLF = 0;\r
8199   }\r
8200   while (length--) {\r
8201     if (*p == '\n') {\r
8202       if (*++p) {\r
8203         *q++ = '\r';\r
8204         *q++ = '\n';\r
8205       } else {\r
8206         delayLF = 1;\r
8207       }\r
8208     } else if (*p == '\007') {\r
8209        MyPlaySound(&sounds[(int)SoundBell]);\r
8210        p++;\r
8211     } else {\r
8212       *q++ = *p++;\r
8213     }\r
8214   }\r
8215   *q = NULLCHAR;\r
8216   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8217   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8218   /* Save current selection */\r
8219   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8220   exlen = GetWindowTextLength(hText);\r
8221   /* Find out whether current end of text is visible */\r
8222   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8223   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8224   /* Trim existing text if it's too long */\r
8225   if (exlen + (q - buf) > CO_MAX) {\r
8226     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8227     sel.cpMin = 0;\r
8228     sel.cpMax = trim;\r
8229     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8230     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8231     exlen -= trim;\r
8232     savesel.cpMin -= trim;\r
8233     savesel.cpMax -= trim;\r
8234     if (exlen < 0) exlen = 0;\r
8235     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8236     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8237   }\r
8238   /* Append the new text */\r
8239   sel.cpMin = exlen;\r
8240   sel.cpMax = exlen;\r
8241   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8242   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8243   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8244   if (forceVisible || exlen == 0 ||\r
8245       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8246        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8247     /* Scroll to make new end of text visible if old end of text\r
8248        was visible or new text is an echo of user typein */\r
8249     sel.cpMin = 9999999;\r
8250     sel.cpMax = 9999999;\r
8251     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8252     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8253     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8254     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8255   }\r
8256   if (savesel.cpMax == exlen || forceVisible) {\r
8257     /* Move insert point to new end of text if it was at the old\r
8258        end of text or if the new text is an echo of user typein */\r
8259     sel.cpMin = 9999999;\r
8260     sel.cpMax = 9999999;\r
8261     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8262   } else {\r
8263     /* Restore previous selection */\r
8264     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8265   }\r
8266   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8267 }\r
8268 \r
8269 /*---------*/\r
8270 \r
8271 \r
8272 void\r
8273 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8274 {\r
8275   char buf[100];\r
8276   char *str;\r
8277   COLORREF oldFg, oldBg;\r
8278   HFONT oldFont;\r
8279   RECT rect;\r
8280 \r
8281   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8282 \r
8283   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8284   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8285   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8286 \r
8287   rect.left = x;\r
8288   rect.right = x + squareSize;\r
8289   rect.top  = y;\r
8290   rect.bottom = y + squareSize;\r
8291   str = buf;\r
8292 \r
8293   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8294                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8295              y, ETO_CLIPPED|ETO_OPAQUE,\r
8296              &rect, str, strlen(str), NULL);\r
8297 \r
8298   (void) SetTextColor(hdc, oldFg);\r
8299   (void) SetBkColor(hdc, oldBg);\r
8300   (void) SelectObject(hdc, oldFont);\r
8301 }\r
8302 \r
8303 void\r
8304 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8305               RECT *rect, char *color, char *flagFell)\r
8306 {\r
8307   char buf[100];\r
8308   char *str;\r
8309   COLORREF oldFg, oldBg;\r
8310   HFONT oldFont;\r
8311 \r
8312   if (appData.clockMode) {\r
8313     if (tinyLayout)\r
8314       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8315     else\r
8316       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8317     str = buf;\r
8318   } else {\r
8319     str = color;\r
8320   }\r
8321 \r
8322   if (highlight) {\r
8323     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8324     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8325   } else {\r
8326     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8327     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8328   }\r
8329   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8330 \r
8331   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8332              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8333              rect, str, strlen(str), NULL);\r
8334   if(logoHeight > 0 && appData.clockMode) {\r
8335       RECT r;\r
8336       sprintf(buf, "%s %s", TimeString(timeRemaining), flagFell);\r
8337       r.top = rect->top + logoHeight/2;\r
8338       r.left = rect->left;\r
8339       r.right = rect->right;\r
8340       r.bottom = rect->bottom;\r
8341       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8342                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8343                  &r, str, strlen(str), NULL);\r
8344   }\r
8345   (void) SetTextColor(hdc, oldFg);\r
8346   (void) SetBkColor(hdc, oldBg);\r
8347   (void) SelectObject(hdc, oldFont);\r
8348 }\r
8349 \r
8350 \r
8351 int\r
8352 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8353            OVERLAPPED *ovl)\r
8354 {\r
8355   int ok, err;\r
8356 \r
8357   /* [AS]  */\r
8358   if( count <= 0 ) {\r
8359     if (appData.debugMode) {\r
8360       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8361     }\r
8362 \r
8363     return ERROR_INVALID_USER_BUFFER;\r
8364   }\r
8365 \r
8366   ResetEvent(ovl->hEvent);\r
8367   ovl->Offset = ovl->OffsetHigh = 0;\r
8368   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8369   if (ok) {\r
8370     err = NO_ERROR;\r
8371   } else {\r
8372     err = GetLastError();\r
8373     if (err == ERROR_IO_PENDING) {\r
8374       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8375       if (ok)\r
8376         err = NO_ERROR;\r
8377       else\r
8378         err = GetLastError();\r
8379     }\r
8380   }\r
8381   return err;\r
8382 }\r
8383 \r
8384 int\r
8385 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8386             OVERLAPPED *ovl)\r
8387 {\r
8388   int ok, err;\r
8389 \r
8390   ResetEvent(ovl->hEvent);\r
8391   ovl->Offset = ovl->OffsetHigh = 0;\r
8392   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8393   if (ok) {\r
8394     err = NO_ERROR;\r
8395   } else {\r
8396     err = GetLastError();\r
8397     if (err == ERROR_IO_PENDING) {\r
8398       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8399       if (ok)\r
8400         err = NO_ERROR;\r
8401       else\r
8402         err = GetLastError();\r
8403     }\r
8404   }\r
8405   return err;\r
8406 }\r
8407 \r
8408 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8409 void CheckForInputBufferFull( InputSource * is )\r
8410 {\r
8411     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8412         /* Look for end of line */\r
8413         char * p = is->buf;\r
8414         \r
8415         while( p < is->next && *p != '\n' ) {\r
8416             p++;\r
8417         }\r
8418 \r
8419         if( p >= is->next ) {\r
8420             if (appData.debugMode) {\r
8421                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8422             }\r
8423 \r
8424             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8425             is->count = (DWORD) -1;\r
8426             is->next = is->buf;\r
8427         }\r
8428     }\r
8429 }\r
8430 \r
8431 DWORD\r
8432 InputThread(LPVOID arg)\r
8433 {\r
8434   InputSource *is;\r
8435   OVERLAPPED ovl;\r
8436 \r
8437   is = (InputSource *) arg;\r
8438   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8439   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8440   while (is->hThread != NULL) {\r
8441     is->error = DoReadFile(is->hFile, is->next,\r
8442                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8443                            &is->count, &ovl);\r
8444     if (is->error == NO_ERROR) {\r
8445       is->next += is->count;\r
8446     } else {\r
8447       if (is->error == ERROR_BROKEN_PIPE) {\r
8448         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8449         is->count = 0;\r
8450       } else {\r
8451         is->count = (DWORD) -1;\r
8452         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8453         break; \r
8454       }\r
8455     }\r
8456 \r
8457     CheckForInputBufferFull( is );\r
8458 \r
8459     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8460 \r
8461     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8462 \r
8463     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8464   }\r
8465 \r
8466   CloseHandle(ovl.hEvent);\r
8467   CloseHandle(is->hFile);\r
8468 \r
8469   if (appData.debugMode) {\r
8470     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8471   }\r
8472 \r
8473   return 0;\r
8474 }\r
8475 \r
8476 \r
8477 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8478 DWORD\r
8479 NonOvlInputThread(LPVOID arg)\r
8480 {\r
8481   InputSource *is;\r
8482   char *p, *q;\r
8483   int i;\r
8484   char prev;\r
8485 \r
8486   is = (InputSource *) arg;\r
8487   while (is->hThread != NULL) {\r
8488     is->error = ReadFile(is->hFile, is->next,\r
8489                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8490                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8491     if (is->error == NO_ERROR) {\r
8492       /* Change CRLF to LF */\r
8493       if (is->next > is->buf) {\r
8494         p = is->next - 1;\r
8495         i = is->count + 1;\r
8496       } else {\r
8497         p = is->next;\r
8498         i = is->count;\r
8499       }\r
8500       q = p;\r
8501       prev = NULLCHAR;\r
8502       while (i > 0) {\r
8503         if (prev == '\r' && *p == '\n') {\r
8504           *(q-1) = '\n';\r
8505           is->count--;\r
8506         } else { \r
8507           *q++ = *p;\r
8508         }\r
8509         prev = *p++;\r
8510         i--;\r
8511       }\r
8512       *q = NULLCHAR;\r
8513       is->next = q;\r
8514     } else {\r
8515       if (is->error == ERROR_BROKEN_PIPE) {\r
8516         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8517         is->count = 0; \r
8518       } else {\r
8519         is->count = (DWORD) -1;\r
8520       }\r
8521     }\r
8522 \r
8523     CheckForInputBufferFull( is );\r
8524 \r
8525     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8526 \r
8527     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8528 \r
8529     if (is->count < 0) break;  /* Quit on error */\r
8530   }\r
8531   CloseHandle(is->hFile);\r
8532   return 0;\r
8533 }\r
8534 \r
8535 DWORD\r
8536 SocketInputThread(LPVOID arg)\r
8537 {\r
8538   InputSource *is;\r
8539 \r
8540   is = (InputSource *) arg;\r
8541   while (is->hThread != NULL) {\r
8542     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8543     if ((int)is->count == SOCKET_ERROR) {\r
8544       is->count = (DWORD) -1;\r
8545       is->error = WSAGetLastError();\r
8546     } else {\r
8547       is->error = NO_ERROR;\r
8548       is->next += is->count;\r
8549       if (is->count == 0 && is->second == is) {\r
8550         /* End of file on stderr; quit with no message */\r
8551         break;\r
8552       }\r
8553     }\r
8554     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8555 \r
8556     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8557 \r
8558     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8559   }\r
8560   return 0;\r
8561 }\r
8562 \r
8563 VOID\r
8564 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8565 {\r
8566   InputSource *is;\r
8567 \r
8568   is = (InputSource *) lParam;\r
8569   if (is->lineByLine) {\r
8570     /* Feed in lines one by one */\r
8571     char *p = is->buf;\r
8572     char *q = p;\r
8573     while (q < is->next) {\r
8574       if (*q++ == '\n') {\r
8575         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8576         p = q;\r
8577       }\r
8578     }\r
8579     \r
8580     /* Move any partial line to the start of the buffer */\r
8581     q = is->buf;\r
8582     while (p < is->next) {\r
8583       *q++ = *p++;\r
8584     }\r
8585     is->next = q;\r
8586 \r
8587     if (is->error != NO_ERROR || is->count == 0) {\r
8588       /* Notify backend of the error.  Note: If there was a partial\r
8589          line at the end, it is not flushed through. */\r
8590       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8591     }\r
8592   } else {\r
8593     /* Feed in the whole chunk of input at once */\r
8594     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8595     is->next = is->buf;\r
8596   }\r
8597 }\r
8598 \r
8599 /*---------------------------------------------------------------------------*\\r
8600  *\r
8601  *  Menu enables. Used when setting various modes.\r
8602  *\r
8603 \*---------------------------------------------------------------------------*/\r
8604 \r
8605 typedef struct {\r
8606   int item;\r
8607   int flags;\r
8608 } Enables;\r
8609 \r
8610 VOID\r
8611 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8612 {\r
8613   while (enab->item > 0) {\r
8614     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8615     enab++;\r
8616   }\r
8617 }\r
8618 \r
8619 Enables gnuEnables[] = {\r
8620   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8621   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8622   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8623   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8624   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8625   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8626   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8627   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8628   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8629   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8630   { -1, -1 }\r
8631 };\r
8632 \r
8633 Enables icsEnables[] = {\r
8634   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8635   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8636   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8638   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8639   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8640   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8641   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8642   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8643   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8644   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8645   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8646   { -1, -1 }\r
8647 };\r
8648 \r
8649 #ifdef ZIPPY\r
8650 Enables zippyEnables[] = {\r
8651   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8652   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8653   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8654   { -1, -1 }\r
8655 };\r
8656 #endif\r
8657 \r
8658 Enables ncpEnables[] = {\r
8659   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8660   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8661   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8662   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8663   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8664   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8665   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8666   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8667   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8668   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8669   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8670   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8671   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8672   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8673   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8674   { -1, -1 }\r
8675 };\r
8676 \r
8677 Enables trainingOnEnables[] = {\r
8678   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8679   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8680   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8681   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8682   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8683   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8684   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8685   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8686   { -1, -1 }\r
8687 };\r
8688 \r
8689 Enables trainingOffEnables[] = {\r
8690   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8691   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8692   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8693   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8694   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8695   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8696   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8697   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8698   { -1, -1 }\r
8699 };\r
8700 \r
8701 /* These modify either ncpEnables or gnuEnables */\r
8702 Enables cmailEnables[] = {\r
8703   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8704   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8705   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8706   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8707   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8708   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8709   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8710   { -1, -1 }\r
8711 };\r
8712 \r
8713 Enables machineThinkingEnables[] = {\r
8714   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8715   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8716   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8717   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8718   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8719   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8720   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8721   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8722   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8723   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8724   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8725   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8726   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8727   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8728   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8729   { -1, -1 }\r
8730 };\r
8731 \r
8732 Enables userThinkingEnables[] = {\r
8733   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8734   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8735   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8736   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8737   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8738   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8739   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8740   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8741   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8742   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8743   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8744   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8745   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8746   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8747   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8748   { -1, -1 }\r
8749 };\r
8750 \r
8751 /*---------------------------------------------------------------------------*\\r
8752  *\r
8753  *  Front-end interface functions exported by XBoard.\r
8754  *  Functions appear in same order as prototypes in frontend.h.\r
8755  * \r
8756 \*---------------------------------------------------------------------------*/\r
8757 VOID\r
8758 ModeHighlight()\r
8759 {\r
8760   static UINT prevChecked = 0;\r
8761   static int prevPausing = 0;\r
8762   UINT nowChecked;\r
8763 \r
8764   if (pausing != prevPausing) {\r
8765     prevPausing = pausing;\r
8766     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8767                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8768     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8769   }\r
8770 \r
8771   switch (gameMode) {\r
8772   case BeginningOfGame:\r
8773     if (appData.icsActive)\r
8774       nowChecked = IDM_IcsClient;\r
8775     else if (appData.noChessProgram)\r
8776       nowChecked = IDM_EditGame;\r
8777     else\r
8778       nowChecked = IDM_MachineBlack;\r
8779     break;\r
8780   case MachinePlaysBlack:\r
8781     nowChecked = IDM_MachineBlack;\r
8782     break;\r
8783   case MachinePlaysWhite:\r
8784     nowChecked = IDM_MachineWhite;\r
8785     break;\r
8786   case TwoMachinesPlay:\r
8787     nowChecked = IDM_TwoMachines;\r
8788     break;\r
8789   case AnalyzeMode:\r
8790     nowChecked = IDM_AnalysisMode;\r
8791     break;\r
8792   case AnalyzeFile:\r
8793     nowChecked = IDM_AnalyzeFile;\r
8794     break;\r
8795   case EditGame:\r
8796     nowChecked = IDM_EditGame;\r
8797     break;\r
8798   case PlayFromGameFile:\r
8799     nowChecked = IDM_LoadGame;\r
8800     break;\r
8801   case EditPosition:\r
8802     nowChecked = IDM_EditPosition;\r
8803     break;\r
8804   case Training:\r
8805     nowChecked = IDM_Training;\r
8806     break;\r
8807   case IcsPlayingWhite:\r
8808   case IcsPlayingBlack:\r
8809   case IcsObserving:\r
8810   case IcsIdle:\r
8811     nowChecked = IDM_IcsClient;\r
8812     break;\r
8813   default:\r
8814   case EndOfGame:\r
8815     nowChecked = 0;\r
8816     break;\r
8817   }\r
8818   if (prevChecked != 0)\r
8819     (void) CheckMenuItem(GetMenu(hwndMain),\r
8820                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8821   if (nowChecked != 0)\r
8822     (void) CheckMenuItem(GetMenu(hwndMain),\r
8823                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8824 \r
8825   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8826     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8827                           MF_BYCOMMAND|MF_ENABLED);\r
8828   } else {\r
8829     (void) EnableMenuItem(GetMenu(hwndMain), \r
8830                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8831   }\r
8832 \r
8833   prevChecked = nowChecked;\r
8834 \r
8835   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8836   if (appData.icsActive) {\r
8837        if (appData.icsEngineAnalyze) {\r
8838                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8839                        MF_BYCOMMAND|MF_CHECKED);\r
8840        } else {\r
8841                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8842                        MF_BYCOMMAND|MF_UNCHECKED);\r
8843        }\r
8844   }\r
8845 }\r
8846 \r
8847 VOID\r
8848 SetICSMode()\r
8849 {\r
8850   HMENU hmenu = GetMenu(hwndMain);\r
8851   SetMenuEnables(hmenu, icsEnables);\r
8852   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8853     MF_BYPOSITION|MF_ENABLED);\r
8854 #ifdef ZIPPY\r
8855   if (appData.zippyPlay) {\r
8856     SetMenuEnables(hmenu, zippyEnables);\r
8857     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8858          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8859           MF_BYCOMMAND|MF_ENABLED);\r
8860   }\r
8861 #endif\r
8862 }\r
8863 \r
8864 VOID\r
8865 SetGNUMode()\r
8866 {\r
8867   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8868 }\r
8869 \r
8870 VOID\r
8871 SetNCPMode()\r
8872 {\r
8873   HMENU hmenu = GetMenu(hwndMain);\r
8874   SetMenuEnables(hmenu, ncpEnables);\r
8875   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8876     MF_BYPOSITION|MF_GRAYED);\r
8877     DrawMenuBar(hwndMain);\r
8878 }\r
8879 \r
8880 VOID\r
8881 SetCmailMode()\r
8882 {\r
8883   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8884 }\r
8885 \r
8886 VOID \r
8887 SetTrainingModeOn()\r
8888 {\r
8889   int i;\r
8890   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8891   for (i = 0; i < N_BUTTONS; i++) {\r
8892     if (buttonDesc[i].hwnd != NULL)\r
8893       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8894   }\r
8895   CommentPopDown();\r
8896 }\r
8897 \r
8898 VOID SetTrainingModeOff()\r
8899 {\r
8900   int i;\r
8901   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8902   for (i = 0; i < N_BUTTONS; i++) {\r
8903     if (buttonDesc[i].hwnd != NULL)\r
8904       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8905   }\r
8906 }\r
8907 \r
8908 \r
8909 VOID\r
8910 SetUserThinkingEnables()\r
8911 {\r
8912   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8913 }\r
8914 \r
8915 VOID\r
8916 SetMachineThinkingEnables()\r
8917 {\r
8918   HMENU hMenu = GetMenu(hwndMain);\r
8919   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8920 \r
8921   SetMenuEnables(hMenu, machineThinkingEnables);\r
8922 \r
8923   if (gameMode == MachinePlaysBlack) {\r
8924     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8925   } else if (gameMode == MachinePlaysWhite) {\r
8926     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8927   } else if (gameMode == TwoMachinesPlay) {\r
8928     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8929   }\r
8930 }\r
8931 \r
8932 \r
8933 VOID\r
8934 DisplayTitle(char *str)\r
8935 {\r
8936   char title[MSG_SIZ], *host;\r
8937   if (str[0] != NULLCHAR) {\r
8938     strcpy(title, str);\r
8939   } else if (appData.icsActive) {\r
8940     if (appData.icsCommPort[0] != NULLCHAR)\r
8941       host = "ICS";\r
8942     else \r
8943       host = appData.icsHost;\r
8944     sprintf(title, "%s: %s", szTitle, host);\r
8945   } else if (appData.noChessProgram) {\r
8946     strcpy(title, szTitle);\r
8947   } else {\r
8948     strcpy(title, szTitle);\r
8949     strcat(title, ": ");\r
8950     strcat(title, first.tidy);\r
8951   }\r
8952   SetWindowText(hwndMain, title);\r
8953 }\r
8954 \r
8955 \r
8956 VOID\r
8957 DisplayMessage(char *str1, char *str2)\r
8958 {\r
8959   HDC hdc;\r
8960   HFONT oldFont;\r
8961   int remain = MESSAGE_TEXT_MAX - 1;\r
8962   int len;\r
8963 \r
8964   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8965   messageText[0] = NULLCHAR;\r
8966   if (*str1) {\r
8967     len = strlen(str1);\r
8968     if (len > remain) len = remain;\r
8969     strncpy(messageText, str1, len);\r
8970     messageText[len] = NULLCHAR;\r
8971     remain -= len;\r
8972   }\r
8973   if (*str2 && remain >= 2) {\r
8974     if (*str1) {\r
8975       strcat(messageText, "  ");\r
8976       remain -= 2;\r
8977     }\r
8978     len = strlen(str2);\r
8979     if (len > remain) len = remain;\r
8980     strncat(messageText, str2, len);\r
8981   }\r
8982   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8983 \r
8984   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8985   hdc = GetDC(hwndMain);\r
8986   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8987   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8988              &messageRect, messageText, strlen(messageText), NULL);\r
8989   (void) SelectObject(hdc, oldFont);\r
8990   (void) ReleaseDC(hwndMain, hdc);\r
8991 }\r
8992 \r
8993 VOID\r
8994 DisplayError(char *str, int error)\r
8995 {\r
8996   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8997   int len;\r
8998 \r
8999   if (error == 0) {\r
9000     strcpy(buf, str);\r
9001   } else {\r
9002     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9003                         NULL, error, LANG_NEUTRAL,\r
9004                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9005     if (len > 0) {\r
9006       sprintf(buf, "%s:\n%s", str, buf2);\r
9007     } else {\r
9008       ErrorMap *em = errmap;\r
9009       while (em->err != 0 && em->err != error) em++;\r
9010       if (em->err != 0) {\r
9011         sprintf(buf, "%s:\n%s", str, em->msg);\r
9012       } else {\r
9013         sprintf(buf, "%s:\nError code %d", str, error);\r
9014       }\r
9015     }\r
9016   }\r
9017   \r
9018   ErrorPopUp("Error", buf);\r
9019 }\r
9020 \r
9021 \r
9022 VOID\r
9023 DisplayMoveError(char *str)\r
9024 {\r
9025   fromX = fromY = -1;\r
9026   ClearHighlights();\r
9027   DrawPosition(FALSE, NULL);\r
9028   if (appData.popupMoveErrors) {\r
9029     ErrorPopUp("Error", str);\r
9030   } else {\r
9031     DisplayMessage(str, "");\r
9032     moveErrorMessageUp = TRUE;\r
9033   }\r
9034 }\r
9035 \r
9036 VOID\r
9037 DisplayFatalError(char *str, int error, int exitStatus)\r
9038 {\r
9039   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9040   int len;\r
9041   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9042 \r
9043   if (error != 0) {\r
9044     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9045                         NULL, error, LANG_NEUTRAL,\r
9046                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9047     if (len > 0) {\r
9048       sprintf(buf, "%s:\n%s", str, buf2);\r
9049     } else {\r
9050       ErrorMap *em = errmap;\r
9051       while (em->err != 0 && em->err != error) em++;\r
9052       if (em->err != 0) {\r
9053         sprintf(buf, "%s:\n%s", str, em->msg);\r
9054       } else {\r
9055         sprintf(buf, "%s:\nError code %d", str, error);\r
9056       }\r
9057     }\r
9058     str = buf;\r
9059   }\r
9060   if (appData.debugMode) {\r
9061     fprintf(debugFP, "%s: %s\n", label, str);\r
9062   }\r
9063   if (appData.popupExitMessage) {\r
9064     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9065                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9066   }\r
9067   ExitEvent(exitStatus);\r
9068 }\r
9069 \r
9070 \r
9071 VOID\r
9072 DisplayInformation(char *str)\r
9073 {\r
9074   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9075 }\r
9076 \r
9077 \r
9078 VOID\r
9079 DisplayNote(char *str)\r
9080 {\r
9081   ErrorPopUp("Note", str);\r
9082 }\r
9083 \r
9084 \r
9085 typedef struct {\r
9086   char *title, *question, *replyPrefix;\r
9087   ProcRef pr;\r
9088 } QuestionParams;\r
9089 \r
9090 LRESULT CALLBACK\r
9091 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9092 {\r
9093   static QuestionParams *qp;\r
9094   char reply[MSG_SIZ];\r
9095   int len, err;\r
9096 \r
9097   switch (message) {\r
9098   case WM_INITDIALOG:\r
9099     qp = (QuestionParams *) lParam;\r
9100     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9101     SetWindowText(hDlg, qp->title);\r
9102     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9103     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9104     return FALSE;\r
9105 \r
9106   case WM_COMMAND:\r
9107     switch (LOWORD(wParam)) {\r
9108     case IDOK:\r
9109       strcpy(reply, qp->replyPrefix);\r
9110       if (*reply) strcat(reply, " ");\r
9111       len = strlen(reply);\r
9112       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9113       strcat(reply, "\n");\r
9114       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9115       EndDialog(hDlg, TRUE);\r
9116       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9117       return TRUE;\r
9118     case IDCANCEL:\r
9119       EndDialog(hDlg, FALSE);\r
9120       return TRUE;\r
9121     default:\r
9122       break;\r
9123     }\r
9124     break;\r
9125   }\r
9126   return FALSE;\r
9127 }\r
9128 \r
9129 VOID\r
9130 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9131 {\r
9132     QuestionParams qp;\r
9133     FARPROC lpProc;\r
9134     \r
9135     qp.title = title;\r
9136     qp.question = question;\r
9137     qp.replyPrefix = replyPrefix;\r
9138     qp.pr = pr;\r
9139     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9140     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9141       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9142     FreeProcInstance(lpProc);\r
9143 }\r
9144 \r
9145 /* [AS] Pick FRC position */\r
9146 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9147 {\r
9148     static int * lpIndexFRC;\r
9149     BOOL index_is_ok;\r
9150     char buf[16];\r
9151 \r
9152     switch( message )\r
9153     {\r
9154     case WM_INITDIALOG:\r
9155         lpIndexFRC = (int *) lParam;\r
9156 \r
9157         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9158 \r
9159         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9160         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9161         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9162         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9163 \r
9164         break;\r
9165 \r
9166     case WM_COMMAND:\r
9167         switch( LOWORD(wParam) ) {\r
9168         case IDOK:\r
9169             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9170             EndDialog( hDlg, 0 );\r
9171             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9172             return TRUE;\r
9173         case IDCANCEL:\r
9174             EndDialog( hDlg, 1 );   \r
9175             return TRUE;\r
9176         case IDC_NFG_Edit:\r
9177             if( HIWORD(wParam) == EN_CHANGE ) {\r
9178                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9179 \r
9180                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9181             }\r
9182             return TRUE;\r
9183         case IDC_NFG_Random:\r
9184             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9185             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9186             return TRUE;\r
9187         }\r
9188 \r
9189         break;\r
9190     }\r
9191 \r
9192     return FALSE;\r
9193 }\r
9194 \r
9195 int NewGameFRC()\r
9196 {\r
9197     int result;\r
9198     int index = appData.defaultFrcPosition;\r
9199     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9200 \r
9201     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9202 \r
9203     if( result == 0 ) {\r
9204         appData.defaultFrcPosition = index;\r
9205     }\r
9206 \r
9207     return result;\r
9208 }\r
9209 \r
9210 /* [AS] Game list options */\r
9211 typedef struct {\r
9212     char id;\r
9213     char * name;\r
9214 } GLT_Item;\r
9215 \r
9216 static GLT_Item GLT_ItemInfo[] = {\r
9217     { GLT_EVENT,      "Event" },\r
9218     { GLT_SITE,       "Site" },\r
9219     { GLT_DATE,       "Date" },\r
9220     { GLT_ROUND,      "Round" },\r
9221     { GLT_PLAYERS,    "Players" },\r
9222     { GLT_RESULT,     "Result" },\r
9223     { GLT_WHITE_ELO,  "White Rating" },\r
9224     { GLT_BLACK_ELO,  "Black Rating" },\r
9225     { GLT_TIME_CONTROL,"Time Control" },\r
9226     { GLT_VARIANT,    "Variant" },\r
9227     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9228     { 0, 0 }\r
9229 };\r
9230 \r
9231 const char * GLT_FindItem( char id )\r
9232 {\r
9233     const char * result = 0;\r
9234 \r
9235     GLT_Item * list = GLT_ItemInfo;\r
9236 \r
9237     while( list->id != 0 ) {\r
9238         if( list->id == id ) {\r
9239             result = list->name;\r
9240             break;\r
9241         }\r
9242 \r
9243         list++;\r
9244     }\r
9245 \r
9246     return result;\r
9247 }\r
9248 \r
9249 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9250 {\r
9251     const char * name = GLT_FindItem( id );\r
9252 \r
9253     if( name != 0 ) {\r
9254         if( index >= 0 ) {\r
9255             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9256         }\r
9257         else {\r
9258             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9259         }\r
9260     }\r
9261 }\r
9262 \r
9263 void GLT_TagsToList( HWND hDlg, char * tags )\r
9264 {\r
9265     char * pc = tags;\r
9266 \r
9267     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9268 \r
9269     while( *pc ) {\r
9270         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9271         pc++;\r
9272     }\r
9273 \r
9274     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9275 \r
9276     pc = GLT_ALL_TAGS;\r
9277 \r
9278     while( *pc ) {\r
9279         if( strchr( tags, *pc ) == 0 ) {\r
9280             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9281         }\r
9282         pc++;\r
9283     }\r
9284 \r
9285     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9286 }\r
9287 \r
9288 char GLT_ListItemToTag( HWND hDlg, int index )\r
9289 {\r
9290     char result = '\0';\r
9291     char name[128];\r
9292 \r
9293     GLT_Item * list = GLT_ItemInfo;\r
9294 \r
9295     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9296         while( list->id != 0 ) {\r
9297             if( strcmp( list->name, name ) == 0 ) {\r
9298                 result = list->id;\r
9299                 break;\r
9300             }\r
9301 \r
9302             list++;\r
9303         }\r
9304     }\r
9305 \r
9306     return result;\r
9307 }\r
9308 \r
9309 void GLT_MoveSelection( HWND hDlg, int delta )\r
9310 {\r
9311     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9312     int idx2 = idx1 + delta;\r
9313     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9314 \r
9315     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9316         char buf[128];\r
9317 \r
9318         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9319         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9320         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9321         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9322     }\r
9323 }\r
9324 \r
9325 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9326 {\r
9327     static char glt[64];\r
9328     static char * lpUserGLT;\r
9329 \r
9330     switch( message )\r
9331     {\r
9332     case WM_INITDIALOG:\r
9333         lpUserGLT = (char *) lParam;\r
9334         \r
9335         strcpy( glt, lpUserGLT );\r
9336 \r
9337         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9338 \r
9339         /* Initialize list */\r
9340         GLT_TagsToList( hDlg, glt );\r
9341 \r
9342         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9343 \r
9344         break;\r
9345 \r
9346     case WM_COMMAND:\r
9347         switch( LOWORD(wParam) ) {\r
9348         case IDOK:\r
9349             {\r
9350                 char * pc = lpUserGLT;\r
9351                 int idx = 0;\r
9352 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9353                 char id;\r
9354 \r
9355                 do {\r
9356                     id = GLT_ListItemToTag( hDlg, idx );\r
9357 \r
9358                     *pc++ = id;\r
9359                     idx++;\r
9360                 } while( id != '\0' );\r
9361             }\r
9362             EndDialog( hDlg, 0 );\r
9363             return TRUE;\r
9364         case IDCANCEL:\r
9365             EndDialog( hDlg, 1 );\r
9366             return TRUE;\r
9367 \r
9368         case IDC_GLT_Default:\r
9369             strcpy( glt, GLT_DEFAULT_TAGS );\r
9370             GLT_TagsToList( hDlg, glt );\r
9371             return TRUE;\r
9372 \r
9373         case IDC_GLT_Restore:\r
9374             strcpy( glt, lpUserGLT );\r
9375             GLT_TagsToList( hDlg, glt );\r
9376             return TRUE;\r
9377 \r
9378         case IDC_GLT_Up:\r
9379             GLT_MoveSelection( hDlg, -1 );\r
9380             return TRUE;\r
9381 \r
9382         case IDC_GLT_Down:\r
9383             GLT_MoveSelection( hDlg, +1 );\r
9384             return TRUE;\r
9385         }\r
9386 \r
9387         break;\r
9388     }\r
9389 \r
9390     return FALSE;\r
9391 }\r
9392 \r
9393 int GameListOptions()\r
9394 {\r
9395     char glt[64];\r
9396     int result;\r
9397     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9398 \r
9399     strcpy( glt, appData.gameListTags );\r
9400 \r
9401     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9402 \r
9403     if( result == 0 ) {\r
9404         /* [AS] Memory leak here! */\r
9405         appData.gameListTags = strdup( glt ); \r
9406     }\r
9407 \r
9408     return result;\r
9409 }\r
9410 \r
9411 \r
9412 VOID\r
9413 DisplayIcsInteractionTitle(char *str)\r
9414 {\r
9415   char consoleTitle[MSG_SIZ];\r
9416 \r
9417   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9418   SetWindowText(hwndConsole, consoleTitle);\r
9419 }\r
9420 \r
9421 void\r
9422 DrawPosition(int fullRedraw, Board board)\r
9423 {\r
9424   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9425 }\r
9426 \r
9427 \r
9428 VOID\r
9429 ResetFrontEnd()\r
9430 {\r
9431   fromX = fromY = -1;\r
9432   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9433     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9434     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9435     dragInfo.lastpos = dragInfo.pos;\r
9436     dragInfo.start.x = dragInfo.start.y = -1;\r
9437     dragInfo.from = dragInfo.start;\r
9438     ReleaseCapture();\r
9439     DrawPosition(TRUE, NULL);\r
9440   }\r
9441 }\r
9442 \r
9443 \r
9444 VOID\r
9445 CommentPopUp(char *title, char *str)\r
9446 {\r
9447   HWND hwnd = GetActiveWindow();\r
9448   EitherCommentPopUp(0, title, str, FALSE);\r
9449   SetActiveWindow(hwnd);\r
9450 }\r
9451 \r
9452 VOID\r
9453 CommentPopDown(void)\r
9454 {\r
9455   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9456   if (commentDialog) {\r
9457     ShowWindow(commentDialog, SW_HIDE);\r
9458   }\r
9459   commentDialogUp = FALSE;\r
9460 }\r
9461 \r
9462 VOID\r
9463 EditCommentPopUp(int index, char *title, char *str)\r
9464 {\r
9465   EitherCommentPopUp(index, title, str, TRUE);\r
9466 }\r
9467 \r
9468 \r
9469 VOID\r
9470 RingBell()\r
9471 {\r
9472   MyPlaySound(&sounds[(int)SoundMove]);\r
9473 }\r
9474 \r
9475 VOID PlayIcsWinSound()\r
9476 {\r
9477   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9478 }\r
9479 \r
9480 VOID PlayIcsLossSound()\r
9481 {\r
9482   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9483 }\r
9484 \r
9485 VOID PlayIcsDrawSound()\r
9486 {\r
9487   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9488 }\r
9489 \r
9490 VOID PlayIcsUnfinishedSound()\r
9491 {\r
9492   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9493 }\r
9494 \r
9495 VOID\r
9496 PlayAlarmSound()\r
9497 {\r
9498   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9499 }\r
9500 \r
9501 \r
9502 VOID\r
9503 EchoOn()\r
9504 {\r
9505   HWND hInput;\r
9506   consoleEcho = TRUE;\r
9507   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9508   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9509   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9510 }\r
9511 \r
9512 \r
9513 VOID\r
9514 EchoOff()\r
9515 {\r
9516   CHARFORMAT cf;\r
9517   HWND hInput;\r
9518   consoleEcho = FALSE;\r
9519   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9520   /* This works OK: set text and background both to the same color */\r
9521   cf = consoleCF;\r
9522   cf.crTextColor = COLOR_ECHOOFF;\r
9523   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9524   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9525 }\r
9526 \r
9527 /* No Raw()...? */\r
9528 \r
9529 void Colorize(ColorClass cc, int continuation)\r
9530 {\r
9531   currentColorClass = cc;\r
9532   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9533   consoleCF.crTextColor = textAttribs[cc].color;\r
9534   consoleCF.dwEffects = textAttribs[cc].effects;\r
9535   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9536 }\r
9537 \r
9538 char *\r
9539 UserName()\r
9540 {\r
9541   static char buf[MSG_SIZ];\r
9542   DWORD bufsiz = MSG_SIZ;\r
9543 \r
9544   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9545         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9546   }\r
9547   if (!GetUserName(buf, &bufsiz)) {\r
9548     /*DisplayError("Error getting user name", GetLastError());*/\r
9549     strcpy(buf, "User");\r
9550   }\r
9551   return buf;\r
9552 }\r
9553 \r
9554 char *\r
9555 HostName()\r
9556 {\r
9557   static char buf[MSG_SIZ];\r
9558   DWORD bufsiz = MSG_SIZ;\r
9559 \r
9560   if (!GetComputerName(buf, &bufsiz)) {\r
9561     /*DisplayError("Error getting host name", GetLastError());*/\r
9562     strcpy(buf, "Unknown");\r
9563   }\r
9564   return buf;\r
9565 }\r
9566 \r
9567 \r
9568 int\r
9569 ClockTimerRunning()\r
9570 {\r
9571   return clockTimerEvent != 0;\r
9572 }\r
9573 \r
9574 int\r
9575 StopClockTimer()\r
9576 {\r
9577   if (clockTimerEvent == 0) return FALSE;\r
9578   KillTimer(hwndMain, clockTimerEvent);\r
9579   clockTimerEvent = 0;\r
9580   return TRUE;\r
9581 }\r
9582 \r
9583 void\r
9584 StartClockTimer(long millisec)\r
9585 {\r
9586   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9587                              (UINT) millisec, NULL);\r
9588 }\r
9589 \r
9590 void\r
9591 DisplayWhiteClock(long timeRemaining, int highlight)\r
9592 {\r
9593   HDC hdc;\r
9594   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9595 \r
9596   if(appData.noGUI) return;\r
9597   hdc = GetDC(hwndMain);\r
9598   if (!IsIconic(hwndMain)) {\r
9599     DisplayAClock(hdc, timeRemaining, highlight, \r
9600                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9601   }\r
9602   if (highlight && iconCurrent == iconBlack) {\r
9603     iconCurrent = iconWhite;\r
9604     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9605     if (IsIconic(hwndMain)) {\r
9606       DrawIcon(hdc, 2, 2, iconCurrent);\r
9607     }\r
9608   }\r
9609   (void) ReleaseDC(hwndMain, hdc);\r
9610   if (hwndConsole)\r
9611     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9612 }\r
9613 \r
9614 void\r
9615 DisplayBlackClock(long timeRemaining, int highlight)\r
9616 {\r
9617   HDC hdc;\r
9618   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9619 \r
9620   if(appData.noGUI) return;\r
9621   hdc = GetDC(hwndMain);\r
9622   if (!IsIconic(hwndMain)) {\r
9623     DisplayAClock(hdc, timeRemaining, highlight, \r
9624                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9625   }\r
9626   if (highlight && iconCurrent == iconWhite) {\r
9627     iconCurrent = iconBlack;\r
9628     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9629     if (IsIconic(hwndMain)) {\r
9630       DrawIcon(hdc, 2, 2, iconCurrent);\r
9631     }\r
9632   }\r
9633   (void) ReleaseDC(hwndMain, hdc);\r
9634   if (hwndConsole)\r
9635     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9636 }\r
9637 \r
9638 \r
9639 int\r
9640 LoadGameTimerRunning()\r
9641 {\r
9642   return loadGameTimerEvent != 0;\r
9643 }\r
9644 \r
9645 int\r
9646 StopLoadGameTimer()\r
9647 {\r
9648   if (loadGameTimerEvent == 0) return FALSE;\r
9649   KillTimer(hwndMain, loadGameTimerEvent);\r
9650   loadGameTimerEvent = 0;\r
9651   return TRUE;\r
9652 }\r
9653 \r
9654 void\r
9655 StartLoadGameTimer(long millisec)\r
9656 {\r
9657   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9658                                 (UINT) millisec, NULL);\r
9659 }\r
9660 \r
9661 void\r
9662 AutoSaveGame()\r
9663 {\r
9664   char *defName;\r
9665   FILE *f;\r
9666   char fileTitle[MSG_SIZ];\r
9667 \r
9668   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9669   f = OpenFileDialog(hwndMain, "a", defName,\r
9670                      appData.oldSaveStyle ? "gam" : "pgn",\r
9671                      GAME_FILT, \r
9672                      "Save Game to File", NULL, fileTitle, NULL);\r
9673   if (f != NULL) {\r
9674     SaveGame(f, 0, "");\r
9675     fclose(f);\r
9676   }\r
9677 }\r
9678 \r
9679 \r
9680 void\r
9681 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9682 {\r
9683   if (delayedTimerEvent != 0) {\r
9684     if (appData.debugMode) {\r
9685       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9686     }\r
9687     KillTimer(hwndMain, delayedTimerEvent);\r
9688     delayedTimerEvent = 0;\r
9689     delayedTimerCallback();\r
9690   }\r
9691   delayedTimerCallback = cb;\r
9692   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9693                                 (UINT) millisec, NULL);\r
9694 }\r
9695 \r
9696 DelayedEventCallback\r
9697 GetDelayedEvent()\r
9698 {\r
9699   if (delayedTimerEvent) {\r
9700     return delayedTimerCallback;\r
9701   } else {\r
9702     return NULL;\r
9703   }\r
9704 }\r
9705 \r
9706 void\r
9707 CancelDelayedEvent()\r
9708 {\r
9709   if (delayedTimerEvent) {\r
9710     KillTimer(hwndMain, delayedTimerEvent);\r
9711     delayedTimerEvent = 0;\r
9712   }\r
9713 }\r
9714 \r
9715 DWORD GetWin32Priority(int nice)\r
9716 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9717 /*\r
9718 REALTIME_PRIORITY_CLASS     0x00000100\r
9719 HIGH_PRIORITY_CLASS         0x00000080\r
9720 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9721 NORMAL_PRIORITY_CLASS       0x00000020\r
9722 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9723 IDLE_PRIORITY_CLASS         0x00000040\r
9724 */\r
9725         if (nice < -15) return 0x00000080;\r
9726         if (nice < 0)   return 0x00008000;\r
9727         if (nice == 0)  return 0x00000020;\r
9728         if (nice < 15)  return 0x00004000;\r
9729         return 0x00000040;\r
9730 }\r
9731 \r
9732 /* Start a child process running the given program.\r
9733    The process's standard output can be read from "from", and its\r
9734    standard input can be written to "to".\r
9735    Exit with fatal error if anything goes wrong.\r
9736    Returns an opaque pointer that can be used to destroy the process\r
9737    later.\r
9738 */\r
9739 int\r
9740 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9741 {\r
9742 #define BUFSIZE 4096\r
9743 \r
9744   HANDLE hChildStdinRd, hChildStdinWr,\r
9745     hChildStdoutRd, hChildStdoutWr;\r
9746   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9747   SECURITY_ATTRIBUTES saAttr;\r
9748   BOOL fSuccess;\r
9749   PROCESS_INFORMATION piProcInfo;\r
9750   STARTUPINFO siStartInfo;\r
9751   ChildProc *cp;\r
9752   char buf[MSG_SIZ];\r
9753   DWORD err;\r
9754 \r
9755   if (appData.debugMode) {\r
9756     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9757   }\r
9758 \r
9759   *pr = NoProc;\r
9760 \r
9761   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9762   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9763   saAttr.bInheritHandle = TRUE;\r
9764   saAttr.lpSecurityDescriptor = NULL;\r
9765 \r
9766   /*\r
9767    * The steps for redirecting child's STDOUT:\r
9768    *     1. Create anonymous pipe to be STDOUT for child.\r
9769    *     2. Create a noninheritable duplicate of read handle,\r
9770    *         and close the inheritable read handle.\r
9771    */\r
9772 \r
9773   /* Create a pipe for the child's STDOUT. */\r
9774   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9775     return GetLastError();\r
9776   }\r
9777 \r
9778   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9779   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9780                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9781                              FALSE,     /* not inherited */\r
9782                              DUPLICATE_SAME_ACCESS);\r
9783   if (! fSuccess) {\r
9784     return GetLastError();\r
9785   }\r
9786   CloseHandle(hChildStdoutRd);\r
9787 \r
9788   /*\r
9789    * The steps for redirecting child's STDIN:\r
9790    *     1. Create anonymous pipe to be STDIN for child.\r
9791    *     2. Create a noninheritable duplicate of write handle,\r
9792    *         and close the inheritable write handle.\r
9793    */\r
9794 \r
9795   /* Create a pipe for the child's STDIN. */\r
9796   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9797     return GetLastError();\r
9798   }\r
9799 \r
9800   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9801   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9802                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9803                              FALSE,     /* not inherited */\r
9804                              DUPLICATE_SAME_ACCESS);\r
9805   if (! fSuccess) {\r
9806     return GetLastError();\r
9807   }\r
9808   CloseHandle(hChildStdinWr);\r
9809 \r
9810   /* Arrange to (1) look in dir for the child .exe file, and\r
9811    * (2) have dir be the child's working directory.  Interpret\r
9812    * dir relative to the directory WinBoard loaded from. */\r
9813   GetCurrentDirectory(MSG_SIZ, buf);\r
9814   SetCurrentDirectory(installDir);\r
9815   SetCurrentDirectory(dir);\r
9816 \r
9817   /* Now create the child process. */\r
9818 \r
9819   siStartInfo.cb = sizeof(STARTUPINFO);\r
9820   siStartInfo.lpReserved = NULL;\r
9821   siStartInfo.lpDesktop = NULL;\r
9822   siStartInfo.lpTitle = NULL;\r
9823   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9824   siStartInfo.cbReserved2 = 0;\r
9825   siStartInfo.lpReserved2 = NULL;\r
9826   siStartInfo.hStdInput = hChildStdinRd;\r
9827   siStartInfo.hStdOutput = hChildStdoutWr;\r
9828   siStartInfo.hStdError = hChildStdoutWr;\r
9829 \r
9830   fSuccess = CreateProcess(NULL,\r
9831                            cmdLine,        /* command line */\r
9832                            NULL,           /* process security attributes */\r
9833                            NULL,           /* primary thread security attrs */\r
9834                            TRUE,           /* handles are inherited */\r
9835                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9836                            NULL,           /* use parent's environment */\r
9837                            NULL,\r
9838                            &siStartInfo, /* STARTUPINFO pointer */\r
9839                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9840 \r
9841   err = GetLastError();\r
9842   SetCurrentDirectory(buf); /* return to prev directory */\r
9843   if (! fSuccess) {\r
9844     return err;\r
9845   }\r
9846 \r
9847   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9848     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9849     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9850   }\r
9851 \r
9852   /* Close the handles we don't need in the parent */\r
9853   CloseHandle(piProcInfo.hThread);\r
9854   CloseHandle(hChildStdinRd);\r
9855   CloseHandle(hChildStdoutWr);\r
9856 \r
9857   /* Prepare return value */\r
9858   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9859   cp->kind = CPReal;\r
9860   cp->hProcess = piProcInfo.hProcess;\r
9861   cp->pid = piProcInfo.dwProcessId;\r
9862   cp->hFrom = hChildStdoutRdDup;\r
9863   cp->hTo = hChildStdinWrDup;\r
9864 \r
9865   *pr = (void *) cp;\r
9866 \r
9867   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9868      2000 where engines sometimes don't see the initial command(s)\r
9869      from WinBoard and hang.  I don't understand how that can happen,\r
9870      but the Sleep is harmless, so I've put it in.  Others have also\r
9871      reported what may be the same problem, so hopefully this will fix\r
9872      it for them too.  */\r
9873   Sleep(500);\r
9874 \r
9875   return NO_ERROR;\r
9876 }\r
9877 \r
9878 \r
9879 void\r
9880 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9881 {\r
9882   ChildProc *cp; int result;\r
9883 \r
9884   cp = (ChildProc *) pr;\r
9885   if (cp == NULL) return;\r
9886 \r
9887   switch (cp->kind) {\r
9888   case CPReal:\r
9889     /* TerminateProcess is considered harmful, so... */\r
9890     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9891     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9892     /* The following doesn't work because the chess program\r
9893        doesn't "have the same console" as WinBoard.  Maybe\r
9894        we could arrange for this even though neither WinBoard\r
9895        nor the chess program uses a console for stdio? */\r
9896     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9897 \r
9898     /* [AS] Special termination modes for misbehaving programs... */\r
9899     if( signal == 9 ) { \r
9900         result = TerminateProcess( cp->hProcess, 0 );\r
9901 \r
9902         if ( appData.debugMode) {\r
9903             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9904         }\r
9905     }\r
9906     else if( signal == 10 ) {\r
9907         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9908 \r
9909         if( dw != WAIT_OBJECT_0 ) {\r
9910             result = TerminateProcess( cp->hProcess, 0 );\r
9911 \r
9912             if ( appData.debugMode) {\r
9913                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9914             }\r
9915 \r
9916         }\r
9917     }\r
9918 \r
9919     CloseHandle(cp->hProcess);\r
9920     break;\r
9921 \r
9922   case CPComm:\r
9923     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9924     break;\r
9925 \r
9926   case CPSock:\r
9927     closesocket(cp->sock);\r
9928     WSACleanup();\r
9929     break;\r
9930 \r
9931   case CPRcmd:\r
9932     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9933     closesocket(cp->sock);\r
9934     closesocket(cp->sock2);\r
9935     WSACleanup();\r
9936     break;\r
9937   }\r
9938   free(cp);\r
9939 }\r
9940 \r
9941 void\r
9942 InterruptChildProcess(ProcRef pr)\r
9943 {\r
9944   ChildProc *cp;\r
9945 \r
9946   cp = (ChildProc *) pr;\r
9947   if (cp == NULL) return;\r
9948   switch (cp->kind) {\r
9949   case CPReal:\r
9950     /* The following doesn't work because the chess program\r
9951        doesn't "have the same console" as WinBoard.  Maybe\r
9952        we could arrange for this even though neither WinBoard\r
9953        nor the chess program uses a console for stdio */\r
9954     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9955     break;\r
9956 \r
9957   case CPComm:\r
9958   case CPSock:\r
9959     /* Can't interrupt */\r
9960     break;\r
9961 \r
9962   case CPRcmd:\r
9963     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9964     break;\r
9965   }\r
9966 }\r
9967 \r
9968 \r
9969 int\r
9970 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9971 {\r
9972   char cmdLine[MSG_SIZ];\r
9973 \r
9974   if (port[0] == NULLCHAR) {\r
9975     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9976   } else {\r
9977     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9978   }\r
9979   return StartChildProcess(cmdLine, "", pr);\r
9980 }\r
9981 \r
9982 \r
9983 /* Code to open TCP sockets */\r
9984 \r
9985 int\r
9986 OpenTCP(char *host, char *port, ProcRef *pr)\r
9987 {\r
9988   ChildProc *cp;\r
9989   int err;\r
9990   SOCKET s;\r
9991   struct sockaddr_in sa, mysa;\r
9992   struct hostent FAR *hp;\r
9993   unsigned short uport;\r
9994   WORD wVersionRequested;\r
9995   WSADATA wsaData;\r
9996 \r
9997   /* Initialize socket DLL */\r
9998   wVersionRequested = MAKEWORD(1, 1);\r
9999   err = WSAStartup(wVersionRequested, &wsaData);\r
10000   if (err != 0) return err;\r
10001 \r
10002   /* Make socket */\r
10003   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10004     err = WSAGetLastError();\r
10005     WSACleanup();\r
10006     return err;\r
10007   }\r
10008 \r
10009   /* Bind local address using (mostly) don't-care values.\r
10010    */\r
10011   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10012   mysa.sin_family = AF_INET;\r
10013   mysa.sin_addr.s_addr = INADDR_ANY;\r
10014   uport = (unsigned short) 0;\r
10015   mysa.sin_port = htons(uport);\r
10016   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10017       == SOCKET_ERROR) {\r
10018     err = WSAGetLastError();\r
10019     WSACleanup();\r
10020     return err;\r
10021   }\r
10022 \r
10023   /* Resolve remote host name */\r
10024   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10025   if (!(hp = gethostbyname(host))) {\r
10026     unsigned int b0, b1, b2, b3;\r
10027 \r
10028     err = WSAGetLastError();\r
10029 \r
10030     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10031       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10032       hp->h_addrtype = AF_INET;\r
10033       hp->h_length = 4;\r
10034       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10035       hp->h_addr_list[0] = (char *) malloc(4);\r
10036       hp->h_addr_list[0][0] = (char) b0;\r
10037       hp->h_addr_list[0][1] = (char) b1;\r
10038       hp->h_addr_list[0][2] = (char) b2;\r
10039       hp->h_addr_list[0][3] = (char) b3;\r
10040     } else {\r
10041       WSACleanup();\r
10042       return err;\r
10043     }\r
10044   }\r
10045   sa.sin_family = hp->h_addrtype;\r
10046   uport = (unsigned short) atoi(port);\r
10047   sa.sin_port = htons(uport);\r
10048   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10049 \r
10050   /* Make connection */\r
10051   if (connect(s, (struct sockaddr *) &sa,\r
10052               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10053     err = WSAGetLastError();\r
10054     WSACleanup();\r
10055     return err;\r
10056   }\r
10057 \r
10058   /* Prepare return value */\r
10059   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10060   cp->kind = CPSock;\r
10061   cp->sock = s;\r
10062   *pr = (ProcRef *) cp;\r
10063 \r
10064   return NO_ERROR;\r
10065 }\r
10066 \r
10067 int\r
10068 OpenCommPort(char *name, ProcRef *pr)\r
10069 {\r
10070   HANDLE h;\r
10071   COMMTIMEOUTS ct;\r
10072   ChildProc *cp;\r
10073   char fullname[MSG_SIZ];\r
10074 \r
10075   if (*name != '\\')\r
10076     sprintf(fullname, "\\\\.\\%s", name);\r
10077   else\r
10078     strcpy(fullname, name);\r
10079 \r
10080   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10081                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10082   if (h == (HANDLE) -1) {\r
10083     return GetLastError();\r
10084   }\r
10085   hCommPort = h;\r
10086 \r
10087   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10088 \r
10089   /* Accumulate characters until a 100ms pause, then parse */\r
10090   ct.ReadIntervalTimeout = 100;\r
10091   ct.ReadTotalTimeoutMultiplier = 0;\r
10092   ct.ReadTotalTimeoutConstant = 0;\r
10093   ct.WriteTotalTimeoutMultiplier = 0;\r
10094   ct.WriteTotalTimeoutConstant = 0;\r
10095   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10096 \r
10097   /* Prepare return value */\r
10098   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10099   cp->kind = CPComm;\r
10100   cp->hFrom = h;\r
10101   cp->hTo = h;\r
10102   *pr = (ProcRef *) cp;\r
10103 \r
10104   return NO_ERROR;\r
10105 }\r
10106 \r
10107 int\r
10108 OpenLoopback(ProcRef *pr)\r
10109 {\r
10110   DisplayFatalError("Not implemented", 0, 1);\r
10111   return NO_ERROR;\r
10112 }\r
10113 \r
10114 \r
10115 int\r
10116 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10117 {\r
10118   ChildProc *cp;\r
10119   int err;\r
10120   SOCKET s, s2, s3;\r
10121   struct sockaddr_in sa, mysa;\r
10122   struct hostent FAR *hp;\r
10123   unsigned short uport;\r
10124   WORD wVersionRequested;\r
10125   WSADATA wsaData;\r
10126   int fromPort;\r
10127   char stderrPortStr[MSG_SIZ];\r
10128 \r
10129   /* Initialize socket DLL */\r
10130   wVersionRequested = MAKEWORD(1, 1);\r
10131   err = WSAStartup(wVersionRequested, &wsaData);\r
10132   if (err != 0) return err;\r
10133 \r
10134   /* Resolve remote host name */\r
10135   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10136   if (!(hp = gethostbyname(host))) {\r
10137     unsigned int b0, b1, b2, b3;\r
10138 \r
10139     err = WSAGetLastError();\r
10140 \r
10141     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10142       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10143       hp->h_addrtype = AF_INET;\r
10144       hp->h_length = 4;\r
10145       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10146       hp->h_addr_list[0] = (char *) malloc(4);\r
10147       hp->h_addr_list[0][0] = (char) b0;\r
10148       hp->h_addr_list[0][1] = (char) b1;\r
10149       hp->h_addr_list[0][2] = (char) b2;\r
10150       hp->h_addr_list[0][3] = (char) b3;\r
10151     } else {\r
10152       WSACleanup();\r
10153       return err;\r
10154     }\r
10155   }\r
10156   sa.sin_family = hp->h_addrtype;\r
10157   uport = (unsigned short) 514;\r
10158   sa.sin_port = htons(uport);\r
10159   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10160 \r
10161   /* Bind local socket to unused "privileged" port address\r
10162    */\r
10163   s = INVALID_SOCKET;\r
10164   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10165   mysa.sin_family = AF_INET;\r
10166   mysa.sin_addr.s_addr = INADDR_ANY;\r
10167   for (fromPort = 1023;; fromPort--) {\r
10168     if (fromPort < 0) {\r
10169       WSACleanup();\r
10170       return WSAEADDRINUSE;\r
10171     }\r
10172     if (s == INVALID_SOCKET) {\r
10173       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10174         err = WSAGetLastError();\r
10175         WSACleanup();\r
10176         return err;\r
10177       }\r
10178     }\r
10179     uport = (unsigned short) fromPort;\r
10180     mysa.sin_port = htons(uport);\r
10181     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10182         == SOCKET_ERROR) {\r
10183       err = WSAGetLastError();\r
10184       if (err == WSAEADDRINUSE) continue;\r
10185       WSACleanup();\r
10186       return err;\r
10187     }\r
10188     if (connect(s, (struct sockaddr *) &sa,\r
10189       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10190       err = WSAGetLastError();\r
10191       if (err == WSAEADDRINUSE) {\r
10192         closesocket(s);\r
10193         s = -1;\r
10194         continue;\r
10195       }\r
10196       WSACleanup();\r
10197       return err;\r
10198     }\r
10199     break;\r
10200   }\r
10201 \r
10202   /* Bind stderr local socket to unused "privileged" port address\r
10203    */\r
10204   s2 = INVALID_SOCKET;\r
10205   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10206   mysa.sin_family = AF_INET;\r
10207   mysa.sin_addr.s_addr = INADDR_ANY;\r
10208   for (fromPort = 1023;; fromPort--) {\r
10209     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10210     if (fromPort < 0) {\r
10211       (void) closesocket(s);\r
10212       WSACleanup();\r
10213       return WSAEADDRINUSE;\r
10214     }\r
10215     if (s2 == INVALID_SOCKET) {\r
10216       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10217         err = WSAGetLastError();\r
10218         closesocket(s);\r
10219         WSACleanup();\r
10220         return err;\r
10221       }\r
10222     }\r
10223     uport = (unsigned short) fromPort;\r
10224     mysa.sin_port = htons(uport);\r
10225     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10226         == SOCKET_ERROR) {\r
10227       err = WSAGetLastError();\r
10228       if (err == WSAEADDRINUSE) continue;\r
10229       (void) closesocket(s);\r
10230       WSACleanup();\r
10231       return err;\r
10232     }\r
10233     if (listen(s2, 1) == SOCKET_ERROR) {\r
10234       err = WSAGetLastError();\r
10235       if (err == WSAEADDRINUSE) {\r
10236         closesocket(s2);\r
10237         s2 = INVALID_SOCKET;\r
10238         continue;\r
10239       }\r
10240       (void) closesocket(s);\r
10241       (void) closesocket(s2);\r
10242       WSACleanup();\r
10243       return err;\r
10244     }\r
10245     break;\r
10246   }\r
10247   prevStderrPort = fromPort; // remember port used\r
10248   sprintf(stderrPortStr, "%d", fromPort);\r
10249 \r
10250   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10251     err = WSAGetLastError();\r
10252     (void) closesocket(s);\r
10253     (void) closesocket(s2);\r
10254     WSACleanup();\r
10255     return err;\r
10256   }\r
10257 \r
10258   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10259     err = WSAGetLastError();\r
10260     (void) closesocket(s);\r
10261     (void) closesocket(s2);\r
10262     WSACleanup();\r
10263     return err;\r
10264   }\r
10265   if (*user == NULLCHAR) user = UserName();\r
10266   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10267     err = WSAGetLastError();\r
10268     (void) closesocket(s);\r
10269     (void) closesocket(s2);\r
10270     WSACleanup();\r
10271     return err;\r
10272   }\r
10273   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10274     err = WSAGetLastError();\r
10275     (void) closesocket(s);\r
10276     (void) closesocket(s2);\r
10277     WSACleanup();\r
10278     return err;\r
10279   }\r
10280 \r
10281   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10282     err = WSAGetLastError();\r
10283     (void) closesocket(s);\r
10284     (void) closesocket(s2);\r
10285     WSACleanup();\r
10286     return err;\r
10287   }\r
10288   (void) closesocket(s2);  /* Stop listening */\r
10289 \r
10290   /* Prepare return value */\r
10291   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10292   cp->kind = CPRcmd;\r
10293   cp->sock = s;\r
10294   cp->sock2 = s3;\r
10295   *pr = (ProcRef *) cp;\r
10296 \r
10297   return NO_ERROR;\r
10298 }\r
10299 \r
10300 \r
10301 InputSourceRef\r
10302 AddInputSource(ProcRef pr, int lineByLine,\r
10303                InputCallback func, VOIDSTAR closure)\r
10304 {\r
10305   InputSource *is, *is2 = NULL;\r
10306   ChildProc *cp = (ChildProc *) pr;\r
10307 \r
10308   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10309   is->lineByLine = lineByLine;\r
10310   is->func = func;\r
10311   is->closure = closure;\r
10312   is->second = NULL;\r
10313   is->next = is->buf;\r
10314   if (pr == NoProc) {\r
10315     is->kind = CPReal;\r
10316     consoleInputSource = is;\r
10317   } else {\r
10318     is->kind = cp->kind;\r
10319     /* \r
10320         [AS] Try to avoid a race condition if the thread is given control too early:\r
10321         we create all threads suspended so that the is->hThread variable can be\r
10322         safely assigned, then let the threads start with ResumeThread.\r
10323     */\r
10324     switch (cp->kind) {\r
10325     case CPReal:\r
10326       is->hFile = cp->hFrom;\r
10327       cp->hFrom = NULL; /* now owned by InputThread */\r
10328       is->hThread =\r
10329         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10330                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10331       break;\r
10332 \r
10333     case CPComm:\r
10334       is->hFile = cp->hFrom;\r
10335       cp->hFrom = NULL; /* now owned by InputThread */\r
10336       is->hThread =\r
10337         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10338                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10339       break;\r
10340 \r
10341     case CPSock:\r
10342       is->sock = cp->sock;\r
10343       is->hThread =\r
10344         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10345                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10346       break;\r
10347 \r
10348     case CPRcmd:\r
10349       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10350       *is2 = *is;\r
10351       is->sock = cp->sock;\r
10352       is->second = is2;\r
10353       is2->sock = cp->sock2;\r
10354       is2->second = is2;\r
10355       is->hThread =\r
10356         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10357                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10358       is2->hThread =\r
10359         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10360                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10361       break;\r
10362     }\r
10363 \r
10364     if( is->hThread != NULL ) {\r
10365         ResumeThread( is->hThread );\r
10366     }\r
10367 \r
10368     if( is2 != NULL && is2->hThread != NULL ) {\r
10369         ResumeThread( is2->hThread );\r
10370     }\r
10371   }\r
10372 \r
10373   return (InputSourceRef) is;\r
10374 }\r
10375 \r
10376 void\r
10377 RemoveInputSource(InputSourceRef isr)\r
10378 {\r
10379   InputSource *is;\r
10380 \r
10381   is = (InputSource *) isr;\r
10382   is->hThread = NULL;  /* tell thread to stop */\r
10383   CloseHandle(is->hThread);\r
10384   if (is->second != NULL) {\r
10385     is->second->hThread = NULL;\r
10386     CloseHandle(is->second->hThread);\r
10387   }\r
10388 }\r
10389 \r
10390 \r
10391 int\r
10392 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10393 {\r
10394   DWORD dOutCount;\r
10395   int outCount = SOCKET_ERROR;\r
10396   ChildProc *cp = (ChildProc *) pr;\r
10397   static OVERLAPPED ovl;\r
10398 \r
10399   if (pr == NoProc) {\r
10400     ConsoleOutput(message, count, FALSE);\r
10401     return count;\r
10402   } \r
10403 \r
10404   if (ovl.hEvent == NULL) {\r
10405     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10406   }\r
10407   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10408 \r
10409   switch (cp->kind) {\r
10410   case CPSock:\r
10411   case CPRcmd:\r
10412     outCount = send(cp->sock, message, count, 0);\r
10413     if (outCount == SOCKET_ERROR) {\r
10414       *outError = WSAGetLastError();\r
10415     } else {\r
10416       *outError = NO_ERROR;\r
10417     }\r
10418     break;\r
10419 \r
10420   case CPReal:\r
10421     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10422                   &dOutCount, NULL)) {\r
10423       *outError = NO_ERROR;\r
10424       outCount = (int) dOutCount;\r
10425     } else {\r
10426       *outError = GetLastError();\r
10427     }\r
10428     break;\r
10429 \r
10430   case CPComm:\r
10431     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10432                             &dOutCount, &ovl);\r
10433     if (*outError == NO_ERROR) {\r
10434       outCount = (int) dOutCount;\r
10435     }\r
10436     break;\r
10437   }\r
10438   return outCount;\r
10439 }\r
10440 \r
10441 int\r
10442 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10443                        long msdelay)\r
10444 {\r
10445   /* Ignore delay, not implemented for WinBoard */\r
10446   return OutputToProcess(pr, message, count, outError);\r
10447 }\r
10448 \r
10449 \r
10450 void\r
10451 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10452                         char *buf, int count, int error)\r
10453 {\r
10454   DisplayFatalError("Not implemented", 0, 1);\r
10455 }\r
10456 \r
10457 /* see wgamelist.c for Game List functions */\r
10458 /* see wedittags.c for Edit Tags functions */\r
10459 \r
10460 \r
10461 VOID\r
10462 ICSInitScript()\r
10463 {\r
10464   FILE *f;\r
10465   char buf[MSG_SIZ];\r
10466   char *dummy;\r
10467 \r
10468   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10469     f = fopen(buf, "r");\r
10470     if (f != NULL) {\r
10471       ProcessICSInitScript(f);\r
10472       fclose(f);\r
10473     }\r
10474   }\r
10475 }\r
10476 \r
10477 \r
10478 VOID\r
10479 StartAnalysisClock()\r
10480 {\r
10481   if (analysisTimerEvent) return;\r
10482   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10483                                         (UINT) 2000, NULL);\r
10484 }\r
10485 \r
10486 LRESULT CALLBACK\r
10487 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10488 {\r
10489   static HANDLE hwndText;\r
10490   RECT rect;\r
10491   static int sizeX, sizeY;\r
10492   int newSizeX, newSizeY, flags;\r
10493   MINMAXINFO *mmi;\r
10494 \r
10495   switch (message) {\r
10496   case WM_INITDIALOG: /* message: initialize dialog box */\r
10497     /* Initialize the dialog items */\r
10498     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10499     SetWindowText(hDlg, analysisTitle);\r
10500     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10501     /* Size and position the dialog */\r
10502     if (!analysisDialog) {\r
10503       analysisDialog = hDlg;\r
10504       flags = SWP_NOZORDER;\r
10505       GetClientRect(hDlg, &rect);\r
10506       sizeX = rect.right;\r
10507       sizeY = rect.bottom;\r
10508       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10509           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10510         WINDOWPLACEMENT wp;\r
10511         EnsureOnScreen(&analysisX, &analysisY);\r
10512         wp.length = sizeof(WINDOWPLACEMENT);\r
10513         wp.flags = 0;\r
10514         wp.showCmd = SW_SHOW;\r
10515         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10516         wp.rcNormalPosition.left = analysisX;\r
10517         wp.rcNormalPosition.right = analysisX + analysisW;\r
10518         wp.rcNormalPosition.top = analysisY;\r
10519         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10520         SetWindowPlacement(hDlg, &wp);\r
10521 \r
10522         GetClientRect(hDlg, &rect);\r
10523         newSizeX = rect.right;\r
10524         newSizeY = rect.bottom;\r
10525         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10526                               newSizeX, newSizeY);\r
10527         sizeX = newSizeX;\r
10528         sizeY = newSizeY;\r
10529       }\r
10530     }\r
10531     return FALSE;\r
10532 \r
10533   case WM_COMMAND: /* message: received a command */\r
10534     switch (LOWORD(wParam)) {\r
10535     case IDCANCEL:\r
10536       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10537           ExitAnalyzeMode();\r
10538           ModeHighlight();\r
10539           return TRUE;\r
10540       }\r
10541       EditGameEvent();\r
10542       return TRUE;\r
10543     default:\r
10544       break;\r
10545     }\r
10546     break;\r
10547 \r
10548   case WM_SIZE:\r
10549     newSizeX = LOWORD(lParam);\r
10550     newSizeY = HIWORD(lParam);\r
10551     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10552     sizeX = newSizeX;\r
10553     sizeY = newSizeY;\r
10554     break;\r
10555 \r
10556   case WM_GETMINMAXINFO:\r
10557     /* Prevent resizing window too small */\r
10558     mmi = (MINMAXINFO *) lParam;\r
10559     mmi->ptMinTrackSize.x = 100;\r
10560     mmi->ptMinTrackSize.y = 100;\r
10561     break;\r
10562   }\r
10563   return FALSE;\r
10564 }\r
10565 \r
10566 VOID\r
10567 AnalysisPopUp(char* title, char* str)\r
10568 {\r
10569   FARPROC lpProc;\r
10570   char *p, *q;\r
10571 \r
10572   /* [AS] */\r
10573   EngineOutputPopUp();\r
10574   return;\r
10575 \r
10576   if (str == NULL) str = "";\r
10577   p = (char *) malloc(2 * strlen(str) + 2);\r
10578   q = p;\r
10579   while (*str) {\r
10580     if (*str == '\n') *q++ = '\r';\r
10581     *q++ = *str++;\r
10582   }\r
10583   *q = NULLCHAR;\r
10584   if (analysisText != NULL) free(analysisText);\r
10585   analysisText = p;\r
10586 \r
10587   if (analysisDialog) {\r
10588     SetWindowText(analysisDialog, title);\r
10589     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10590     ShowWindow(analysisDialog, SW_SHOW);\r
10591   } else {\r
10592     analysisTitle = title;\r
10593     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10594     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10595                  hwndMain, (DLGPROC)lpProc);\r
10596     FreeProcInstance(lpProc);\r
10597   }\r
10598   analysisDialogUp = TRUE;  \r
10599 }\r
10600 \r
10601 VOID\r
10602 AnalysisPopDown()\r
10603 {\r
10604   if (analysisDialog) {\r
10605     ShowWindow(analysisDialog, SW_HIDE);\r
10606   }\r
10607   analysisDialogUp = FALSE;  \r
10608 }\r
10609 \r
10610 \r
10611 VOID\r
10612 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10613 {\r
10614   highlightInfo.sq[0].x = fromX;\r
10615   highlightInfo.sq[0].y = fromY;\r
10616   highlightInfo.sq[1].x = toX;\r
10617   highlightInfo.sq[1].y = toY;\r
10618 }\r
10619 \r
10620 VOID\r
10621 ClearHighlights()\r
10622 {\r
10623   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10624     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10625 }\r
10626 \r
10627 VOID\r
10628 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10629 {\r
10630   premoveHighlightInfo.sq[0].x = fromX;\r
10631   premoveHighlightInfo.sq[0].y = fromY;\r
10632   premoveHighlightInfo.sq[1].x = toX;\r
10633   premoveHighlightInfo.sq[1].y = toY;\r
10634 }\r
10635 \r
10636 VOID\r
10637 ClearPremoveHighlights()\r
10638 {\r
10639   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10640     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10641 }\r
10642 \r
10643 VOID\r
10644 ShutDownFrontEnd()\r
10645 {\r
10646   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10647   DeleteClipboardTempFiles();\r
10648 }\r
10649 \r
10650 void\r
10651 BoardToTop()\r
10652 {\r
10653     if (IsIconic(hwndMain))\r
10654       ShowWindow(hwndMain, SW_RESTORE);\r
10655 \r
10656     SetActiveWindow(hwndMain);\r
10657 }\r
10658 \r
10659 /*\r
10660  * Prototypes for animation support routines\r
10661  */\r
10662 static void ScreenSquare(int column, int row, POINT * pt);\r
10663 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10664      POINT frames[], int * nFrames);\r
10665 \r
10666 \r
10667 void\r
10668 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10669 {       // [HGM] atomic: animate blast wave\r
10670         int i;\r
10671 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10672         explodeInfo.fromX = fromX;\r
10673         explodeInfo.fromY = fromY;\r
10674         explodeInfo.toX = toX;\r
10675         explodeInfo.toY = toY;\r
10676         for(i=1; i<nFrames; i++) {\r
10677             explodeInfo.radius = (i*180)/(nFrames-1);\r
10678             DrawPosition(FALSE, NULL);\r
10679             Sleep(appData.animSpeed);\r
10680         }\r
10681         explodeInfo.radius = 0;\r
10682         DrawPosition(TRUE, NULL);\r
10683 }\r
10684 \r
10685 #define kFactor 4\r
10686 \r
10687 void\r
10688 AnimateMove(board, fromX, fromY, toX, toY)\r
10689      Board board;\r
10690      int fromX;\r
10691      int fromY;\r
10692      int toX;\r
10693      int toY;\r
10694 {\r
10695   ChessSquare piece;\r
10696   POINT start, finish, mid;\r
10697   POINT frames[kFactor * 2 + 1];\r
10698   int nFrames, n;\r
10699 \r
10700   if (!appData.animate) return;\r
10701   if (doingSizing) return;\r
10702   if (fromY < 0 || fromX < 0) return;\r
10703   piece = board[fromY][fromX];\r
10704   if (piece >= EmptySquare) return;\r
10705 \r
10706   ScreenSquare(fromX, fromY, &start);\r
10707   ScreenSquare(toX, toY, &finish);\r
10708 \r
10709   /* All pieces except knights move in straight line */\r
10710   if (piece != WhiteKnight && piece != BlackKnight) {\r
10711     mid.x = start.x + (finish.x - start.x) / 2;\r
10712     mid.y = start.y + (finish.y - start.y) / 2;\r
10713   } else {\r
10714     /* Knight: make diagonal movement then straight */\r
10715     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10716        mid.x = start.x + (finish.x - start.x) / 2;\r
10717        mid.y = finish.y;\r
10718      } else {\r
10719        mid.x = finish.x;\r
10720        mid.y = start.y + (finish.y - start.y) / 2;\r
10721      }\r
10722   }\r
10723   \r
10724   /* Don't use as many frames for very short moves */\r
10725   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10726     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10727   else\r
10728     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10729 \r
10730   animInfo.from.x = fromX;\r
10731   animInfo.from.y = fromY;\r
10732   animInfo.to.x = toX;\r
10733   animInfo.to.y = toY;\r
10734   animInfo.lastpos = start;\r
10735   animInfo.piece = piece;\r
10736   for (n = 0; n < nFrames; n++) {\r
10737     animInfo.pos = frames[n];\r
10738     DrawPosition(FALSE, NULL);\r
10739     animInfo.lastpos = animInfo.pos;\r
10740     Sleep(appData.animSpeed);\r
10741   }\r
10742   animInfo.pos = finish;\r
10743   DrawPosition(FALSE, NULL);\r
10744   animInfo.piece = EmptySquare;\r
10745   if(gameInfo.variant == VariantAtomic && \r
10746      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10747         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10748 }\r
10749 \r
10750 /*      Convert board position to corner of screen rect and color       */\r
10751 \r
10752 static void\r
10753 ScreenSquare(column, row, pt)\r
10754      int column; int row; POINT * pt;\r
10755 {\r
10756   if (flipView) {\r
10757     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10758     pt->y = lineGap + row * (squareSize + lineGap);\r
10759   } else {\r
10760     pt->x = lineGap + column * (squareSize + lineGap);\r
10761     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10762   }\r
10763 }\r
10764 \r
10765 /*      Generate a series of frame coords from start->mid->finish.\r
10766         The movement rate doubles until the half way point is\r
10767         reached, then halves back down to the final destination,\r
10768         which gives a nice slow in/out effect. The algorithmn\r
10769         may seem to generate too many intermediates for short\r
10770         moves, but remember that the purpose is to attract the\r
10771         viewers attention to the piece about to be moved and\r
10772         then to where it ends up. Too few frames would be less\r
10773         noticeable.                                             */\r
10774 \r
10775 static void\r
10776 Tween(start, mid, finish, factor, frames, nFrames)\r
10777      POINT * start; POINT * mid;\r
10778      POINT * finish; int factor;\r
10779      POINT frames[]; int * nFrames;\r
10780 {\r
10781   int n, fraction = 1, count = 0;\r
10782 \r
10783   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10784   for (n = 0; n < factor; n++)\r
10785     fraction *= 2;\r
10786   for (n = 0; n < factor; n++) {\r
10787     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10788     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10789     count ++;\r
10790     fraction = fraction / 2;\r
10791   }\r
10792   \r
10793   /* Midpoint */\r
10794   frames[count] = *mid;\r
10795   count ++;\r
10796   \r
10797   /* Slow out, stepping 1/2, then 1/4, ... */\r
10798   fraction = 2;\r
10799   for (n = 0; n < factor; n++) {\r
10800     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10801     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10802     count ++;\r
10803     fraction = fraction * 2;\r
10804   }\r
10805   *nFrames = count;\r
10806 }\r
10807 \r
10808 void\r
10809 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10810 {\r
10811 #if 0\r
10812     char buf[256];\r
10813 \r
10814     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10815         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10816 \r
10817     OutputDebugString( buf );\r
10818 #endif\r
10819 \r
10820     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10821 \r
10822     EvalGraphSet( first, last, current, pvInfoList );\r
10823 }\r
10824 \r
10825 void SetProgramStats( FrontEndProgramStats * stats )\r
10826 {\r
10827 #if 0\r
10828     char buf[1024];\r
10829 \r
10830     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10831         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10832 \r
10833     OutputDebugString( buf );\r
10834 #endif\r
10835 \r
10836     EngineOutputUpdate( stats );\r
10837 }\r