patches a bug in chosing the initial board size on a virgin startup I introduced...
[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;\r
156 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
157 static int squareSize, lineGap, minorSize;\r
158 static int winWidth, winHeight, winW, winH;\r
159 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
160 static int logoHeight = 0;\r
161 static char messageText[MESSAGE_TEXT_MAX];\r
162 static int clockTimerEvent = 0;\r
163 static int loadGameTimerEvent = 0;\r
164 static int analysisTimerEvent = 0;\r
165 static DelayedEventCallback delayedTimerCallback;\r
166 static int delayedTimerEvent = 0;\r
167 static int buttonCount = 2;\r
168 char *icsTextMenuString;\r
169 char *icsNames;\r
170 char *firstChessProgramNames;\r
171 char *secondChessProgramNames;\r
172 \r
173 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
174 \r
175 #define PALETTESIZE 256\r
176 \r
177 HINSTANCE hInst;          /* current instance */\r
178 HWND hwndMain = NULL;        /* root window*/\r
179 HWND hwndConsole = NULL;\r
180 BOOLEAN alwaysOnTop = FALSE;\r
181 RECT boardRect;\r
182 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
183   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
184 HPALETTE hPal;\r
185 ColorClass currentColorClass;\r
186 \r
187 HWND hCommPort = NULL;    /* currently open comm port */\r
188 static HWND hwndPause;    /* pause button */\r
189 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
190 static HBRUSH lightSquareBrush, darkSquareBrush,\r
191   blackSquareBrush, /* [HGM] for band between board and holdings */\r
192   explodeBrush,     /* [HGM] atomic */\r
193   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
194 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
195 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
196 static HPEN gridPen = NULL;\r
197 static HPEN highlightPen = NULL;\r
198 static HPEN premovePen = NULL;\r
199 static NPLOGPALETTE pLogPal;\r
200 static BOOL paletteChanged = FALSE;\r
201 static HICON iconWhite, iconBlack, iconCurrent;\r
202 static int doingSizing = FALSE;\r
203 static int lastSizing = 0;\r
204 static int prevStderrPort;\r
205 static HBITMAP userLogo;\r
206 \r
207 /* [AS] Support for background textures */\r
208 #define BACK_TEXTURE_MODE_DISABLED      0\r
209 #define BACK_TEXTURE_MODE_PLAIN         1\r
210 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
211 \r
212 static HBITMAP liteBackTexture = NULL;\r
213 static HBITMAP darkBackTexture = NULL;\r
214 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int backTextureSquareSize = 0;\r
217 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
218 \r
219 #if __GNUC__ && !defined(_winmajor)\r
220 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
221 #else\r
222 #define oldDialog (_winmajor < 4)\r
223 #endif\r
224 \r
225 char *defaultTextAttribs[] = \r
226 {\r
227   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
228   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
229   COLOR_NONE\r
230 };\r
231 \r
232 typedef struct {\r
233   char *name;\r
234   int squareSize;\r
235   int lineGap;\r
236   int smallLayout;\r
237   int tinyLayout;\r
238   int cliWidth, cliHeight;\r
239 } SizeInfo;\r
240 \r
241 SizeInfo sizeInfo[] = \r
242 {\r
243   { "tiny",     21, 0, 1, 1, 0, 0 },\r
244   { "teeny",    25, 1, 1, 1, 0, 0 },\r
245   { "dinky",    29, 1, 1, 1, 0, 0 },\r
246   { "petite",   33, 1, 1, 1, 0, 0 },\r
247   { "slim",     37, 2, 1, 0, 0, 0 },\r
248   { "small",    40, 2, 1, 0, 0, 0 },\r
249   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
250   { "middling", 49, 2, 0, 0, 0, 0 },\r
251   { "average",  54, 2, 0, 0, 0, 0 },\r
252   { "moderate", 58, 3, 0, 0, 0, 0 },\r
253   { "medium",   64, 3, 0, 0, 0, 0 },\r
254   { "bulky",    72, 3, 0, 0, 0, 0 },\r
255   { "large",    80, 3, 0, 0, 0, 0 },\r
256   { "big",      87, 3, 0, 0, 0, 0 },\r
257   { "huge",     95, 3, 0, 0, 0, 0 },\r
258   { "giant",    108, 3, 0, 0, 0, 0 },\r
259   { "colossal", 116, 4, 0, 0, 0, 0 },\r
260   { "titanic",  129, 4, 0, 0, 0, 0 },\r
261   { NULL, 0, 0, 0, 0, 0, 0 }\r
262 };\r
263 \r
264 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
265 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
266 {\r
267   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
268   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
269   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
270   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
283   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
284   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
285 };\r
286 \r
287 MyFont *font[NUM_SIZES][NUM_FONTS];\r
288 \r
289 typedef struct {\r
290   char *label;\r
291   int id;\r
292   HWND hwnd;\r
293   WNDPROC wndproc;\r
294 } MyButtonDesc;\r
295 \r
296 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
297 #define N_BUTTONS 5\r
298 \r
299 MyButtonDesc buttonDesc[N_BUTTONS] =\r
300 {\r
301   {"<<", IDM_ToStart, NULL, NULL},\r
302   {"<", IDM_Backward, NULL, NULL},\r
303   {"P", IDM_Pause, NULL, NULL},\r
304   {">", IDM_Forward, NULL, NULL},\r
305   {">>", IDM_ToEnd, NULL, NULL},\r
306 };\r
307 \r
308 int tinyLayout = 0, smallLayout = 0;\r
309 #define MENU_BAR_ITEMS 6\r
310 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
311   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
312   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
313 };\r
314 \r
315 \r
316 MySound sounds[(int)NSoundClasses];\r
317 MyTextAttribs textAttribs[(int)NColorClasses];\r
318 \r
319 MyColorizeAttribs colorizeAttribs[] = {\r
320   { (COLORREF)0, 0, "Shout Text" },\r
321   { (COLORREF)0, 0, "SShout/CShout" },\r
322   { (COLORREF)0, 0, "Channel 1 Text" },\r
323   { (COLORREF)0, 0, "Channel Text" },\r
324   { (COLORREF)0, 0, "Kibitz Text" },\r
325   { (COLORREF)0, 0, "Tell Text" },\r
326   { (COLORREF)0, 0, "Challenge Text" },\r
327   { (COLORREF)0, 0, "Request Text" },\r
328   { (COLORREF)0, 0, "Seek Text" },\r
329   { (COLORREF)0, 0, "Normal Text" },\r
330   { (COLORREF)0, 0, "None" }\r
331 };\r
332 \r
333 \r
334 \r
335 static char *commentTitle;\r
336 static char *commentText;\r
337 static int commentIndex;\r
338 static Boolean editComment = FALSE;\r
339 HWND commentDialog = NULL;\r
340 BOOLEAN commentDialogUp = FALSE;\r
341 static int commentX, commentY, commentH, commentW;\r
342 \r
343 static char *analysisTitle;\r
344 static char *analysisText;\r
345 HWND analysisDialog = NULL;\r
346 BOOLEAN analysisDialogUp = FALSE;\r
347 static int analysisX, analysisY, analysisH, analysisW;\r
348 \r
349 char errorTitle[MSG_SIZ];\r
350 char errorMessage[2*MSG_SIZ];\r
351 HWND errorDialog = NULL;\r
352 BOOLEAN moveErrorMessageUp = FALSE;\r
353 BOOLEAN consoleEcho = TRUE;\r
354 CHARFORMAT consoleCF;\r
355 COLORREF consoleBackgroundColor;\r
356 \r
357 char *programVersion;\r
358 \r
359 #define CPReal 1\r
360 #define CPComm 2\r
361 #define CPSock 3\r
362 #define CPRcmd 4\r
363 typedef int CPKind;\r
364 \r
365 typedef struct {\r
366   CPKind kind;\r
367   HANDLE hProcess;\r
368   DWORD pid;\r
369   HANDLE hTo;\r
370   HANDLE hFrom;\r
371   SOCKET sock;\r
372   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
373 } ChildProc;\r
374 \r
375 #define INPUT_SOURCE_BUF_SIZE 4096\r
376 \r
377 typedef struct _InputSource {\r
378   CPKind kind;\r
379   HANDLE hFile;\r
380   SOCKET sock;\r
381   int lineByLine;\r
382   HANDLE hThread;\r
383   DWORD id;\r
384   char buf[INPUT_SOURCE_BUF_SIZE];\r
385   char *next;\r
386   DWORD count;\r
387   int error;\r
388   InputCallback func;\r
389   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
390   VOIDSTAR closure;\r
391 } InputSource;\r
392 \r
393 InputSource *consoleInputSource;\r
394 \r
395 DCB dcb;\r
396 \r
397 /* forward */\r
398 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
399 VOID ConsoleCreate();\r
400 LRESULT CALLBACK\r
401   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
402 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
403 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
404 VOID ParseCommSettings(char *arg, DCB *dcb);\r
405 LRESULT CALLBACK\r
406   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
407 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
408 void ParseIcsTextMenu(char *icsTextMenuString);\r
409 VOID PopUpMoveDialog(char firstchar);\r
410 VOID PopUpNameDialog(char firstchar);\r
411 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
412 \r
413 /* [AS] */\r
414 int NewGameFRC();\r
415 int GameListOptions();\r
416 \r
417 HWND moveHistoryDialog = NULL;\r
418 BOOLEAN moveHistoryDialogUp = FALSE;\r
419 \r
420 WindowPlacement wpMoveHistory;\r
421 \r
422 HWND evalGraphDialog = NULL;\r
423 BOOLEAN evalGraphDialogUp = FALSE;\r
424 \r
425 WindowPlacement wpEvalGraph;\r
426 \r
427 HWND engineOutputDialog = NULL;\r
428 BOOLEAN engineOutputDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpEngineOutput;\r
431 WindowPlacement wpGameList;\r
432 WindowPlacement wpConsole;\r
433 \r
434 VOID MoveHistoryPopUp();\r
435 VOID MoveHistoryPopDown();\r
436 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
437 BOOL MoveHistoryIsUp();\r
438 \r
439 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 VOID EvalGraphPopUp();\r
441 VOID EvalGraphPopDown();\r
442 BOOL EvalGraphIsUp();\r
443 \r
444 VOID EngineOutputPopUp();\r
445 VOID EngineOutputPopDown();\r
446 BOOL EngineOutputIsUp();\r
447 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
448 \r
449 VOID GothicPopUp(char *title, VariantClass variant);\r
450 /*\r
451  * Setting "frozen" should disable all user input other than deleting\r
452  * the window.  We do this while engines are initializing themselves.\r
453  */\r
454 static int frozen = 0;\r
455 static int oldMenuItemState[MENU_BAR_ITEMS];\r
456 void FreezeUI()\r
457 {\r
458   HMENU hmenu;\r
459   int i;\r
460 \r
461   if (frozen) return;\r
462   frozen = 1;\r
463   hmenu = GetMenu(hwndMain);\r
464   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
465     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
466   }\r
467   DrawMenuBar(hwndMain);\r
468 }\r
469 \r
470 /* Undo a FreezeUI */\r
471 void ThawUI()\r
472 {\r
473   HMENU hmenu;\r
474   int i;\r
475 \r
476   if (!frozen) return;\r
477   frozen = 0;\r
478   hmenu = GetMenu(hwndMain);\r
479   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
480     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
481   }\r
482   DrawMenuBar(hwndMain);\r
483 }\r
484 \r
485 /*---------------------------------------------------------------------------*\\r
486  *\r
487  * WinMain\r
488  *\r
489 \*---------------------------------------------------------------------------*/\r
490 \r
491 int APIENTRY\r
492 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
493         LPSTR lpCmdLine, int nCmdShow)\r
494 {\r
495   MSG msg;\r
496   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
497 //  INITCOMMONCONTROLSEX ex;\r
498 \r
499   debugFP = stderr;\r
500 \r
501   LoadLibrary("RICHED32.DLL");\r
502   consoleCF.cbSize = sizeof(CHARFORMAT);\r
503 \r
504   if (!InitApplication(hInstance)) {\r
505     return (FALSE);\r
506   }\r
507   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
508     return (FALSE);\r
509   }\r
510 \r
511 //  InitCommonControlsEx(&ex);\r
512   InitCommonControls();\r
513 \r
514   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
515   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
516   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
517 \r
518   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
519 \r
520   while (GetMessage(&msg, /* message structure */\r
521                     NULL, /* handle of window receiving the message */\r
522                     0,    /* lowest message to examine */\r
523                     0))   /* highest message to examine */\r
524     {\r
525       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
526           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
527           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
528           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
529           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
530           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
531           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
532           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
533           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
534           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
535         TranslateMessage(&msg); /* Translates virtual key codes */\r
536         DispatchMessage(&msg);  /* Dispatches message to window */\r
537       }\r
538     }\r
539 \r
540 \r
541   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
542 }\r
543 \r
544 /*---------------------------------------------------------------------------*\\r
545  *\r
546  * Initialization functions\r
547  *\r
548 \*---------------------------------------------------------------------------*/\r
549 \r
550 void\r
551 SetUserLogo()\r
552 {   // update user logo if necessary\r
553     static char oldUserName[MSG_SIZ], *curName;\r
554 \r
555     if(appData.autoLogo) {\r
556           curName = UserName();\r
557           if(strcmp(curName, oldUserName)) {\r
558                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
559                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
560                 strcpy(oldUserName, curName);\r
561           }\r
562     }\r
563 }\r
564 \r
565 BOOL\r
566 InitApplication(HINSTANCE hInstance)\r
567 {\r
568   WNDCLASS wc;\r
569 \r
570   /* Fill in window class structure with parameters that describe the */\r
571   /* main window. */\r
572 \r
573   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
574   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
575   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
576   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
577   wc.hInstance     = hInstance;         /* Owner of this class */\r
578   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
579   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
580   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
581   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
582   wc.lpszClassName = szAppName;                 /* Name to register as */\r
583 \r
584   /* Register the window class and return success/failure code. */\r
585   if (!RegisterClass(&wc)) return FALSE;\r
586 \r
587   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
588   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
589   wc.cbClsExtra    = 0;\r
590   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
591   wc.hInstance     = hInstance;\r
592   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
593   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
594   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
595   wc.lpszMenuName  = NULL;\r
596   wc.lpszClassName = szConsoleName;\r
597 \r
598   if (!RegisterClass(&wc)) return FALSE;\r
599   return TRUE;\r
600 }\r
601 \r
602 \r
603 /* Set by InitInstance, used by EnsureOnScreen */\r
604 int screenHeight, screenWidth;\r
605 \r
606 void\r
607 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
608 {\r
609 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
610   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
611   if (*x > screenWidth - 32) *x = 0;\r
612   if (*y > screenHeight - 32) *y = 0;\r
613   if (*x < minX) *x = minX;\r
614   if (*y < minY) *y = minY;\r
615 }\r
616 \r
617 BOOL\r
618 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
619 {\r
620   HWND hwnd; /* Main window handle. */\r
621   int ibs;\r
622   WINDOWPLACEMENT wp;\r
623   char *filepart;\r
624 \r
625   hInst = hInstance;    /* Store instance handle in our global variable */\r
626 \r
627   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
628     *filepart = NULLCHAR;\r
629   } else {\r
630     GetCurrentDirectory(MSG_SIZ, installDir);\r
631   }\r
632   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
633   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
634   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
635   if (appData.debugMode) {\r
636     debugFP = fopen(appData.nameOfDebugFile, "w");\r
637     setbuf(debugFP, NULL);\r
638   }\r
639 \r
640   InitBackEnd1();\r
641 \r
642 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
643 //  InitEngineUCI( installDir, &second );\r
644 \r
645   /* Create a main window for this application instance. */\r
646   hwnd = CreateWindow(szAppName, szTitle,\r
647                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
648                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
649                       NULL, NULL, hInstance, NULL);\r
650   hwndMain = hwnd;\r
651 \r
652   /* If window could not be created, return "failure" */\r
653   if (!hwnd) {\r
654     return (FALSE);\r
655   }\r
656 \r
657   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
658   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
659       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
660 \r
661       if (first.programLogo == NULL && appData.debugMode) {\r
662           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
663       }\r
664   } else if(appData.autoLogo) {\r
665       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
666         char buf[MSG_SIZ];\r
667         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
668         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
669       }\r
670   }\r
671 \r
672   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
673       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
674 \r
675       if (second.programLogo == NULL && appData.debugMode) {\r
676           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
677       }\r
678   } else if(appData.autoLogo) {\r
679       char buf[MSG_SIZ];\r
680       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
681         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
682         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
683       } else\r
684       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
685         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
686         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
687       }\r
688   }\r
689 \r
690   SetUserLogo();\r
691 \r
692   iconWhite = LoadIcon(hInstance, "icon_white");\r
693   iconBlack = LoadIcon(hInstance, "icon_black");\r
694   iconCurrent = iconWhite;\r
695   InitDrawingColors();\r
696   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
697   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
698   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
699     /* Compute window size for each board size, and use the largest\r
700        size that fits on this screen as the default. */\r
701     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
702     if (boardSize == (BoardSize)-1 &&\r
703         winH <= screenHeight\r
704            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
705         && winW <= screenWidth) {\r
706       boardSize = (BoardSize)ibs;\r
707     }\r
708   }\r
709 \r
710   InitDrawingSizes(boardSize, 0);\r
711   InitMenuChecks();\r
712   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
713 \r
714   /* [AS] Load textures if specified */\r
715   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
716   \r
717   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
718       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
719       liteBackTextureMode = appData.liteBackTextureMode;\r
720 \r
721       if (liteBackTexture == NULL && appData.debugMode) {\r
722           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
723       }\r
724   }\r
725   \r
726   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
727       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
728       darkBackTextureMode = appData.darkBackTextureMode;\r
729 \r
730       if (darkBackTexture == NULL && appData.debugMode) {\r
731           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
732       }\r
733   }\r
734 \r
735   mysrandom( (unsigned) time(NULL) );\r
736 \r
737   /* [AS] Restore layout */\r
738   if( wpMoveHistory.visible ) {\r
739       MoveHistoryPopUp();\r
740   }\r
741 \r
742   if( wpEvalGraph.visible ) {\r
743       EvalGraphPopUp();\r
744   }\r
745 \r
746   if( wpEngineOutput.visible ) {\r
747       EngineOutputPopUp();\r
748   }\r
749 \r
750   InitBackEnd2();\r
751 \r
752   /* Make the window visible; update its client area; and return "success" */\r
753   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
754   wp.length = sizeof(WINDOWPLACEMENT);\r
755   wp.flags = 0;\r
756   wp.showCmd = nCmdShow;\r
757   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
758   wp.rcNormalPosition.left = boardX;\r
759   wp.rcNormalPosition.right = boardX + winWidth;\r
760   wp.rcNormalPosition.top = boardY;\r
761   wp.rcNormalPosition.bottom = boardY + winHeight;\r
762   SetWindowPlacement(hwndMain, &wp);\r
763 \r
764   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
765                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
766 \r
767   if (hwndConsole) {\r
768 #if AOT_CONSOLE\r
769     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
770                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
771 #endif\r
772     ShowWindow(hwndConsole, nCmdShow);\r
773   }\r
774   UpdateWindow(hwnd);\r
775 \r
776   return TRUE;\r
777 \r
778 }\r
779 \r
780 \r
781 typedef enum {\r
782   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
783   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
784   ArgSettingsFilename,\r
785   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
786 } ArgType;\r
787 \r
788 typedef struct {\r
789   char *argName;\r
790   ArgType argType;\r
791   /***\r
792   union {\r
793     String *pString;       // ArgString\r
794     int *pInt;             // ArgInt\r
795     float *pFloat;         // ArgFloat\r
796     Boolean *pBoolean;     // ArgBoolean\r
797     COLORREF *pColor;      // ArgColor\r
798     ColorClass cc;         // ArgAttribs\r
799     String *pFilename;     // ArgFilename\r
800     BoardSize *pBoardSize; // ArgBoardSize\r
801     int whichFont;         // ArgFont\r
802     DCB *pDCB;             // ArgCommSettings\r
803     String *pFilename;     // ArgSettingsFilename\r
804   } argLoc;\r
805   ***/\r
806   LPVOID argLoc;\r
807   BOOL save;\r
808 } ArgDescriptor;\r
809 \r
810 int junk;\r
811 ArgDescriptor argDescriptors[] = {\r
812   /* positional arguments */\r
813   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
814   { "", ArgNone, NULL },\r
815   /* keyword arguments */\r
816   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
817   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
818   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
819   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
820   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
821   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
822   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
823   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
824   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
825   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
826   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
827   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
828   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
829   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
830   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
831   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
832   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
833   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
834     FALSE },\r
835   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
836     FALSE },\r
837   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
838     FALSE },\r
839   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
840   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
841     FALSE },\r
842   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
843   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
844   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
845   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
846   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
847   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
848   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
849   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
850   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
851   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
852   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
853   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
854   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
855   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
856   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
857   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
858   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
859   /*!!bitmapDirectory?*/\r
860   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
861   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
862   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
863   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
864   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
865   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
866   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
867   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
868   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
869   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
870   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
871   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
872   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
873   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
874   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
875   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
876   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
877   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
878   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
879   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
880   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
881   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
882   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
883   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
884   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
885   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
886   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
887   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
888   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
889   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
890   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
891   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
892   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
893   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
894   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
895   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
896   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
897   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
898   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
899   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
900   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
901   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
902   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
903   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
904   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
905   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
906   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
907   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
908   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
909   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
910   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
911   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
912   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
913   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
914   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
915   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
916   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
917   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
918   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
919   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
920   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
921   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
922   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
923   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
924   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
925   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
926   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
927   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
928   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
929   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
930   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
931   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
932   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
933   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
934   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
935   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
936   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
937   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
938   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
939   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
940   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
941   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
942   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
943   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
944   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
945   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
946   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
947   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
948   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
949   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
950   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
951   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
952   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
953   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
954     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
955   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
956   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
957   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
958   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
959   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
960   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
961   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
962   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
963     TRUE }, /* must come after all fonts */\r
964   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
965   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
966     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
967   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
968   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
969   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
970   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
971   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
972   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
973   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
974   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
975   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
976   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
977   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
978   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
979   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
980   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
981   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
982   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
983   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
984   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
985   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
986   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
987   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
988   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
989   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
990   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
991   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
992   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
993   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
994   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
995 #if 0\r
996   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
997   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
998 #endif\r
999   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1000   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1001   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1002   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1003   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1004   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1005   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1006   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1007   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1008   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1009   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1010   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1011   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1012   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1013   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1014   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1015   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1016   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1017   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1018   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1019   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1020   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1021   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1022   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1023   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1024   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1025   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1026   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1027   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1028   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1029   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1030   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1031   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1032   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1033   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1034   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1035   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1036   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1037   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1038   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1039   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1040   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1041   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1042   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1043   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1044   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1045   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1046   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1047   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1048   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1049   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1050   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1051   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1052   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1053   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1054   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1055   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1056   { "highlightLastMove", ArgBoolean,\r
1057     (LPVOID) &appData.highlightLastMove, TRUE },\r
1058   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1059   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1060   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1061   { "highlightDragging", ArgBoolean,\r
1062     (LPVOID) &appData.highlightDragging, TRUE },\r
1063   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1064   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1065   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1066   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1067   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1068   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1069   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1070   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1071   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1072   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1073   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1074   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1075   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1076   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1077   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1078   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1079   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1080   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1081   { "soundShout", ArgFilename,\r
1082     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1083   { "soundSShout", ArgFilename,\r
1084     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1085   { "soundChannel1", ArgFilename,\r
1086     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1087   { "soundChannel", ArgFilename,\r
1088     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1089   { "soundKibitz", ArgFilename,\r
1090     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1091   { "soundTell", ArgFilename,\r
1092     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1093   { "soundChallenge", ArgFilename,\r
1094     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1095   { "soundRequest", ArgFilename,\r
1096     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1097   { "soundSeek", ArgFilename,\r
1098     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1099   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1100   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1101   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1102   { "soundIcsLoss", ArgFilename, \r
1103     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1104   { "soundIcsDraw", ArgFilename, \r
1105     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1106   { "soundIcsUnfinished", ArgFilename, \r
1107     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1108   { "soundIcsAlarm", ArgFilename, \r
1109     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1110   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1111   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1112   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1113   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1114   { "reuseChessPrograms", ArgBoolean,\r
1115     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1116   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1117   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1118   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1119   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1120   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1121   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1122   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1123   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1124   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1125   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1126   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1127   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1128   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1129   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1130   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1131     TRUE },\r
1132   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1133     TRUE },\r
1134   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1135   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1136   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1137   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1138   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1139   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1140   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1141   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1142   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1143   /* [AS] New features */\r
1144   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1145   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1146   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1147   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1148   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1149   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1150   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1151   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1152   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1153   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1154   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1155   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1156   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1157   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1158   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1159   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1160   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1161   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1162   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1163   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1164   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1165   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1166   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1167   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1168   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1169   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1170   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1171   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1172   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1173   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1174   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1175   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1176   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1177   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1178   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1179   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1180   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1181   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1182   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1183   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1184   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1185   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1186   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1187   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1188   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1189   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1190   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1191   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1192   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1193   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1194 \r
1195   /* [HGM] board-size, adjudication and misc. options */\r
1196   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1197   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1198   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1199   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1200   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1201   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1202   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1203   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1204   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1205   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1206   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1207   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1208   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1209   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1210   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1211   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1212   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1213   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1214   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1215   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1216   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1217   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1218   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1219   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1220   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1221   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1222   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1223   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1224   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1225   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1226   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1227 \r
1228 #ifdef ZIPPY\r
1229   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1230   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1231   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1232   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1233   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1234   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1235   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1236   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1237   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1238   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1239   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1240   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1241   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1242     FALSE },\r
1243   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1244   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1245   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1246   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1247   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1248   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1249   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1250     FALSE },\r
1251   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1252   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1253   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1254   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1255   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1256   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1257   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1258   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1259   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1260   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1261   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1262   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1263   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1264   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1265   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1266   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1267   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1268   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1269   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1270 #endif\r
1271   /* [HGM] options for broadcasting and time odds */\r
1272   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1273   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1274   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1275   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1276   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1277   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1278   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1279   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1280   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1281   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1282   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1283 \r
1284   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1285   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1286   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1287   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1288   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1289   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1290   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1291   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1292   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1293   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1294   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1295   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1296   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1297   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1298   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1299   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1300   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1301   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1302   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1303   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1304   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1305   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1306   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1307   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1308   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1309   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1310   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1311   /* [AS] Layout stuff */\r
1312   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1313   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1314   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1315   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1316   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1317 \r
1318   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1319   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1320   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1321   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1322   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1323 \r
1324   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1325   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1326   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1327   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1328   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1329 \r
1330   { NULL, ArgNone, NULL, FALSE }\r
1331 };\r
1332 \r
1333 \r
1334 /* Kludge for indirection files on command line */\r
1335 char* lastIndirectionFilename;\r
1336 ArgDescriptor argDescriptorIndirection =\r
1337 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1338 \r
1339 \r
1340 VOID\r
1341 ExitArgError(char *msg, char *badArg)\r
1342 {\r
1343   char buf[MSG_SIZ];\r
1344 \r
1345   sprintf(buf, "%s %s", msg, badArg);\r
1346   DisplayFatalError(buf, 0, 2);\r
1347   exit(2);\r
1348 }\r
1349 \r
1350 /* Command line font name parser.  NULL name means do nothing.\r
1351    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1352    For backward compatibility, syntax without the colon is also\r
1353    accepted, but font names with digits in them won't work in that case.\r
1354 */\r
1355 VOID\r
1356 ParseFontName(char *name, MyFontParams *mfp)\r
1357 {\r
1358   char *p, *q;\r
1359   if (name == NULL) return;\r
1360   p = name;\r
1361   q = strchr(p, ':');\r
1362   if (q) {\r
1363     if (q - p >= sizeof(mfp->faceName))\r
1364       ExitArgError("Font name too long:", name);\r
1365     memcpy(mfp->faceName, p, q - p);\r
1366     mfp->faceName[q - p] = NULLCHAR;\r
1367     p = q + 1;\r
1368   } else {\r
1369     q = mfp->faceName;\r
1370     while (*p && !isdigit(*p)) {\r
1371       *q++ = *p++;\r
1372       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1373         ExitArgError("Font name too long:", name);\r
1374     }\r
1375     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1376     *q = NULLCHAR;\r
1377   }\r
1378   if (!*p) ExitArgError("Font point size missing:", name);\r
1379   mfp->pointSize = (float) atof(p);\r
1380   mfp->bold = (strchr(p, 'b') != NULL);\r
1381   mfp->italic = (strchr(p, 'i') != NULL);\r
1382   mfp->underline = (strchr(p, 'u') != NULL);\r
1383   mfp->strikeout = (strchr(p, 's') != NULL);\r
1384 }\r
1385 \r
1386 /* Color name parser.\r
1387    X version accepts X color names, but this one\r
1388    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1389 COLORREF\r
1390 ParseColorName(char *name)\r
1391 {\r
1392   int red, green, blue, count;\r
1393   char buf[MSG_SIZ];\r
1394 \r
1395   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1396   if (count != 3) {\r
1397     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1398       &red, &green, &blue);\r
1399   }\r
1400   if (count != 3) {\r
1401     sprintf(buf, "Can't parse color name %s", name);\r
1402     DisplayError(buf, 0);\r
1403     return RGB(0, 0, 0);\r
1404   }\r
1405   return PALETTERGB(red, green, blue);\r
1406 }\r
1407 \r
1408 \r
1409 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1410 {\r
1411   char *e = argValue;\r
1412   int eff = 0;\r
1413 \r
1414   while (*e) {\r
1415     if (*e == 'b')      eff |= CFE_BOLD;\r
1416     else if (*e == 'i') eff |= CFE_ITALIC;\r
1417     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1418     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1419     else if (*e == '#' || isdigit(*e)) break;\r
1420     e++;\r
1421   }\r
1422   *effects = eff;\r
1423   *color   = ParseColorName(e);\r
1424 }\r
1425 \r
1426 \r
1427 BoardSize\r
1428 ParseBoardSize(char *name)\r
1429 {\r
1430   BoardSize bs = SizeTiny;\r
1431   while (sizeInfo[bs].name != NULL) {\r
1432     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1433     bs++;\r
1434   }\r
1435   ExitArgError("Unrecognized board size value", name);\r
1436   return bs; /* not reached */\r
1437 }\r
1438 \r
1439 \r
1440 char\r
1441 StringGet(void *getClosure)\r
1442 {\r
1443   char **p = (char **) getClosure;\r
1444   return *((*p)++);\r
1445 }\r
1446 \r
1447 char\r
1448 FileGet(void *getClosure)\r
1449 {\r
1450   int c;\r
1451   FILE* f = (FILE*) getClosure;\r
1452 \r
1453   c = getc(f);\r
1454   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1455   if (c == EOF)\r
1456     return NULLCHAR;\r
1457   else\r
1458     return (char) c;\r
1459 }\r
1460 \r
1461 /* Parse settings file named "name". If file found, return the\r
1462    full name in fullname and return TRUE; else return FALSE */\r
1463 BOOLEAN\r
1464 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1465 {\r
1466   char *dummy;\r
1467   FILE *f;\r
1468   int ok; char buf[MSG_SIZ];\r
1469 \r
1470   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1471   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1472     sprintf(buf, "%s.ini", name);\r
1473     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1474   }\r
1475   if (ok) {\r
1476     f = fopen(fullname, "r");\r
1477     if (f != NULL) {\r
1478       ParseArgs(FileGet, f);\r
1479       fclose(f);\r
1480       return TRUE;\r
1481     }\r
1482   }\r
1483   return FALSE;\r
1484 }\r
1485 \r
1486 VOID\r
1487 ParseArgs(GetFunc get, void *cl)\r
1488 {\r
1489   char argName[ARG_MAX];\r
1490   char argValue[ARG_MAX];\r
1491   ArgDescriptor *ad;\r
1492   char start;\r
1493   char *q;\r
1494   int i, octval;\r
1495   char ch;\r
1496   int posarg = 0;\r
1497 \r
1498   ch = get(cl);\r
1499   for (;;) {\r
1500     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1501     if (ch == NULLCHAR) break;\r
1502     if (ch == ';') {\r
1503       /* Comment to end of line */\r
1504       ch = get(cl);\r
1505       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1506       continue;\r
1507     } else if (ch == '/' || ch == '-') {\r
1508       /* Switch */\r
1509       q = argName;\r
1510       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1511              ch != '\n' && ch != '\t') {\r
1512         *q++ = ch;\r
1513         ch = get(cl);\r
1514       }\r
1515       *q = NULLCHAR;\r
1516 \r
1517       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1518         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1519 \r
1520       if (ad->argName == NULL)\r
1521         ExitArgError("Unrecognized argument", argName);\r
1522 \r
1523     } else if (ch == '@') {\r
1524       /* Indirection file */\r
1525       ad = &argDescriptorIndirection;\r
1526       ch = get(cl);\r
1527     } else {\r
1528       /* Positional argument */\r
1529       ad = &argDescriptors[posarg++];\r
1530       strcpy(argName, ad->argName);\r
1531     }\r
1532 \r
1533     if (ad->argType == ArgTrue) {\r
1534       *(Boolean *) ad->argLoc = TRUE;\r
1535       continue;\r
1536     }\r
1537     if (ad->argType == ArgFalse) {\r
1538       *(Boolean *) ad->argLoc = FALSE;\r
1539       continue;\r
1540     }\r
1541 \r
1542     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1543     if (ch == NULLCHAR || ch == '\n') {\r
1544       ExitArgError("No value provided for argument", argName);\r
1545     }\r
1546     q = argValue;\r
1547     if (ch == '{') {\r
1548       // Quoting with { }.  No characters have to (or can) be escaped.\r
1549       // Thus the string cannot contain a '}' character.\r
1550       start = ch;\r
1551       ch = get(cl);\r
1552       while (start) {\r
1553         switch (ch) {\r
1554         case NULLCHAR:\r
1555           start = NULLCHAR;\r
1556           break;\r
1557           \r
1558         case '}':\r
1559           ch = get(cl);\r
1560           start = NULLCHAR;\r
1561           break;\r
1562 \r
1563         default:\r
1564           *q++ = ch;\r
1565           ch = get(cl);\r
1566           break;\r
1567         }\r
1568       }   \r
1569     } else if (ch == '\'' || ch == '"') {\r
1570       // Quoting with ' ' or " ", with \ as escape character.\r
1571       // Inconvenient for long strings that may contain Windows filenames.\r
1572       start = ch;\r
1573       ch = get(cl);\r
1574       while (start) {\r
1575         switch (ch) {\r
1576         case NULLCHAR:\r
1577           start = NULLCHAR;\r
1578           break;\r
1579 \r
1580         default:\r
1581         not_special:\r
1582           *q++ = ch;\r
1583           ch = get(cl);\r
1584           break;\r
1585 \r
1586         case '\'':\r
1587         case '\"':\r
1588           if (ch == start) {\r
1589             ch = get(cl);\r
1590             start = NULLCHAR;\r
1591             break;\r
1592           } else {\r
1593             goto not_special;\r
1594           }\r
1595 \r
1596         case '\\':\r
1597           if (ad->argType == ArgFilename\r
1598               || ad->argType == ArgSettingsFilename) {\r
1599               goto not_special;\r
1600           }\r
1601           ch = get(cl);\r
1602           switch (ch) {\r
1603           case NULLCHAR:\r
1604             ExitArgError("Incomplete \\ escape in value for", argName);\r
1605             break;\r
1606           case 'n':\r
1607             *q++ = '\n';\r
1608             ch = get(cl);\r
1609             break;\r
1610           case 'r':\r
1611             *q++ = '\r';\r
1612             ch = get(cl);\r
1613             break;\r
1614           case 't':\r
1615             *q++ = '\t';\r
1616             ch = get(cl);\r
1617             break;\r
1618           case 'b':\r
1619             *q++ = '\b';\r
1620             ch = get(cl);\r
1621             break;\r
1622           case 'f':\r
1623             *q++ = '\f';\r
1624             ch = get(cl);\r
1625             break;\r
1626           default:\r
1627             octval = 0;\r
1628             for (i = 0; i < 3; i++) {\r
1629               if (ch >= '0' && ch <= '7') {\r
1630                 octval = octval*8 + (ch - '0');\r
1631                 ch = get(cl);\r
1632               } else {\r
1633                 break;\r
1634               }\r
1635             }\r
1636             if (i > 0) {\r
1637               *q++ = (char) octval;\r
1638             } else {\r
1639               *q++ = ch;\r
1640               ch = get(cl);\r
1641             }\r
1642             break;\r
1643           }\r
1644           break;\r
1645         }\r
1646       }\r
1647     } else {\r
1648       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1649         *q++ = ch;\r
1650         ch = get(cl);\r
1651       }\r
1652     }\r
1653     *q = NULLCHAR;\r
1654 \r
1655     switch (ad->argType) {\r
1656     case ArgInt:\r
1657       *(int *) ad->argLoc = atoi(argValue);\r
1658       break;\r
1659 \r
1660     case ArgX:\r
1661       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1662       break;\r
1663 \r
1664     case ArgY:\r
1665       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1666       break;\r
1667 \r
1668     case ArgZ:\r
1669       *(int *) ad->argLoc = atoi(argValue);\r
1670       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1671       break;\r
1672 \r
1673     case ArgFloat:\r
1674       *(float *) ad->argLoc = (float) atof(argValue);\r
1675       break;\r
1676 \r
1677     case ArgString:\r
1678     case ArgFilename:\r
1679       *(char **) ad->argLoc = strdup(argValue);\r
1680       break;\r
1681 \r
1682     case ArgSettingsFilename:\r
1683       {\r
1684         char fullname[MSG_SIZ];\r
1685         if (ParseSettingsFile(argValue, fullname)) {\r
1686           if (ad->argLoc != NULL) {\r
1687             *(char **) ad->argLoc = strdup(fullname);\r
1688           }\r
1689         } else {\r
1690           if (ad->argLoc != NULL) {\r
1691           } else {\r
1692             ExitArgError("Failed to open indirection file", argValue);\r
1693           }\r
1694         }\r
1695       }\r
1696       break;\r
1697 \r
1698     case ArgBoolean:\r
1699       switch (argValue[0]) {\r
1700       case 't':\r
1701       case 'T':\r
1702         *(Boolean *) ad->argLoc = TRUE;\r
1703         break;\r
1704       case 'f':\r
1705       case 'F':\r
1706         *(Boolean *) ad->argLoc = FALSE;\r
1707         break;\r
1708       default:\r
1709         ExitArgError("Unrecognized boolean argument value", argValue);\r
1710         break;\r
1711       }\r
1712       break;\r
1713 \r
1714     case ArgColor:\r
1715       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1716       break;\r
1717 \r
1718     case ArgAttribs: {\r
1719       ColorClass cc = (ColorClass)ad->argLoc;\r
1720       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1721       }\r
1722       break;\r
1723       \r
1724     case ArgBoardSize:\r
1725       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1726       break;\r
1727 \r
1728     case ArgFont:\r
1729       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1730       break;\r
1731 \r
1732     case ArgCommSettings:\r
1733       ParseCommSettings(argValue, &dcb);\r
1734       break;\r
1735 \r
1736     case ArgNone:\r
1737       ExitArgError("Unrecognized argument", argValue);\r
1738       break;\r
1739     case ArgTrue:\r
1740     case ArgFalse: ;\r
1741     }\r
1742   }\r
1743 }\r
1744 \r
1745 VOID\r
1746 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1747 {\r
1748   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1749   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1750   DeleteDC(hdc);\r
1751   lf->lfWidth = 0;\r
1752   lf->lfEscapement = 0;\r
1753   lf->lfOrientation = 0;\r
1754   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1755   lf->lfItalic = mfp->italic;\r
1756   lf->lfUnderline = mfp->underline;\r
1757   lf->lfStrikeOut = mfp->strikeout;\r
1758   lf->lfCharSet = DEFAULT_CHARSET;\r
1759   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1760   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1761   lf->lfQuality = DEFAULT_QUALITY;\r
1762   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1763   strcpy(lf->lfFaceName, mfp->faceName);\r
1764 }\r
1765 \r
1766 VOID\r
1767 CreateFontInMF(MyFont *mf)\r
1768 {\r
1769   LFfromMFP(&mf->lf, &mf->mfp);\r
1770   if (mf->hf) DeleteObject(mf->hf);\r
1771   mf->hf = CreateFontIndirect(&mf->lf);\r
1772 }\r
1773 \r
1774 VOID\r
1775 SetDefaultTextAttribs()\r
1776 {\r
1777   ColorClass cc;\r
1778   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1779     ParseAttribs(&textAttribs[cc].color, \r
1780                  &textAttribs[cc].effects, \r
1781                  defaultTextAttribs[cc]);\r
1782   }\r
1783 }\r
1784 \r
1785 VOID\r
1786 SetDefaultSounds()\r
1787 {\r
1788   ColorClass cc;\r
1789   SoundClass sc;\r
1790   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1791     textAttribs[cc].sound.name = strdup("");\r
1792     textAttribs[cc].sound.data = NULL;\r
1793   }\r
1794   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1795     sounds[sc].name = strdup("");\r
1796     sounds[sc].data = NULL;\r
1797   }\r
1798   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1799 }\r
1800 \r
1801 VOID\r
1802 LoadAllSounds()\r
1803 {\r
1804   ColorClass cc;\r
1805   SoundClass sc;\r
1806   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1807     MyLoadSound(&textAttribs[cc].sound);\r
1808   }\r
1809   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1810     MyLoadSound(&sounds[sc]);\r
1811   }\r
1812 }\r
1813 \r
1814 VOID\r
1815 InitAppData(LPSTR lpCmdLine)\r
1816 {\r
1817   int i, j;\r
1818   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1819   char *dummy, *p;\r
1820 \r
1821   programName = szAppName;\r
1822 \r
1823   /* Initialize to defaults */\r
1824   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1825   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1826   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1827   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1828   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1829   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1830   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1831   SetDefaultTextAttribs();\r
1832   SetDefaultSounds();\r
1833   appData.movesPerSession = MOVES_PER_SESSION;\r
1834   appData.initString = INIT_STRING;\r
1835   appData.secondInitString = INIT_STRING;\r
1836   appData.firstComputerString = COMPUTER_STRING;\r
1837   appData.secondComputerString = COMPUTER_STRING;\r
1838   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1839   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1840   appData.firstPlaysBlack = FALSE;\r
1841   appData.noChessProgram = FALSE;\r
1842   chessProgram = FALSE;\r
1843   appData.firstHost = FIRST_HOST;\r
1844   appData.secondHost = SECOND_HOST;\r
1845   appData.firstDirectory = FIRST_DIRECTORY;\r
1846   appData.secondDirectory = SECOND_DIRECTORY;\r
1847   appData.bitmapDirectory = "";\r
1848   appData.remoteShell = REMOTE_SHELL;\r
1849   appData.remoteUser = "";\r
1850   appData.timeDelay = TIME_DELAY;\r
1851   appData.timeControl = TIME_CONTROL;\r
1852   appData.timeIncrement = TIME_INCREMENT;\r
1853   appData.icsActive = FALSE;\r
1854   appData.icsHost = "";\r
1855   appData.icsPort = ICS_PORT;\r
1856   appData.icsCommPort = ICS_COMM_PORT;\r
1857   appData.icsLogon = ICS_LOGON;\r
1858   appData.icsHelper = "";\r
1859   appData.useTelnet = FALSE;\r
1860   appData.telnetProgram = TELNET_PROGRAM;\r
1861   appData.gateway = "";\r
1862   appData.loadGameFile = "";\r
1863   appData.loadGameIndex = 0;\r
1864   appData.saveGameFile = "";\r
1865   appData.autoSaveGames = FALSE;\r
1866   appData.loadPositionFile = "";\r
1867   appData.loadPositionIndex = 1;\r
1868   appData.savePositionFile = "";\r
1869   appData.matchMode = FALSE;\r
1870   appData.matchGames = 0;\r
1871   appData.monoMode = FALSE;\r
1872   appData.debugMode = FALSE;\r
1873   appData.clockMode = TRUE;\r
1874   boardSize = (BoardSize) -1; /* determine by screen size */\r
1875   appData.Iconic = FALSE; /*unused*/\r
1876   appData.searchTime = "";\r
1877   appData.searchDepth = 0;\r
1878   appData.showCoords = FALSE;\r
1879   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1880   appData.autoCallFlag = FALSE;\r
1881   appData.flipView = FALSE;\r
1882   appData.autoFlipView = TRUE;\r
1883   appData.cmailGameName = "";\r
1884   appData.alwaysPromoteToQueen = FALSE;\r
1885   appData.oldSaveStyle = FALSE;\r
1886   appData.quietPlay = FALSE;\r
1887   appData.showThinking = FALSE;\r
1888   appData.ponderNextMove = TRUE;\r
1889   appData.periodicUpdates = TRUE;\r
1890   appData.popupExitMessage = TRUE;\r
1891   appData.popupMoveErrors = FALSE;\r
1892   appData.autoObserve = FALSE;\r
1893   appData.autoComment = FALSE;\r
1894   appData.animate = TRUE;\r
1895   appData.animSpeed = 10;\r
1896   appData.animateDragging = TRUE;\r
1897   appData.highlightLastMove = TRUE;\r
1898   appData.getMoveList = TRUE;\r
1899   appData.testLegality = TRUE;\r
1900   appData.premove = TRUE;\r
1901   appData.premoveWhite = FALSE;\r
1902   appData.premoveWhiteText = "";\r
1903   appData.premoveBlack = FALSE;\r
1904   appData.premoveBlackText = "";\r
1905   appData.icsAlarm = TRUE;\r
1906   appData.icsAlarmTime = 5000;\r
1907   appData.autoRaiseBoard = TRUE;\r
1908   appData.localLineEditing = TRUE;\r
1909   appData.colorize = TRUE;\r
1910   appData.reuseFirst = TRUE;\r
1911   appData.reuseSecond = TRUE;\r
1912   appData.blindfold = FALSE;\r
1913   appData.icsEngineAnalyze = FALSE;\r
1914   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1915   dcb.DCBlength = sizeof(DCB);\r
1916   dcb.BaudRate = 9600;\r
1917   dcb.fBinary = TRUE;\r
1918   dcb.fParity = FALSE;\r
1919   dcb.fOutxCtsFlow = FALSE;\r
1920   dcb.fOutxDsrFlow = FALSE;\r
1921   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1922   dcb.fDsrSensitivity = FALSE;\r
1923   dcb.fTXContinueOnXoff = TRUE;\r
1924   dcb.fOutX = FALSE;\r
1925   dcb.fInX = FALSE;\r
1926   dcb.fNull = FALSE;\r
1927   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1928   dcb.fAbortOnError = FALSE;\r
1929   dcb.ByteSize = 7;\r
1930   dcb.Parity = SPACEPARITY;\r
1931   dcb.StopBits = ONESTOPBIT;\r
1932   settingsFileName = SETTINGS_FILE;\r
1933   saveSettingsOnExit = TRUE;\r
1934   boardX = CW_USEDEFAULT;\r
1935   boardY = CW_USEDEFAULT;\r
1936   analysisX = CW_USEDEFAULT; \r
1937   analysisY = CW_USEDEFAULT; \r
1938   analysisW = CW_USEDEFAULT;\r
1939   analysisH = CW_USEDEFAULT;\r
1940   commentX = CW_USEDEFAULT; \r
1941   commentY = CW_USEDEFAULT; \r
1942   commentW = CW_USEDEFAULT;\r
1943   commentH = CW_USEDEFAULT;\r
1944   editTagsX = CW_USEDEFAULT; \r
1945   editTagsY = CW_USEDEFAULT; \r
1946   editTagsW = CW_USEDEFAULT;\r
1947   editTagsH = CW_USEDEFAULT;\r
1948   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1949   icsNames = ICS_NAMES;\r
1950   firstChessProgramNames = FCP_NAMES;\r
1951   secondChessProgramNames = SCP_NAMES;\r
1952   appData.initialMode = "";\r
1953   appData.variant = "normal";\r
1954   appData.firstProtocolVersion = PROTOVER;\r
1955   appData.secondProtocolVersion = PROTOVER;\r
1956   appData.showButtonBar = TRUE;\r
1957 \r
1958    /* [AS] New properties (see comments in header file) */\r
1959   appData.firstScoreIsAbsolute = FALSE;\r
1960   appData.secondScoreIsAbsolute = FALSE;\r
1961   appData.saveExtendedInfoInPGN = FALSE;\r
1962   appData.hideThinkingFromHuman = FALSE;\r
1963   appData.liteBackTextureFile = "";\r
1964   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1965   appData.darkBackTextureFile = "";\r
1966   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1967   appData.renderPiecesWithFont = "";\r
1968   appData.fontToPieceTable = "";\r
1969   appData.fontBackColorWhite = 0;\r
1970   appData.fontForeColorWhite = 0;\r
1971   appData.fontBackColorBlack = 0;\r
1972   appData.fontForeColorBlack = 0;\r
1973   appData.fontPieceSize = 80;\r
1974   appData.overrideLineGap = 1;\r
1975   appData.adjudicateLossThreshold = 0;\r
1976   appData.delayBeforeQuit = 0;\r
1977   appData.delayAfterQuit = 0;\r
1978   appData.nameOfDebugFile = "winboard.debug";\r
1979   appData.pgnEventHeader = "Computer Chess Game";\r
1980   appData.defaultFrcPosition = -1;\r
1981   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1982   appData.saveOutOfBookInfo = TRUE;\r
1983   appData.showEvalInMoveHistory = TRUE;\r
1984   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1985   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1986   appData.highlightMoveWithArrow = FALSE;\r
1987   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1988   appData.useStickyWindows = TRUE;\r
1989   appData.adjudicateDrawMoves = 0;\r
1990   appData.autoDisplayComment = TRUE;\r
1991   appData.autoDisplayTags = TRUE;\r
1992   appData.firstIsUCI = FALSE;\r
1993   appData.secondIsUCI = FALSE;\r
1994   appData.firstHasOwnBookUCI = TRUE;\r
1995   appData.secondHasOwnBookUCI = TRUE;\r
1996   appData.polyglotDir = "";\r
1997   appData.usePolyglotBook = FALSE;\r
1998   appData.polyglotBook = "";\r
1999   appData.defaultHashSize = 64;\r
2000   appData.defaultCacheSizeEGTB = 4;\r
2001   appData.defaultPathEGTB = "c:\\egtb";\r
2002   appData.firstOptions = "";\r
2003   appData.secondOptions = "";\r
2004 \r
2005   InitWindowPlacement( &wpGameList );\r
2006   InitWindowPlacement( &wpMoveHistory );\r
2007   InitWindowPlacement( &wpEvalGraph );\r
2008   InitWindowPlacement( &wpEngineOutput );\r
2009   InitWindowPlacement( &wpConsole );\r
2010 \r
2011   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2012   appData.NrFiles      = -1;\r
2013   appData.NrRanks      = -1;\r
2014   appData.holdingsSize = -1;\r
2015   appData.testClaims   = FALSE;\r
2016   appData.checkMates   = FALSE;\r
2017   appData.materialDraws= FALSE;\r
2018   appData.trivialDraws = FALSE;\r
2019   appData.ruleMoves    = 51;\r
2020   appData.drawRepeats  = 6;\r
2021   appData.matchPause   = 10000;\r
2022   appData.alphaRank    = FALSE;\r
2023   appData.allWhite     = FALSE;\r
2024   appData.upsideDown   = FALSE;\r
2025   appData.serverPause  = 15;\r
2026   appData.serverMovesName   = NULL;\r
2027   appData.suppressLoadMoves = FALSE;\r
2028   appData.firstTimeOdds  = 1;\r
2029   appData.secondTimeOdds = 1;\r
2030   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2031   appData.secondAccumulateTC = 1;\r
2032   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2033   appData.secondNPS = -1;\r
2034   appData.engineComments = 1;\r
2035   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2036   appData.egtFormats = "";\r
2037 \r
2038 #ifdef ZIPPY\r
2039   appData.zippyTalk = ZIPPY_TALK;\r
2040   appData.zippyPlay = ZIPPY_PLAY;\r
2041   appData.zippyLines = ZIPPY_LINES;\r
2042   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2043   appData.zippyPassword = ZIPPY_PASSWORD;\r
2044   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2045   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2046   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2047   appData.zippyUseI = ZIPPY_USE_I;\r
2048   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2049   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2050   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2051   appData.zippyGameStart = ZIPPY_GAME_START;\r
2052   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2053   appData.zippyAbort = ZIPPY_ABORT;\r
2054   appData.zippyVariants = ZIPPY_VARIANTS;\r
2055   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2056   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2057 #endif\r
2058 \r
2059   /* Point font array elements to structures and\r
2060      parse default font names */\r
2061   for (i=0; i<NUM_FONTS; i++) {\r
2062     for (j=0; j<NUM_SIZES; j++) {\r
2063       font[j][i] = &fontRec[j][i];\r
2064       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2065     }\r
2066   }\r
2067   \r
2068   /* Parse default settings file if any */\r
2069   if (ParseSettingsFile(settingsFileName, buf)) {\r
2070     settingsFileName = strdup(buf);\r
2071   }\r
2072 \r
2073   /* Parse command line */\r
2074   ParseArgs(StringGet, &lpCmdLine);\r
2075 \r
2076   /* [HGM] make sure board size is acceptable */\r
2077   if(appData.NrFiles > BOARD_SIZE ||\r
2078      appData.NrRanks > BOARD_SIZE   )\r
2079       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2080 \r
2081   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2082    * with options from the command line, we now make an even higher priority\r
2083    * overrule by WB options attached to the engine command line. This so that\r
2084    * tournament managers can use WB options (such as /timeOdds) that follow\r
2085    * the engines.\r
2086    */\r
2087   if(appData.firstChessProgram != NULL) {\r
2088       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2089       static char *f = "first";\r
2090       char buf[MSG_SIZ], *q = buf;\r
2091       if(p != NULL) { // engine command line contains WinBoard options\r
2092           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2093           ParseArgs(StringGet, &q);\r
2094           p[-1] = 0; // cut them offengine command line\r
2095       }\r
2096   }\r
2097   // now do same for second chess program\r
2098   if(appData.secondChessProgram != NULL) {\r
2099       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2100       static char *s = "second";\r
2101       char buf[MSG_SIZ], *q = buf;\r
2102       if(p != NULL) { // engine command line contains WinBoard options\r
2103           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2104           ParseArgs(StringGet, &q);\r
2105           p[-1] = 0; // cut them offengine command line\r
2106       }\r
2107   }\r
2108 \r
2109 \r
2110   /* Propagate options that affect others */\r
2111   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2112   if (appData.icsActive || appData.noChessProgram) {\r
2113      chessProgram = FALSE;  /* not local chess program mode */\r
2114   }\r
2115 \r
2116   /* Open startup dialog if needed */\r
2117   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2118       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2119       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2120                         *appData.secondChessProgram == NULLCHAR))) {\r
2121     FARPROC lpProc;\r
2122     \r
2123     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2124     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2125     FreeProcInstance(lpProc);\r
2126   }\r
2127 \r
2128   /* Make sure save files land in the right (?) directory */\r
2129   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2130     appData.saveGameFile = strdup(buf);\r
2131   }\r
2132   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2133     appData.savePositionFile = strdup(buf);\r
2134   }\r
2135 \r
2136   /* Finish initialization for fonts and sounds */\r
2137   for (i=0; i<NUM_FONTS; i++) {\r
2138     for (j=0; j<NUM_SIZES; j++) {\r
2139       CreateFontInMF(font[j][i]);\r
2140     }\r
2141   }\r
2142   /* xboard, and older WinBoards, controlled the move sound with the\r
2143      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2144      always turn the option on (so that the backend will call us),\r
2145      then let the user turn the sound off by setting it to silence if\r
2146      desired.  To accommodate old winboard.ini files saved by old\r
2147      versions of WinBoard, we also turn off the sound if the option\r
2148      was initially set to false. */\r
2149   if (!appData.ringBellAfterMoves) {\r
2150     sounds[(int)SoundMove].name = strdup("");\r
2151     appData.ringBellAfterMoves = TRUE;\r
2152   }\r
2153   GetCurrentDirectory(MSG_SIZ, currDir);\r
2154   SetCurrentDirectory(installDir);\r
2155   LoadAllSounds();\r
2156   SetCurrentDirectory(currDir);\r
2157 \r
2158   p = icsTextMenuString;\r
2159   if (p[0] == '@') {\r
2160     FILE* f = fopen(p + 1, "r");\r
2161     if (f == NULL) {\r
2162       DisplayFatalError(p + 1, errno, 2);\r
2163       return;\r
2164     }\r
2165     i = fread(buf, 1, sizeof(buf)-1, f);\r
2166     fclose(f);\r
2167     buf[i] = NULLCHAR;\r
2168     p = buf;\r
2169   }\r
2170   ParseIcsTextMenu(strdup(p));\r
2171 }\r
2172 \r
2173 \r
2174 VOID\r
2175 InitMenuChecks()\r
2176 {\r
2177   HMENU hmenu = GetMenu(hwndMain);\r
2178 \r
2179   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2180                         MF_BYCOMMAND|((appData.icsActive &&\r
2181                                        *appData.icsCommPort != NULLCHAR) ?\r
2182                                       MF_ENABLED : MF_GRAYED));\r
2183   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2184                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2185                                      MF_CHECKED : MF_UNCHECKED));\r
2186 }\r
2187 \r
2188 \r
2189 VOID\r
2190 SaveSettings(char* name)\r
2191 {\r
2192   FILE *f;\r
2193   ArgDescriptor *ad;\r
2194   WINDOWPLACEMENT wp;\r
2195   char dir[MSG_SIZ];\r
2196 \r
2197   if (!hwndMain) return;\r
2198 \r
2199   GetCurrentDirectory(MSG_SIZ, dir);\r
2200   SetCurrentDirectory(installDir);\r
2201   f = fopen(name, "w");\r
2202   SetCurrentDirectory(dir);\r
2203   if (f == NULL) {\r
2204     DisplayError(name, errno);\r
2205     return;\r
2206   }\r
2207   fprintf(f, ";\n");\r
2208   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2209   fprintf(f, ";\n");\r
2210   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2211   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2212   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2213   fprintf(f, ";\n");\r
2214 \r
2215   wp.length = sizeof(WINDOWPLACEMENT);\r
2216   GetWindowPlacement(hwndMain, &wp);\r
2217   boardX = wp.rcNormalPosition.left;\r
2218   boardY = wp.rcNormalPosition.top;\r
2219 \r
2220   if (hwndConsole) {\r
2221     GetWindowPlacement(hwndConsole, &wp);\r
2222     wpConsole.x = wp.rcNormalPosition.left;\r
2223     wpConsole.y = wp.rcNormalPosition.top;\r
2224     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2225     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2226   }\r
2227 \r
2228   if (analysisDialog) {\r
2229     GetWindowPlacement(analysisDialog, &wp);\r
2230     analysisX = wp.rcNormalPosition.left;\r
2231     analysisY = wp.rcNormalPosition.top;\r
2232     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2233     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2234   }\r
2235 \r
2236   if (commentDialog) {\r
2237     GetWindowPlacement(commentDialog, &wp);\r
2238     commentX = wp.rcNormalPosition.left;\r
2239     commentY = wp.rcNormalPosition.top;\r
2240     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2241     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2242   }\r
2243 \r
2244   if (editTagsDialog) {\r
2245     GetWindowPlacement(editTagsDialog, &wp);\r
2246     editTagsX = wp.rcNormalPosition.left;\r
2247     editTagsY = wp.rcNormalPosition.top;\r
2248     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2249     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2250   }\r
2251 \r
2252   if (gameListDialog) {\r
2253     GetWindowPlacement(gameListDialog, &wp);\r
2254     wpGameList.x = wp.rcNormalPosition.left;\r
2255     wpGameList.y = wp.rcNormalPosition.top;\r
2256     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2257     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2258   }\r
2259 \r
2260   /* [AS] Move history */\r
2261   wpMoveHistory.visible = MoveHistoryIsUp();\r
2262   \r
2263   if( moveHistoryDialog ) {\r
2264     GetWindowPlacement(moveHistoryDialog, &wp);\r
2265     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2266     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2267     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2268     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2269   }\r
2270 \r
2271   /* [AS] Eval graph */\r
2272   wpEvalGraph.visible = EvalGraphIsUp();\r
2273 \r
2274   if( evalGraphDialog ) {\r
2275     GetWindowPlacement(evalGraphDialog, &wp);\r
2276     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2277     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2278     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2279     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2280   }\r
2281 \r
2282   /* [AS] Engine output */\r
2283   wpEngineOutput.visible = EngineOutputIsUp();\r
2284 \r
2285   if( engineOutputDialog ) {\r
2286     GetWindowPlacement(engineOutputDialog, &wp);\r
2287     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2288     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2289     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2290     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2291   }\r
2292 \r
2293   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2294     if (!ad->save) continue;\r
2295     switch (ad->argType) {\r
2296     case ArgString:\r
2297       {\r
2298         char *p = *(char **)ad->argLoc;\r
2299         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2300           /* Quote multiline values or \-containing values\r
2301              with { } if possible */\r
2302           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2303         } else {\r
2304           /* Else quote with " " */\r
2305           fprintf(f, "/%s=\"", ad->argName);\r
2306           while (*p) {\r
2307             if (*p == '\n') fprintf(f, "\n");\r
2308             else if (*p == '\r') fprintf(f, "\\r");\r
2309             else if (*p == '\t') fprintf(f, "\\t");\r
2310             else if (*p == '\b') fprintf(f, "\\b");\r
2311             else if (*p == '\f') fprintf(f, "\\f");\r
2312             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2313             else if (*p == '\"') fprintf(f, "\\\"");\r
2314             else if (*p == '\\') fprintf(f, "\\\\");\r
2315             else putc(*p, f);\r
2316             p++;\r
2317           }\r
2318           fprintf(f, "\"\n");\r
2319         }\r
2320       }\r
2321       break;\r
2322     case ArgInt:\r
2323     case ArgZ:\r
2324       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2325       break;\r
2326     case ArgX:\r
2327       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2328       break;\r
2329     case ArgY:\r
2330       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2331       break;\r
2332     case ArgFloat:\r
2333       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2334       break;\r
2335     case ArgBoolean:\r
2336       fprintf(f, "/%s=%s\n", ad->argName, \r
2337         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2338       break;\r
2339     case ArgTrue:\r
2340       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2341       break;\r
2342     case ArgFalse:\r
2343       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2344       break;\r
2345     case ArgColor:\r
2346       {\r
2347         COLORREF color = *(COLORREF *)ad->argLoc;\r
2348         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2349           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2350       }\r
2351       break;\r
2352     case ArgAttribs:\r
2353       {\r
2354         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2355         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2356           (ta->effects & CFE_BOLD) ? "b" : "",\r
2357           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2358           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2359           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2360           (ta->effects) ? " " : "",\r
2361           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2362       }\r
2363       break;\r
2364     case ArgFilename:\r
2365       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2366         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2367       } else {\r
2368         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2369       }\r
2370       break;\r
2371     case ArgBoardSize:\r
2372       fprintf(f, "/%s=%s\n", ad->argName,\r
2373               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2374       break;\r
2375     case ArgFont:\r
2376       {\r
2377         int bs;\r
2378         for (bs=0; bs<NUM_SIZES; bs++) {\r
2379           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2380           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2381           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2382             ad->argName, mfp->faceName, mfp->pointSize,\r
2383             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2384             mfp->bold ? "b" : "",\r
2385             mfp->italic ? "i" : "",\r
2386             mfp->underline ? "u" : "",\r
2387             mfp->strikeout ? "s" : "");\r
2388         }\r
2389       }\r
2390       break;\r
2391     case ArgCommSettings:\r
2392       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2393     case ArgNone:\r
2394     case ArgSettingsFilename: ;\r
2395     }\r
2396   }\r
2397   fclose(f);\r
2398 }\r
2399 \r
2400 \r
2401 \r
2402 /*---------------------------------------------------------------------------*\\r
2403  *\r
2404  * GDI board drawing routines\r
2405  *\r
2406 \*---------------------------------------------------------------------------*/\r
2407 \r
2408 /* [AS] Draw square using background texture */\r
2409 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2410 {\r
2411     XFORM   x;\r
2412 \r
2413     if( mode == 0 ) {\r
2414         return; /* Should never happen! */\r
2415     }\r
2416 \r
2417     SetGraphicsMode( dst, GM_ADVANCED );\r
2418 \r
2419     switch( mode ) {\r
2420     case 1:\r
2421         /* Identity */\r
2422         break;\r
2423     case 2:\r
2424         /* X reflection */\r
2425         x.eM11 = -1.0;\r
2426         x.eM12 = 0;\r
2427         x.eM21 = 0;\r
2428         x.eM22 = 1.0;\r
2429         x.eDx = (FLOAT) dw + dx - 1;\r
2430         x.eDy = 0;\r
2431         dx = 0;\r
2432         SetWorldTransform( dst, &x );\r
2433         break;\r
2434     case 3:\r
2435         /* Y reflection */\r
2436         x.eM11 = 1.0;\r
2437         x.eM12 = 0;\r
2438         x.eM21 = 0;\r
2439         x.eM22 = -1.0;\r
2440         x.eDx = 0;\r
2441         x.eDy = (FLOAT) dh + dy - 1;\r
2442         dy = 0;\r
2443         SetWorldTransform( dst, &x );\r
2444         break;\r
2445     case 4:\r
2446         /* X/Y flip */\r
2447         x.eM11 = 0;\r
2448         x.eM12 = 1.0;\r
2449         x.eM21 = 1.0;\r
2450         x.eM22 = 0;\r
2451         x.eDx = (FLOAT) dx;\r
2452         x.eDy = (FLOAT) dy;\r
2453         dx = 0;\r
2454         dy = 0;\r
2455         SetWorldTransform( dst, &x );\r
2456         break;\r
2457     }\r
2458 \r
2459     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2460 \r
2461     x.eM11 = 1.0;\r
2462     x.eM12 = 0;\r
2463     x.eM21 = 0;\r
2464     x.eM22 = 1.0;\r
2465     x.eDx = 0;\r
2466     x.eDy = 0;\r
2467     SetWorldTransform( dst, &x );\r
2468 \r
2469     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2470 }\r
2471 \r
2472 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2473 enum {\r
2474     PM_WP = (int) WhitePawn, \r
2475     PM_WN = (int) WhiteKnight, \r
2476     PM_WB = (int) WhiteBishop, \r
2477     PM_WR = (int) WhiteRook, \r
2478     PM_WQ = (int) WhiteQueen, \r
2479     PM_WF = (int) WhiteFerz, \r
2480     PM_WW = (int) WhiteWazir, \r
2481     PM_WE = (int) WhiteAlfil, \r
2482     PM_WM = (int) WhiteMan, \r
2483     PM_WO = (int) WhiteCannon, \r
2484     PM_WU = (int) WhiteUnicorn, \r
2485     PM_WH = (int) WhiteNightrider, \r
2486     PM_WA = (int) WhiteAngel, \r
2487     PM_WC = (int) WhiteMarshall, \r
2488     PM_WAB = (int) WhiteCardinal, \r
2489     PM_WD = (int) WhiteDragon, \r
2490     PM_WL = (int) WhiteLance, \r
2491     PM_WS = (int) WhiteCobra, \r
2492     PM_WV = (int) WhiteFalcon, \r
2493     PM_WSG = (int) WhiteSilver, \r
2494     PM_WG = (int) WhiteGrasshopper, \r
2495     PM_WK = (int) WhiteKing,\r
2496     PM_BP = (int) BlackPawn, \r
2497     PM_BN = (int) BlackKnight, \r
2498     PM_BB = (int) BlackBishop, \r
2499     PM_BR = (int) BlackRook, \r
2500     PM_BQ = (int) BlackQueen, \r
2501     PM_BF = (int) BlackFerz, \r
2502     PM_BW = (int) BlackWazir, \r
2503     PM_BE = (int) BlackAlfil, \r
2504     PM_BM = (int) BlackMan,\r
2505     PM_BO = (int) BlackCannon, \r
2506     PM_BU = (int) BlackUnicorn, \r
2507     PM_BH = (int) BlackNightrider, \r
2508     PM_BA = (int) BlackAngel, \r
2509     PM_BC = (int) BlackMarshall, \r
2510     PM_BG = (int) BlackGrasshopper, \r
2511     PM_BAB = (int) BlackCardinal,\r
2512     PM_BD = (int) BlackDragon,\r
2513     PM_BL = (int) BlackLance,\r
2514     PM_BS = (int) BlackCobra,\r
2515     PM_BV = (int) BlackFalcon,\r
2516     PM_BSG = (int) BlackSilver,\r
2517     PM_BK = (int) BlackKing\r
2518 };\r
2519 \r
2520 static HFONT hPieceFont = NULL;\r
2521 static HBITMAP hPieceMask[(int) EmptySquare];\r
2522 static HBITMAP hPieceFace[(int) EmptySquare];\r
2523 static int fontBitmapSquareSize = 0;\r
2524 static char pieceToFontChar[(int) EmptySquare] =\r
2525                               { 'p', 'n', 'b', 'r', 'q', \r
2526                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2527                       'k', 'o', 'm', 'v', 't', 'w', \r
2528                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2529                                                               'l' };\r
2530 \r
2531 extern BOOL SetCharTable( char *table, const char * map );\r
2532 /* [HGM] moved to backend.c */\r
2533 \r
2534 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2535 {\r
2536     HBRUSH hbrush;\r
2537     BYTE r1 = GetRValue( color );\r
2538     BYTE g1 = GetGValue( color );\r
2539     BYTE b1 = GetBValue( color );\r
2540     BYTE r2 = r1 / 2;\r
2541     BYTE g2 = g1 / 2;\r
2542     BYTE b2 = b1 / 2;\r
2543     RECT rc;\r
2544 \r
2545     /* Create a uniform background first */\r
2546     hbrush = CreateSolidBrush( color );\r
2547     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2548     FillRect( hdc, &rc, hbrush );\r
2549     DeleteObject( hbrush );\r
2550     \r
2551     if( mode == 1 ) {\r
2552         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2553         int steps = squareSize / 2;\r
2554         int i;\r
2555 \r
2556         for( i=0; i<steps; i++ ) {\r
2557             BYTE r = r1 - (r1-r2) * i / steps;\r
2558             BYTE g = g1 - (g1-g2) * i / steps;\r
2559             BYTE b = b1 - (b1-b2) * i / steps;\r
2560 \r
2561             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2562             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2563             FillRect( hdc, &rc, hbrush );\r
2564             DeleteObject(hbrush);\r
2565         }\r
2566     }\r
2567     else if( mode == 2 ) {\r
2568         /* Diagonal gradient, good more or less for every piece */\r
2569         POINT triangle[3];\r
2570         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2571         HBRUSH hbrush_old;\r
2572         int steps = squareSize;\r
2573         int i;\r
2574 \r
2575         triangle[0].x = squareSize - steps;\r
2576         triangle[0].y = squareSize;\r
2577         triangle[1].x = squareSize;\r
2578         triangle[1].y = squareSize;\r
2579         triangle[2].x = squareSize;\r
2580         triangle[2].y = squareSize - steps;\r
2581 \r
2582         for( i=0; i<steps; i++ ) {\r
2583             BYTE r = r1 - (r1-r2) * i / steps;\r
2584             BYTE g = g1 - (g1-g2) * i / steps;\r
2585             BYTE b = b1 - (b1-b2) * i / steps;\r
2586 \r
2587             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2588             hbrush_old = SelectObject( hdc, hbrush );\r
2589             Polygon( hdc, triangle, 3 );\r
2590             SelectObject( hdc, hbrush_old );\r
2591             DeleteObject(hbrush);\r
2592             triangle[0].x++;\r
2593             triangle[2].y++;\r
2594         }\r
2595 \r
2596         SelectObject( hdc, hpen );\r
2597     }\r
2598 }\r
2599 \r
2600 /*\r
2601     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2602     seems to work ok. The main problem here is to find the "inside" of a chess\r
2603     piece: follow the steps as explained below.\r
2604 */\r
2605 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2606 {\r
2607     HBITMAP hbm;\r
2608     HBITMAP hbm_old;\r
2609     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2610     RECT rc;\r
2611     SIZE sz;\r
2612     POINT pt;\r
2613     int backColor = whitePieceColor; \r
2614     int foreColor = blackPieceColor;\r
2615     \r
2616     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2617         backColor = appData.fontBackColorWhite;\r
2618         foreColor = appData.fontForeColorWhite;\r
2619     }\r
2620     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2621         backColor = appData.fontBackColorBlack;\r
2622         foreColor = appData.fontForeColorBlack;\r
2623     }\r
2624 \r
2625     /* Mask */\r
2626     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2627 \r
2628     hbm_old = SelectObject( hdc, hbm );\r
2629 \r
2630     rc.left = 0;\r
2631     rc.top = 0;\r
2632     rc.right = squareSize;\r
2633     rc.bottom = squareSize;\r
2634 \r
2635     /* Step 1: background is now black */\r
2636     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2637 \r
2638     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2639 \r
2640     pt.x = (squareSize - sz.cx) / 2;\r
2641     pt.y = (squareSize - sz.cy) / 2;\r
2642 \r
2643     SetBkMode( hdc, TRANSPARENT );\r
2644     SetTextColor( hdc, chroma );\r
2645     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2646     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2647 \r
2648     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2649     /* Step 3: the area outside the piece is filled with white */\r
2650 //    FloodFill( hdc, 0, 0, chroma );\r
2651     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2652     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2653     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2654     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2655     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2656     /* \r
2657         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2658         but if the start point is not inside the piece we're lost!\r
2659         There should be a better way to do this... if we could create a region or path\r
2660         from the fill operation we would be fine for example.\r
2661     */\r
2662 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2663     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2664 \r
2665     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2666         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2667         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2668 \r
2669         SelectObject( dc2, bm2 );\r
2670         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2671         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2672         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2673         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2674         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2675 \r
2676         DeleteDC( dc2 );\r
2677         DeleteObject( bm2 );\r
2678     }\r
2679 \r
2680     SetTextColor( hdc, 0 );\r
2681     /* \r
2682         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2683         draw the piece again in black for safety.\r
2684     */\r
2685     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2686 \r
2687     SelectObject( hdc, hbm_old );\r
2688 \r
2689     if( hPieceMask[index] != NULL ) {\r
2690         DeleteObject( hPieceMask[index] );\r
2691     }\r
2692 \r
2693     hPieceMask[index] = hbm;\r
2694 \r
2695     /* Face */\r
2696     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2697 \r
2698     SelectObject( hdc, hbm );\r
2699 \r
2700     {\r
2701         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2702         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2703         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2704 \r
2705         SelectObject( dc1, hPieceMask[index] );\r
2706         SelectObject( dc2, bm2 );\r
2707         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2708         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2709         \r
2710         /* \r
2711             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2712             the piece background and deletes (makes transparent) the rest.\r
2713             Thanks to that mask, we are free to paint the background with the greates\r
2714             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2715             We use this, to make gradients and give the pieces a "roundish" look.\r
2716         */\r
2717         SetPieceBackground( hdc, backColor, 2 );\r
2718         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2719 \r
2720         DeleteDC( dc2 );\r
2721         DeleteDC( dc1 );\r
2722         DeleteObject( bm2 );\r
2723     }\r
2724 \r
2725     SetTextColor( hdc, foreColor );\r
2726     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2727 \r
2728     SelectObject( hdc, hbm_old );\r
2729 \r
2730     if( hPieceFace[index] != NULL ) {\r
2731         DeleteObject( hPieceFace[index] );\r
2732     }\r
2733 \r
2734     hPieceFace[index] = hbm;\r
2735 }\r
2736 \r
2737 static int TranslatePieceToFontPiece( int piece )\r
2738 {\r
2739     switch( piece ) {\r
2740     case BlackPawn:\r
2741         return PM_BP;\r
2742     case BlackKnight:\r
2743         return PM_BN;\r
2744     case BlackBishop:\r
2745         return PM_BB;\r
2746     case BlackRook:\r
2747         return PM_BR;\r
2748     case BlackQueen:\r
2749         return PM_BQ;\r
2750     case BlackKing:\r
2751         return PM_BK;\r
2752     case WhitePawn:\r
2753         return PM_WP;\r
2754     case WhiteKnight:\r
2755         return PM_WN;\r
2756     case WhiteBishop:\r
2757         return PM_WB;\r
2758     case WhiteRook:\r
2759         return PM_WR;\r
2760     case WhiteQueen:\r
2761         return PM_WQ;\r
2762     case WhiteKing:\r
2763         return PM_WK;\r
2764 \r
2765     case BlackAngel:\r
2766         return PM_BA;\r
2767     case BlackMarshall:\r
2768         return PM_BC;\r
2769     case BlackFerz:\r
2770         return PM_BF;\r
2771     case BlackNightrider:\r
2772         return PM_BH;\r
2773     case BlackAlfil:\r
2774         return PM_BE;\r
2775     case BlackWazir:\r
2776         return PM_BW;\r
2777     case BlackUnicorn:\r
2778         return PM_BU;\r
2779     case BlackCannon:\r
2780         return PM_BO;\r
2781     case BlackGrasshopper:\r
2782         return PM_BG;\r
2783     case BlackMan:\r
2784         return PM_BM;\r
2785     case BlackSilver:\r
2786         return PM_BSG;\r
2787     case BlackLance:\r
2788         return PM_BL;\r
2789     case BlackFalcon:\r
2790         return PM_BV;\r
2791     case BlackCobra:\r
2792         return PM_BS;\r
2793     case BlackCardinal:\r
2794         return PM_BAB;\r
2795     case BlackDragon:\r
2796         return PM_BD;\r
2797 \r
2798     case WhiteAngel:\r
2799         return PM_WA;\r
2800     case WhiteMarshall:\r
2801         return PM_WC;\r
2802     case WhiteFerz:\r
2803         return PM_WF;\r
2804     case WhiteNightrider:\r
2805         return PM_WH;\r
2806     case WhiteAlfil:\r
2807         return PM_WE;\r
2808     case WhiteWazir:\r
2809         return PM_WW;\r
2810     case WhiteUnicorn:\r
2811         return PM_WU;\r
2812     case WhiteCannon:\r
2813         return PM_WO;\r
2814     case WhiteGrasshopper:\r
2815         return PM_WG;\r
2816     case WhiteMan:\r
2817         return PM_WM;\r
2818     case WhiteSilver:\r
2819         return PM_WSG;\r
2820     case WhiteLance:\r
2821         return PM_WL;\r
2822     case WhiteFalcon:\r
2823         return PM_WV;\r
2824     case WhiteCobra:\r
2825         return PM_WS;\r
2826     case WhiteCardinal:\r
2827         return PM_WAB;\r
2828     case WhiteDragon:\r
2829         return PM_WD;\r
2830     }\r
2831 \r
2832     return 0;\r
2833 }\r
2834 \r
2835 void CreatePiecesFromFont()\r
2836 {\r
2837     LOGFONT lf;\r
2838     HDC hdc_window = NULL;\r
2839     HDC hdc = NULL;\r
2840     HFONT hfont_old;\r
2841     int fontHeight;\r
2842     int i;\r
2843 \r
2844     if( fontBitmapSquareSize < 0 ) {\r
2845         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2846         return;\r
2847     }\r
2848 \r
2849     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2850         fontBitmapSquareSize = -1;\r
2851         return;\r
2852     }\r
2853 \r
2854     if( fontBitmapSquareSize != squareSize ) {\r
2855         hdc_window = GetDC( hwndMain );\r
2856         hdc = CreateCompatibleDC( hdc_window );\r
2857 \r
2858         if( hPieceFont != NULL ) {\r
2859             DeleteObject( hPieceFont );\r
2860         }\r
2861         else {\r
2862             for( i=0; i<=(int)BlackKing; i++ ) {\r
2863                 hPieceMask[i] = NULL;\r
2864                 hPieceFace[i] = NULL;\r
2865             }\r
2866         }\r
2867 \r
2868         fontHeight = 75;\r
2869 \r
2870         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2871             fontHeight = appData.fontPieceSize;\r
2872         }\r
2873 \r
2874         fontHeight = (fontHeight * squareSize) / 100;\r
2875 \r
2876         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2877         lf.lfWidth = 0;\r
2878         lf.lfEscapement = 0;\r
2879         lf.lfOrientation = 0;\r
2880         lf.lfWeight = FW_NORMAL;\r
2881         lf.lfItalic = 0;\r
2882         lf.lfUnderline = 0;\r
2883         lf.lfStrikeOut = 0;\r
2884         lf.lfCharSet = DEFAULT_CHARSET;\r
2885         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2886         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2887         lf.lfQuality = PROOF_QUALITY;\r
2888         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2889         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2890         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2891 \r
2892         hPieceFont = CreateFontIndirect( &lf );\r
2893 \r
2894         if( hPieceFont == NULL ) {\r
2895             fontBitmapSquareSize = -2;\r
2896         }\r
2897         else {\r
2898             /* Setup font-to-piece character table */\r
2899             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2900                 /* No (or wrong) global settings, try to detect the font */\r
2901                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2902                     /* Alpha */\r
2903                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2904                 }\r
2905                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2906                     /* DiagramTT* family */\r
2907                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2908                 }\r
2909                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2910                     /* Fairy symbols */\r
2911                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2912                 }\r
2913                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2914                     /* Good Companion (Some characters get warped as literal :-( */\r
2915                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2916                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2917                     SetCharTable(pieceToFontChar, s);\r
2918                 }\r
2919                 else {\r
2920                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2921                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2922                 }\r
2923             }\r
2924 \r
2925             /* Create bitmaps */\r
2926             hfont_old = SelectObject( hdc, hPieceFont );\r
2927 #if 0\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2931             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2932             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2933             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2934             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2935             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2936             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2937             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2938             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2939             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2940 \r
2941             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2942             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2943             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2944             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2945             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2946             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2947             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2948             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2949             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2950             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2951             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2952             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2953             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2954             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2955             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2956             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2957             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2958             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2959             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2960             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2961             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2962             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2963             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2964             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2965             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2966             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2967             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2968             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2969             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2970             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2971             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2972             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2973 #else\r
2974             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2975                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2976                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2977 #endif\r
2978             SelectObject( hdc, hfont_old );\r
2979 \r
2980             fontBitmapSquareSize = squareSize;\r
2981         }\r
2982     }\r
2983 \r
2984     if( hdc != NULL ) {\r
2985         DeleteDC( hdc );\r
2986     }\r
2987 \r
2988     if( hdc_window != NULL ) {\r
2989         ReleaseDC( hwndMain, hdc_window );\r
2990     }\r
2991 }\r
2992 \r
2993 HBITMAP\r
2994 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2995 {\r
2996   char name[128];\r
2997 \r
2998   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2999   if (gameInfo.event &&\r
3000       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3001       strcmp(name, "k80s") == 0) {\r
3002     strcpy(name, "tim");\r
3003   }\r
3004   return LoadBitmap(hinst, name);\r
3005 }\r
3006 \r
3007 \r
3008 /* Insert a color into the program's logical palette\r
3009    structure.  This code assumes the given color is\r
3010    the result of the RGB or PALETTERGB macro, and it\r
3011    knows how those macros work (which is documented).\r
3012 */\r
3013 VOID\r
3014 InsertInPalette(COLORREF color)\r
3015 {\r
3016   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3017 \r
3018   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3019     DisplayFatalError("Too many colors", 0, 1);\r
3020     pLogPal->palNumEntries--;\r
3021     return;\r
3022   }\r
3023 \r
3024   pe->peFlags = (char) 0;\r
3025   pe->peRed = (char) (0xFF & color);\r
3026   pe->peGreen = (char) (0xFF & (color >> 8));\r
3027   pe->peBlue = (char) (0xFF & (color >> 16));\r
3028   return;\r
3029 }\r
3030 \r
3031 \r
3032 VOID\r
3033 InitDrawingColors()\r
3034 {\r
3035   if (pLogPal == NULL) {\r
3036     /* Allocate enough memory for a logical palette with\r
3037      * PALETTESIZE entries and set the size and version fields\r
3038      * of the logical palette structure.\r
3039      */\r
3040     pLogPal = (NPLOGPALETTE)\r
3041       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3042                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3043     pLogPal->palVersion    = 0x300;\r
3044   }\r
3045   pLogPal->palNumEntries = 0;\r
3046 \r
3047   InsertInPalette(lightSquareColor);\r
3048   InsertInPalette(darkSquareColor);\r
3049   InsertInPalette(whitePieceColor);\r
3050   InsertInPalette(blackPieceColor);\r
3051   InsertInPalette(highlightSquareColor);\r
3052   InsertInPalette(premoveHighlightColor);\r
3053 \r
3054   /*  create a logical color palette according the information\r
3055    *  in the LOGPALETTE structure.\r
3056    */\r
3057   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3058 \r
3059   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3060   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3061   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3062   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3063   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3064   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3065   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3066   /* [AS] Force rendering of the font-based pieces */\r
3067   if( fontBitmapSquareSize > 0 ) {\r
3068     fontBitmapSquareSize = 0;\r
3069   }\r
3070 }\r
3071 \r
3072 \r
3073 int\r
3074 BoardWidth(int boardSize, int n)\r
3075 { /* [HGM] argument n added to allow different width and height */\r
3076   int lineGap = sizeInfo[boardSize].lineGap;\r
3077 \r
3078   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3079       lineGap = appData.overrideLineGap;\r
3080   }\r
3081 \r
3082   return (n + 1) * lineGap +\r
3083           n * sizeInfo[boardSize].squareSize;\r
3084 }\r
3085 \r
3086 /* Respond to board resize by dragging edge */\r
3087 VOID\r
3088 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3089 {\r
3090   BoardSize newSize = NUM_SIZES - 1;\r
3091   static int recurse = 0;\r
3092   if (IsIconic(hwndMain)) return;\r
3093   if (recurse > 0) return;\r
3094   recurse++;\r
3095   while (newSize > 0) {\r
3096         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3097         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3098            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3099     newSize--;\r
3100   } \r
3101   boardSize = newSize;\r
3102   InitDrawingSizes(boardSize, flags);\r
3103   recurse--;\r
3104 }\r
3105 \r
3106 \r
3107 \r
3108 VOID\r
3109 InitDrawingSizes(BoardSize boardSize, int flags)\r
3110 {\r
3111   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3112   ChessSquare piece;\r
3113   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3114   HDC hdc;\r
3115   SIZE clockSize, messageSize;\r
3116   HFONT oldFont;\r
3117   char buf[MSG_SIZ];\r
3118   char *str;\r
3119   HMENU hmenu = GetMenu(hwndMain);\r
3120   RECT crect, wrect, oldRect;\r
3121   int offby;\r
3122   LOGBRUSH logbrush;\r
3123 \r
3124   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3125   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3126 \r
3127   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3128   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3129 \r
3130   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3131   oldRect.top = boardY;\r
3132   oldRect.right = boardX + winWidth;\r
3133   oldRect.bottom = boardY + winHeight;\r
3134 \r
3135   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3136   smallLayout = sizeInfo[boardSize].smallLayout;\r
3137   squareSize = sizeInfo[boardSize].squareSize;\r
3138   lineGap = sizeInfo[boardSize].lineGap;\r
3139   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3140 \r
3141   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3142       lineGap = appData.overrideLineGap;\r
3143   }\r
3144 \r
3145   if (tinyLayout != oldTinyLayout) {\r
3146     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3147     if (tinyLayout) {\r
3148       style &= ~WS_SYSMENU;\r
3149       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3150                  "&Minimize\tCtrl+F4");\r
3151     } else {\r
3152       style |= WS_SYSMENU;\r
3153       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3154     }\r
3155     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3156 \r
3157     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3158       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3159         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3160     }\r
3161     DrawMenuBar(hwndMain);\r
3162   }\r
3163 \r
3164   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3165   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3166 \r
3167   /* Get text area sizes */\r
3168   hdc = GetDC(hwndMain);\r
3169   if (appData.clockMode) {\r
3170     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3171   } else {\r
3172     sprintf(buf, "White");\r
3173   }\r
3174   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3175   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3176   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3177   str = "We only care about the height here";\r
3178   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3179   SelectObject(hdc, oldFont);\r
3180   ReleaseDC(hwndMain, hdc);\r
3181 \r
3182   /* Compute where everything goes */\r
3183   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3184         /* [HGM] logo: if either logo is on, reserve space for it */\r
3185         logoHeight =  2*clockSize.cy;\r
3186         leftLogoRect.left   = OUTER_MARGIN;\r
3187         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3188         leftLogoRect.top    = OUTER_MARGIN;\r
3189         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3190 \r
3191         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3192         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3193         rightLogoRect.top    = OUTER_MARGIN;\r
3194         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3195 \r
3196 \r
3197     whiteRect.left = leftLogoRect.right;\r
3198     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3199     whiteRect.top = OUTER_MARGIN;\r
3200     whiteRect.bottom = whiteRect.top + logoHeight;\r
3201 \r
3202     blackRect.right = rightLogoRect.left;\r
3203     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3204     blackRect.top = whiteRect.top;\r
3205     blackRect.bottom = whiteRect.bottom;\r
3206   } else {\r
3207     whiteRect.left = OUTER_MARGIN;\r
3208     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3209     whiteRect.top = OUTER_MARGIN;\r
3210     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3211 \r
3212     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3213     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3214     blackRect.top = whiteRect.top;\r
3215     blackRect.bottom = whiteRect.bottom;\r
3216   }\r
3217 \r
3218   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3219   if (appData.showButtonBar) {\r
3220     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3221       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3222   } else {\r
3223     messageRect.right = OUTER_MARGIN + boardWidth;\r
3224   }\r
3225   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3226   messageRect.bottom = messageRect.top + messageSize.cy;\r
3227 \r
3228   boardRect.left = OUTER_MARGIN;\r
3229   boardRect.right = boardRect.left + boardWidth;\r
3230   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3231   boardRect.bottom = boardRect.top + boardHeight;\r
3232 \r
3233   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3234   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3235   oldBoardSize = boardSize;\r
3236   oldTinyLayout = tinyLayout;\r
3237   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3238   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3239     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3240   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3241   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3242   winHeight = winH; //       without disturbing window attachments\r
3243   GetWindowRect(hwndMain, &wrect);\r
3244   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3245                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3246 \r
3247   // [HGM] placement: let attached windows follow size change.\r
3248   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3249   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3250   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3251   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3252   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3253 \r
3254   /* compensate if menu bar wrapped */\r
3255   GetClientRect(hwndMain, &crect);\r
3256   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3257   winHeight += offby;\r
3258   switch (flags) {\r
3259   case WMSZ_TOPLEFT:\r
3260     SetWindowPos(hwndMain, NULL, \r
3261                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3262                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3263     break;\r
3264 \r
3265   case WMSZ_TOPRIGHT:\r
3266   case WMSZ_TOP:\r
3267     SetWindowPos(hwndMain, NULL, \r
3268                  wrect.left, wrect.bottom - winHeight, \r
3269                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3270     break;\r
3271 \r
3272   case WMSZ_BOTTOMLEFT:\r
3273   case WMSZ_LEFT:\r
3274     SetWindowPos(hwndMain, NULL, \r
3275                  wrect.right - winWidth, wrect.top, \r
3276                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3277     break;\r
3278 \r
3279   case WMSZ_BOTTOMRIGHT:\r
3280   case WMSZ_BOTTOM:\r
3281   case WMSZ_RIGHT:\r
3282   default:\r
3283     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3284                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3285     break;\r
3286   }\r
3287 \r
3288   hwndPause = NULL;\r
3289   for (i = 0; i < N_BUTTONS; i++) {\r
3290     if (buttonDesc[i].hwnd != NULL) {\r
3291       DestroyWindow(buttonDesc[i].hwnd);\r
3292       buttonDesc[i].hwnd = NULL;\r
3293     }\r
3294     if (appData.showButtonBar) {\r
3295       buttonDesc[i].hwnd =\r
3296         CreateWindow("BUTTON", buttonDesc[i].label,\r
3297                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3298                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3299                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3300                      (HMENU) buttonDesc[i].id,\r
3301                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3302       if (tinyLayout) {\r
3303         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3304                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3305                     MAKELPARAM(FALSE, 0));\r
3306       }\r
3307       if (buttonDesc[i].id == IDM_Pause)\r
3308         hwndPause = buttonDesc[i].hwnd;\r
3309       buttonDesc[i].wndproc = (WNDPROC)\r
3310         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3311     }\r
3312   }\r
3313   if (gridPen != NULL) DeleteObject(gridPen);\r
3314   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3315   if (premovePen != NULL) DeleteObject(premovePen);\r
3316   if (lineGap != 0) {\r
3317     logbrush.lbStyle = BS_SOLID;\r
3318     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3319     gridPen =\r
3320       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3321                    lineGap, &logbrush, 0, NULL);\r
3322     logbrush.lbColor = highlightSquareColor;\r
3323     highlightPen =\r
3324       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3325                    lineGap, &logbrush, 0, NULL);\r
3326 \r
3327     logbrush.lbColor = premoveHighlightColor; \r
3328     premovePen =\r
3329       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3330                    lineGap, &logbrush, 0, NULL);\r
3331 \r
3332     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3333     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3334       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3335       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3336         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3337       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3338         BOARD_WIDTH * (squareSize + lineGap);\r
3339       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3340     }\r
3341     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3342       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3343       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3344         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3345         lineGap / 2 + (i * (squareSize + lineGap));\r
3346       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3347         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3348       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3349     }\r
3350   }\r
3351 \r
3352   /* [HGM] Licensing requirement */\r
3353 #ifdef GOTHIC\r
3354   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3355 #endif\r
3356 #ifdef FALCON\r
3357   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3358 #endif\r
3359   GothicPopUp( "", VariantNormal);\r
3360 \r
3361 \r
3362 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3363 \r
3364   /* Load piece bitmaps for this board size */\r
3365   for (i=0; i<=2; i++) {\r
3366     for (piece = WhitePawn;\r
3367          (int) piece < (int) BlackPawn;\r
3368          piece = (ChessSquare) ((int) piece + 1)) {\r
3369       if (pieceBitmap[i][piece] != NULL)\r
3370         DeleteObject(pieceBitmap[i][piece]);\r
3371     }\r
3372   }\r
3373 \r
3374   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3375   // Orthodox Chess pieces\r
3376   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3377   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3378   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3379   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3380   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3381   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3382   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3383   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3384   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3385   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3386   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3387   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3388   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3389   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3390   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3391   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3392     // in Shogi, Hijack the unused Queen for Lance\r
3393     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3394     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3395     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3396   } else {\r
3397     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3398     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3399     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3400   }\r
3401 \r
3402   if(squareSize <= 72 && squareSize >= 33) { \r
3403     /* A & C are available in most sizes now */\r
3404     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3405       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3406       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3407       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3408       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3409       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3410       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3411       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3412       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3413       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3414       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3415       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3416       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3417     } else { // Smirf-like\r
3418       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3419       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3420       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3421     }\r
3422     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3423       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3424       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3425       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3426     } else { // WinBoard standard\r
3427       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3428       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3429       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3430     }\r
3431   }\r
3432 \r
3433 \r
3434   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3435     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3436     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3437     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3438     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3439     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3440     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3441     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3442     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3443     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3444     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3445     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3446     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3447     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3448     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3449     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3450     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3451     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3452     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3453     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3454     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3455     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3456     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3457     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3458     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3459     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3460     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3461     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3462     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3463     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3464     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3465 \r
3466     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3467       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3468       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3469       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3470       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3471       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3472       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3473       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3474       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3475       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3476       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3477       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3478       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3479     } else {\r
3480       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3481       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3482       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3483       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3484       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3485       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3486       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3487       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3488       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3489       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3490       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3491       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3492     }\r
3493 \r
3494   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3495     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3496     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3497     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3498     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3499     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3500     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3501     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3502     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3503     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3504     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3505     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3506     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3507     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3508     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3509   }\r
3510 \r
3511 \r
3512   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3513   /* special Shogi support in this size */\r
3514   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3515       for (piece = WhitePawn;\r
3516            (int) piece < (int) BlackPawn;\r
3517            piece = (ChessSquare) ((int) piece + 1)) {\r
3518         if (pieceBitmap[i][piece] != NULL)\r
3519           DeleteObject(pieceBitmap[i][piece]);\r
3520       }\r
3521     }\r
3522   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3523   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3524   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3525   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3526   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3527   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3528   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3529   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3530   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3531   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3532   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3533   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3534   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3535   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3536   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3537   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3538   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3539   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3540   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3541   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3542   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3543   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3544   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3545   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3546   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3547   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3548   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3549   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3550   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3551   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3552   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3553   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3554   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3555   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3556   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3557   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3558   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3559   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3560   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3561   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3562   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3563   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3564   minorSize = 0;\r
3565   }\r
3566 }\r
3567 \r
3568 HBITMAP\r
3569 PieceBitmap(ChessSquare p, int kind)\r
3570 {\r
3571   if ((int) p >= (int) BlackPawn)\r
3572     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3573 \r
3574   return pieceBitmap[kind][(int) p];\r
3575 }\r
3576 \r
3577 /***************************************************************/\r
3578 \r
3579 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3580 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3581 /*\r
3582 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3583 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3584 */\r
3585 \r
3586 VOID\r
3587 SquareToPos(int row, int column, int * x, int * y)\r
3588 {\r
3589   if (flipView) {\r
3590     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3591     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3592   } else {\r
3593     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3594     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3595   }\r
3596 }\r
3597 \r
3598 VOID\r
3599 DrawCoordsOnDC(HDC hdc)\r
3600 {\r
3601   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
3602   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
3603   char str[2] = { NULLCHAR, NULLCHAR };\r
3604   int oldMode, oldAlign, x, y, start, i;\r
3605   HFONT oldFont;\r
3606   HBRUSH oldBrush;\r
3607 \r
3608   if (!appData.showCoords)\r
3609     return;\r
3610 \r
3611   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3612 \r
3613   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3614   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3615   oldAlign = GetTextAlign(hdc);\r
3616   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3617 \r
3618   y = boardRect.top + lineGap;\r
3619   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3620 \r
3621   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3622   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3623     str[0] = files[start + i];\r
3624     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3625     y += squareSize + lineGap;\r
3626   }\r
3627 \r
3628   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3629 \r
3630   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3631   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3632     str[0] = ranks[start + i];\r
3633     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3634     x += squareSize + lineGap;\r
3635   }    \r
3636 \r
3637   SelectObject(hdc, oldBrush);\r
3638   SetBkMode(hdc, oldMode);\r
3639   SetTextAlign(hdc, oldAlign);\r
3640   SelectObject(hdc, oldFont);\r
3641 }\r
3642 \r
3643 VOID\r
3644 DrawGridOnDC(HDC hdc)\r
3645 {\r
3646   HPEN oldPen;\r
3647  \r
3648   if (lineGap != 0) {\r
3649     oldPen = SelectObject(hdc, gridPen);\r
3650     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3651     SelectObject(hdc, oldPen);\r
3652   }\r
3653 }\r
3654 \r
3655 #define HIGHLIGHT_PEN 0\r
3656 #define PREMOVE_PEN   1\r
3657 \r
3658 VOID\r
3659 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3660 {\r
3661   int x1, y1;\r
3662   HPEN oldPen, hPen;\r
3663   if (lineGap == 0) return;\r
3664   if (flipView) {\r
3665     x1 = boardRect.left +\r
3666       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3667     y1 = boardRect.top +\r
3668       lineGap/2 + y * (squareSize + lineGap);\r
3669   } else {\r
3670     x1 = boardRect.left +\r
3671       lineGap/2 + x * (squareSize + lineGap);\r
3672     y1 = boardRect.top +\r
3673       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3674   }\r
3675   hPen = pen ? premovePen : highlightPen;\r
3676   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3677   MoveToEx(hdc, x1, y1, NULL);\r
3678   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3679   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3680   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3681   LineTo(hdc, x1, y1);\r
3682   SelectObject(hdc, oldPen);\r
3683 }\r
3684 \r
3685 VOID\r
3686 DrawHighlightsOnDC(HDC hdc)\r
3687 {\r
3688   int i;\r
3689   for (i=0; i<2; i++) {\r
3690     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3691       DrawHighlightOnDC(hdc, TRUE,\r
3692                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3693                         HIGHLIGHT_PEN);\r
3694   }\r
3695   for (i=0; i<2; i++) {\r
3696     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3697         premoveHighlightInfo.sq[i].y >= 0) {\r
3698         DrawHighlightOnDC(hdc, TRUE,\r
3699                           premoveHighlightInfo.sq[i].x, \r
3700                           premoveHighlightInfo.sq[i].y,\r
3701                           PREMOVE_PEN);\r
3702     }\r
3703   }\r
3704 }\r
3705 \r
3706 /* Note: sqcolor is used only in monoMode */\r
3707 /* Note that this code is largely duplicated in woptions.c,\r
3708    function DrawSampleSquare, so that needs to be updated too */\r
3709 VOID\r
3710 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3711 {\r
3712   HBITMAP oldBitmap;\r
3713   HBRUSH oldBrush;\r
3714   int tmpSize;\r
3715 \r
3716   if (appData.blindfold) return;\r
3717 \r
3718   /* [AS] Use font-based pieces if needed */\r
3719   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3720     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3721     CreatePiecesFromFont();\r
3722 \r
3723     if( fontBitmapSquareSize == squareSize ) {\r
3724         int index = TranslatePieceToFontPiece(piece);\r
3725 \r
3726         SelectObject( tmphdc, hPieceMask[ index ] );\r
3727 \r
3728         BitBlt( hdc,\r
3729             x, y,\r
3730             squareSize, squareSize,\r
3731             tmphdc,\r
3732             0, 0,\r
3733             SRCAND );\r
3734 \r
3735         SelectObject( tmphdc, hPieceFace[ index ] );\r
3736 \r
3737         BitBlt( hdc,\r
3738             x, y,\r
3739             squareSize, squareSize,\r
3740             tmphdc,\r
3741             0, 0,\r
3742             SRCPAINT );\r
3743 \r
3744         return;\r
3745     }\r
3746   }\r
3747 \r
3748   if (appData.monoMode) {\r
3749     SelectObject(tmphdc, PieceBitmap(piece, \r
3750       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3751     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3752            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3753   } else {\r
3754     tmpSize = squareSize;\r
3755     if(minorSize &&\r
3756         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3757          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3758       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3759       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3760       x += (squareSize - minorSize)>>1;\r
3761       y += squareSize - minorSize - 2;\r
3762       tmpSize = minorSize;\r
3763     }\r
3764     if (color || appData.allWhite ) {\r
3765       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3766       if( color )\r
3767               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3768       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3769       if(appData.upsideDown && color==flipView)\r
3770         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3771       else\r
3772         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3773 #if 0\r
3774       /* Use black piece color for outline of white pieces */\r
3775       /* Not sure this looks really good (though xboard does it).\r
3776          Maybe better to have another selectable color, default black */\r
3777       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3778       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3779       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3780 #else\r
3781       /* Use black for outline of white pieces */\r
3782       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3783       if(appData.upsideDown && color==flipView)\r
3784         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3785       else\r
3786         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3787 #endif\r
3788     } else {\r
3789 #if 0\r
3790       /* Use white piece color for details of black pieces */\r
3791       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3792          WHITE_PIECE ones aren't always the right shape. */\r
3793       /* Not sure this looks really good (though xboard does it).\r
3794          Maybe better to have another selectable color, default medium gray? */\r
3795       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3796       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3797       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3798       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3799       SelectObject(hdc, blackPieceBrush);\r
3800       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3801 #else\r
3802       /* Use square color for details of black pieces */\r
3803       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3804       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3805       if(appData.upsideDown && !flipView)\r
3806         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3807       else\r
3808         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3809 #endif\r
3810     }\r
3811     SelectObject(hdc, oldBrush);\r
3812     SelectObject(tmphdc, oldBitmap);\r
3813   }\r
3814 }\r
3815 \r
3816 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3817 int GetBackTextureMode( int algo )\r
3818 {\r
3819     int result = BACK_TEXTURE_MODE_DISABLED;\r
3820 \r
3821     switch( algo ) \r
3822     {\r
3823         case BACK_TEXTURE_MODE_PLAIN:\r
3824             result = 1; /* Always use identity map */\r
3825             break;\r
3826         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3827             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3828             break;\r
3829     }\r
3830 \r
3831     return result;\r
3832 }\r
3833 \r
3834 /* \r
3835     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3836     to handle redraws cleanly (as random numbers would always be different).\r
3837 */\r
3838 VOID RebuildTextureSquareInfo()\r
3839 {\r
3840     BITMAP bi;\r
3841     int lite_w = 0;\r
3842     int lite_h = 0;\r
3843     int dark_w = 0;\r
3844     int dark_h = 0;\r
3845     int row;\r
3846     int col;\r
3847 \r
3848     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3849 \r
3850     if( liteBackTexture != NULL ) {\r
3851         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3852             lite_w = bi.bmWidth;\r
3853             lite_h = bi.bmHeight;\r
3854         }\r
3855     }\r
3856 \r
3857     if( darkBackTexture != NULL ) {\r
3858         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3859             dark_w = bi.bmWidth;\r
3860             dark_h = bi.bmHeight;\r
3861         }\r
3862     }\r
3863 \r
3864     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3865         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3866             if( (col + row) & 1 ) {\r
3867                 /* Lite square */\r
3868                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3869                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3870                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3871                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3872                 }\r
3873             }\r
3874             else {\r
3875                 /* Dark square */\r
3876                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3877                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3878                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3879                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3880                 }\r
3881             }\r
3882         }\r
3883     }\r
3884 }\r
3885 \r
3886 /* [AS] Arrow highlighting support */\r
3887 \r
3888 static int A_WIDTH = 5; /* Width of arrow body */\r
3889 \r
3890 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3891 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3892 \r
3893 static double Sqr( double x )\r
3894 {\r
3895     return x*x;\r
3896 }\r
3897 \r
3898 static int Round( double x )\r
3899 {\r
3900     return (int) (x + 0.5);\r
3901 }\r
3902 \r
3903 /* Draw an arrow between two points using current settings */\r
3904 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3905 {\r
3906     POINT arrow[7];\r
3907     double dx, dy, j, k, x, y;\r
3908 \r
3909     if( d_x == s_x ) {\r
3910         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3911 \r
3912         arrow[0].x = s_x + A_WIDTH;\r
3913         arrow[0].y = s_y;\r
3914 \r
3915         arrow[1].x = s_x + A_WIDTH;\r
3916         arrow[1].y = d_y - h;\r
3917 \r
3918         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3919         arrow[2].y = d_y - h;\r
3920 \r
3921         arrow[3].x = d_x;\r
3922         arrow[3].y = d_y;\r
3923 \r
3924         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3925         arrow[4].y = d_y - h;\r
3926 \r
3927         arrow[5].x = s_x - A_WIDTH;\r
3928         arrow[5].y = d_y - h;\r
3929 \r
3930         arrow[6].x = s_x - A_WIDTH;\r
3931         arrow[6].y = s_y;\r
3932     }\r
3933     else if( d_y == s_y ) {\r
3934         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3935 \r
3936         arrow[0].x = s_x;\r
3937         arrow[0].y = s_y + A_WIDTH;\r
3938 \r
3939         arrow[1].x = d_x - w;\r
3940         arrow[1].y = s_y + A_WIDTH;\r
3941 \r
3942         arrow[2].x = d_x - w;\r
3943         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3944 \r
3945         arrow[3].x = d_x;\r
3946         arrow[3].y = d_y;\r
3947 \r
3948         arrow[4].x = d_x - w;\r
3949         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3950 \r
3951         arrow[5].x = d_x - w;\r
3952         arrow[5].y = s_y - A_WIDTH;\r
3953 \r
3954         arrow[6].x = s_x;\r
3955         arrow[6].y = s_y - A_WIDTH;\r
3956     }\r
3957     else {\r
3958         /* [AS] Needed a lot of paper for this! :-) */\r
3959         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3960         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3961   \r
3962         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3963 \r
3964         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3965 \r
3966         x = s_x;\r
3967         y = s_y;\r
3968 \r
3969         arrow[0].x = Round(x - j);\r
3970         arrow[0].y = Round(y + j*dx);\r
3971 \r
3972         arrow[1].x = Round(x + j);\r
3973         arrow[1].y = Round(y - j*dx);\r
3974 \r
3975         if( d_x > s_x ) {\r
3976             x = (double) d_x - k;\r
3977             y = (double) d_y - k*dy;\r
3978         }\r
3979         else {\r
3980             x = (double) d_x + k;\r
3981             y = (double) d_y + k*dy;\r
3982         }\r
3983 \r
3984         arrow[2].x = Round(x + j);\r
3985         arrow[2].y = Round(y - j*dx);\r
3986 \r
3987         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3988         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3989 \r
3990         arrow[4].x = d_x;\r
3991         arrow[4].y = d_y;\r
3992 \r
3993         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3994         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3995 \r
3996         arrow[6].x = Round(x - j);\r
3997         arrow[6].y = Round(y + j*dx);\r
3998     }\r
3999 \r
4000     Polygon( hdc, arrow, 7 );\r
4001 }\r
4002 \r
4003 /* [AS] Draw an arrow between two squares */\r
4004 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4005 {\r
4006     int s_x, s_y, d_x, d_y;\r
4007     HPEN hpen;\r
4008     HPEN holdpen;\r
4009     HBRUSH hbrush;\r
4010     HBRUSH holdbrush;\r
4011     LOGBRUSH stLB;\r
4012 \r
4013     if( s_col == d_col && s_row == d_row ) {\r
4014         return;\r
4015     }\r
4016 \r
4017     /* Get source and destination points */\r
4018     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4019     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4020 \r
4021     if( d_y > s_y ) {\r
4022         d_y += squareSize / 4;\r
4023     }\r
4024     else if( d_y < s_y ) {\r
4025         d_y += 3 * squareSize / 4;\r
4026     }\r
4027     else {\r
4028         d_y += squareSize / 2;\r
4029     }\r
4030 \r
4031     if( d_x > s_x ) {\r
4032         d_x += squareSize / 4;\r
4033     }\r
4034     else if( d_x < s_x ) {\r
4035         d_x += 3 * squareSize / 4;\r
4036     }\r
4037     else {\r
4038         d_x += squareSize / 2;\r
4039     }\r
4040 \r
4041     s_x += squareSize / 2;\r
4042     s_y += squareSize / 2;\r
4043 \r
4044     /* Adjust width */\r
4045     A_WIDTH = squareSize / 14;\r
4046 \r
4047     /* Draw */\r
4048     stLB.lbStyle = BS_SOLID;\r
4049     stLB.lbColor = appData.highlightArrowColor;\r
4050     stLB.lbHatch = 0;\r
4051 \r
4052     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4053     holdpen = SelectObject( hdc, hpen );\r
4054     hbrush = CreateBrushIndirect( &stLB );\r
4055     holdbrush = SelectObject( hdc, hbrush );\r
4056 \r
4057     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4058 \r
4059     SelectObject( hdc, holdpen );\r
4060     SelectObject( hdc, holdbrush );\r
4061     DeleteObject( hpen );\r
4062     DeleteObject( hbrush );\r
4063 }\r
4064 \r
4065 BOOL HasHighlightInfo()\r
4066 {\r
4067     BOOL result = FALSE;\r
4068 \r
4069     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4070         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4071     {\r
4072         result = TRUE;\r
4073     }\r
4074 \r
4075     return result;\r
4076 }\r
4077 \r
4078 BOOL IsDrawArrowEnabled()\r
4079 {\r
4080     BOOL result = FALSE;\r
4081 \r
4082     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4083         result = TRUE;\r
4084     }\r
4085 \r
4086     return result;\r
4087 }\r
4088 \r
4089 VOID DrawArrowHighlight( HDC hdc )\r
4090 {\r
4091     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4092         DrawArrowBetweenSquares( hdc,\r
4093             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4094             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4095     }\r
4096 }\r
4097 \r
4098 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4099 {\r
4100     HRGN result = NULL;\r
4101 \r
4102     if( HasHighlightInfo() ) {\r
4103         int x1, y1, x2, y2;\r
4104         int sx, sy, dx, dy;\r
4105 \r
4106         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4107         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4108 \r
4109         sx = MIN( x1, x2 );\r
4110         sy = MIN( y1, y2 );\r
4111         dx = MAX( x1, x2 ) + squareSize;\r
4112         dy = MAX( y1, y2 ) + squareSize;\r
4113 \r
4114         result = CreateRectRgn( sx, sy, dx, dy );\r
4115     }\r
4116 \r
4117     return result;\r
4118 }\r
4119 \r
4120 /*\r
4121     Warning: this function modifies the behavior of several other functions. \r
4122     \r
4123     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4124     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4125     repaint is scattered all over the place, which is not good for features such as\r
4126     "arrow highlighting" that require a full repaint of the board.\r
4127 \r
4128     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4129     user interaction, when speed is not so important) but especially to avoid errors\r
4130     in the displayed graphics.\r
4131 \r
4132     In such patched places, I always try refer to this function so there is a single\r
4133     place to maintain knowledge.\r
4134     \r
4135     To restore the original behavior, just return FALSE unconditionally.\r
4136 */\r
4137 BOOL IsFullRepaintPreferrable()\r
4138 {\r
4139     BOOL result = FALSE;\r
4140 \r
4141     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4142         /* Arrow may appear on the board */\r
4143         result = TRUE;\r
4144     }\r
4145 \r
4146     return result;\r
4147 }\r
4148 \r
4149 /* \r
4150     This function is called by DrawPosition to know whether a full repaint must\r
4151     be forced or not.\r
4152 \r
4153     Only DrawPosition may directly call this function, which makes use of \r
4154     some state information. Other function should call DrawPosition specifying \r
4155     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4156 */\r
4157 BOOL DrawPositionNeedsFullRepaint()\r
4158 {\r
4159     BOOL result = FALSE;\r
4160 \r
4161     /* \r
4162         Probably a slightly better policy would be to trigger a full repaint\r
4163         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4164         but animation is fast enough that it's difficult to notice.\r
4165     */\r
4166     if( animInfo.piece == EmptySquare ) {\r
4167         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4168             result = TRUE;\r
4169         }\r
4170     }\r
4171 \r
4172     return result;\r
4173 }\r
4174 \r
4175 VOID\r
4176 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4177 {\r
4178   int row, column, x, y, square_color, piece_color;\r
4179   ChessSquare piece;\r
4180   HBRUSH oldBrush;\r
4181   HDC texture_hdc = NULL;\r
4182 \r
4183   /* [AS] Initialize background textures if needed */\r
4184   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4185       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4186       if( backTextureSquareSize != squareSize \r
4187        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4188           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4189           backTextureSquareSize = squareSize;\r
4190           RebuildTextureSquareInfo();\r
4191       }\r
4192 \r
4193       texture_hdc = CreateCompatibleDC( hdc );\r
4194   }\r
4195 \r
4196   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4197     for (column = 0; column < BOARD_WIDTH; column++) {\r
4198   \r
4199       SquareToPos(row, column, &x, &y);\r
4200 \r
4201       piece = board[row][column];\r
4202 \r
4203       square_color = ((column + row) % 2) == 1;\r
4204       if( gameInfo.variant == VariantXiangqi ) {\r
4205           square_color = !InPalace(row, column);\r
4206           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4207           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4208       }\r
4209       piece_color = (int) piece < (int) BlackPawn;\r
4210 \r
4211 \r
4212       /* [HGM] holdings file: light square or black */\r
4213       if(column == BOARD_LEFT-2) {\r
4214             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4215                 square_color = 1;\r
4216             else {\r
4217                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4218                 continue;\r
4219             }\r
4220       } else\r
4221       if(column == BOARD_RGHT + 1 ) {\r
4222             if( row < gameInfo.holdingsSize )\r
4223                 square_color = 1;\r
4224             else {\r
4225                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4226                 continue;\r
4227             }\r
4228       }\r
4229       if(column == BOARD_LEFT-1 ) /* left align */\r
4230             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4231       else if( column == BOARD_RGHT) /* right align */\r
4232             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4233       else\r
4234       if (appData.monoMode) {\r
4235         if (piece == EmptySquare) {\r
4236           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4237                  square_color ? WHITENESS : BLACKNESS);\r
4238         } else {\r
4239           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4240         }\r
4241       } \r
4242       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4243           /* [AS] Draw the square using a texture bitmap */\r
4244           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4245           int r = row, c = column; // [HGM] do not flip board in flipView\r
4246           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4247 \r
4248           DrawTile( x, y, \r
4249               squareSize, squareSize, \r
4250               hdc, \r
4251               texture_hdc,\r
4252               backTextureSquareInfo[r][c].mode,\r
4253               backTextureSquareInfo[r][c].x,\r
4254               backTextureSquareInfo[r][c].y );\r
4255 \r
4256           SelectObject( texture_hdc, hbm );\r
4257 \r
4258           if (piece != EmptySquare) {\r
4259               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4260           }\r
4261       }\r
4262       else {\r
4263         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4264 \r
4265         oldBrush = SelectObject(hdc, brush );\r
4266         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4267         SelectObject(hdc, oldBrush);\r
4268         if (piece != EmptySquare)\r
4269           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4270       }\r
4271     }\r
4272   }\r
4273 \r
4274   if( texture_hdc != NULL ) {\r
4275     DeleteDC( texture_hdc );\r
4276   }\r
4277 }\r
4278 \r
4279 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4280 void fputDW(FILE *f, int x)\r
4281 {\r
4282         fputc(x     & 255, f);\r
4283         fputc(x>>8  & 255, f);\r
4284         fputc(x>>16 & 255, f);\r
4285         fputc(x>>24 & 255, f);\r
4286 }\r
4287 \r
4288 #define MAX_CLIPS 200   /* more than enough */\r
4289 \r
4290 VOID\r
4291 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4292 {\r
4293 //  HBITMAP bufferBitmap;\r
4294   BITMAP bi;\r
4295 //  RECT Rect;\r
4296   HDC tmphdc;\r
4297   HBITMAP hbm;\r
4298   int w = 100, h = 50;\r
4299 \r
4300   if(logo == NULL) return;\r
4301 //  GetClientRect(hwndMain, &Rect);\r
4302 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4303 //                                      Rect.bottom-Rect.top+1);\r
4304   tmphdc = CreateCompatibleDC(hdc);\r
4305   hbm = SelectObject(tmphdc, logo);\r
4306   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4307             w = bi.bmWidth;\r
4308             h = bi.bmHeight;\r
4309   }\r
4310   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4311                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4312   SelectObject(tmphdc, hbm);\r
4313   DeleteDC(tmphdc);\r
4314 }\r
4315 \r
4316 VOID\r
4317 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4318 {\r
4319   static Board lastReq, lastDrawn;\r
4320   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4321   static int lastDrawnFlipView = 0;\r
4322   static int lastReqValid = 0, lastDrawnValid = 0;\r
4323   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4324   HDC tmphdc;\r
4325   HDC hdcmem;\r
4326   HBITMAP bufferBitmap;\r
4327   HBITMAP oldBitmap;\r
4328   RECT Rect;\r
4329   HRGN clips[MAX_CLIPS];\r
4330   ChessSquare dragged_piece = EmptySquare;\r
4331 \r
4332   /* I'm undecided on this - this function figures out whether a full\r
4333    * repaint is necessary on its own, so there's no real reason to have the\r
4334    * caller tell it that.  I think this can safely be set to FALSE - but\r
4335    * if we trust the callers not to request full repaints unnessesarily, then\r
4336    * we could skip some clipping work.  In other words, only request a full\r
4337    * redraw when the majority of pieces have changed positions (ie. flip, \r
4338    * gamestart and similar)  --Hawk\r
4339    */\r
4340   Boolean fullrepaint = repaint;\r
4341 \r
4342   if( DrawPositionNeedsFullRepaint() ) {\r
4343       fullrepaint = TRUE;\r
4344   }\r
4345 \r
4346 #if 0\r
4347   if( fullrepaint ) {\r
4348       static int repaint_count = 0;\r
4349       char buf[128];\r
4350 \r
4351       repaint_count++;\r
4352       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4353       OutputDebugString( buf );\r
4354   }\r
4355 #endif\r
4356 \r
4357   if (board == NULL) {\r
4358     if (!lastReqValid) {\r
4359       return;\r
4360     }\r
4361     board = lastReq;\r
4362   } else {\r
4363     CopyBoard(lastReq, board);\r
4364     lastReqValid = 1;\r
4365   }\r
4366 \r
4367   if (doingSizing) {\r
4368     return;\r
4369   }\r
4370 \r
4371   if (IsIconic(hwndMain)) {\r
4372     return;\r
4373   }\r
4374 \r
4375   if (hdc == NULL) {\r
4376     hdc = GetDC(hwndMain);\r
4377     if (!appData.monoMode) {\r
4378       SelectPalette(hdc, hPal, FALSE);\r
4379       RealizePalette(hdc);\r
4380     }\r
4381     releaseDC = TRUE;\r
4382   } else {\r
4383     releaseDC = FALSE;\r
4384   }\r
4385 \r
4386 #if 0\r
4387   fprintf(debugFP, "*******************************\n"\r
4388                    "repaint = %s\n"\r
4389                    "dragInfo.from (%d,%d)\n"\r
4390                    "dragInfo.start (%d,%d)\n"\r
4391                    "dragInfo.pos (%d,%d)\n"\r
4392                    "dragInfo.lastpos (%d,%d)\n", \r
4393                     repaint ? "TRUE" : "FALSE",\r
4394                     dragInfo.from.x, dragInfo.from.y, \r
4395                     dragInfo.start.x, dragInfo.start.y,\r
4396                     dragInfo.pos.x, dragInfo.pos.y,\r
4397                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4398   fprintf(debugFP, "prev:  ");\r
4399   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4400     for (column = 0; column < BOARD_WIDTH; column++) {\r
4401       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4402     }\r
4403   }\r
4404   fprintf(debugFP, "\n");\r
4405   fprintf(debugFP, "board: ");\r
4406   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4407     for (column = 0; column < BOARD_WIDTH; column++) {\r
4408       fprintf(debugFP, "%d ", board[row][column]);\r
4409     }\r
4410   }\r
4411   fprintf(debugFP, "\n");\r
4412   fflush(debugFP);\r
4413 #endif\r
4414 \r
4415   /* Create some work-DCs */\r
4416   hdcmem = CreateCompatibleDC(hdc);\r
4417   tmphdc = CreateCompatibleDC(hdc);\r
4418 \r
4419   /* If dragging is in progress, we temporarely remove the piece */\r
4420   /* [HGM] or temporarily decrease count if stacked              */\r
4421   /*       !! Moved to before board compare !!                   */\r
4422   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4423     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4424     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4425             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4426         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4427     } else \r
4428     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4429             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4430         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4431     } else \r
4432         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4433   }\r
4434 \r
4435   /* Figure out which squares need updating by comparing the \r
4436    * newest board with the last drawn board and checking if\r
4437    * flipping has changed.\r
4438    */\r
4439   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4440     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4441       for (column = 0; column < BOARD_WIDTH; column++) {\r
4442         if (lastDrawn[row][column] != board[row][column]) {\r
4443           SquareToPos(row, column, &x, &y);\r
4444           clips[num_clips++] =\r
4445             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4446         }\r
4447       }\r
4448     }\r
4449     for (i=0; i<2; i++) {\r
4450       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4451           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4452         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4453             lastDrawnHighlight.sq[i].y >= 0) {\r
4454           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4455                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4456           clips[num_clips++] =\r
4457             CreateRectRgn(x - lineGap, y - lineGap, \r
4458                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4459         }\r
4460         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4461           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4462           clips[num_clips++] =\r
4463             CreateRectRgn(x - lineGap, y - lineGap, \r
4464                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4465         }\r
4466       }\r
4467     }\r
4468     for (i=0; i<2; i++) {\r
4469       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4470           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4471         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4472             lastDrawnPremove.sq[i].y >= 0) {\r
4473           SquareToPos(lastDrawnPremove.sq[i].y,\r
4474                       lastDrawnPremove.sq[i].x, &x, &y);\r
4475           clips[num_clips++] =\r
4476             CreateRectRgn(x - lineGap, y - lineGap, \r
4477                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4478         }\r
4479         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4480             premoveHighlightInfo.sq[i].y >= 0) {\r
4481           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4482                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4483           clips[num_clips++] =\r
4484             CreateRectRgn(x - lineGap, y - lineGap, \r
4485                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4486         }\r
4487       }\r
4488     }\r
4489   } else {\r
4490     fullrepaint = TRUE;\r
4491   }\r
4492 \r
4493   /* Create a buffer bitmap - this is the actual bitmap\r
4494    * being written to.  When all the work is done, we can\r
4495    * copy it to the real DC (the screen).  This avoids\r
4496    * the problems with flickering.\r
4497    */\r
4498   GetClientRect(hwndMain, &Rect);\r
4499   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4500                                         Rect.bottom-Rect.top+1);\r
4501   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4502   if (!appData.monoMode) {\r
4503     SelectPalette(hdcmem, hPal, FALSE);\r
4504   }\r
4505 \r
4506   /* Create clips for dragging */\r
4507   if (!fullrepaint) {\r
4508     if (dragInfo.from.x >= 0) {\r
4509       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4510       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4511     }\r
4512     if (dragInfo.start.x >= 0) {\r
4513       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4514       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4515     }\r
4516     if (dragInfo.pos.x >= 0) {\r
4517       x = dragInfo.pos.x - squareSize / 2;\r
4518       y = dragInfo.pos.y - squareSize / 2;\r
4519       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4520     }\r
4521     if (dragInfo.lastpos.x >= 0) {\r
4522       x = dragInfo.lastpos.x - squareSize / 2;\r
4523       y = dragInfo.lastpos.y - squareSize / 2;\r
4524       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4525     }\r
4526   }\r
4527 \r
4528   /* Are we animating a move?  \r
4529    * If so, \r
4530    *   - remove the piece from the board (temporarely)\r
4531    *   - calculate the clipping region\r
4532    */\r
4533   if (!fullrepaint) {\r
4534     if (animInfo.piece != EmptySquare) {\r
4535       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4536       x = boardRect.left + animInfo.lastpos.x;\r
4537       y = boardRect.top + animInfo.lastpos.y;\r
4538       x2 = boardRect.left + animInfo.pos.x;\r
4539       y2 = boardRect.top + animInfo.pos.y;\r
4540       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4541       /* Slight kludge.  The real problem is that after AnimateMove is\r
4542          done, the position on the screen does not match lastDrawn.\r
4543          This currently causes trouble only on e.p. captures in\r
4544          atomic, where the piece moves to an empty square and then\r
4545          explodes.  The old and new positions both had an empty square\r
4546          at the destination, but animation has drawn a piece there and\r
4547          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4548       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4549     }\r
4550   }\r
4551 \r
4552   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4553   if (num_clips == 0)\r
4554     fullrepaint = TRUE;\r
4555 \r
4556   /* Set clipping on the memory DC */\r
4557   if (!fullrepaint) {\r
4558     SelectClipRgn(hdcmem, clips[0]);\r
4559     for (x = 1; x < num_clips; x++) {\r
4560       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4561         abort();  // this should never ever happen!\r
4562     }\r
4563   }\r
4564 \r
4565   /* Do all the drawing to the memory DC */\r
4566   if(explodeInfo.radius) { // [HGM] atomic\r
4567         HBRUSH oldBrush;\r
4568         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4569         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4570         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4571         x += squareSize/2;\r
4572         y += squareSize/2;\r
4573         if(!fullrepaint) {\r
4574           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4575           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4576         }\r
4577         DrawGridOnDC(hdcmem);\r
4578         DrawHighlightsOnDC(hdcmem);\r
4579         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4580         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4581         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4582         SelectObject(hdcmem, oldBrush);\r
4583   } else {\r
4584     DrawGridOnDC(hdcmem);\r
4585     DrawHighlightsOnDC(hdcmem);\r
4586     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4587   }\r
4588   if(logoHeight) {\r
4589         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4590         if(appData.autoLogo) {\r
4591           \r
4592           switch(gameMode) { // pick logos based on game mode\r
4593             case IcsObserving:\r
4594                 whiteLogo = second.programLogo; // ICS logo\r
4595                 blackLogo = second.programLogo;\r
4596             default:\r
4597                 break;\r
4598             case IcsPlayingWhite:\r
4599                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4600                 blackLogo = second.programLogo; // ICS logo\r
4601                 break;\r
4602             case IcsPlayingBlack:\r
4603                 whiteLogo = second.programLogo; // ICS logo\r
4604                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4605                 break;\r
4606             case TwoMachinesPlay:\r
4607                 if(first.twoMachinesColor[0] == 'b') {\r
4608                     whiteLogo = second.programLogo;\r
4609                     blackLogo = first.programLogo;\r
4610                 }\r
4611                 break;\r
4612             case MachinePlaysWhite:\r
4613                 blackLogo = userLogo;\r
4614                 break;\r
4615             case MachinePlaysBlack:\r
4616                 whiteLogo = userLogo;\r
4617                 blackLogo = first.programLogo;\r
4618           }\r
4619         }\r
4620         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4621         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4622   }\r
4623 \r
4624   if( appData.highlightMoveWithArrow ) {\r
4625     DrawArrowHighlight(hdcmem);\r
4626   }\r
4627 \r
4628   DrawCoordsOnDC(hdcmem);\r
4629 \r
4630   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4631                  /* to make sure lastDrawn contains what is actually drawn */\r
4632 \r
4633   /* Put the dragged piece back into place and draw it (out of place!) */\r
4634     if (dragged_piece != EmptySquare) {\r
4635     /* [HGM] or restack */\r
4636     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4637                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4638     else\r
4639     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4640                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4641     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4642     x = dragInfo.pos.x - squareSize / 2;\r
4643     y = dragInfo.pos.y - squareSize / 2;\r
4644     DrawPieceOnDC(hdcmem, dragged_piece,\r
4645                   ((int) dragged_piece < (int) BlackPawn), \r
4646                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4647   }   \r
4648   \r
4649   /* Put the animated piece back into place and draw it */\r
4650   if (animInfo.piece != EmptySquare) {\r
4651     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4652     x = boardRect.left + animInfo.pos.x;\r
4653     y = boardRect.top + animInfo.pos.y;\r
4654     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4655                   ((int) animInfo.piece < (int) BlackPawn),\r
4656                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4657   }\r
4658 \r
4659   /* Release the bufferBitmap by selecting in the old bitmap \r
4660    * and delete the memory DC\r
4661    */\r
4662   SelectObject(hdcmem, oldBitmap);\r
4663   DeleteDC(hdcmem);\r
4664 \r
4665   /* Set clipping on the target DC */\r
4666   if (!fullrepaint) {\r
4667     SelectClipRgn(hdc, clips[0]);\r
4668     for (x = 1; x < num_clips; x++) {\r
4669       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4670         abort();   // this should never ever happen!\r
4671     } \r
4672   }\r
4673 \r
4674   /* Copy the new bitmap onto the screen in one go.\r
4675    * This way we avoid any flickering\r
4676    */\r
4677   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4678   BitBlt(hdc, boardRect.left, boardRect.top,\r
4679          boardRect.right - boardRect.left,\r
4680          boardRect.bottom - boardRect.top,\r
4681          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4682   if(saveDiagFlag) { \r
4683     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4684     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4685 \r
4686     GetObject(bufferBitmap, sizeof(b), &b);\r
4687     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4688         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4689         bih.biWidth = b.bmWidth;\r
4690         bih.biHeight = b.bmHeight;\r
4691         bih.biPlanes = 1;\r
4692         bih.biBitCount = b.bmBitsPixel;\r
4693         bih.biCompression = 0;\r
4694         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4695         bih.biXPelsPerMeter = 0;\r
4696         bih.biYPelsPerMeter = 0;\r
4697         bih.biClrUsed = 0;\r
4698         bih.biClrImportant = 0;\r
4699 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4700 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4701         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4702 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4703 \r
4704 #if 1\r
4705         wb = b.bmWidthBytes;\r
4706         // count colors\r
4707         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4708                 int k = ((int*) pData)[i];\r
4709                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4710                 if(j >= 16) break;\r
4711                 color[j] = k;\r
4712                 if(j >= nrColors) nrColors = j+1;\r
4713         }\r
4714         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4715                 INT p = 0;\r
4716                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4717                     for(w=0; w<(wb>>2); w+=2) {\r
4718                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4719                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4720                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4721                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4722                         pData[p++] = m | j<<4;\r
4723                     }\r
4724                     while(p&3) pData[p++] = 0;\r
4725                 }\r
4726                 fac = 3;\r
4727                 wb = ((wb+31)>>5)<<2;\r
4728         }\r
4729         // write BITMAPFILEHEADER\r
4730         fprintf(diagFile, "BM");\r
4731         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4732         fputDW(diagFile, 0);\r
4733         fputDW(diagFile, 0x36 + (fac?64:0));\r
4734         // write BITMAPINFOHEADER\r
4735         fputDW(diagFile, 40);\r
4736         fputDW(diagFile, b.bmWidth);\r
4737         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4738         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4739         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4740         fputDW(diagFile, 0);\r
4741         fputDW(diagFile, 0);\r
4742         fputDW(diagFile, 0);\r
4743         fputDW(diagFile, 0);\r
4744         fputDW(diagFile, 0);\r
4745         fputDW(diagFile, 0);\r
4746         // write color table\r
4747         if(fac)\r
4748         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4749         // write bitmap data\r
4750         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4751                 fputc(pData[i], diagFile);\r
4752 #endif\r
4753      }\r
4754   }\r
4755 \r
4756   SelectObject(tmphdc, oldBitmap);\r
4757 \r
4758   /* Massive cleanup */\r
4759   for (x = 0; x < num_clips; x++)\r
4760     DeleteObject(clips[x]);\r
4761 \r
4762   DeleteDC(tmphdc);\r
4763   DeleteObject(bufferBitmap);\r
4764 \r
4765   if (releaseDC) \r
4766     ReleaseDC(hwndMain, hdc);\r
4767   \r
4768   if (lastDrawnFlipView != flipView) {\r
4769     if (flipView)\r
4770       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4771     else\r
4772       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4773   }\r
4774 \r
4775 /*  CopyBoard(lastDrawn, board);*/\r
4776   lastDrawnHighlight = highlightInfo;\r
4777   lastDrawnPremove   = premoveHighlightInfo;\r
4778   lastDrawnFlipView = flipView;\r
4779   lastDrawnValid = 1;\r
4780 }\r
4781 \r
4782 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4783 int\r
4784 SaveDiagram(f)\r
4785      FILE *f;\r
4786 {\r
4787     saveDiagFlag = 1; diagFile = f;\r
4788     HDCDrawPosition(NULL, TRUE, NULL);\r
4789 \r
4790     saveDiagFlag = 0;\r
4791 \r
4792 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4793     \r
4794     fclose(f);\r
4795     return TRUE;\r
4796 }\r
4797 \r
4798 \r
4799 /*---------------------------------------------------------------------------*\\r
4800 | CLIENT PAINT PROCEDURE\r
4801 |   This is the main event-handler for the WM_PAINT message.\r
4802 |\r
4803 \*---------------------------------------------------------------------------*/\r
4804 VOID\r
4805 PaintProc(HWND hwnd)\r
4806 {\r
4807   HDC         hdc;\r
4808   PAINTSTRUCT ps;\r
4809   HFONT       oldFont;\r
4810 \r
4811   if((hdc = BeginPaint(hwnd, &ps))) {\r
4812     if (IsIconic(hwnd)) {\r
4813       DrawIcon(hdc, 2, 2, iconCurrent);\r
4814     } else {\r
4815       if (!appData.monoMode) {\r
4816         SelectPalette(hdc, hPal, FALSE);\r
4817         RealizePalette(hdc);\r
4818       }\r
4819       HDCDrawPosition(hdc, 1, NULL);\r
4820       oldFont =\r
4821         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4822       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4823                  ETO_CLIPPED|ETO_OPAQUE,\r
4824                  &messageRect, messageText, strlen(messageText), NULL);\r
4825       SelectObject(hdc, oldFont);\r
4826       DisplayBothClocks();\r
4827     }\r
4828     EndPaint(hwnd,&ps);\r
4829   }\r
4830 \r
4831   return;\r
4832 }\r
4833 \r
4834 \r
4835 /*\r
4836  * If the user selects on a border boundary, return -1; if off the board,\r
4837  *   return -2.  Otherwise map the event coordinate to the square.\r
4838  * The offset boardRect.left or boardRect.top must already have been\r
4839  *   subtracted from x.\r
4840  */\r
4841 int\r
4842 EventToSquare(int x)\r
4843 {\r
4844   if (x <= 0)\r
4845     return -2;\r
4846   if (x < lineGap)\r
4847     return -1;\r
4848   x -= lineGap;\r
4849   if ((x % (squareSize + lineGap)) >= squareSize)\r
4850     return -1;\r
4851   x /= (squareSize + lineGap);\r
4852   if (x >= BOARD_SIZE)\r
4853     return -2;\r
4854   return x;\r
4855 }\r
4856 \r
4857 typedef struct {\r
4858   char piece;\r
4859   int command;\r
4860   char* name;\r
4861 } DropEnable;\r
4862 \r
4863 DropEnable dropEnables[] = {\r
4864   { 'P', DP_Pawn, "Pawn" },\r
4865   { 'N', DP_Knight, "Knight" },\r
4866   { 'B', DP_Bishop, "Bishop" },\r
4867   { 'R', DP_Rook, "Rook" },\r
4868   { 'Q', DP_Queen, "Queen" },\r
4869 };\r
4870 \r
4871 VOID\r
4872 SetupDropMenu(HMENU hmenu)\r
4873 {\r
4874   int i, count, enable;\r
4875   char *p;\r
4876   extern char white_holding[], black_holding[];\r
4877   char item[MSG_SIZ];\r
4878 \r
4879   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4880     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4881                dropEnables[i].piece);\r
4882     count = 0;\r
4883     while (p && *p++ == dropEnables[i].piece) count++;\r
4884     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4885     enable = count > 0 || !appData.testLegality\r
4886       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4887                       && !appData.icsActive);\r
4888     ModifyMenu(hmenu, dropEnables[i].command,\r
4889                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4890                dropEnables[i].command, item);\r
4891   }\r
4892 }\r
4893 \r
4894 static int fromX = -1, fromY = -1, toX, toY;\r
4895 \r
4896 /* Event handler for mouse messages */\r
4897 VOID\r
4898 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4899 {\r
4900   int x, y;\r
4901   POINT pt;\r
4902   static int recursive = 0;\r
4903   HMENU hmenu;\r
4904 //  BOOLEAN needsRedraw = FALSE;\r
4905   BOOLEAN saveAnimate;\r
4906   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4907   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4908   ChessMove moveType;\r
4909 \r
4910   if (recursive) {\r
4911     if (message == WM_MBUTTONUP) {\r
4912       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4913          to the middle button: we simulate pressing the left button too!\r
4914          */\r
4915       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4916       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4917     }\r
4918     return;\r
4919   }\r
4920   recursive++;\r
4921   \r
4922   pt.x = LOWORD(lParam);\r
4923   pt.y = HIWORD(lParam);\r
4924   x = EventToSquare(pt.x - boardRect.left);\r
4925   y = EventToSquare(pt.y - boardRect.top);\r
4926   if (!flipView && y >= 0) {\r
4927     y = BOARD_HEIGHT - 1 - y;\r
4928   }\r
4929   if (flipView && x >= 0) {\r
4930     x = BOARD_WIDTH - 1 - x;\r
4931   }\r
4932 \r
4933   switch (message) {\r
4934   case WM_LBUTTONDOWN:\r
4935     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4936         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4937         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4938         if(gameInfo.holdingsWidth && \r
4939                 (WhiteOnMove(currentMove) \r
4940                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4941                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4942             // click in right holdings, for determining promotion piece\r
4943             ChessSquare p = boards[currentMove][y][x];\r
4944             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4945             if(p != EmptySquare) {\r
4946                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4947                 fromX = fromY = -1;\r
4948                 break;\r
4949             }\r
4950         }\r
4951         DrawPosition(FALSE, boards[currentMove]);\r
4952         break;\r
4953     }\r
4954     ErrorPopDown();\r
4955     sameAgain = FALSE;\r
4956     if (y == -2) {\r
4957       /* Downclick vertically off board; check if on clock */\r
4958       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4959         if (gameMode == EditPosition) {\r
4960           SetWhiteToPlayEvent();\r
4961         } else if (gameMode == IcsPlayingBlack ||\r
4962                    gameMode == MachinePlaysWhite) {\r
4963           CallFlagEvent();\r
4964         } else if (gameMode == EditGame) {\r
4965           AdjustClock(flipClock, -1);\r
4966         }\r
4967       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4968         if (gameMode == EditPosition) {\r
4969           SetBlackToPlayEvent();\r
4970         } else if (gameMode == IcsPlayingWhite ||\r
4971                    gameMode == MachinePlaysBlack) {\r
4972           CallFlagEvent();\r
4973         } else if (gameMode == EditGame) {\r
4974           AdjustClock(!flipClock, -1);\r
4975         }\r
4976       }\r
4977       if (!appData.highlightLastMove) {\r
4978         ClearHighlights();\r
4979         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4980       }\r
4981       fromX = fromY = -1;\r
4982       dragInfo.start.x = dragInfo.start.y = -1;\r
4983       dragInfo.from = dragInfo.start;\r
4984       break;\r
4985     } else if (x < 0 || y < 0\r
4986       /* [HGM] block clicks between board and holdings */\r
4987               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4988               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4989               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4990         /* EditPosition, empty square, or different color piece;\r
4991            click-click move is possible */\r
4992                                ) {\r
4993       break;\r
4994     } else if (fromX == x && fromY == y) {\r
4995       /* Downclick on same square again */\r
4996       ClearHighlights();\r
4997       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4998       sameAgain = TRUE;  \r
4999     } else if (fromX != -1 &&\r
5000                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5001                                                                         ) {\r
5002       /* Downclick on different square. */\r
5003       /* [HGM] if on holdings file, should count as new first click ! */\r
5004       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5005         toX = x;\r
5006         toY = y;\r
5007         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5008            to make sure move is legal before showing promotion popup */\r
5009         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5010         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5011                 fromX = fromY = -1; \r
5012                 ClearHighlights();\r
5013                 DrawPosition(FALSE, boards[currentMove]);\r
5014                 break; \r
5015         } else \r
5016         if(moveType != ImpossibleMove) {\r
5017           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5018           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5019             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5020               appData.alwaysPromoteToQueen)) {\r
5021                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5022                   if (!appData.highlightLastMove) {\r
5023                       ClearHighlights();\r
5024                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5025                   }\r
5026           } else\r
5027           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5028                   SetHighlights(fromX, fromY, toX, toY);\r
5029                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5030                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5031                      If promotion to Q is legal, all are legal! */\r
5032                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5033                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5034                     // kludge to temporarily execute move on display, wthout promotng yet\r
5035                     promotionChoice = TRUE;\r
5036                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5037                     boards[currentMove][toY][toX] = p;\r
5038                     DrawPosition(FALSE, boards[currentMove]);\r
5039                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5040                     boards[currentMove][toY][toX] = q;\r
5041                   } else\r
5042                   PromotionPopup(hwnd);\r
5043           } else {       /* not a promotion */\r
5044              if (appData.animate || appData.highlightLastMove) {\r
5045                  SetHighlights(fromX, fromY, toX, toY);\r
5046              } else {\r
5047                  ClearHighlights();\r
5048              }\r
5049              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5050              fromX = fromY = -1;\r
5051              if (appData.animate && !appData.highlightLastMove) {\r
5052                   ClearHighlights();\r
5053                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5054              }\r
5055           }\r
5056           break;\r
5057         }\r
5058         if (gotPremove) {\r
5059             /* [HGM] it seemed that braces were missing here */\r
5060             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5061             fromX = fromY = -1;\r
5062             break;\r
5063         }\r
5064       }\r
5065       ClearHighlights();\r
5066       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5067     }\r
5068     /* First downclick, or restart on a square with same color piece */\r
5069     if (!frozen && OKToStartUserMove(x, y)) {\r
5070       fromX = x;\r
5071       fromY = y;\r
5072       dragInfo.lastpos = pt;\r
5073       dragInfo.from.x = fromX;\r
5074       dragInfo.from.y = fromY;\r
5075       dragInfo.start = dragInfo.from;\r
5076       SetCapture(hwndMain);\r
5077     } else {\r
5078       fromX = fromY = -1;\r
5079       dragInfo.start.x = dragInfo.start.y = -1;\r
5080       dragInfo.from = dragInfo.start;\r
5081       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5082     }\r
5083     break;\r
5084 \r
5085   case WM_LBUTTONUP:\r
5086     ReleaseCapture();\r
5087     if (fromX == -1) break;\r
5088     if (x == fromX && y == fromY) {\r
5089       dragInfo.from.x = dragInfo.from.y = -1;\r
5090       /* Upclick on same square */\r
5091       if (sameAgain) {\r
5092         /* Clicked same square twice: abort click-click move */\r
5093         fromX = fromY = -1;\r
5094         gotPremove = 0;\r
5095         ClearPremoveHighlights();\r
5096       } else {\r
5097         /* First square clicked: start click-click move */\r
5098         SetHighlights(fromX, fromY, -1, -1);\r
5099       }\r
5100       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5101     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5102       /* Errant click; ignore */\r
5103       break;\r
5104     } else {\r
5105       /* Finish drag move. */\r
5106     if (appData.debugMode) {\r
5107         fprintf(debugFP, "release\n");\r
5108     }\r
5109       dragInfo.from.x = dragInfo.from.y = -1;\r
5110       toX = x;\r
5111       toY = y;\r
5112       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5113       appData.animate = appData.animate && !appData.animateDragging;\r
5114       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5115       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5116                 fromX = fromY = -1; \r
5117                 ClearHighlights();\r
5118                 DrawPosition(FALSE, boards[currentMove]);\r
5119                 break; \r
5120       } else \r
5121       if(moveType != ImpossibleMove) {\r
5122           /* [HGM] use move type to determine if move is promotion.\r
5123              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5124           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5125             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5126               appData.alwaysPromoteToQueen)) \r
5127                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5128           else \r
5129           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5130                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5131                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5132                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5133                     // kludge to temporarily execute move on display, wthout promotng yet\r
5134                     promotionChoice = TRUE;\r
5135                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5136                     boards[currentMove][toY][toX] = p;\r
5137                     DrawPosition(FALSE, boards[currentMove]);\r
5138                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5139                     boards[currentMove][toY][toX] = q;\r
5140                     break;\r
5141                   } else\r
5142                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5143           } else {\r
5144             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5145                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5146                                         moveType == WhiteCapturesEnPassant || \r
5147                                         moveType == BlackCapturesEnPassant   ) )\r
5148                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5149             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5150           }\r
5151       }\r
5152       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5153       appData.animate = saveAnimate;\r
5154       fromX = fromY = -1;\r
5155       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5156         ClearHighlights();\r
5157       }\r
5158       if (appData.animate || appData.animateDragging ||\r
5159           appData.highlightDragging || gotPremove) {\r
5160         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5161       }\r
5162     }\r
5163     dragInfo.start.x = dragInfo.start.y = -1; \r
5164     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5165     break;\r
5166 \r
5167   case WM_MOUSEMOVE:\r
5168     if ((appData.animateDragging || appData.highlightDragging)\r
5169         && (wParam & MK_LBUTTON)\r
5170         && dragInfo.from.x >= 0) \r
5171     {\r
5172       BOOL full_repaint = FALSE;\r
5173 \r
5174       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5175       if (appData.animateDragging) {\r
5176         dragInfo.pos = pt;\r
5177       }\r
5178       if (appData.highlightDragging) {\r
5179         SetHighlights(fromX, fromY, x, y);\r
5180         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5181             full_repaint = TRUE;\r
5182         }\r
5183       }\r
5184       \r
5185       DrawPosition( full_repaint, NULL);\r
5186       \r
5187       dragInfo.lastpos = dragInfo.pos;\r
5188     }\r
5189     break;\r
5190 \r
5191   case WM_MOUSEWHEEL: // [DM]\r
5192     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5193        /* Mouse Wheel is being rolled forward\r
5194         * Play moves forward\r
5195         */\r
5196        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5197                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5198        /* Mouse Wheel is being rolled backward\r
5199         * Play moves backward\r
5200         */\r
5201        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5202                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5203     }\r
5204     break;\r
5205 \r
5206   case WM_MBUTTONDOWN:\r
5207   case WM_RBUTTONDOWN:\r
5208     ErrorPopDown();\r
5209     ReleaseCapture();\r
5210     fromX = fromY = -1;\r
5211     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5212     dragInfo.start.x = dragInfo.start.y = -1;\r
5213     dragInfo.from = dragInfo.start;\r
5214     dragInfo.lastpos = dragInfo.pos;\r
5215     if (appData.highlightDragging) {\r
5216       ClearHighlights();\r
5217     }\r
5218     if(y == -2) {\r
5219       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5220       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5221           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5222       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5223           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5224       }\r
5225     }\r
5226     DrawPosition(TRUE, NULL);\r
5227 \r
5228     switch (gameMode) {\r
5229     case EditPosition:\r
5230     case IcsExamining:\r
5231       if (x < 0 || y < 0) break;\r
5232       fromX = x;\r
5233       fromY = y;\r
5234       if (message == WM_MBUTTONDOWN) {\r
5235         buttonCount = 3;  /* even if system didn't think so */\r
5236         if (wParam & MK_SHIFT) \r
5237           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5238         else\r
5239           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5240       } else { /* message == WM_RBUTTONDOWN */\r
5241 #if 0\r
5242         if (buttonCount == 3) {\r
5243           if (wParam & MK_SHIFT) \r
5244             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5245           else\r
5246             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5247         } else {\r
5248           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5249         }\r
5250 #else\r
5251         /* Just have one menu, on the right button.  Windows users don't\r
5252            think to try the middle one, and sometimes other software steals\r
5253            it, or it doesn't really exist. */\r
5254         if(gameInfo.variant != VariantShogi)\r
5255             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5256         else\r
5257             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5258 #endif\r
5259       }\r
5260       break;\r
5261     case IcsPlayingWhite:\r
5262     case IcsPlayingBlack:\r
5263     case EditGame:\r
5264     case MachinePlaysWhite:\r
5265     case MachinePlaysBlack:\r
5266       if (appData.testLegality &&\r
5267           gameInfo.variant != VariantBughouse &&\r
5268           gameInfo.variant != VariantCrazyhouse) break;\r
5269       if (x < 0 || y < 0) break;\r
5270       fromX = x;\r
5271       fromY = y;\r
5272       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5273       SetupDropMenu(hmenu);\r
5274       MenuPopup(hwnd, pt, hmenu, -1);\r
5275       break;\r
5276     default:\r
5277       break;\r
5278     }\r
5279     break;\r
5280   }\r
5281 \r
5282   recursive--;\r
5283 }\r
5284 \r
5285 /* Preprocess messages for buttons in main window */\r
5286 LRESULT CALLBACK\r
5287 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5288 {\r
5289   int id = GetWindowLong(hwnd, GWL_ID);\r
5290   int i, dir;\r
5291 \r
5292   for (i=0; i<N_BUTTONS; i++) {\r
5293     if (buttonDesc[i].id == id) break;\r
5294   }\r
5295   if (i == N_BUTTONS) return 0;\r
5296   switch (message) {\r
5297   case WM_KEYDOWN:\r
5298     switch (wParam) {\r
5299     case VK_LEFT:\r
5300     case VK_RIGHT:\r
5301       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5302       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5303       return TRUE;\r
5304     }\r
5305     break;\r
5306   case WM_CHAR:\r
5307     switch (wParam) {\r
5308     case '\r':\r
5309       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5310       return TRUE;\r
5311     case '\t':\r
5312       if (appData.icsActive) {\r
5313         if (GetKeyState(VK_SHIFT) < 0) {\r
5314           /* shifted */\r
5315           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5316           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5317           SetFocus(h);\r
5318         } else {\r
5319           /* unshifted */\r
5320           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5321           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5322           SetFocus(h);\r
5323         }\r
5324         return TRUE;\r
5325       }\r
5326       break;\r
5327     default:\r
5328       if (appData.icsActive) {\r
5329         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5330         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5331         SetFocus(h);\r
5332         SendMessage(h, WM_CHAR, wParam, lParam);\r
5333         return TRUE;\r
5334       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5335         PopUpMoveDialog((char)wParam);\r
5336       }\r
5337       break;\r
5338     }\r
5339     break;\r
5340   }\r
5341   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5342 }\r
5343 \r
5344 /* Process messages for Promotion dialog box */\r
5345 LRESULT CALLBACK\r
5346 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5347 {\r
5348   char promoChar;\r
5349 \r
5350   switch (message) {\r
5351   case WM_INITDIALOG: /* message: initialize dialog box */\r
5352     /* Center the dialog over the application window */\r
5353     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5354     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5355       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5356        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5357                SW_SHOW : SW_HIDE);\r
5358     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5359     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5360        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5361          PieceToChar(WhiteAngel) != '~') ||\r
5362         (PieceToChar(BlackAngel) >= 'A' &&\r
5363          PieceToChar(BlackAngel) != '~')   ) ?\r
5364                SW_SHOW : SW_HIDE);\r
5365     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5366        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5367          PieceToChar(WhiteMarshall) != '~') ||\r
5368         (PieceToChar(BlackMarshall) >= 'A' &&\r
5369          PieceToChar(BlackMarshall) != '~')   ) ?\r
5370                SW_SHOW : SW_HIDE);\r
5371     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5372     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5373        gameInfo.variant != VariantShogi ?\r
5374                SW_SHOW : SW_HIDE);\r
5375     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5376        gameInfo.variant != VariantShogi ?\r
5377                SW_SHOW : SW_HIDE);\r
5378     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5379        gameInfo.variant == VariantShogi ?\r
5380                SW_SHOW : SW_HIDE);\r
5381     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5382        gameInfo.variant == VariantShogi ?\r
5383                SW_SHOW : SW_HIDE);\r
5384     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5385        gameInfo.variant == VariantSuper ?\r
5386                SW_SHOW : SW_HIDE);\r
5387     return TRUE;\r
5388 \r
5389   case WM_COMMAND: /* message: received a command */\r
5390     switch (LOWORD(wParam)) {\r
5391     case IDCANCEL:\r
5392       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5393       ClearHighlights();\r
5394       DrawPosition(FALSE, NULL);\r
5395       return TRUE;\r
5396     case PB_King:\r
5397       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5398       break;\r
5399     case PB_Queen:\r
5400       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5401       break;\r
5402     case PB_Rook:\r
5403       promoChar = PieceToChar(BlackRook);\r
5404       break;\r
5405     case PB_Bishop:\r
5406       promoChar = PieceToChar(BlackBishop);\r
5407       break;\r
5408     case PB_Chancellor:\r
5409       promoChar = PieceToChar(BlackMarshall);\r
5410       break;\r
5411     case PB_Archbishop:\r
5412       promoChar = PieceToChar(BlackAngel);\r
5413       break;\r
5414     case PB_Knight:\r
5415       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5416       break;\r
5417     default:\r
5418       return FALSE;\r
5419     }\r
5420     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5421     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5422        only show the popup when we are already sure the move is valid or\r
5423        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5424        will figure out it is a promotion from the promoChar. */\r
5425     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5426     if (!appData.highlightLastMove) {\r
5427       ClearHighlights();\r
5428       DrawPosition(FALSE, NULL);\r
5429     }\r
5430     return TRUE;\r
5431   }\r
5432   return FALSE;\r
5433 }\r
5434 \r
5435 /* Pop up promotion dialog */\r
5436 VOID\r
5437 PromotionPopup(HWND hwnd)\r
5438 {\r
5439   FARPROC lpProc;\r
5440 \r
5441   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5442   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5443     hwnd, (DLGPROC)lpProc);\r
5444   FreeProcInstance(lpProc);\r
5445 }\r
5446 \r
5447 /* Toggle ShowThinking */\r
5448 VOID\r
5449 ToggleShowThinking()\r
5450 {\r
5451   appData.showThinking = !appData.showThinking;\r
5452   ShowThinkingEvent();\r
5453 }\r
5454 \r
5455 VOID\r
5456 LoadGameDialog(HWND hwnd, char* title)\r
5457 {\r
5458   UINT number = 0;\r
5459   FILE *f;\r
5460   char fileTitle[MSG_SIZ];\r
5461   f = OpenFileDialog(hwnd, "rb", "",\r
5462                      appData.oldSaveStyle ? "gam" : "pgn",\r
5463                      GAME_FILT,\r
5464                      title, &number, fileTitle, NULL);\r
5465   if (f != NULL) {\r
5466     cmailMsgLoaded = FALSE;\r
5467     if (number == 0) {\r
5468       int error = GameListBuild(f);\r
5469       if (error) {\r
5470         DisplayError("Cannot build game list", error);\r
5471       } else if (!ListEmpty(&gameList) &&\r
5472                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5473         GameListPopUp(f, fileTitle);\r
5474         return;\r
5475       }\r
5476       GameListDestroy();\r
5477       number = 1;\r
5478     }\r
5479     LoadGame(f, number, fileTitle, FALSE);\r
5480   }\r
5481 }\r
5482 \r
5483 VOID\r
5484 ChangedConsoleFont()\r
5485 {\r
5486   CHARFORMAT cfmt;\r
5487   CHARRANGE tmpsel, sel;\r
5488   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5489   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5490   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5491   PARAFORMAT paraf;\r
5492 \r
5493   cfmt.cbSize = sizeof(CHARFORMAT);\r
5494   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5495   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5496   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5497    * size.  This was undocumented in the version of MSVC++ that I had\r
5498    * when I wrote the code, but is apparently documented now.\r
5499    */\r
5500   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5501   cfmt.bCharSet = f->lf.lfCharSet;\r
5502   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5503   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5504   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5505   /* Why are the following seemingly needed too? */\r
5506   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5507   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5508   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5509   tmpsel.cpMin = 0;\r
5510   tmpsel.cpMax = -1; /*999999?*/\r
5511   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5512   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5513   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5514    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5515    */\r
5516   paraf.cbSize = sizeof(paraf);\r
5517   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5518   paraf.dxStartIndent = 0;\r
5519   paraf.dxOffset = WRAP_INDENT;\r
5520   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5521   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5522 }\r
5523 \r
5524 /*---------------------------------------------------------------------------*\\r
5525  *\r
5526  * Window Proc for main window\r
5527  *\r
5528 \*---------------------------------------------------------------------------*/\r
5529 \r
5530 /* Process messages for main window, etc. */\r
5531 LRESULT CALLBACK\r
5532 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5533 {\r
5534   FARPROC lpProc;\r
5535   int wmId, wmEvent;\r
5536   char *defName;\r
5537   FILE *f;\r
5538   UINT number;\r
5539   char fileTitle[MSG_SIZ];\r
5540   char buf[MSG_SIZ];\r
5541   static SnapData sd;\r
5542 \r
5543   switch (message) {\r
5544 \r
5545   case WM_PAINT: /* message: repaint portion of window */\r
5546     PaintProc(hwnd);\r
5547     break;\r
5548 \r
5549   case WM_ERASEBKGND:\r
5550     if (IsIconic(hwnd)) {\r
5551       /* Cheat; change the message */\r
5552       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5553     } else {\r
5554       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5555     }\r
5556     break;\r
5557 \r
5558   case WM_LBUTTONDOWN:\r
5559   case WM_MBUTTONDOWN:\r
5560   case WM_RBUTTONDOWN:\r
5561   case WM_LBUTTONUP:\r
5562   case WM_MBUTTONUP:\r
5563   case WM_RBUTTONUP:\r
5564   case WM_MOUSEMOVE:\r
5565   case WM_MOUSEWHEEL:\r
5566     MouseEvent(hwnd, message, wParam, lParam);\r
5567     break;\r
5568 \r
5569   case WM_CHAR:\r
5570     \r
5571     if (appData.icsActive) {\r
5572       if (wParam == '\t') {\r
5573         if (GetKeyState(VK_SHIFT) < 0) {\r
5574           /* shifted */\r
5575           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5576           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5577           SetFocus(h);\r
5578         } else {\r
5579           /* unshifted */\r
5580           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5581           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5582           SetFocus(h);\r
5583         }\r
5584       } else {\r
5585         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5586         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5587         SetFocus(h);\r
5588         SendMessage(h, message, wParam, lParam);\r
5589       }\r
5590     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5591       PopUpMoveDialog((char)wParam);\r
5592     }\r
5593     break;\r
5594 \r
5595   case WM_PALETTECHANGED:\r
5596     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5597       int nnew;\r
5598       HDC hdc = GetDC(hwndMain);\r
5599       SelectPalette(hdc, hPal, TRUE);\r
5600       nnew = RealizePalette(hdc);\r
5601       if (nnew > 0) {\r
5602         paletteChanged = TRUE;\r
5603 #if 0\r
5604         UpdateColors(hdc);\r
5605 #else\r
5606         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5607 #endif\r
5608       }\r
5609       ReleaseDC(hwnd, hdc);\r
5610     }\r
5611     break;\r
5612 \r
5613   case WM_QUERYNEWPALETTE:\r
5614     if (!appData.monoMode /*&& paletteChanged*/) {\r
5615       int nnew;\r
5616       HDC hdc = GetDC(hwndMain);\r
5617       paletteChanged = FALSE;\r
5618       SelectPalette(hdc, hPal, FALSE);\r
5619       nnew = RealizePalette(hdc);\r
5620       if (nnew > 0) {\r
5621         InvalidateRect(hwnd, &boardRect, FALSE);\r
5622       }\r
5623       ReleaseDC(hwnd, hdc);\r
5624       return TRUE;\r
5625     }\r
5626     return FALSE;\r
5627 \r
5628   case WM_COMMAND: /* message: command from application menu */\r
5629     wmId    = LOWORD(wParam);\r
5630     wmEvent = HIWORD(wParam);\r
5631 \r
5632     switch (wmId) {\r
5633     case IDM_NewGame:\r
5634       ResetGameEvent();\r
5635       AnalysisPopDown();\r
5636       break;\r
5637 \r
5638     case IDM_NewGameFRC:\r
5639       if( NewGameFRC() == 0 ) {\r
5640         ResetGameEvent();\r
5641         AnalysisPopDown();\r
5642       }\r
5643       break;\r
5644 \r
5645     case IDM_NewVariant:\r
5646       NewVariantPopup(hwnd);\r
5647       break;\r
5648 \r
5649     case IDM_LoadGame:\r
5650       LoadGameDialog(hwnd, "Load Game from File");\r
5651       break;\r
5652 \r
5653     case IDM_LoadNextGame:\r
5654       ReloadGame(1);\r
5655       break;\r
5656 \r
5657     case IDM_LoadPrevGame:\r
5658       ReloadGame(-1);\r
5659       break;\r
5660 \r
5661     case IDM_ReloadGame:\r
5662       ReloadGame(0);\r
5663       break;\r
5664 \r
5665     case IDM_LoadPosition:\r
5666       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5667         Reset(FALSE, TRUE);\r
5668       }\r
5669       number = 1;\r
5670       f = OpenFileDialog(hwnd, "rb", "",\r
5671                          appData.oldSaveStyle ? "pos" : "fen",\r
5672                          POSITION_FILT,\r
5673                          "Load Position from File", &number, fileTitle, NULL);\r
5674       if (f != NULL) {\r
5675         LoadPosition(f, number, fileTitle);\r
5676       }\r
5677       break;\r
5678 \r
5679     case IDM_LoadNextPosition:\r
5680       ReloadPosition(1);\r
5681       break;\r
5682 \r
5683     case IDM_LoadPrevPosition:\r
5684       ReloadPosition(-1);\r
5685       break;\r
5686 \r
5687     case IDM_ReloadPosition:\r
5688       ReloadPosition(0);\r
5689       break;\r
5690 \r
5691     case IDM_SaveGame:\r
5692       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5693       f = OpenFileDialog(hwnd, "a", defName,\r
5694                          appData.oldSaveStyle ? "gam" : "pgn",\r
5695                          GAME_FILT,\r
5696                          "Save Game to File", NULL, fileTitle, NULL);\r
5697       if (f != NULL) {\r
5698         SaveGame(f, 0, "");\r
5699       }\r
5700       break;\r
5701 \r
5702     case IDM_SavePosition:\r
5703       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5704       f = OpenFileDialog(hwnd, "a", defName,\r
5705                          appData.oldSaveStyle ? "pos" : "fen",\r
5706                          POSITION_FILT,\r
5707                          "Save Position to File", NULL, fileTitle, NULL);\r
5708       if (f != NULL) {\r
5709         SavePosition(f, 0, "");\r
5710       }\r
5711       break;\r
5712 \r
5713     case IDM_SaveDiagram:\r
5714       defName = "diagram";\r
5715       f = OpenFileDialog(hwnd, "wb", defName,\r
5716                          "bmp",\r
5717                          DIAGRAM_FILT,\r
5718                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5719       if (f != NULL) {\r
5720         SaveDiagram(f);\r
5721       }\r
5722       break;\r
5723 \r
5724     case IDM_CopyGame:\r
5725       CopyGameToClipboard();\r
5726       break;\r
5727 \r
5728     case IDM_PasteGame:\r
5729       PasteGameFromClipboard();\r
5730       break;\r
5731 \r
5732     case IDM_CopyGameListToClipboard:\r
5733       CopyGameListToClipboard();\r
5734       break;\r
5735 \r
5736     /* [AS] Autodetect FEN or PGN data */\r
5737     case IDM_PasteAny:\r
5738       PasteGameOrFENFromClipboard();\r
5739       break;\r
5740 \r
5741     /* [AS] Move history */\r
5742     case IDM_ShowMoveHistory:\r
5743         if( MoveHistoryIsUp() ) {\r
5744             MoveHistoryPopDown();\r
5745         }\r
5746         else {\r
5747             MoveHistoryPopUp();\r
5748         }\r
5749         break;\r
5750 \r
5751     /* [AS] Eval graph */\r
5752     case IDM_ShowEvalGraph:\r
5753         if( EvalGraphIsUp() ) {\r
5754             EvalGraphPopDown();\r
5755         }\r
5756         else {\r
5757             EvalGraphPopUp();\r
5758         }\r
5759         break;\r
5760 \r
5761     /* [AS] Engine output */\r
5762     case IDM_ShowEngineOutput:\r
5763         if( EngineOutputIsUp() ) {\r
5764             EngineOutputPopDown();\r
5765         }\r
5766         else {\r
5767             EngineOutputPopUp();\r
5768         }\r
5769         break;\r
5770 \r
5771     /* [AS] User adjudication */\r
5772     case IDM_UserAdjudication_White:\r
5773         UserAdjudicationEvent( +1 );\r
5774         break;\r
5775 \r
5776     case IDM_UserAdjudication_Black:\r
5777         UserAdjudicationEvent( -1 );\r
5778         break;\r
5779 \r
5780     case IDM_UserAdjudication_Draw:\r
5781         UserAdjudicationEvent( 0 );\r
5782         break;\r
5783 \r
5784     /* [AS] Game list options dialog */\r
5785     case IDM_GameListOptions:\r
5786       GameListOptions();\r
5787       break;\r
5788 \r
5789     case IDM_CopyPosition:\r
5790       CopyFENToClipboard();\r
5791       break;\r
5792 \r
5793     case IDM_PastePosition:\r
5794       PasteFENFromClipboard();\r
5795       break;\r
5796 \r
5797     case IDM_MailMove:\r
5798       MailMoveEvent();\r
5799       break;\r
5800 \r
5801     case IDM_ReloadCMailMsg:\r
5802       Reset(TRUE, TRUE);\r
5803       ReloadCmailMsgEvent(FALSE);\r
5804       break;\r
5805 \r
5806     case IDM_Minimize:\r
5807       ShowWindow(hwnd, SW_MINIMIZE);\r
5808       break;\r
5809 \r
5810     case IDM_Exit:\r
5811       ExitEvent(0);\r
5812       break;\r
5813 \r
5814     case IDM_MachineWhite:\r
5815       MachineWhiteEvent();\r
5816       /*\r
5817        * refresh the tags dialog only if it's visible\r
5818        */\r
5819       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5820           char *tags;\r
5821           tags = PGNTags(&gameInfo);\r
5822           TagsPopUp(tags, CmailMsg());\r
5823           free(tags);\r
5824       }\r
5825       break;\r
5826 \r
5827     case IDM_MachineBlack:\r
5828       MachineBlackEvent();\r
5829       /*\r
5830        * refresh the tags dialog only if it's visible\r
5831        */\r
5832       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5833           char *tags;\r
5834           tags = PGNTags(&gameInfo);\r
5835           TagsPopUp(tags, CmailMsg());\r
5836           free(tags);\r
5837       }\r
5838       break;\r
5839 \r
5840     case IDM_TwoMachines:\r
5841       TwoMachinesEvent();\r
5842       /*\r
5843        * refresh the tags dialog only if it's visible\r
5844        */\r
5845       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5846           char *tags;\r
5847           tags = PGNTags(&gameInfo);\r
5848           TagsPopUp(tags, CmailMsg());\r
5849           free(tags);\r
5850       }\r
5851       break;\r
5852 \r
5853     case IDM_AnalysisMode:\r
5854       if (!first.analysisSupport) {\r
5855         sprintf(buf, "%s does not support analysis", first.tidy);\r
5856         DisplayError(buf, 0);\r
5857       } else {\r
5858         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5859         if (appData.icsActive) {\r
5860                if (gameMode != IcsObserving) {\r
5861                        sprintf(buf, "You are not observing a game");\r
5862                        DisplayError(buf, 0);\r
5863                        /* secure check */\r
5864                        if (appData.icsEngineAnalyze) {\r
5865                                if (appData.debugMode) \r
5866                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5867                                ExitAnalyzeMode();\r
5868                                ModeHighlight();\r
5869                                break;\r
5870                        }\r
5871                        break;\r
5872                } else {\r
5873                        /* if enable, user want disable icsEngineAnalyze */\r
5874                        if (appData.icsEngineAnalyze) {\r
5875                                ExitAnalyzeMode();\r
5876                                ModeHighlight();\r
5877                                break;\r
5878                        }\r
5879                        appData.icsEngineAnalyze = TRUE;\r
5880                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5881                }\r
5882         } \r
5883         if (!appData.showThinking) ToggleShowThinking();\r
5884         AnalyzeModeEvent();\r
5885       }\r
5886       break;\r
5887 \r
5888     case IDM_AnalyzeFile:\r
5889       if (!first.analysisSupport) {\r
5890         char buf[MSG_SIZ];\r
5891         sprintf(buf, "%s does not support analysis", first.tidy);\r
5892         DisplayError(buf, 0);\r
5893       } else {\r
5894         if (!appData.showThinking) ToggleShowThinking();\r
5895         AnalyzeFileEvent();\r
5896         LoadGameDialog(hwnd, "Analyze Game from File");\r
5897         AnalysisPeriodicEvent(1);\r
5898       }\r
5899       break;\r
5900 \r
5901     case IDM_IcsClient:\r
5902       IcsClientEvent();\r
5903       break;\r
5904 \r
5905     case IDM_EditGame:\r
5906       EditGameEvent();\r
5907       break;\r
5908 \r
5909     case IDM_EditPosition:\r
5910       EditPositionEvent();\r
5911       break;\r
5912 \r
5913     case IDM_Training:\r
5914       TrainingEvent();\r
5915       break;\r
5916 \r
5917     case IDM_ShowGameList:\r
5918       ShowGameListProc();\r
5919       break;\r
5920 \r
5921     case IDM_EditTags:\r
5922       EditTagsProc();\r
5923       break;\r
5924 \r
5925     case IDM_EditComment:\r
5926       if (commentDialogUp && editComment) {\r
5927         CommentPopDown();\r
5928       } else {\r
5929         EditCommentEvent();\r
5930       }\r
5931       break;\r
5932 \r
5933     case IDM_Pause:\r
5934       PauseEvent();\r
5935       break;\r
5936 \r
5937     case IDM_Accept:\r
5938       AcceptEvent();\r
5939       break;\r
5940 \r
5941     case IDM_Decline:\r
5942       DeclineEvent();\r
5943       break;\r
5944 \r
5945     case IDM_Rematch:\r
5946       RematchEvent();\r
5947       break;\r
5948 \r
5949     case IDM_CallFlag:\r
5950       CallFlagEvent();\r
5951       break;\r
5952 \r
5953     case IDM_Draw:\r
5954       DrawEvent();\r
5955       break;\r
5956 \r
5957     case IDM_Adjourn:\r
5958       AdjournEvent();\r
5959       break;\r
5960 \r
5961     case IDM_Abort:\r
5962       AbortEvent();\r
5963       break;\r
5964 \r
5965     case IDM_Resign:\r
5966       ResignEvent();\r
5967       break;\r
5968 \r
5969     case IDM_StopObserving:\r
5970       StopObservingEvent();\r
5971       break;\r
5972 \r
5973     case IDM_StopExamining:\r
5974       StopExaminingEvent();\r
5975       break;\r
5976 \r
5977     case IDM_TypeInMove:\r
5978       PopUpMoveDialog('\000');\r
5979       break;\r
5980 \r
5981     case IDM_TypeInName:\r
5982       PopUpNameDialog('\000');\r
5983       break;\r
5984 \r
5985     case IDM_Backward:\r
5986       BackwardEvent();\r
5987       SetFocus(hwndMain);\r
5988       break;\r
5989 \r
5990     case IDM_Forward:\r
5991       ForwardEvent();\r
5992       SetFocus(hwndMain);\r
5993       break;\r
5994 \r
5995     case IDM_ToStart:\r
5996       ToStartEvent();\r
5997       SetFocus(hwndMain);\r
5998       break;\r
5999 \r
6000     case IDM_ToEnd:\r
6001       ToEndEvent();\r
6002       SetFocus(hwndMain);\r
6003       break;\r
6004 \r
6005     case IDM_Revert:\r
6006       RevertEvent();\r
6007       break;\r
6008 \r
6009     case IDM_TruncateGame:\r
6010       TruncateGameEvent();\r
6011       break;\r
6012 \r
6013     case IDM_MoveNow:\r
6014       MoveNowEvent();\r
6015       break;\r
6016 \r
6017     case IDM_RetractMove:\r
6018       RetractMoveEvent();\r
6019       break;\r
6020 \r
6021     case IDM_FlipView:\r
6022       flipView = !flipView;\r
6023       DrawPosition(FALSE, NULL);\r
6024       break;\r
6025 \r
6026     case IDM_FlipClock:\r
6027       flipClock = !flipClock;\r
6028       DisplayBothClocks();\r
6029       DrawPosition(FALSE, NULL);\r
6030       break;\r
6031 \r
6032     case IDM_GeneralOptions:\r
6033       GeneralOptionsPopup(hwnd);\r
6034       DrawPosition(TRUE, NULL);\r
6035       break;\r
6036 \r
6037     case IDM_BoardOptions:\r
6038       BoardOptionsPopup(hwnd);\r
6039       break;\r
6040 \r
6041     case IDM_EnginePlayOptions:\r
6042       EnginePlayOptionsPopup(hwnd);\r
6043       break;\r
6044 \r
6045     case IDM_OptionsUCI:\r
6046       UciOptionsPopup(hwnd);\r
6047       break;\r
6048 \r
6049     case IDM_IcsOptions:\r
6050       IcsOptionsPopup(hwnd);\r
6051       break;\r
6052 \r
6053     case IDM_Fonts:\r
6054       FontsOptionsPopup(hwnd);\r
6055       break;\r
6056 \r
6057     case IDM_Sounds:\r
6058       SoundOptionsPopup(hwnd);\r
6059       break;\r
6060 \r
6061     case IDM_CommPort:\r
6062       CommPortOptionsPopup(hwnd);\r
6063       break;\r
6064 \r
6065     case IDM_LoadOptions:\r
6066       LoadOptionsPopup(hwnd);\r
6067       break;\r
6068 \r
6069     case IDM_SaveOptions:\r
6070       SaveOptionsPopup(hwnd);\r
6071       break;\r
6072 \r
6073     case IDM_TimeControl:\r
6074       TimeControlOptionsPopup(hwnd);\r
6075       break;\r
6076 \r
6077     case IDM_SaveSettings:\r
6078       SaveSettings(settingsFileName);\r
6079       break;\r
6080 \r
6081     case IDM_SaveSettingsOnExit:\r
6082       saveSettingsOnExit = !saveSettingsOnExit;\r
6083       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6084                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6085                                          MF_CHECKED : MF_UNCHECKED));\r
6086       break;\r
6087 \r
6088     case IDM_Hint:\r
6089       HintEvent();\r
6090       break;\r
6091 \r
6092     case IDM_Book:\r
6093       BookEvent();\r
6094       break;\r
6095 \r
6096     case IDM_AboutGame:\r
6097       AboutGameEvent();\r
6098       break;\r
6099 \r
6100     case IDM_Debug:\r
6101       appData.debugMode = !appData.debugMode;\r
6102       if (appData.debugMode) {\r
6103         char dir[MSG_SIZ];\r
6104         GetCurrentDirectory(MSG_SIZ, dir);\r
6105         SetCurrentDirectory(installDir);\r
6106         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6107         SetCurrentDirectory(dir);\r
6108         setbuf(debugFP, NULL);\r
6109       } else {\r
6110         fclose(debugFP);\r
6111         debugFP = NULL;\r
6112       }\r
6113       break;\r
6114 \r
6115     case IDM_HELPCONTENTS:\r
6116       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6117         MessageBox (GetFocus(),\r
6118                     "Unable to activate help",\r
6119                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6120       }\r
6121       break;\r
6122 \r
6123     case IDM_HELPSEARCH:\r
6124       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6125         MessageBox (GetFocus(),\r
6126                     "Unable to activate help",\r
6127                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6128       }\r
6129       break;\r
6130 \r
6131     case IDM_HELPHELP:\r
6132       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6133         MessageBox (GetFocus(),\r
6134                     "Unable to activate help",\r
6135                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6136       }\r
6137       break;\r
6138 \r
6139     case IDM_ABOUT:\r
6140       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6141       DialogBox(hInst, \r
6142         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6143         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6144       FreeProcInstance(lpProc);\r
6145       break;\r
6146 \r
6147     case IDM_DirectCommand1:\r
6148       AskQuestionEvent("Direct Command",\r
6149                        "Send to chess program:", "", "1");\r
6150       break;\r
6151     case IDM_DirectCommand2:\r
6152       AskQuestionEvent("Direct Command",\r
6153                        "Send to second chess program:", "", "2");\r
6154       break;\r
6155 \r
6156     case EP_WhitePawn:\r
6157       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6158       fromX = fromY = -1;\r
6159       break;\r
6160 \r
6161     case EP_WhiteKnight:\r
6162       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6163       fromX = fromY = -1;\r
6164       break;\r
6165 \r
6166     case EP_WhiteBishop:\r
6167       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6168       fromX = fromY = -1;\r
6169       break;\r
6170 \r
6171     case EP_WhiteRook:\r
6172       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6173       fromX = fromY = -1;\r
6174       break;\r
6175 \r
6176     case EP_WhiteQueen:\r
6177       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6178       fromX = fromY = -1;\r
6179       break;\r
6180 \r
6181     case EP_WhiteFerz:\r
6182       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6183       fromX = fromY = -1;\r
6184       break;\r
6185 \r
6186     case EP_WhiteWazir:\r
6187       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6188       fromX = fromY = -1;\r
6189       break;\r
6190 \r
6191     case EP_WhiteAlfil:\r
6192       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6193       fromX = fromY = -1;\r
6194       break;\r
6195 \r
6196     case EP_WhiteCannon:\r
6197       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6198       fromX = fromY = -1;\r
6199       break;\r
6200 \r
6201     case EP_WhiteCardinal:\r
6202       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6203       fromX = fromY = -1;\r
6204       break;\r
6205 \r
6206     case EP_WhiteMarshall:\r
6207       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6208       fromX = fromY = -1;\r
6209       break;\r
6210 \r
6211     case EP_WhiteKing:\r
6212       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6213       fromX = fromY = -1;\r
6214       break;\r
6215 \r
6216     case EP_BlackPawn:\r
6217       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6218       fromX = fromY = -1;\r
6219       break;\r
6220 \r
6221     case EP_BlackKnight:\r
6222       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6223       fromX = fromY = -1;\r
6224       break;\r
6225 \r
6226     case EP_BlackBishop:\r
6227       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6228       fromX = fromY = -1;\r
6229       break;\r
6230 \r
6231     case EP_BlackRook:\r
6232       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6233       fromX = fromY = -1;\r
6234       break;\r
6235 \r
6236     case EP_BlackQueen:\r
6237       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6238       fromX = fromY = -1;\r
6239       break;\r
6240 \r
6241     case EP_BlackFerz:\r
6242       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6243       fromX = fromY = -1;\r
6244       break;\r
6245 \r
6246     case EP_BlackWazir:\r
6247       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6248       fromX = fromY = -1;\r
6249       break;\r
6250 \r
6251     case EP_BlackAlfil:\r
6252       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6253       fromX = fromY = -1;\r
6254       break;\r
6255 \r
6256     case EP_BlackCannon:\r
6257       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6258       fromX = fromY = -1;\r
6259       break;\r
6260 \r
6261     case EP_BlackCardinal:\r
6262       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6263       fromX = fromY = -1;\r
6264       break;\r
6265 \r
6266     case EP_BlackMarshall:\r
6267       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6268       fromX = fromY = -1;\r
6269       break;\r
6270 \r
6271     case EP_BlackKing:\r
6272       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6273       fromX = fromY = -1;\r
6274       break;\r
6275 \r
6276     case EP_EmptySquare:\r
6277       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6278       fromX = fromY = -1;\r
6279       break;\r
6280 \r
6281     case EP_ClearBoard:\r
6282       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6283       fromX = fromY = -1;\r
6284       break;\r
6285 \r
6286     case EP_White:\r
6287       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6288       fromX = fromY = -1;\r
6289       break;\r
6290 \r
6291     case EP_Black:\r
6292       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6293       fromX = fromY = -1;\r
6294       break;\r
6295 \r
6296     case EP_Promote:\r
6297       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6298       fromX = fromY = -1;\r
6299       break;\r
6300 \r
6301     case EP_Demote:\r
6302       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6303       fromX = fromY = -1;\r
6304       break;\r
6305 \r
6306     case DP_Pawn:\r
6307       DropMenuEvent(WhitePawn, fromX, fromY);\r
6308       fromX = fromY = -1;\r
6309       break;\r
6310 \r
6311     case DP_Knight:\r
6312       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6313       fromX = fromY = -1;\r
6314       break;\r
6315 \r
6316     case DP_Bishop:\r
6317       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6318       fromX = fromY = -1;\r
6319       break;\r
6320 \r
6321     case DP_Rook:\r
6322       DropMenuEvent(WhiteRook, fromX, fromY);\r
6323       fromX = fromY = -1;\r
6324       break;\r
6325 \r
6326     case DP_Queen:\r
6327       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6328       fromX = fromY = -1;\r
6329       break;\r
6330 \r
6331     default:\r
6332       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6333     }\r
6334     break;\r
6335 \r
6336   case WM_TIMER:\r
6337     switch (wParam) {\r
6338     case CLOCK_TIMER_ID:\r
6339       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6340       clockTimerEvent = 0;\r
6341       DecrementClocks(); /* call into back end */\r
6342       break;\r
6343     case LOAD_GAME_TIMER_ID:\r
6344       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6345       loadGameTimerEvent = 0;\r
6346       AutoPlayGameLoop(); /* call into back end */\r
6347       break;\r
6348     case ANALYSIS_TIMER_ID:\r
6349       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6350                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6351         AnalysisPeriodicEvent(0);\r
6352       } else {\r
6353         KillTimer(hwnd, analysisTimerEvent);\r
6354         analysisTimerEvent = 0;\r
6355       }\r
6356       break;\r
6357     case DELAYED_TIMER_ID:\r
6358       KillTimer(hwnd, delayedTimerEvent);\r
6359       delayedTimerEvent = 0;\r
6360       delayedTimerCallback();\r
6361       break;\r
6362     }\r
6363     break;\r
6364 \r
6365   case WM_USER_Input:\r
6366     InputEvent(hwnd, message, wParam, lParam);\r
6367     break;\r
6368 \r
6369   /* [AS] Also move "attached" child windows */\r
6370   case WM_WINDOWPOSCHANGING:\r
6371 \r
6372     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6373         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6374 \r
6375         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6376             /* Window is moving */\r
6377             RECT rcMain;\r
6378 \r
6379 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6380             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6381             rcMain.right  = boardX + winWidth;\r
6382             rcMain.top    = boardY;\r
6383             rcMain.bottom = boardY + winHeight;\r
6384             \r
6385             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6386             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6387             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6388             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6389             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6390             boardX = lpwp->x;\r
6391             boardY = lpwp->y;\r
6392         }\r
6393     }\r
6394     break;\r
6395 \r
6396   /* [AS] Snapping */\r
6397   case WM_ENTERSIZEMOVE:\r
6398     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6399     if (hwnd == hwndMain) {\r
6400       doingSizing = TRUE;\r
6401       lastSizing = 0;\r
6402     }\r
6403     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6404     break;\r
6405 \r
6406   case WM_SIZING:\r
6407     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6408     if (hwnd == hwndMain) {\r
6409       lastSizing = wParam;\r
6410     }\r
6411     break;\r
6412 \r
6413   case WM_MOVING:\r
6414     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6415       return OnMoving( &sd, hwnd, wParam, lParam );\r
6416 \r
6417   case WM_EXITSIZEMOVE:\r
6418     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6419     if (hwnd == hwndMain) {\r
6420       RECT client;\r
6421       doingSizing = FALSE;\r
6422       InvalidateRect(hwnd, &boardRect, FALSE);\r
6423       GetClientRect(hwnd, &client);\r
6424       ResizeBoard(client.right, client.bottom, lastSizing);\r
6425       lastSizing = 0;\r
6426       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6427     }\r
6428     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6429     break;\r
6430 \r
6431   case WM_DESTROY: /* message: window being destroyed */\r
6432     PostQuitMessage(0);\r
6433     break;\r
6434 \r
6435   case WM_CLOSE:\r
6436     if (hwnd == hwndMain) {\r
6437       ExitEvent(0);\r
6438     }\r
6439     break;\r
6440 \r
6441   default:      /* Passes it on if unprocessed */\r
6442     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6443   }\r
6444   return 0;\r
6445 }\r
6446 \r
6447 /*---------------------------------------------------------------------------*\\r
6448  *\r
6449  * Misc utility routines\r
6450  *\r
6451 \*---------------------------------------------------------------------------*/\r
6452 \r
6453 /*\r
6454  * Decent random number generator, at least not as bad as Windows\r
6455  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6456  */\r
6457 unsigned int randstate;\r
6458 \r
6459 int\r
6460 myrandom(void)\r
6461 {\r
6462   randstate = randstate * 1664525 + 1013904223;\r
6463   return (int) randstate & 0x7fffffff;\r
6464 }\r
6465 \r
6466 void\r
6467 mysrandom(unsigned int seed)\r
6468 {\r
6469   randstate = seed;\r
6470 }\r
6471 \r
6472 \r
6473 /* \r
6474  * returns TRUE if user selects a different color, FALSE otherwise \r
6475  */\r
6476 \r
6477 BOOL\r
6478 ChangeColor(HWND hwnd, COLORREF *which)\r
6479 {\r
6480   static BOOL firstTime = TRUE;\r
6481   static DWORD customColors[16];\r
6482   CHOOSECOLOR cc;\r
6483   COLORREF newcolor;\r
6484   int i;\r
6485   ColorClass ccl;\r
6486 \r
6487   if (firstTime) {\r
6488     /* Make initial colors in use available as custom colors */\r
6489     /* Should we put the compiled-in defaults here instead? */\r
6490     i = 0;\r
6491     customColors[i++] = lightSquareColor & 0xffffff;\r
6492     customColors[i++] = darkSquareColor & 0xffffff;\r
6493     customColors[i++] = whitePieceColor & 0xffffff;\r
6494     customColors[i++] = blackPieceColor & 0xffffff;\r
6495     customColors[i++] = highlightSquareColor & 0xffffff;\r
6496     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6497 \r
6498     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6499       customColors[i++] = textAttribs[ccl].color;\r
6500     }\r
6501     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6502     firstTime = FALSE;\r
6503   }\r
6504 \r
6505   cc.lStructSize = sizeof(cc);\r
6506   cc.hwndOwner = hwnd;\r
6507   cc.hInstance = NULL;\r
6508   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6509   cc.lpCustColors = (LPDWORD) customColors;\r
6510   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6511 \r
6512   if (!ChooseColor(&cc)) return FALSE;\r
6513 \r
6514   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6515   if (newcolor == *which) return FALSE;\r
6516   *which = newcolor;\r
6517   return TRUE;\r
6518 \r
6519   /*\r
6520   InitDrawingColors();\r
6521   InvalidateRect(hwnd, &boardRect, FALSE);\r
6522   */\r
6523 }\r
6524 \r
6525 BOOLEAN\r
6526 MyLoadSound(MySound *ms)\r
6527 {\r
6528   BOOL ok = FALSE;\r
6529   struct stat st;\r
6530   FILE *f;\r
6531 \r
6532   if (ms->data) free(ms->data);\r
6533   ms->data = NULL;\r
6534 \r
6535   switch (ms->name[0]) {\r
6536   case NULLCHAR:\r
6537     /* Silence */\r
6538     ok = TRUE;\r
6539     break;\r
6540   case '$':\r
6541     /* System sound from Control Panel.  Don't preload here. */\r
6542     ok = TRUE;\r
6543     break;\r
6544   case '!':\r
6545     if (ms->name[1] == NULLCHAR) {\r
6546       /* "!" alone = silence */\r
6547       ok = TRUE;\r
6548     } else {\r
6549       /* Builtin wave resource.  Error if not found. */\r
6550       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6551       if (h == NULL) break;\r
6552       ms->data = (void *)LoadResource(hInst, h);\r
6553       if (h == NULL) break;\r
6554       ok = TRUE;\r
6555     }\r
6556     break;\r
6557   default:\r
6558     /* .wav file.  Error if not found. */\r
6559     f = fopen(ms->name, "rb");\r
6560     if (f == NULL) break;\r
6561     if (fstat(fileno(f), &st) < 0) break;\r
6562     ms->data = malloc(st.st_size);\r
6563     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6564     fclose(f);\r
6565     ok = TRUE;\r
6566     break;\r
6567   }\r
6568   if (!ok) {\r
6569     char buf[MSG_SIZ];\r
6570     sprintf(buf, "Error loading sound %s", ms->name);\r
6571     DisplayError(buf, GetLastError());\r
6572   }\r
6573   return ok;\r
6574 }\r
6575 \r
6576 BOOLEAN\r
6577 MyPlaySound(MySound *ms)\r
6578 {\r
6579   BOOLEAN ok = FALSE;\r
6580   switch (ms->name[0]) {\r
6581   case NULLCHAR:\r
6582     /* Silence */\r
6583     ok = TRUE;\r
6584     break;\r
6585   case '$':\r
6586     /* System sound from Control Panel (deprecated feature).\r
6587        "$" alone or an unset sound name gets default beep (still in use). */\r
6588     if (ms->name[1]) {\r
6589       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6590     }\r
6591     if (!ok) ok = MessageBeep(MB_OK);\r
6592     break; \r
6593   case '!':\r
6594     /* Builtin wave resource, or "!" alone for silence */\r
6595     if (ms->name[1]) {\r
6596       if (ms->data == NULL) return FALSE;\r
6597       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6598     } else {\r
6599       ok = TRUE;\r
6600     }\r
6601     break;\r
6602   default:\r
6603     /* .wav file.  Error if not found. */\r
6604     if (ms->data == NULL) return FALSE;\r
6605     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6606     break;\r
6607   }\r
6608   /* Don't print an error: this can happen innocently if the sound driver\r
6609      is busy; for instance, if another instance of WinBoard is playing\r
6610      a sound at about the same time. */\r
6611 #if 0\r
6612   if (!ok) {\r
6613     char buf[MSG_SIZ];\r
6614     sprintf(buf, "Error playing sound %s", ms->name);\r
6615     DisplayError(buf, GetLastError());\r
6616   }\r
6617 #endif\r
6618   return ok;\r
6619 }\r
6620 \r
6621 \r
6622 LRESULT CALLBACK\r
6623 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6624 {\r
6625   BOOL ok;\r
6626   OPENFILENAME *ofn;\r
6627   static UINT *number; /* gross that this is static */\r
6628 \r
6629   switch (message) {\r
6630   case WM_INITDIALOG: /* message: initialize dialog box */\r
6631     /* Center the dialog over the application window */\r
6632     ofn = (OPENFILENAME *) lParam;\r
6633     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6634       number = (UINT *) ofn->lCustData;\r
6635       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6636     } else {\r
6637       number = NULL;\r
6638     }\r
6639     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6640     return FALSE;  /* Allow for further processing */\r
6641 \r
6642   case WM_COMMAND:\r
6643     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6644       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6645     }\r
6646     return FALSE;  /* Allow for further processing */\r
6647   }\r
6648   return FALSE;\r
6649 }\r
6650 \r
6651 UINT APIENTRY\r
6652 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6653 {\r
6654   static UINT *number;\r
6655   OPENFILENAME *ofname;\r
6656   OFNOTIFY *ofnot;\r
6657   switch (uiMsg) {\r
6658   case WM_INITDIALOG:\r
6659     ofname = (OPENFILENAME *)lParam;\r
6660     number = (UINT *)(ofname->lCustData);\r
6661     break;\r
6662   case WM_NOTIFY:\r
6663     ofnot = (OFNOTIFY *)lParam;\r
6664     if (ofnot->hdr.code == CDN_FILEOK) {\r
6665       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6666     }\r
6667     break;\r
6668   }\r
6669   return 0;\r
6670 }\r
6671 \r
6672 \r
6673 FILE *\r
6674 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6675                char *nameFilt, char *dlgTitle, UINT *number,\r
6676                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6677 {\r
6678   OPENFILENAME openFileName;\r
6679   char buf1[MSG_SIZ];\r
6680   FILE *f;\r
6681 \r
6682   if (fileName == NULL) fileName = buf1;\r
6683   if (defName == NULL) {\r
6684     strcpy(fileName, "*.");\r
6685     strcat(fileName, defExt);\r
6686   } else {\r
6687     strcpy(fileName, defName);\r
6688   }\r
6689   if (fileTitle) strcpy(fileTitle, "");\r
6690   if (number) *number = 0;\r
6691 \r
6692   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6693   openFileName.hwndOwner         = hwnd;\r
6694   openFileName.hInstance         = (HANDLE) hInst;\r
6695   openFileName.lpstrFilter       = nameFilt;\r
6696   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6697   openFileName.nMaxCustFilter    = 0L;\r
6698   openFileName.nFilterIndex      = 1L;\r
6699   openFileName.lpstrFile         = fileName;\r
6700   openFileName.nMaxFile          = MSG_SIZ;\r
6701   openFileName.lpstrFileTitle    = fileTitle;\r
6702   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6703   openFileName.lpstrInitialDir   = NULL;\r
6704   openFileName.lpstrTitle        = dlgTitle;\r
6705   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6706     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6707     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6708     | (oldDialog ? 0 : OFN_EXPLORER);\r
6709   openFileName.nFileOffset       = 0;\r
6710   openFileName.nFileExtension    = 0;\r
6711   openFileName.lpstrDefExt       = defExt;\r
6712   openFileName.lCustData         = (LONG) number;\r
6713   openFileName.lpfnHook          = oldDialog ?\r
6714     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6715   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6716 \r
6717   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6718                         GetOpenFileName(&openFileName)) {\r
6719     /* open the file */\r
6720     f = fopen(openFileName.lpstrFile, write);\r
6721     if (f == NULL) {\r
6722       MessageBox(hwnd, "File open failed", NULL,\r
6723                  MB_OK|MB_ICONEXCLAMATION);\r
6724       return NULL;\r
6725     }\r
6726   } else {\r
6727     int err = CommDlgExtendedError();\r
6728     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6729     return FALSE;\r
6730   }\r
6731   return f;\r
6732 }\r
6733 \r
6734 \r
6735 \r
6736 VOID APIENTRY\r
6737 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6738 {\r
6739   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6740 \r
6741   /*\r
6742    * Get the first pop-up menu in the menu template. This is the\r
6743    * menu that TrackPopupMenu displays.\r
6744    */\r
6745   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6746 \r
6747   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6748 \r
6749   /*\r
6750    * TrackPopup uses screen coordinates, so convert the\r
6751    * coordinates of the mouse click to screen coordinates.\r
6752    */\r
6753   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6754 \r
6755   /* Draw and track the floating pop-up menu. */\r
6756   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6757                  pt.x, pt.y, 0, hwnd, NULL);\r
6758 \r
6759   /* Destroy the menu.*/\r
6760   DestroyMenu(hmenu);\r
6761 }\r
6762    \r
6763 typedef struct {\r
6764   HWND hDlg, hText;\r
6765   int sizeX, sizeY, newSizeX, newSizeY;\r
6766   HDWP hdwp;\r
6767 } ResizeEditPlusButtonsClosure;\r
6768 \r
6769 BOOL CALLBACK\r
6770 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6771 {\r
6772   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6773   RECT rect;\r
6774   POINT pt;\r
6775 \r
6776   if (hChild == cl->hText) return TRUE;\r
6777   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6778   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6779   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6780   ScreenToClient(cl->hDlg, &pt);\r
6781   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6782     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6783   return TRUE;\r
6784 }\r
6785 \r
6786 /* Resize a dialog that has a (rich) edit field filling most of\r
6787    the top, with a row of buttons below */\r
6788 VOID\r
6789 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6790 {\r
6791   RECT rectText;\r
6792   int newTextHeight, newTextWidth;\r
6793   ResizeEditPlusButtonsClosure cl;\r
6794   \r
6795   /*if (IsIconic(hDlg)) return;*/\r
6796   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6797   \r
6798   cl.hdwp = BeginDeferWindowPos(8);\r
6799 \r
6800   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6801   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6802   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6803   if (newTextHeight < 0) {\r
6804     newSizeY += -newTextHeight;\r
6805     newTextHeight = 0;\r
6806   }\r
6807   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6808     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6809 \r
6810   cl.hDlg = hDlg;\r
6811   cl.hText = hText;\r
6812   cl.sizeX = sizeX;\r
6813   cl.sizeY = sizeY;\r
6814   cl.newSizeX = newSizeX;\r
6815   cl.newSizeY = newSizeY;\r
6816   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6817 \r
6818   EndDeferWindowPos(cl.hdwp);\r
6819 }\r
6820 \r
6821 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6822 {\r
6823     RECT    rChild, rParent;\r
6824     int     wChild, hChild, wParent, hParent;\r
6825     int     wScreen, hScreen, xNew, yNew;\r
6826     HDC     hdc;\r
6827 \r
6828     /* Get the Height and Width of the child window */\r
6829     GetWindowRect (hwndChild, &rChild);\r
6830     wChild = rChild.right - rChild.left;\r
6831     hChild = rChild.bottom - rChild.top;\r
6832 \r
6833     /* Get the Height and Width of the parent window */\r
6834     GetWindowRect (hwndParent, &rParent);\r
6835     wParent = rParent.right - rParent.left;\r
6836     hParent = rParent.bottom - rParent.top;\r
6837 \r
6838     /* Get the display limits */\r
6839     hdc = GetDC (hwndChild);\r
6840     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6841     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6842     ReleaseDC(hwndChild, hdc);\r
6843 \r
6844     /* Calculate new X position, then adjust for screen */\r
6845     xNew = rParent.left + ((wParent - wChild) /2);\r
6846     if (xNew < 0) {\r
6847         xNew = 0;\r
6848     } else if ((xNew+wChild) > wScreen) {\r
6849         xNew = wScreen - wChild;\r
6850     }\r
6851 \r
6852     /* Calculate new Y position, then adjust for screen */\r
6853     if( mode == 0 ) {\r
6854         yNew = rParent.top  + ((hParent - hChild) /2);\r
6855     }\r
6856     else {\r
6857         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6858     }\r
6859 \r
6860     if (yNew < 0) {\r
6861         yNew = 0;\r
6862     } else if ((yNew+hChild) > hScreen) {\r
6863         yNew = hScreen - hChild;\r
6864     }\r
6865 \r
6866     /* Set it, and return */\r
6867     return SetWindowPos (hwndChild, NULL,\r
6868                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6869 }\r
6870 \r
6871 /* Center one window over another */\r
6872 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6873 {\r
6874     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6875 }\r
6876 \r
6877 /*---------------------------------------------------------------------------*\\r
6878  *\r
6879  * Startup Dialog functions\r
6880  *\r
6881 \*---------------------------------------------------------------------------*/\r
6882 void\r
6883 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6884 {\r
6885   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6886 \r
6887   while (*cd != NULL) {\r
6888     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6889     cd++;\r
6890   }\r
6891 }\r
6892 \r
6893 void\r
6894 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6895 {\r
6896   char buf1[ARG_MAX];\r
6897   int len;\r
6898 \r
6899   if (str[0] == '@') {\r
6900     FILE* f = fopen(str + 1, "r");\r
6901     if (f == NULL) {\r
6902       DisplayFatalError(str + 1, errno, 2);\r
6903       return;\r
6904     }\r
6905     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6906     fclose(f);\r
6907     buf1[len] = NULLCHAR;\r
6908     str = buf1;\r
6909   }\r
6910 \r
6911   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6912 \r
6913   for (;;) {\r
6914     char buf[MSG_SIZ];\r
6915     char *end = strchr(str, '\n');\r
6916     if (end == NULL) return;\r
6917     memcpy(buf, str, end - str);\r
6918     buf[end - str] = NULLCHAR;\r
6919     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6920     str = end + 1;\r
6921   }\r
6922 }\r
6923 \r
6924 void\r
6925 SetStartupDialogEnables(HWND hDlg)\r
6926 {\r
6927   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6928     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6929     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6930   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6931     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6932   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6933     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6934   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6935     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6936   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6937     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6938     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6939     IsDlgButtonChecked(hDlg, OPT_View));\r
6940 }\r
6941 \r
6942 char *\r
6943 QuoteForFilename(char *filename)\r
6944 {\r
6945   int dquote, space;\r
6946   dquote = strchr(filename, '"') != NULL;\r
6947   space = strchr(filename, ' ') != NULL;\r
6948   if (dquote || space) {\r
6949     if (dquote) {\r
6950       return "'";\r
6951     } else {\r
6952       return "\"";\r
6953     }\r
6954   } else {\r
6955     return "";\r
6956   }\r
6957 }\r
6958 \r
6959 VOID\r
6960 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6961 {\r
6962   char buf[MSG_SIZ];\r
6963   char *q;\r
6964 \r
6965   InitComboStringsFromOption(hwndCombo, nthnames);\r
6966   q = QuoteForFilename(nthcp);\r
6967   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6968   if (*nthdir != NULLCHAR) {\r
6969     q = QuoteForFilename(nthdir);\r
6970     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6971   }\r
6972   if (*nthcp == NULLCHAR) {\r
6973     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\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 \r
6980 LRESULT CALLBACK\r
6981 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6982 {\r
6983   char buf[MSG_SIZ];\r
6984   HANDLE hwndCombo;\r
6985   char *p;\r
6986 \r
6987   switch (message) {\r
6988   case WM_INITDIALOG:\r
6989     /* Center the dialog */\r
6990     CenterWindow (hDlg, GetDesktopWindow());\r
6991     /* Initialize the dialog items */\r
6992     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6993                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6994                   firstChessProgramNames);\r
6995     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6996                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6997                   secondChessProgramNames);\r
6998     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6999     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7000     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7001     if (*appData.icsHelper != NULLCHAR) {\r
7002       char *q = QuoteForFilename(appData.icsHelper);\r
7003       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7004     }\r
7005     if (*appData.icsHost == NULLCHAR) {\r
7006       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7007       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7008     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7009       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7010       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7011     }\r
7012 \r
7013     if (appData.icsActive) {\r
7014       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7015     }\r
7016     else if (appData.noChessProgram) {\r
7017       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7018     }\r
7019     else {\r
7020       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7021     }\r
7022 \r
7023     SetStartupDialogEnables(hDlg);\r
7024     return TRUE;\r
7025 \r
7026   case WM_COMMAND:\r
7027     switch (LOWORD(wParam)) {\r
7028     case IDOK:\r
7029       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7030         strcpy(buf, "/fcp=");\r
7031         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7032         p = buf;\r
7033         ParseArgs(StringGet, &p);\r
7034         strcpy(buf, "/scp=");\r
7035         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7036         p = buf;\r
7037         ParseArgs(StringGet, &p);\r
7038         appData.noChessProgram = FALSE;\r
7039         appData.icsActive = FALSE;\r
7040       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7041         strcpy(buf, "/ics /icshost=");\r
7042         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7043         p = buf;\r
7044         ParseArgs(StringGet, &p);\r
7045         if (appData.zippyPlay) {\r
7046           strcpy(buf, "/fcp=");\r
7047           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7048           p = buf;\r
7049           ParseArgs(StringGet, &p);\r
7050         }\r
7051       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7052         appData.noChessProgram = TRUE;\r
7053         appData.icsActive = FALSE;\r
7054       } else {\r
7055         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7056                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7057         return TRUE;\r
7058       }\r
7059       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7060         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7061         p = buf;\r
7062         ParseArgs(StringGet, &p);\r
7063       }\r
7064       EndDialog(hDlg, TRUE);\r
7065       return TRUE;\r
7066 \r
7067     case IDCANCEL:\r
7068       ExitEvent(0);\r
7069       return TRUE;\r
7070 \r
7071     case IDM_HELPCONTENTS:\r
7072       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7073         MessageBox (GetFocus(),\r
7074                     "Unable to activate help",\r
7075                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7076       }\r
7077       break;\r
7078 \r
7079     default:\r
7080       SetStartupDialogEnables(hDlg);\r
7081       break;\r
7082     }\r
7083     break;\r
7084   }\r
7085   return FALSE;\r
7086 }\r
7087 \r
7088 /*---------------------------------------------------------------------------*\\r
7089  *\r
7090  * About box dialog functions\r
7091  *\r
7092 \*---------------------------------------------------------------------------*/\r
7093 \r
7094 /* Process messages for "About" dialog box */\r
7095 LRESULT CALLBACK\r
7096 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7097 {\r
7098   switch (message) {\r
7099   case WM_INITDIALOG: /* message: initialize dialog box */\r
7100     /* Center the dialog over the application window */\r
7101     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7102     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7103     return (TRUE);\r
7104 \r
7105   case WM_COMMAND: /* message: received a command */\r
7106     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7107         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7108       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7109       return (TRUE);\r
7110     }\r
7111     break;\r
7112   }\r
7113   return (FALSE);\r
7114 }\r
7115 \r
7116 /*---------------------------------------------------------------------------*\\r
7117  *\r
7118  * Comment Dialog functions\r
7119  *\r
7120 \*---------------------------------------------------------------------------*/\r
7121 \r
7122 LRESULT CALLBACK\r
7123 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7124 {\r
7125   static HANDLE hwndText = NULL;\r
7126   int len, newSizeX, newSizeY, flags;\r
7127   static int sizeX, sizeY;\r
7128   char *str;\r
7129   RECT rect;\r
7130   MINMAXINFO *mmi;\r
7131 \r
7132   switch (message) {\r
7133   case WM_INITDIALOG: /* message: initialize dialog box */\r
7134     /* Initialize the dialog items */\r
7135     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7136     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7137     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7138     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7139     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7140     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7141     SetWindowText(hDlg, commentTitle);\r
7142     if (editComment) {\r
7143       SetFocus(hwndText);\r
7144     } else {\r
7145       SetFocus(GetDlgItem(hDlg, IDOK));\r
7146     }\r
7147     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7148                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7149                 MAKELPARAM(FALSE, 0));\r
7150     /* Size and position the dialog */\r
7151     if (!commentDialog) {\r
7152       commentDialog = hDlg;\r
7153       flags = SWP_NOZORDER;\r
7154       GetClientRect(hDlg, &rect);\r
7155       sizeX = rect.right;\r
7156       sizeY = rect.bottom;\r
7157       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7158           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7159         WINDOWPLACEMENT wp;\r
7160         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7161         wp.length = sizeof(WINDOWPLACEMENT);\r
7162         wp.flags = 0;\r
7163         wp.showCmd = SW_SHOW;\r
7164         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7165         wp.rcNormalPosition.left = commentX;\r
7166         wp.rcNormalPosition.right = commentX + commentW;\r
7167         wp.rcNormalPosition.top = commentY;\r
7168         wp.rcNormalPosition.bottom = commentY + commentH;\r
7169         SetWindowPlacement(hDlg, &wp);\r
7170 \r
7171         GetClientRect(hDlg, &rect);\r
7172         newSizeX = rect.right;\r
7173         newSizeY = rect.bottom;\r
7174         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7175                               newSizeX, newSizeY);\r
7176         sizeX = newSizeX;\r
7177         sizeY = newSizeY;\r
7178       }\r
7179     }\r
7180     return FALSE;\r
7181 \r
7182   case WM_COMMAND: /* message: received a command */\r
7183     switch (LOWORD(wParam)) {\r
7184     case IDOK:\r
7185       if (editComment) {\r
7186         char *p, *q;\r
7187         /* Read changed options from the dialog box */\r
7188         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7189         len = GetWindowTextLength(hwndText);\r
7190         str = (char *) malloc(len + 1);\r
7191         GetWindowText(hwndText, str, len + 1);\r
7192         p = q = str;\r
7193         while (*q) {\r
7194           if (*q == '\r')\r
7195             q++;\r
7196           else\r
7197             *p++ = *q++;\r
7198         }\r
7199         *p = NULLCHAR;\r
7200         ReplaceComment(commentIndex, str);\r
7201         free(str);\r
7202       }\r
7203       CommentPopDown();\r
7204       return TRUE;\r
7205 \r
7206     case IDCANCEL:\r
7207     case OPT_CancelComment:\r
7208       CommentPopDown();\r
7209       return TRUE;\r
7210 \r
7211     case OPT_ClearComment:\r
7212       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7213       break;\r
7214 \r
7215     case OPT_EditComment:\r
7216       EditCommentEvent();\r
7217       return TRUE;\r
7218 \r
7219     default:\r
7220       break;\r
7221     }\r
7222     break;\r
7223 \r
7224   case WM_SIZE:\r
7225     newSizeX = LOWORD(lParam);\r
7226     newSizeY = HIWORD(lParam);\r
7227     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7228     sizeX = newSizeX;\r
7229     sizeY = newSizeY;\r
7230     break;\r
7231 \r
7232   case WM_GETMINMAXINFO:\r
7233     /* Prevent resizing window too small */\r
7234     mmi = (MINMAXINFO *) lParam;\r
7235     mmi->ptMinTrackSize.x = 100;\r
7236     mmi->ptMinTrackSize.y = 100;\r
7237     break;\r
7238   }\r
7239   return FALSE;\r
7240 }\r
7241 \r
7242 VOID\r
7243 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7244 {\r
7245   FARPROC lpProc;\r
7246   char *p, *q;\r
7247 \r
7248   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7249 \r
7250   if (str == NULL) str = "";\r
7251   p = (char *) malloc(2 * strlen(str) + 2);\r
7252   q = p;\r
7253   while (*str) {\r
7254     if (*str == '\n') *q++ = '\r';\r
7255     *q++ = *str++;\r
7256   }\r
7257   *q = NULLCHAR;\r
7258   if (commentText != NULL) free(commentText);\r
7259 \r
7260   commentIndex = index;\r
7261   commentTitle = title;\r
7262   commentText = p;\r
7263   editComment = edit;\r
7264 \r
7265   if (commentDialog) {\r
7266     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7267     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7268   } else {\r
7269     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7270     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7271                  hwndMain, (DLGPROC)lpProc);\r
7272     FreeProcInstance(lpProc);\r
7273   }\r
7274   commentDialogUp = TRUE;\r
7275 }\r
7276 \r
7277 \r
7278 /*---------------------------------------------------------------------------*\\r
7279  *\r
7280  * Type-in move dialog functions\r
7281  * \r
7282 \*---------------------------------------------------------------------------*/\r
7283 \r
7284 LRESULT CALLBACK\r
7285 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7286 {\r
7287   char move[MSG_SIZ];\r
7288   HWND hInput;\r
7289   ChessMove moveType;\r
7290   int fromX, fromY, toX, toY;\r
7291   char promoChar;\r
7292 \r
7293   switch (message) {\r
7294   case WM_INITDIALOG:\r
7295     move[0] = (char) lParam;\r
7296     move[1] = NULLCHAR;\r
7297     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7298     hInput = GetDlgItem(hDlg, OPT_Move);\r
7299     SetWindowText(hInput, move);\r
7300     SetFocus(hInput);\r
7301     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7302     return FALSE;\r
7303 \r
7304   case WM_COMMAND:\r
7305     switch (LOWORD(wParam)) {\r
7306     case IDOK:\r
7307       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7308         gameMode != Training) {\r
7309         DisplayMoveError("Displayed move is not current");\r
7310       } else {\r
7311         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7312         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7313           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7314           if (gameMode != Training)\r
7315               forwardMostMove = currentMove;\r
7316           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7317         } else {\r
7318           DisplayMoveError("Could not parse move");\r
7319         }\r
7320       }\r
7321       EndDialog(hDlg, TRUE);\r
7322       return TRUE;\r
7323     case IDCANCEL:\r
7324       EndDialog(hDlg, FALSE);\r
7325       return TRUE;\r
7326     default:\r
7327       break;\r
7328     }\r
7329     break;\r
7330   }\r
7331   return FALSE;\r
7332 }\r
7333 \r
7334 VOID\r
7335 PopUpMoveDialog(char firstchar)\r
7336 {\r
7337     FARPROC lpProc;\r
7338     \r
7339     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7340         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7341         gameMode == AnalyzeMode || gameMode == EditGame || \r
7342         gameMode == EditPosition || gameMode == IcsExamining ||\r
7343         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7344         gameMode == Training) {\r
7345       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7346       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7347         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7348       FreeProcInstance(lpProc);\r
7349     }\r
7350 }\r
7351 \r
7352 /*---------------------------------------------------------------------------*\\r
7353  *\r
7354  * Type-in name dialog functions\r
7355  * \r
7356 \*---------------------------------------------------------------------------*/\r
7357 \r
7358 LRESULT CALLBACK\r
7359 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7360 {\r
7361   char move[MSG_SIZ];\r
7362   HWND hInput;\r
7363 \r
7364   switch (message) {\r
7365   case WM_INITDIALOG:\r
7366     move[0] = (char) lParam;\r
7367     move[1] = NULLCHAR;\r
7368     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7369     hInput = GetDlgItem(hDlg, OPT_Name);\r
7370     SetWindowText(hInput, move);\r
7371     SetFocus(hInput);\r
7372     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7373     return FALSE;\r
7374 \r
7375   case WM_COMMAND:\r
7376     switch (LOWORD(wParam)) {\r
7377     case IDOK:\r
7378       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7379       appData.userName = strdup(move);\r
7380       SetUserLogo();\r
7381 \r
7382       EndDialog(hDlg, TRUE);\r
7383       return TRUE;\r
7384     case IDCANCEL:\r
7385       EndDialog(hDlg, FALSE);\r
7386       return TRUE;\r
7387     default:\r
7388       break;\r
7389     }\r
7390     break;\r
7391   }\r
7392   return FALSE;\r
7393 }\r
7394 \r
7395 VOID\r
7396 PopUpNameDialog(char firstchar)\r
7397 {\r
7398     FARPROC lpProc;\r
7399     \r
7400       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7401       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7402         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7403       FreeProcInstance(lpProc);\r
7404 }\r
7405 \r
7406 /*---------------------------------------------------------------------------*\\r
7407  *\r
7408  *  Error dialogs\r
7409  * \r
7410 \*---------------------------------------------------------------------------*/\r
7411 \r
7412 /* Nonmodal error box */\r
7413 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7414                              WPARAM wParam, LPARAM lParam);\r
7415 \r
7416 VOID\r
7417 ErrorPopUp(char *title, char *content)\r
7418 {\r
7419   FARPROC lpProc;\r
7420   char *p, *q;\r
7421   BOOLEAN modal = hwndMain == NULL;\r
7422 \r
7423   p = content;\r
7424   q = errorMessage;\r
7425   while (*p) {\r
7426     if (*p == '\n') {\r
7427       if (modal) {\r
7428         *q++ = ' ';\r
7429         p++;\r
7430       } else {\r
7431         *q++ = '\r';\r
7432         *q++ = *p++;\r
7433       }\r
7434     } else {\r
7435       *q++ = *p++;\r
7436     }\r
7437   }\r
7438   *q = NULLCHAR;\r
7439   strncpy(errorTitle, title, sizeof(errorTitle));\r
7440   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7441   \r
7442   if (modal) {\r
7443     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7444   } else {\r
7445     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7446     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7447                  hwndMain, (DLGPROC)lpProc);\r
7448     FreeProcInstance(lpProc);\r
7449   }\r
7450 }\r
7451 \r
7452 VOID\r
7453 ErrorPopDown()\r
7454 {\r
7455   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7456   if (errorDialog == NULL) return;\r
7457   DestroyWindow(errorDialog);\r
7458   errorDialog = NULL;\r
7459 }\r
7460 \r
7461 LRESULT CALLBACK\r
7462 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7463 {\r
7464   HANDLE hwndText;\r
7465   RECT rChild;\r
7466 \r
7467   switch (message) {\r
7468   case WM_INITDIALOG:\r
7469     GetWindowRect(hDlg, &rChild);\r
7470 \r
7471     /*\r
7472     SetWindowPos(hDlg, NULL, rChild.left,\r
7473       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7474       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7475     */\r
7476 \r
7477     /* \r
7478         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7479         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7480         and it doesn't work when you resize the dialog.\r
7481         For now, just give it a default position.\r
7482     */\r
7483     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7484 \r
7485     errorDialog = hDlg;\r
7486     SetWindowText(hDlg, errorTitle);\r
7487     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7488     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7489     return FALSE;\r
7490 \r
7491   case WM_COMMAND:\r
7492     switch (LOWORD(wParam)) {\r
7493     case IDOK:\r
7494     case IDCANCEL:\r
7495       if (errorDialog == hDlg) errorDialog = NULL;\r
7496       DestroyWindow(hDlg);\r
7497       return TRUE;\r
7498 \r
7499     default:\r
7500       break;\r
7501     }\r
7502     break;\r
7503   }\r
7504   return FALSE;\r
7505 }\r
7506 \r
7507 #ifdef GOTHIC\r
7508 HWND gothicDialog = NULL;\r
7509 \r
7510 LRESULT CALLBACK\r
7511 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7512 {\r
7513   HANDLE hwndText;\r
7514   RECT rChild;\r
7515   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7516 \r
7517   switch (message) {\r
7518   case WM_INITDIALOG:\r
7519     GetWindowRect(hDlg, &rChild);\r
7520 \r
7521     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7522                                                              SWP_NOZORDER);\r
7523 \r
7524     /* \r
7525         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7526         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7527         and it doesn't work when you resize the dialog.\r
7528         For now, just give it a default position.\r
7529     */\r
7530     gothicDialog = hDlg;\r
7531     SetWindowText(hDlg, errorTitle);\r
7532     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7533     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7534     return FALSE;\r
7535 \r
7536   case WM_COMMAND:\r
7537     switch (LOWORD(wParam)) {\r
7538     case IDOK:\r
7539     case IDCANCEL:\r
7540       if (errorDialog == hDlg) errorDialog = NULL;\r
7541       DestroyWindow(hDlg);\r
7542       return TRUE;\r
7543 \r
7544     default:\r
7545       break;\r
7546     }\r
7547     break;\r
7548   }\r
7549   return FALSE;\r
7550 }\r
7551 \r
7552 VOID\r
7553 GothicPopUp(char *title, VariantClass variant)\r
7554 {\r
7555   FARPROC lpProc;\r
7556   static char *lastTitle;\r
7557 \r
7558   strncpy(errorTitle, title, sizeof(errorTitle));\r
7559   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7560 \r
7561   if(lastTitle != title && gothicDialog != NULL) {\r
7562     DestroyWindow(gothicDialog);\r
7563     gothicDialog = NULL;\r
7564   }\r
7565   if(variant != VariantNormal && gothicDialog == NULL) {\r
7566     title = lastTitle;\r
7567     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7568     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7569                  hwndMain, (DLGPROC)lpProc);\r
7570     FreeProcInstance(lpProc);\r
7571   }\r
7572 }\r
7573 #endif\r
7574 \r
7575 /*---------------------------------------------------------------------------*\\r
7576  *\r
7577  *  Ics Interaction console functions\r
7578  *\r
7579 \*---------------------------------------------------------------------------*/\r
7580 \r
7581 #define HISTORY_SIZE 64\r
7582 static char *history[HISTORY_SIZE];\r
7583 int histIn = 0, histP = 0;\r
7584 \r
7585 VOID\r
7586 SaveInHistory(char *cmd)\r
7587 {\r
7588   if (history[histIn] != NULL) {\r
7589     free(history[histIn]);\r
7590     history[histIn] = NULL;\r
7591   }\r
7592   if (*cmd == NULLCHAR) return;\r
7593   history[histIn] = StrSave(cmd);\r
7594   histIn = (histIn + 1) % HISTORY_SIZE;\r
7595   if (history[histIn] != NULL) {\r
7596     free(history[histIn]);\r
7597     history[histIn] = NULL;\r
7598   }\r
7599   histP = histIn;\r
7600 }\r
7601 \r
7602 char *\r
7603 PrevInHistory(char *cmd)\r
7604 {\r
7605   int newhp;\r
7606   if (histP == histIn) {\r
7607     if (history[histIn] != NULL) free(history[histIn]);\r
7608     history[histIn] = StrSave(cmd);\r
7609   }\r
7610   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7611   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7612   histP = newhp;\r
7613   return history[histP];\r
7614 }\r
7615 \r
7616 char *\r
7617 NextInHistory()\r
7618 {\r
7619   if (histP == histIn) return NULL;\r
7620   histP = (histP + 1) % HISTORY_SIZE;\r
7621   return history[histP];\r
7622 }\r
7623 \r
7624 typedef struct {\r
7625   char *item;\r
7626   char *command;\r
7627   BOOLEAN getname;\r
7628   BOOLEAN immediate;\r
7629 } IcsTextMenuEntry;\r
7630 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7631 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7632 \r
7633 void\r
7634 ParseIcsTextMenu(char *icsTextMenuString)\r
7635 {\r
7636 //  int flags = 0;\r
7637   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7638   char *p = icsTextMenuString;\r
7639   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7640     free(e->item);\r
7641     e->item = NULL;\r
7642     if (e->command != NULL) {\r
7643       free(e->command);\r
7644       e->command = NULL;\r
7645     }\r
7646     e++;\r
7647   }\r
7648   e = icsTextMenuEntry;\r
7649   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7650     if (*p == ';' || *p == '\n') {\r
7651       e->item = strdup("-");\r
7652       e->command = NULL;\r
7653       p++;\r
7654     } else if (*p == '-') {\r
7655       e->item = strdup("-");\r
7656       e->command = NULL;\r
7657       p++;\r
7658       if (*p) p++;\r
7659     } else {\r
7660       char *q, *r, *s, *t;\r
7661       char c;\r
7662       q = strchr(p, ',');\r
7663       if (q == NULL) break;\r
7664       *q = NULLCHAR;\r
7665       r = strchr(q + 1, ',');\r
7666       if (r == NULL) break;\r
7667       *r = NULLCHAR;\r
7668       s = strchr(r + 1, ',');\r
7669       if (s == NULL) break;\r
7670       *s = NULLCHAR;\r
7671       c = ';';\r
7672       t = strchr(s + 1, c);\r
7673       if (t == NULL) {\r
7674         c = '\n';\r
7675         t = strchr(s + 1, c);\r
7676       }\r
7677       if (t != NULL) *t = NULLCHAR;\r
7678       e->item = strdup(p);\r
7679       e->command = strdup(q + 1);\r
7680       e->getname = *(r + 1) != '0';\r
7681       e->immediate = *(s + 1) != '0';\r
7682       *q = ',';\r
7683       *r = ',';\r
7684       *s = ',';\r
7685       if (t == NULL) break;\r
7686       *t = c;\r
7687       p = t + 1;\r
7688     }\r
7689     e++;\r
7690   } \r
7691 }\r
7692 \r
7693 HMENU\r
7694 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7695 {\r
7696   HMENU hmenu, h;\r
7697   int i = 0;\r
7698   hmenu = LoadMenu(hInst, "TextMenu");\r
7699   h = GetSubMenu(hmenu, 0);\r
7700   while (e->item) {\r
7701     if (strcmp(e->item, "-") == 0) {\r
7702       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7703     } else {\r
7704       if (e->item[0] == '|') {\r
7705         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7706                    IDM_CommandX + i, &e->item[1]);\r
7707       } else {\r
7708         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7709       }\r
7710     }\r
7711     e++;\r
7712     i++;\r
7713   } \r
7714   return hmenu;\r
7715 }\r
7716 \r
7717 WNDPROC consoleTextWindowProc;\r
7718 \r
7719 void\r
7720 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7721 {\r
7722   char buf[MSG_SIZ], name[MSG_SIZ];\r
7723   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7724   CHARRANGE sel;\r
7725 \r
7726   if (!getname) {\r
7727     SetWindowText(hInput, command);\r
7728     if (immediate) {\r
7729       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7730     } else {\r
7731       sel.cpMin = 999999;\r
7732       sel.cpMax = 999999;\r
7733       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7734       SetFocus(hInput);\r
7735     }\r
7736     return;\r
7737   }    \r
7738   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7739   if (sel.cpMin == sel.cpMax) {\r
7740     /* Expand to surrounding word */\r
7741     TEXTRANGE tr;\r
7742     do {\r
7743       tr.chrg.cpMax = sel.cpMin;\r
7744       tr.chrg.cpMin = --sel.cpMin;\r
7745       if (sel.cpMin < 0) break;\r
7746       tr.lpstrText = name;\r
7747       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7748     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7749     sel.cpMin++;\r
7750 \r
7751     do {\r
7752       tr.chrg.cpMin = sel.cpMax;\r
7753       tr.chrg.cpMax = ++sel.cpMax;\r
7754       tr.lpstrText = name;\r
7755       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7756     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7757     sel.cpMax--;\r
7758 \r
7759     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7760       MessageBeep(MB_ICONEXCLAMATION);\r
7761       return;\r
7762     }\r
7763     tr.chrg = sel;\r
7764     tr.lpstrText = name;\r
7765     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7766   } else {\r
7767     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7768       MessageBeep(MB_ICONEXCLAMATION);\r
7769       return;\r
7770     }\r
7771     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7772   }\r
7773   if (immediate) {\r
7774     sprintf(buf, "%s %s", command, name);\r
7775     SetWindowText(hInput, buf);\r
7776     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7777   } else {\r
7778     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7779     SetWindowText(hInput, buf);\r
7780     sel.cpMin = 999999;\r
7781     sel.cpMax = 999999;\r
7782     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7783     SetFocus(hInput);\r
7784   }\r
7785 }\r
7786 \r
7787 LRESULT CALLBACK \r
7788 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7789 {\r
7790   HWND hInput;\r
7791   CHARRANGE sel;\r
7792 \r
7793   switch (message) {\r
7794   case WM_KEYDOWN:\r
7795     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7796     switch (wParam) {\r
7797     case VK_PRIOR:\r
7798       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7799       return 0;\r
7800     case VK_NEXT:\r
7801       sel.cpMin = 999999;\r
7802       sel.cpMax = 999999;\r
7803       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7804       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7805       return 0;\r
7806     }\r
7807     break;\r
7808   case WM_CHAR:\r
7809     if (wParam == '\t') {\r
7810       if (GetKeyState(VK_SHIFT) < 0) {\r
7811         /* shifted */\r
7812         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7813         if (buttonDesc[0].hwnd) {\r
7814           SetFocus(buttonDesc[0].hwnd);\r
7815         } else {\r
7816           SetFocus(hwndMain);\r
7817         }\r
7818       } else {\r
7819         /* unshifted */\r
7820         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7821       }\r
7822     } else {\r
7823       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7824       SetFocus(hInput);\r
7825       SendMessage(hInput, message, wParam, lParam);\r
7826     }\r
7827     return 0;\r
7828   case WM_PASTE:\r
7829     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7830     SetFocus(hInput);\r
7831     return SendMessage(hInput, message, wParam, lParam);\r
7832   case WM_MBUTTONDOWN:\r
7833     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7834   case WM_RBUTTONDOWN:\r
7835     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7836       /* Move selection here if it was empty */\r
7837       POINT pt;\r
7838       pt.x = LOWORD(lParam);\r
7839       pt.y = HIWORD(lParam);\r
7840       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7841       if (sel.cpMin == sel.cpMax) {\r
7842         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7843         sel.cpMax = sel.cpMin;\r
7844         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7845       }\r
7846       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7847     }\r
7848     return 0;\r
7849   case WM_RBUTTONUP:\r
7850     if (GetKeyState(VK_SHIFT) & ~1) {\r
7851       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7852         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7853     } else {\r
7854       POINT pt;\r
7855       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7856       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7857       if (sel.cpMin == sel.cpMax) {\r
7858         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7859         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7860       }\r
7861       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7862         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7863       }\r
7864       pt.x = LOWORD(lParam);\r
7865       pt.y = HIWORD(lParam);\r
7866       MenuPopup(hwnd, pt, hmenu, -1);\r
7867     }\r
7868     return 0;\r
7869   case WM_COMMAND:\r
7870     switch (LOWORD(wParam)) {\r
7871     case IDM_QuickPaste:\r
7872       {\r
7873         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7874         if (sel.cpMin == sel.cpMax) {\r
7875           MessageBeep(MB_ICONEXCLAMATION);\r
7876           return 0;\r
7877         }\r
7878         SendMessage(hwnd, WM_COPY, 0, 0);\r
7879         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7880         SendMessage(hInput, WM_PASTE, 0, 0);\r
7881         SetFocus(hInput);\r
7882         return 0;\r
7883       }\r
7884     case IDM_Cut:\r
7885       SendMessage(hwnd, WM_CUT, 0, 0);\r
7886       return 0;\r
7887     case IDM_Paste:\r
7888       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7889       return 0;\r
7890     case IDM_Copy:\r
7891       SendMessage(hwnd, WM_COPY, 0, 0);\r
7892       return 0;\r
7893     default:\r
7894       {\r
7895         int i = LOWORD(wParam) - IDM_CommandX;\r
7896         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7897             icsTextMenuEntry[i].command != NULL) {\r
7898           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7899                    icsTextMenuEntry[i].getname,\r
7900                    icsTextMenuEntry[i].immediate);\r
7901           return 0;\r
7902         }\r
7903       }\r
7904       break;\r
7905     }\r
7906     break;\r
7907   }\r
7908   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7909 }\r
7910 \r
7911 WNDPROC consoleInputWindowProc;\r
7912 \r
7913 LRESULT CALLBACK\r
7914 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7915 {\r
7916   char buf[MSG_SIZ];\r
7917   char *p;\r
7918   static BOOL sendNextChar = FALSE;\r
7919   static BOOL quoteNextChar = FALSE;\r
7920   InputSource *is = consoleInputSource;\r
7921   CHARFORMAT cf;\r
7922   CHARRANGE sel;\r
7923 \r
7924   switch (message) {\r
7925   case WM_CHAR:\r
7926     if (!appData.localLineEditing || sendNextChar) {\r
7927       is->buf[0] = (CHAR) wParam;\r
7928       is->count = 1;\r
7929       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7930       sendNextChar = FALSE;\r
7931       return 0;\r
7932     }\r
7933     if (quoteNextChar) {\r
7934       buf[0] = (char) wParam;\r
7935       buf[1] = NULLCHAR;\r
7936       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7937       quoteNextChar = FALSE;\r
7938       return 0;\r
7939     }\r
7940     switch (wParam) {\r
7941     case '\r':   /* Enter key */\r
7942       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7943       if (consoleEcho) SaveInHistory(is->buf);\r
7944       is->buf[is->count++] = '\n';\r
7945       is->buf[is->count] = NULLCHAR;\r
7946       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7947       if (consoleEcho) {\r
7948         ConsoleOutput(is->buf, is->count, TRUE);\r
7949       } else if (appData.localLineEditing) {\r
7950         ConsoleOutput("\n", 1, TRUE);\r
7951       }\r
7952       /* fall thru */\r
7953     case '\033': /* Escape key */\r
7954       SetWindowText(hwnd, "");\r
7955       cf.cbSize = sizeof(CHARFORMAT);\r
7956       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7957       if (consoleEcho) {\r
7958         cf.crTextColor = textAttribs[ColorNormal].color;\r
7959       } else {\r
7960         cf.crTextColor = COLOR_ECHOOFF;\r
7961       }\r
7962       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7963       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7964       return 0;\r
7965     case '\t':   /* Tab key */\r
7966       if (GetKeyState(VK_SHIFT) < 0) {\r
7967         /* shifted */\r
7968         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7969       } else {\r
7970         /* unshifted */\r
7971         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7972         if (buttonDesc[0].hwnd) {\r
7973           SetFocus(buttonDesc[0].hwnd);\r
7974         } else {\r
7975           SetFocus(hwndMain);\r
7976         }\r
7977       }\r
7978       return 0;\r
7979     case '\023': /* Ctrl+S */\r
7980       sendNextChar = TRUE;\r
7981       return 0;\r
7982     case '\021': /* Ctrl+Q */\r
7983       quoteNextChar = TRUE;\r
7984       return 0;\r
7985     default:\r
7986       break;\r
7987     }\r
7988     break;\r
7989   case WM_KEYDOWN:\r
7990     switch (wParam) {\r
7991     case VK_UP:\r
7992       GetWindowText(hwnd, buf, MSG_SIZ);\r
7993       p = PrevInHistory(buf);\r
7994       if (p != NULL) {\r
7995         SetWindowText(hwnd, p);\r
7996         sel.cpMin = 999999;\r
7997         sel.cpMax = 999999;\r
7998         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7999         return 0;\r
8000       }\r
8001       break;\r
8002     case VK_DOWN:\r
8003       p = NextInHistory();\r
8004       if (p != NULL) {\r
8005         SetWindowText(hwnd, p);\r
8006         sel.cpMin = 999999;\r
8007         sel.cpMax = 999999;\r
8008         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8009         return 0;\r
8010       }\r
8011       break;\r
8012     case VK_HOME:\r
8013     case VK_END:\r
8014       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8015       /* fall thru */\r
8016     case VK_PRIOR:\r
8017     case VK_NEXT:\r
8018       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8019       return 0;\r
8020     }\r
8021     break;\r
8022   case WM_MBUTTONDOWN:\r
8023     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8024       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8025     break;\r
8026   case WM_RBUTTONUP:\r
8027     if (GetKeyState(VK_SHIFT) & ~1) {\r
8028       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8029         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8030     } else {\r
8031       POINT pt;\r
8032       HMENU hmenu;\r
8033       hmenu = LoadMenu(hInst, "InputMenu");\r
8034       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8035       if (sel.cpMin == sel.cpMax) {\r
8036         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8037         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8038       }\r
8039       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8040         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8041       }\r
8042       pt.x = LOWORD(lParam);\r
8043       pt.y = HIWORD(lParam);\r
8044       MenuPopup(hwnd, pt, hmenu, -1);\r
8045     }\r
8046     return 0;\r
8047   case WM_COMMAND:\r
8048     switch (LOWORD(wParam)) { \r
8049     case IDM_Undo:\r
8050       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8051       return 0;\r
8052     case IDM_SelectAll:\r
8053       sel.cpMin = 0;\r
8054       sel.cpMax = -1; /*999999?*/\r
8055       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8056       return 0;\r
8057     case IDM_Cut:\r
8058       SendMessage(hwnd, WM_CUT, 0, 0);\r
8059       return 0;\r
8060     case IDM_Paste:\r
8061       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8062       return 0;\r
8063     case IDM_Copy:\r
8064       SendMessage(hwnd, WM_COPY, 0, 0);\r
8065       return 0;\r
8066     }\r
8067     break;\r
8068   }\r
8069   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8070 }\r
8071 \r
8072 #define CO_MAX  100000\r
8073 #define CO_TRIM   1000\r
8074 \r
8075 LRESULT CALLBACK\r
8076 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8077 {\r
8078   static SnapData sd;\r
8079   static HWND hText, hInput /*, hFocus*/;\r
8080 //  InputSource *is = consoleInputSource;\r
8081   RECT rect;\r
8082   static int sizeX, sizeY;\r
8083   int newSizeX, newSizeY;\r
8084   MINMAXINFO *mmi;\r
8085 \r
8086   switch (message) {\r
8087   case WM_INITDIALOG: /* message: initialize dialog box */\r
8088     hwndConsole = hDlg;\r
8089     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8090     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8091     SetFocus(hInput);\r
8092     consoleTextWindowProc = (WNDPROC)\r
8093       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8094     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8095     consoleInputWindowProc = (WNDPROC)\r
8096       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8097     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8098     Colorize(ColorNormal, TRUE);\r
8099     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8100     ChangedConsoleFont();\r
8101     GetClientRect(hDlg, &rect);\r
8102     sizeX = rect.right;\r
8103     sizeY = rect.bottom;\r
8104     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8105         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8106       WINDOWPLACEMENT wp;\r
8107       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8108       wp.length = sizeof(WINDOWPLACEMENT);\r
8109       wp.flags = 0;\r
8110       wp.showCmd = SW_SHOW;\r
8111       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8112       wp.rcNormalPosition.left = wpConsole.x;\r
8113       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8114       wp.rcNormalPosition.top = wpConsole.y;\r
8115       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8116       SetWindowPlacement(hDlg, &wp);\r
8117     }\r
8118 #if 0 \r
8119    // [HGM] Chessknight's change 2004-07-13\r
8120    else { /* Determine Defaults */\r
8121        WINDOWPLACEMENT wp;\r
8122        wpConsole.x = winWidth + 1;\r
8123        wpConsole.y = boardY;\r
8124        wpConsoleW = screenWidth -  winWidth;\r
8125        wpConsoleH = winHeight;\r
8126        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8127        wp.length = sizeof(WINDOWPLACEMENT);\r
8128        wp.flags = 0;\r
8129        wp.showCmd = SW_SHOW;\r
8130        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8131        wp.rcNormalPosition.left = wpConsole.x;\r
8132        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8133        wp.rcNormalPosition.top = wpConsole.y;\r
8134        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8135        SetWindowPlacement(hDlg, &wp);\r
8136     }\r
8137 #endif\r
8138     return FALSE;\r
8139 \r
8140   case WM_SETFOCUS:\r
8141     SetFocus(hInput);\r
8142     return 0;\r
8143 \r
8144   case WM_CLOSE:\r
8145     ExitEvent(0);\r
8146     /* not reached */\r
8147     break;\r
8148 \r
8149   case WM_SIZE:\r
8150     if (IsIconic(hDlg)) break;\r
8151     newSizeX = LOWORD(lParam);\r
8152     newSizeY = HIWORD(lParam);\r
8153     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8154       RECT rectText, rectInput;\r
8155       POINT pt;\r
8156       int newTextHeight, newTextWidth;\r
8157       GetWindowRect(hText, &rectText);\r
8158       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8159       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8160       if (newTextHeight < 0) {\r
8161         newSizeY += -newTextHeight;\r
8162         newTextHeight = 0;\r
8163       }\r
8164       SetWindowPos(hText, NULL, 0, 0,\r
8165         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8166       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8167       pt.x = rectInput.left;\r
8168       pt.y = rectInput.top + newSizeY - sizeY;\r
8169       ScreenToClient(hDlg, &pt);\r
8170       SetWindowPos(hInput, NULL, \r
8171         pt.x, pt.y, /* needs client coords */   \r
8172         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8173         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8174     }\r
8175     sizeX = newSizeX;\r
8176     sizeY = newSizeY;\r
8177     break;\r
8178 \r
8179   case WM_GETMINMAXINFO:\r
8180     /* Prevent resizing window too small */\r
8181     mmi = (MINMAXINFO *) lParam;\r
8182     mmi->ptMinTrackSize.x = 100;\r
8183     mmi->ptMinTrackSize.y = 100;\r
8184     break;\r
8185 \r
8186   /* [AS] Snapping */\r
8187   case WM_ENTERSIZEMOVE:\r
8188     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8189 \r
8190   case WM_SIZING:\r
8191     return OnSizing( &sd, hDlg, wParam, lParam );\r
8192 \r
8193   case WM_MOVING:\r
8194     return OnMoving( &sd, hDlg, wParam, lParam );\r
8195 \r
8196   case WM_EXITSIZEMOVE:\r
8197     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8198   }\r
8199 \r
8200   return DefWindowProc(hDlg, message, wParam, lParam);\r
8201 }\r
8202 \r
8203 \r
8204 VOID\r
8205 ConsoleCreate()\r
8206 {\r
8207   HWND hCons;\r
8208   if (hwndConsole) return;\r
8209   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8210   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8211 }\r
8212 \r
8213 \r
8214 VOID\r
8215 ConsoleOutput(char* data, int length, int forceVisible)\r
8216 {\r
8217   HWND hText;\r
8218   int trim, exlen;\r
8219   char *p, *q;\r
8220   char buf[CO_MAX+1];\r
8221   POINT pEnd;\r
8222   RECT rect;\r
8223   static int delayLF = 0;\r
8224   CHARRANGE savesel, sel;\r
8225 \r
8226   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8227   p = data;\r
8228   q = buf;\r
8229   if (delayLF) {\r
8230     *q++ = '\r';\r
8231     *q++ = '\n';\r
8232     delayLF = 0;\r
8233   }\r
8234   while (length--) {\r
8235     if (*p == '\n') {\r
8236       if (*++p) {\r
8237         *q++ = '\r';\r
8238         *q++ = '\n';\r
8239       } else {\r
8240         delayLF = 1;\r
8241       }\r
8242     } else if (*p == '\007') {\r
8243        MyPlaySound(&sounds[(int)SoundBell]);\r
8244        p++;\r
8245     } else {\r
8246       *q++ = *p++;\r
8247     }\r
8248   }\r
8249   *q = NULLCHAR;\r
8250   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8251   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8252   /* Save current selection */\r
8253   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8254   exlen = GetWindowTextLength(hText);\r
8255   /* Find out whether current end of text is visible */\r
8256   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8257   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8258   /* Trim existing text if it's too long */\r
8259   if (exlen + (q - buf) > CO_MAX) {\r
8260     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8261     sel.cpMin = 0;\r
8262     sel.cpMax = trim;\r
8263     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8264     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8265     exlen -= trim;\r
8266     savesel.cpMin -= trim;\r
8267     savesel.cpMax -= trim;\r
8268     if (exlen < 0) exlen = 0;\r
8269     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8270     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8271   }\r
8272   /* Append the new text */\r
8273   sel.cpMin = exlen;\r
8274   sel.cpMax = exlen;\r
8275   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8276   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8277   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8278   if (forceVisible || exlen == 0 ||\r
8279       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8280        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8281     /* Scroll to make new end of text visible if old end of text\r
8282        was visible or new text is an echo of user typein */\r
8283     sel.cpMin = 9999999;\r
8284     sel.cpMax = 9999999;\r
8285     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8286     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8287     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8288     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8289   }\r
8290   if (savesel.cpMax == exlen || forceVisible) {\r
8291     /* Move insert point to new end of text if it was at the old\r
8292        end of text or if the new text is an echo of user typein */\r
8293     sel.cpMin = 9999999;\r
8294     sel.cpMax = 9999999;\r
8295     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8296   } else {\r
8297     /* Restore previous selection */\r
8298     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8299   }\r
8300   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8301 }\r
8302 \r
8303 /*---------*/\r
8304 \r
8305 \r
8306 void\r
8307 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8308 {\r
8309   char buf[100];\r
8310   char *str;\r
8311   COLORREF oldFg, oldBg;\r
8312   HFONT oldFont;\r
8313   RECT rect;\r
8314 \r
8315   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8316 \r
8317   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8318   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8319   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8320 \r
8321   rect.left = x;\r
8322   rect.right = x + squareSize;\r
8323   rect.top  = y;\r
8324   rect.bottom = y + squareSize;\r
8325   str = buf;\r
8326 \r
8327   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8328                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8329              y, ETO_CLIPPED|ETO_OPAQUE,\r
8330              &rect, str, strlen(str), NULL);\r
8331 \r
8332   (void) SetTextColor(hdc, oldFg);\r
8333   (void) SetBkColor(hdc, oldBg);\r
8334   (void) SelectObject(hdc, oldFont);\r
8335 }\r
8336 \r
8337 void\r
8338 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8339               RECT *rect, char *color, char *flagFell)\r
8340 {\r
8341   char buf[100];\r
8342   char *str;\r
8343   COLORREF oldFg, oldBg;\r
8344   HFONT oldFont;\r
8345 \r
8346   if (appData.clockMode) {\r
8347     if (tinyLayout)\r
8348       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8349     else\r
8350       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8351     str = buf;\r
8352   } else {\r
8353     str = color;\r
8354   }\r
8355 \r
8356   if (highlight) {\r
8357     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8358     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8359   } else {\r
8360     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8361     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8362   }\r
8363   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8364 \r
8365   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8366              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8367              rect, str, strlen(str), NULL);\r
8368   if(logoHeight > 0 && appData.clockMode) {\r
8369       RECT r;\r
8370       sprintf(buf, "%s %s", TimeString(timeRemaining), flagFell);\r
8371       r.top = rect->top + logoHeight/2;\r
8372       r.left = rect->left;\r
8373       r.right = rect->right;\r
8374       r.bottom = rect->bottom;\r
8375       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8376                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8377                  &r, str, strlen(str), NULL);\r
8378   }\r
8379   (void) SetTextColor(hdc, oldFg);\r
8380   (void) SetBkColor(hdc, oldBg);\r
8381   (void) SelectObject(hdc, oldFont);\r
8382 }\r
8383 \r
8384 \r
8385 int\r
8386 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8387            OVERLAPPED *ovl)\r
8388 {\r
8389   int ok, err;\r
8390 \r
8391   /* [AS]  */\r
8392   if( count <= 0 ) {\r
8393     if (appData.debugMode) {\r
8394       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8395     }\r
8396 \r
8397     return ERROR_INVALID_USER_BUFFER;\r
8398   }\r
8399 \r
8400   ResetEvent(ovl->hEvent);\r
8401   ovl->Offset = ovl->OffsetHigh = 0;\r
8402   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8403   if (ok) {\r
8404     err = NO_ERROR;\r
8405   } else {\r
8406     err = GetLastError();\r
8407     if (err == ERROR_IO_PENDING) {\r
8408       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8409       if (ok)\r
8410         err = NO_ERROR;\r
8411       else\r
8412         err = GetLastError();\r
8413     }\r
8414   }\r
8415   return err;\r
8416 }\r
8417 \r
8418 int\r
8419 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8420             OVERLAPPED *ovl)\r
8421 {\r
8422   int ok, err;\r
8423 \r
8424   ResetEvent(ovl->hEvent);\r
8425   ovl->Offset = ovl->OffsetHigh = 0;\r
8426   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8427   if (ok) {\r
8428     err = NO_ERROR;\r
8429   } else {\r
8430     err = GetLastError();\r
8431     if (err == ERROR_IO_PENDING) {\r
8432       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8433       if (ok)\r
8434         err = NO_ERROR;\r
8435       else\r
8436         err = GetLastError();\r
8437     }\r
8438   }\r
8439   return err;\r
8440 }\r
8441 \r
8442 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8443 void CheckForInputBufferFull( InputSource * is )\r
8444 {\r
8445     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8446         /* Look for end of line */\r
8447         char * p = is->buf;\r
8448         \r
8449         while( p < is->next && *p != '\n' ) {\r
8450             p++;\r
8451         }\r
8452 \r
8453         if( p >= is->next ) {\r
8454             if (appData.debugMode) {\r
8455                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8456             }\r
8457 \r
8458             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8459             is->count = (DWORD) -1;\r
8460             is->next = is->buf;\r
8461         }\r
8462     }\r
8463 }\r
8464 \r
8465 DWORD\r
8466 InputThread(LPVOID arg)\r
8467 {\r
8468   InputSource *is;\r
8469   OVERLAPPED ovl;\r
8470 \r
8471   is = (InputSource *) arg;\r
8472   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8473   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8474   while (is->hThread != NULL) {\r
8475     is->error = DoReadFile(is->hFile, is->next,\r
8476                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8477                            &is->count, &ovl);\r
8478     if (is->error == NO_ERROR) {\r
8479       is->next += is->count;\r
8480     } else {\r
8481       if (is->error == ERROR_BROKEN_PIPE) {\r
8482         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8483         is->count = 0;\r
8484       } else {\r
8485         is->count = (DWORD) -1;\r
8486         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8487         break; \r
8488       }\r
8489     }\r
8490 \r
8491     CheckForInputBufferFull( is );\r
8492 \r
8493     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8494 \r
8495     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8496 \r
8497     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8498   }\r
8499 \r
8500   CloseHandle(ovl.hEvent);\r
8501   CloseHandle(is->hFile);\r
8502 \r
8503   if (appData.debugMode) {\r
8504     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8505   }\r
8506 \r
8507   return 0;\r
8508 }\r
8509 \r
8510 \r
8511 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8512 DWORD\r
8513 NonOvlInputThread(LPVOID arg)\r
8514 {\r
8515   InputSource *is;\r
8516   char *p, *q;\r
8517   int i;\r
8518   char prev;\r
8519 \r
8520   is = (InputSource *) arg;\r
8521   while (is->hThread != NULL) {\r
8522     is->error = ReadFile(is->hFile, is->next,\r
8523                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8524                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8525     if (is->error == NO_ERROR) {\r
8526       /* Change CRLF to LF */\r
8527       if (is->next > is->buf) {\r
8528         p = is->next - 1;\r
8529         i = is->count + 1;\r
8530       } else {\r
8531         p = is->next;\r
8532         i = is->count;\r
8533       }\r
8534       q = p;\r
8535       prev = NULLCHAR;\r
8536       while (i > 0) {\r
8537         if (prev == '\r' && *p == '\n') {\r
8538           *(q-1) = '\n';\r
8539           is->count--;\r
8540         } else { \r
8541           *q++ = *p;\r
8542         }\r
8543         prev = *p++;\r
8544         i--;\r
8545       }\r
8546       *q = NULLCHAR;\r
8547       is->next = q;\r
8548     } else {\r
8549       if (is->error == ERROR_BROKEN_PIPE) {\r
8550         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8551         is->count = 0; \r
8552       } else {\r
8553         is->count = (DWORD) -1;\r
8554       }\r
8555     }\r
8556 \r
8557     CheckForInputBufferFull( is );\r
8558 \r
8559     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8560 \r
8561     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8562 \r
8563     if (is->count < 0) break;  /* Quit on error */\r
8564   }\r
8565   CloseHandle(is->hFile);\r
8566   return 0;\r
8567 }\r
8568 \r
8569 DWORD\r
8570 SocketInputThread(LPVOID arg)\r
8571 {\r
8572   InputSource *is;\r
8573 \r
8574   is = (InputSource *) arg;\r
8575   while (is->hThread != NULL) {\r
8576     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8577     if ((int)is->count == SOCKET_ERROR) {\r
8578       is->count = (DWORD) -1;\r
8579       is->error = WSAGetLastError();\r
8580     } else {\r
8581       is->error = NO_ERROR;\r
8582       is->next += is->count;\r
8583       if (is->count == 0 && is->second == is) {\r
8584         /* End of file on stderr; quit with no message */\r
8585         break;\r
8586       }\r
8587     }\r
8588     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8589 \r
8590     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8591 \r
8592     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8593   }\r
8594   return 0;\r
8595 }\r
8596 \r
8597 VOID\r
8598 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8599 {\r
8600   InputSource *is;\r
8601 \r
8602   is = (InputSource *) lParam;\r
8603   if (is->lineByLine) {\r
8604     /* Feed in lines one by one */\r
8605     char *p = is->buf;\r
8606     char *q = p;\r
8607     while (q < is->next) {\r
8608       if (*q++ == '\n') {\r
8609         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8610         p = q;\r
8611       }\r
8612     }\r
8613     \r
8614     /* Move any partial line to the start of the buffer */\r
8615     q = is->buf;\r
8616     while (p < is->next) {\r
8617       *q++ = *p++;\r
8618     }\r
8619     is->next = q;\r
8620 \r
8621     if (is->error != NO_ERROR || is->count == 0) {\r
8622       /* Notify backend of the error.  Note: If there was a partial\r
8623          line at the end, it is not flushed through. */\r
8624       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8625     }\r
8626   } else {\r
8627     /* Feed in the whole chunk of input at once */\r
8628     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8629     is->next = is->buf;\r
8630   }\r
8631 }\r
8632 \r
8633 /*---------------------------------------------------------------------------*\\r
8634  *\r
8635  *  Menu enables. Used when setting various modes.\r
8636  *\r
8637 \*---------------------------------------------------------------------------*/\r
8638 \r
8639 typedef struct {\r
8640   int item;\r
8641   int flags;\r
8642 } Enables;\r
8643 \r
8644 VOID\r
8645 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8646 {\r
8647   while (enab->item > 0) {\r
8648     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8649     enab++;\r
8650   }\r
8651 }\r
8652 \r
8653 Enables gnuEnables[] = {\r
8654   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8656   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8657   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8658   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8659   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8660   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8661   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8662   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8663   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8664   { -1, -1 }\r
8665 };\r
8666 \r
8667 Enables icsEnables[] = {\r
8668   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8669   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8670   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8671   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8672   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8673   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8674   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8675   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8676   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8677   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8678   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8679   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8680   { -1, -1 }\r
8681 };\r
8682 \r
8683 #ifdef ZIPPY\r
8684 Enables zippyEnables[] = {\r
8685   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8686   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8687   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8688   { -1, -1 }\r
8689 };\r
8690 #endif\r
8691 \r
8692 Enables ncpEnables[] = {\r
8693   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8694   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8695   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8696   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8697   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8698   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8699   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8700   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8701   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8702   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8703   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8704   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8705   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8706   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8707   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8708   { -1, -1 }\r
8709 };\r
8710 \r
8711 Enables trainingOnEnables[] = {\r
8712   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8713   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8714   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8715   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8716   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8717   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8718   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8719   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8720   { -1, -1 }\r
8721 };\r
8722 \r
8723 Enables trainingOffEnables[] = {\r
8724   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8725   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8726   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8727   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8728   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8729   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8730   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8731   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8732   { -1, -1 }\r
8733 };\r
8734 \r
8735 /* These modify either ncpEnables or gnuEnables */\r
8736 Enables cmailEnables[] = {\r
8737   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8738   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8739   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8740   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8741   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8742   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8743   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8744   { -1, -1 }\r
8745 };\r
8746 \r
8747 Enables machineThinkingEnables[] = {\r
8748   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8749   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8750   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8751   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8752   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8753   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8754   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8755   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8756   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8757   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8758   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8759   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8760   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8761   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8762   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8763   { -1, -1 }\r
8764 };\r
8765 \r
8766 Enables userThinkingEnables[] = {\r
8767   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8768   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8769   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8770   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8771   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8772   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8773   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8774   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8775   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8776   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8777   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8778   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8779   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8780   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8781   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8782   { -1, -1 }\r
8783 };\r
8784 \r
8785 /*---------------------------------------------------------------------------*\\r
8786  *\r
8787  *  Front-end interface functions exported by XBoard.\r
8788  *  Functions appear in same order as prototypes in frontend.h.\r
8789  * \r
8790 \*---------------------------------------------------------------------------*/\r
8791 VOID\r
8792 ModeHighlight()\r
8793 {\r
8794   static UINT prevChecked = 0;\r
8795   static int prevPausing = 0;\r
8796   UINT nowChecked;\r
8797 \r
8798   if (pausing != prevPausing) {\r
8799     prevPausing = pausing;\r
8800     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8801                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8802     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8803   }\r
8804 \r
8805   switch (gameMode) {\r
8806   case BeginningOfGame:\r
8807     if (appData.icsActive)\r
8808       nowChecked = IDM_IcsClient;\r
8809     else if (appData.noChessProgram)\r
8810       nowChecked = IDM_EditGame;\r
8811     else\r
8812       nowChecked = IDM_MachineBlack;\r
8813     break;\r
8814   case MachinePlaysBlack:\r
8815     nowChecked = IDM_MachineBlack;\r
8816     break;\r
8817   case MachinePlaysWhite:\r
8818     nowChecked = IDM_MachineWhite;\r
8819     break;\r
8820   case TwoMachinesPlay:\r
8821     nowChecked = IDM_TwoMachines;\r
8822     break;\r
8823   case AnalyzeMode:\r
8824     nowChecked = IDM_AnalysisMode;\r
8825     break;\r
8826   case AnalyzeFile:\r
8827     nowChecked = IDM_AnalyzeFile;\r
8828     break;\r
8829   case EditGame:\r
8830     nowChecked = IDM_EditGame;\r
8831     break;\r
8832   case PlayFromGameFile:\r
8833     nowChecked = IDM_LoadGame;\r
8834     break;\r
8835   case EditPosition:\r
8836     nowChecked = IDM_EditPosition;\r
8837     break;\r
8838   case Training:\r
8839     nowChecked = IDM_Training;\r
8840     break;\r
8841   case IcsPlayingWhite:\r
8842   case IcsPlayingBlack:\r
8843   case IcsObserving:\r
8844   case IcsIdle:\r
8845     nowChecked = IDM_IcsClient;\r
8846     break;\r
8847   default:\r
8848   case EndOfGame:\r
8849     nowChecked = 0;\r
8850     break;\r
8851   }\r
8852   if (prevChecked != 0)\r
8853     (void) CheckMenuItem(GetMenu(hwndMain),\r
8854                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8855   if (nowChecked != 0)\r
8856     (void) CheckMenuItem(GetMenu(hwndMain),\r
8857                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8858 \r
8859   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8860     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8861                           MF_BYCOMMAND|MF_ENABLED);\r
8862   } else {\r
8863     (void) EnableMenuItem(GetMenu(hwndMain), \r
8864                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8865   }\r
8866 \r
8867   prevChecked = nowChecked;\r
8868 \r
8869   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8870   if (appData.icsActive) {\r
8871        if (appData.icsEngineAnalyze) {\r
8872                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8873                        MF_BYCOMMAND|MF_CHECKED);\r
8874        } else {\r
8875                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8876                        MF_BYCOMMAND|MF_UNCHECKED);\r
8877        }\r
8878   }\r
8879 }\r
8880 \r
8881 VOID\r
8882 SetICSMode()\r
8883 {\r
8884   HMENU hmenu = GetMenu(hwndMain);\r
8885   SetMenuEnables(hmenu, icsEnables);\r
8886   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8887     MF_BYPOSITION|MF_ENABLED);\r
8888 #ifdef ZIPPY\r
8889   if (appData.zippyPlay) {\r
8890     SetMenuEnables(hmenu, zippyEnables);\r
8891     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8892          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8893           MF_BYCOMMAND|MF_ENABLED);\r
8894   }\r
8895 #endif\r
8896 }\r
8897 \r
8898 VOID\r
8899 SetGNUMode()\r
8900 {\r
8901   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8902 }\r
8903 \r
8904 VOID\r
8905 SetNCPMode()\r
8906 {\r
8907   HMENU hmenu = GetMenu(hwndMain);\r
8908   SetMenuEnables(hmenu, ncpEnables);\r
8909   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8910     MF_BYPOSITION|MF_GRAYED);\r
8911     DrawMenuBar(hwndMain);\r
8912 }\r
8913 \r
8914 VOID\r
8915 SetCmailMode()\r
8916 {\r
8917   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8918 }\r
8919 \r
8920 VOID \r
8921 SetTrainingModeOn()\r
8922 {\r
8923   int i;\r
8924   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8925   for (i = 0; i < N_BUTTONS; i++) {\r
8926     if (buttonDesc[i].hwnd != NULL)\r
8927       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8928   }\r
8929   CommentPopDown();\r
8930 }\r
8931 \r
8932 VOID SetTrainingModeOff()\r
8933 {\r
8934   int i;\r
8935   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8936   for (i = 0; i < N_BUTTONS; i++) {\r
8937     if (buttonDesc[i].hwnd != NULL)\r
8938       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8939   }\r
8940 }\r
8941 \r
8942 \r
8943 VOID\r
8944 SetUserThinkingEnables()\r
8945 {\r
8946   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8947 }\r
8948 \r
8949 VOID\r
8950 SetMachineThinkingEnables()\r
8951 {\r
8952   HMENU hMenu = GetMenu(hwndMain);\r
8953   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8954 \r
8955   SetMenuEnables(hMenu, machineThinkingEnables);\r
8956 \r
8957   if (gameMode == MachinePlaysBlack) {\r
8958     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8959   } else if (gameMode == MachinePlaysWhite) {\r
8960     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8961   } else if (gameMode == TwoMachinesPlay) {\r
8962     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8963   }\r
8964 }\r
8965 \r
8966 \r
8967 VOID\r
8968 DisplayTitle(char *str)\r
8969 {\r
8970   char title[MSG_SIZ], *host;\r
8971   if (str[0] != NULLCHAR) {\r
8972     strcpy(title, str);\r
8973   } else if (appData.icsActive) {\r
8974     if (appData.icsCommPort[0] != NULLCHAR)\r
8975       host = "ICS";\r
8976     else \r
8977       host = appData.icsHost;\r
8978     sprintf(title, "%s: %s", szTitle, host);\r
8979   } else if (appData.noChessProgram) {\r
8980     strcpy(title, szTitle);\r
8981   } else {\r
8982     strcpy(title, szTitle);\r
8983     strcat(title, ": ");\r
8984     strcat(title, first.tidy);\r
8985   }\r
8986   SetWindowText(hwndMain, title);\r
8987 }\r
8988 \r
8989 \r
8990 VOID\r
8991 DisplayMessage(char *str1, char *str2)\r
8992 {\r
8993   HDC hdc;\r
8994   HFONT oldFont;\r
8995   int remain = MESSAGE_TEXT_MAX - 1;\r
8996   int len;\r
8997 \r
8998   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8999   messageText[0] = NULLCHAR;\r
9000   if (*str1) {\r
9001     len = strlen(str1);\r
9002     if (len > remain) len = remain;\r
9003     strncpy(messageText, str1, len);\r
9004     messageText[len] = NULLCHAR;\r
9005     remain -= len;\r
9006   }\r
9007   if (*str2 && remain >= 2) {\r
9008     if (*str1) {\r
9009       strcat(messageText, "  ");\r
9010       remain -= 2;\r
9011     }\r
9012     len = strlen(str2);\r
9013     if (len > remain) len = remain;\r
9014     strncat(messageText, str2, len);\r
9015   }\r
9016   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9017 \r
9018   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9019   hdc = GetDC(hwndMain);\r
9020   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9021   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9022              &messageRect, messageText, strlen(messageText), NULL);\r
9023   (void) SelectObject(hdc, oldFont);\r
9024   (void) ReleaseDC(hwndMain, hdc);\r
9025 }\r
9026 \r
9027 VOID\r
9028 DisplayError(char *str, int error)\r
9029 {\r
9030   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9031   int len;\r
9032 \r
9033   if (error == 0) {\r
9034     strcpy(buf, str);\r
9035   } else {\r
9036     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9037                         NULL, error, LANG_NEUTRAL,\r
9038                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9039     if (len > 0) {\r
9040       sprintf(buf, "%s:\n%s", str, buf2);\r
9041     } else {\r
9042       ErrorMap *em = errmap;\r
9043       while (em->err != 0 && em->err != error) em++;\r
9044       if (em->err != 0) {\r
9045         sprintf(buf, "%s:\n%s", str, em->msg);\r
9046       } else {\r
9047         sprintf(buf, "%s:\nError code %d", str, error);\r
9048       }\r
9049     }\r
9050   }\r
9051   \r
9052   ErrorPopUp("Error", buf);\r
9053 }\r
9054 \r
9055 \r
9056 VOID\r
9057 DisplayMoveError(char *str)\r
9058 {\r
9059   fromX = fromY = -1;\r
9060   ClearHighlights();\r
9061   DrawPosition(FALSE, NULL);\r
9062   if (appData.popupMoveErrors) {\r
9063     ErrorPopUp("Error", str);\r
9064   } else {\r
9065     DisplayMessage(str, "");\r
9066     moveErrorMessageUp = TRUE;\r
9067   }\r
9068 }\r
9069 \r
9070 VOID\r
9071 DisplayFatalError(char *str, int error, int exitStatus)\r
9072 {\r
9073   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9074   int len;\r
9075   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9076 \r
9077   if (error != 0) {\r
9078     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9079                         NULL, error, LANG_NEUTRAL,\r
9080                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9081     if (len > 0) {\r
9082       sprintf(buf, "%s:\n%s", str, buf2);\r
9083     } else {\r
9084       ErrorMap *em = errmap;\r
9085       while (em->err != 0 && em->err != error) em++;\r
9086       if (em->err != 0) {\r
9087         sprintf(buf, "%s:\n%s", str, em->msg);\r
9088       } else {\r
9089         sprintf(buf, "%s:\nError code %d", str, error);\r
9090       }\r
9091     }\r
9092     str = buf;\r
9093   }\r
9094   if (appData.debugMode) {\r
9095     fprintf(debugFP, "%s: %s\n", label, str);\r
9096   }\r
9097   if (appData.popupExitMessage) {\r
9098     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9099                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9100   }\r
9101   ExitEvent(exitStatus);\r
9102 }\r
9103 \r
9104 \r
9105 VOID\r
9106 DisplayInformation(char *str)\r
9107 {\r
9108   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9109 }\r
9110 \r
9111 \r
9112 VOID\r
9113 DisplayNote(char *str)\r
9114 {\r
9115   ErrorPopUp("Note", str);\r
9116 }\r
9117 \r
9118 \r
9119 typedef struct {\r
9120   char *title, *question, *replyPrefix;\r
9121   ProcRef pr;\r
9122 } QuestionParams;\r
9123 \r
9124 LRESULT CALLBACK\r
9125 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9126 {\r
9127   static QuestionParams *qp;\r
9128   char reply[MSG_SIZ];\r
9129   int len, err;\r
9130 \r
9131   switch (message) {\r
9132   case WM_INITDIALOG:\r
9133     qp = (QuestionParams *) lParam;\r
9134     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9135     SetWindowText(hDlg, qp->title);\r
9136     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9137     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9138     return FALSE;\r
9139 \r
9140   case WM_COMMAND:\r
9141     switch (LOWORD(wParam)) {\r
9142     case IDOK:\r
9143       strcpy(reply, qp->replyPrefix);\r
9144       if (*reply) strcat(reply, " ");\r
9145       len = strlen(reply);\r
9146       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9147       strcat(reply, "\n");\r
9148       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9149       EndDialog(hDlg, TRUE);\r
9150       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9151       return TRUE;\r
9152     case IDCANCEL:\r
9153       EndDialog(hDlg, FALSE);\r
9154       return TRUE;\r
9155     default:\r
9156       break;\r
9157     }\r
9158     break;\r
9159   }\r
9160   return FALSE;\r
9161 }\r
9162 \r
9163 VOID\r
9164 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9165 {\r
9166     QuestionParams qp;\r
9167     FARPROC lpProc;\r
9168     \r
9169     qp.title = title;\r
9170     qp.question = question;\r
9171     qp.replyPrefix = replyPrefix;\r
9172     qp.pr = pr;\r
9173     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9174     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9175       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9176     FreeProcInstance(lpProc);\r
9177 }\r
9178 \r
9179 /* [AS] Pick FRC position */\r
9180 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9181 {\r
9182     static int * lpIndexFRC;\r
9183     BOOL index_is_ok;\r
9184     char buf[16];\r
9185 \r
9186     switch( message )\r
9187     {\r
9188     case WM_INITDIALOG:\r
9189         lpIndexFRC = (int *) lParam;\r
9190 \r
9191         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9192 \r
9193         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9194         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9195         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9196         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9197 \r
9198         break;\r
9199 \r
9200     case WM_COMMAND:\r
9201         switch( LOWORD(wParam) ) {\r
9202         case IDOK:\r
9203             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9204             EndDialog( hDlg, 0 );\r
9205             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9206             return TRUE;\r
9207         case IDCANCEL:\r
9208             EndDialog( hDlg, 1 );   \r
9209             return TRUE;\r
9210         case IDC_NFG_Edit:\r
9211             if( HIWORD(wParam) == EN_CHANGE ) {\r
9212                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9213 \r
9214                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9215             }\r
9216             return TRUE;\r
9217         case IDC_NFG_Random:\r
9218             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9219             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9220             return TRUE;\r
9221         }\r
9222 \r
9223         break;\r
9224     }\r
9225 \r
9226     return FALSE;\r
9227 }\r
9228 \r
9229 int NewGameFRC()\r
9230 {\r
9231     int result;\r
9232     int index = appData.defaultFrcPosition;\r
9233     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9234 \r
9235     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9236 \r
9237     if( result == 0 ) {\r
9238         appData.defaultFrcPosition = index;\r
9239     }\r
9240 \r
9241     return result;\r
9242 }\r
9243 \r
9244 /* [AS] Game list options */\r
9245 typedef struct {\r
9246     char id;\r
9247     char * name;\r
9248 } GLT_Item;\r
9249 \r
9250 static GLT_Item GLT_ItemInfo[] = {\r
9251     { GLT_EVENT,      "Event" },\r
9252     { GLT_SITE,       "Site" },\r
9253     { GLT_DATE,       "Date" },\r
9254     { GLT_ROUND,      "Round" },\r
9255     { GLT_PLAYERS,    "Players" },\r
9256     { GLT_RESULT,     "Result" },\r
9257     { GLT_WHITE_ELO,  "White Rating" },\r
9258     { GLT_BLACK_ELO,  "Black Rating" },\r
9259     { GLT_TIME_CONTROL,"Time Control" },\r
9260     { GLT_VARIANT,    "Variant" },\r
9261     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9262     { 0, 0 }\r
9263 };\r
9264 \r
9265 const char * GLT_FindItem( char id )\r
9266 {\r
9267     const char * result = 0;\r
9268 \r
9269     GLT_Item * list = GLT_ItemInfo;\r
9270 \r
9271     while( list->id != 0 ) {\r
9272         if( list->id == id ) {\r
9273             result = list->name;\r
9274             break;\r
9275         }\r
9276 \r
9277         list++;\r
9278     }\r
9279 \r
9280     return result;\r
9281 }\r
9282 \r
9283 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9284 {\r
9285     const char * name = GLT_FindItem( id );\r
9286 \r
9287     if( name != 0 ) {\r
9288         if( index >= 0 ) {\r
9289             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9290         }\r
9291         else {\r
9292             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9293         }\r
9294     }\r
9295 }\r
9296 \r
9297 void GLT_TagsToList( HWND hDlg, char * tags )\r
9298 {\r
9299     char * pc = tags;\r
9300 \r
9301     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9302 \r
9303     while( *pc ) {\r
9304         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9305         pc++;\r
9306     }\r
9307 \r
9308     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9309 \r
9310     pc = GLT_ALL_TAGS;\r
9311 \r
9312     while( *pc ) {\r
9313         if( strchr( tags, *pc ) == 0 ) {\r
9314             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9315         }\r
9316         pc++;\r
9317     }\r
9318 \r
9319     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9320 }\r
9321 \r
9322 char GLT_ListItemToTag( HWND hDlg, int index )\r
9323 {\r
9324     char result = '\0';\r
9325     char name[128];\r
9326 \r
9327     GLT_Item * list = GLT_ItemInfo;\r
9328 \r
9329     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9330         while( list->id != 0 ) {\r
9331             if( strcmp( list->name, name ) == 0 ) {\r
9332                 result = list->id;\r
9333                 break;\r
9334             }\r
9335 \r
9336             list++;\r
9337         }\r
9338     }\r
9339 \r
9340     return result;\r
9341 }\r
9342 \r
9343 void GLT_MoveSelection( HWND hDlg, int delta )\r
9344 {\r
9345     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9346     int idx2 = idx1 + delta;\r
9347     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9348 \r
9349     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9350         char buf[128];\r
9351 \r
9352         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9353         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9354         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9355         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9356     }\r
9357 }\r
9358 \r
9359 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9360 {\r
9361     static char glt[64];\r
9362     static char * lpUserGLT;\r
9363 \r
9364     switch( message )\r
9365     {\r
9366     case WM_INITDIALOG:\r
9367         lpUserGLT = (char *) lParam;\r
9368         \r
9369         strcpy( glt, lpUserGLT );\r
9370 \r
9371         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9372 \r
9373         /* Initialize list */\r
9374         GLT_TagsToList( hDlg, glt );\r
9375 \r
9376         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9377 \r
9378         break;\r
9379 \r
9380     case WM_COMMAND:\r
9381         switch( LOWORD(wParam) ) {\r
9382         case IDOK:\r
9383             {\r
9384                 char * pc = lpUserGLT;\r
9385                 int idx = 0;\r
9386 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9387                 char id;\r
9388 \r
9389                 do {\r
9390                     id = GLT_ListItemToTag( hDlg, idx );\r
9391 \r
9392                     *pc++ = id;\r
9393                     idx++;\r
9394                 } while( id != '\0' );\r
9395             }\r
9396             EndDialog( hDlg, 0 );\r
9397             return TRUE;\r
9398         case IDCANCEL:\r
9399             EndDialog( hDlg, 1 );\r
9400             return TRUE;\r
9401 \r
9402         case IDC_GLT_Default:\r
9403             strcpy( glt, GLT_DEFAULT_TAGS );\r
9404             GLT_TagsToList( hDlg, glt );\r
9405             return TRUE;\r
9406 \r
9407         case IDC_GLT_Restore:\r
9408             strcpy( glt, lpUserGLT );\r
9409             GLT_TagsToList( hDlg, glt );\r
9410             return TRUE;\r
9411 \r
9412         case IDC_GLT_Up:\r
9413             GLT_MoveSelection( hDlg, -1 );\r
9414             return TRUE;\r
9415 \r
9416         case IDC_GLT_Down:\r
9417             GLT_MoveSelection( hDlg, +1 );\r
9418             return TRUE;\r
9419         }\r
9420 \r
9421         break;\r
9422     }\r
9423 \r
9424     return FALSE;\r
9425 }\r
9426 \r
9427 int GameListOptions()\r
9428 {\r
9429     char glt[64];\r
9430     int result;\r
9431     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9432 \r
9433     strcpy( glt, appData.gameListTags );\r
9434 \r
9435     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9436 \r
9437     if( result == 0 ) {\r
9438         /* [AS] Memory leak here! */\r
9439         appData.gameListTags = strdup( glt ); \r
9440     }\r
9441 \r
9442     return result;\r
9443 }\r
9444 \r
9445 \r
9446 VOID\r
9447 DisplayIcsInteractionTitle(char *str)\r
9448 {\r
9449   char consoleTitle[MSG_SIZ];\r
9450 \r
9451   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9452   SetWindowText(hwndConsole, consoleTitle);\r
9453 }\r
9454 \r
9455 void\r
9456 DrawPosition(int fullRedraw, Board board)\r
9457 {\r
9458   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9459 }\r
9460 \r
9461 \r
9462 VOID\r
9463 ResetFrontEnd()\r
9464 {\r
9465   fromX = fromY = -1;\r
9466   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9467     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9468     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9469     dragInfo.lastpos = dragInfo.pos;\r
9470     dragInfo.start.x = dragInfo.start.y = -1;\r
9471     dragInfo.from = dragInfo.start;\r
9472     ReleaseCapture();\r
9473     DrawPosition(TRUE, NULL);\r
9474   }\r
9475 }\r
9476 \r
9477 \r
9478 VOID\r
9479 CommentPopUp(char *title, char *str)\r
9480 {\r
9481   HWND hwnd = GetActiveWindow();\r
9482   EitherCommentPopUp(0, title, str, FALSE);\r
9483   SetActiveWindow(hwnd);\r
9484 }\r
9485 \r
9486 VOID\r
9487 CommentPopDown(void)\r
9488 {\r
9489   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9490   if (commentDialog) {\r
9491     ShowWindow(commentDialog, SW_HIDE);\r
9492   }\r
9493   commentDialogUp = FALSE;\r
9494 }\r
9495 \r
9496 VOID\r
9497 EditCommentPopUp(int index, char *title, char *str)\r
9498 {\r
9499   EitherCommentPopUp(index, title, str, TRUE);\r
9500 }\r
9501 \r
9502 \r
9503 VOID\r
9504 RingBell()\r
9505 {\r
9506   MyPlaySound(&sounds[(int)SoundMove]);\r
9507 }\r
9508 \r
9509 VOID PlayIcsWinSound()\r
9510 {\r
9511   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9512 }\r
9513 \r
9514 VOID PlayIcsLossSound()\r
9515 {\r
9516   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9517 }\r
9518 \r
9519 VOID PlayIcsDrawSound()\r
9520 {\r
9521   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9522 }\r
9523 \r
9524 VOID PlayIcsUnfinishedSound()\r
9525 {\r
9526   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9527 }\r
9528 \r
9529 VOID\r
9530 PlayAlarmSound()\r
9531 {\r
9532   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9533 }\r
9534 \r
9535 \r
9536 VOID\r
9537 EchoOn()\r
9538 {\r
9539   HWND hInput;\r
9540   consoleEcho = TRUE;\r
9541   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9542   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9543   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9544 }\r
9545 \r
9546 \r
9547 VOID\r
9548 EchoOff()\r
9549 {\r
9550   CHARFORMAT cf;\r
9551   HWND hInput;\r
9552   consoleEcho = FALSE;\r
9553   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9554   /* This works OK: set text and background both to the same color */\r
9555   cf = consoleCF;\r
9556   cf.crTextColor = COLOR_ECHOOFF;\r
9557   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9558   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9559 }\r
9560 \r
9561 /* No Raw()...? */\r
9562 \r
9563 void Colorize(ColorClass cc, int continuation)\r
9564 {\r
9565   currentColorClass = cc;\r
9566   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9567   consoleCF.crTextColor = textAttribs[cc].color;\r
9568   consoleCF.dwEffects = textAttribs[cc].effects;\r
9569   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9570 }\r
9571 \r
9572 char *\r
9573 UserName()\r
9574 {\r
9575   static char buf[MSG_SIZ];\r
9576   DWORD bufsiz = MSG_SIZ;\r
9577 \r
9578   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9579         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9580   }\r
9581   if (!GetUserName(buf, &bufsiz)) {\r
9582     /*DisplayError("Error getting user name", GetLastError());*/\r
9583     strcpy(buf, "User");\r
9584   }\r
9585   return buf;\r
9586 }\r
9587 \r
9588 char *\r
9589 HostName()\r
9590 {\r
9591   static char buf[MSG_SIZ];\r
9592   DWORD bufsiz = MSG_SIZ;\r
9593 \r
9594   if (!GetComputerName(buf, &bufsiz)) {\r
9595     /*DisplayError("Error getting host name", GetLastError());*/\r
9596     strcpy(buf, "Unknown");\r
9597   }\r
9598   return buf;\r
9599 }\r
9600 \r
9601 \r
9602 int\r
9603 ClockTimerRunning()\r
9604 {\r
9605   return clockTimerEvent != 0;\r
9606 }\r
9607 \r
9608 int\r
9609 StopClockTimer()\r
9610 {\r
9611   if (clockTimerEvent == 0) return FALSE;\r
9612   KillTimer(hwndMain, clockTimerEvent);\r
9613   clockTimerEvent = 0;\r
9614   return TRUE;\r
9615 }\r
9616 \r
9617 void\r
9618 StartClockTimer(long millisec)\r
9619 {\r
9620   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9621                              (UINT) millisec, NULL);\r
9622 }\r
9623 \r
9624 void\r
9625 DisplayWhiteClock(long timeRemaining, int highlight)\r
9626 {\r
9627   HDC hdc;\r
9628   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9629 \r
9630   if(appData.noGUI) return;\r
9631   hdc = GetDC(hwndMain);\r
9632   if (!IsIconic(hwndMain)) {\r
9633     DisplayAClock(hdc, timeRemaining, highlight, \r
9634                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9635   }\r
9636   if (highlight && iconCurrent == iconBlack) {\r
9637     iconCurrent = iconWhite;\r
9638     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9639     if (IsIconic(hwndMain)) {\r
9640       DrawIcon(hdc, 2, 2, iconCurrent);\r
9641     }\r
9642   }\r
9643   (void) ReleaseDC(hwndMain, hdc);\r
9644   if (hwndConsole)\r
9645     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9646 }\r
9647 \r
9648 void\r
9649 DisplayBlackClock(long timeRemaining, int highlight)\r
9650 {\r
9651   HDC hdc;\r
9652   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9653 \r
9654   if(appData.noGUI) return;\r
9655   hdc = GetDC(hwndMain);\r
9656   if (!IsIconic(hwndMain)) {\r
9657     DisplayAClock(hdc, timeRemaining, highlight, \r
9658                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9659   }\r
9660   if (highlight && iconCurrent == iconWhite) {\r
9661     iconCurrent = iconBlack;\r
9662     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9663     if (IsIconic(hwndMain)) {\r
9664       DrawIcon(hdc, 2, 2, iconCurrent);\r
9665     }\r
9666   }\r
9667   (void) ReleaseDC(hwndMain, hdc);\r
9668   if (hwndConsole)\r
9669     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9670 }\r
9671 \r
9672 \r
9673 int\r
9674 LoadGameTimerRunning()\r
9675 {\r
9676   return loadGameTimerEvent != 0;\r
9677 }\r
9678 \r
9679 int\r
9680 StopLoadGameTimer()\r
9681 {\r
9682   if (loadGameTimerEvent == 0) return FALSE;\r
9683   KillTimer(hwndMain, loadGameTimerEvent);\r
9684   loadGameTimerEvent = 0;\r
9685   return TRUE;\r
9686 }\r
9687 \r
9688 void\r
9689 StartLoadGameTimer(long millisec)\r
9690 {\r
9691   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9692                                 (UINT) millisec, NULL);\r
9693 }\r
9694 \r
9695 void\r
9696 AutoSaveGame()\r
9697 {\r
9698   char *defName;\r
9699   FILE *f;\r
9700   char fileTitle[MSG_SIZ];\r
9701 \r
9702   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9703   f = OpenFileDialog(hwndMain, "a", defName,\r
9704                      appData.oldSaveStyle ? "gam" : "pgn",\r
9705                      GAME_FILT, \r
9706                      "Save Game to File", NULL, fileTitle, NULL);\r
9707   if (f != NULL) {\r
9708     SaveGame(f, 0, "");\r
9709     fclose(f);\r
9710   }\r
9711 }\r
9712 \r
9713 \r
9714 void\r
9715 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9716 {\r
9717   if (delayedTimerEvent != 0) {\r
9718     if (appData.debugMode) {\r
9719       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9720     }\r
9721     KillTimer(hwndMain, delayedTimerEvent);\r
9722     delayedTimerEvent = 0;\r
9723     delayedTimerCallback();\r
9724   }\r
9725   delayedTimerCallback = cb;\r
9726   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9727                                 (UINT) millisec, NULL);\r
9728 }\r
9729 \r
9730 DelayedEventCallback\r
9731 GetDelayedEvent()\r
9732 {\r
9733   if (delayedTimerEvent) {\r
9734     return delayedTimerCallback;\r
9735   } else {\r
9736     return NULL;\r
9737   }\r
9738 }\r
9739 \r
9740 void\r
9741 CancelDelayedEvent()\r
9742 {\r
9743   if (delayedTimerEvent) {\r
9744     KillTimer(hwndMain, delayedTimerEvent);\r
9745     delayedTimerEvent = 0;\r
9746   }\r
9747 }\r
9748 \r
9749 DWORD GetWin32Priority(int nice)\r
9750 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9751 /*\r
9752 REALTIME_PRIORITY_CLASS     0x00000100\r
9753 HIGH_PRIORITY_CLASS         0x00000080\r
9754 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9755 NORMAL_PRIORITY_CLASS       0x00000020\r
9756 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9757 IDLE_PRIORITY_CLASS         0x00000040\r
9758 */\r
9759         if (nice < -15) return 0x00000080;\r
9760         if (nice < 0)   return 0x00008000;\r
9761         if (nice == 0)  return 0x00000020;\r
9762         if (nice < 15)  return 0x00004000;\r
9763         return 0x00000040;\r
9764 }\r
9765 \r
9766 /* Start a child process running the given program.\r
9767    The process's standard output can be read from "from", and its\r
9768    standard input can be written to "to".\r
9769    Exit with fatal error if anything goes wrong.\r
9770    Returns an opaque pointer that can be used to destroy the process\r
9771    later.\r
9772 */\r
9773 int\r
9774 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9775 {\r
9776 #define BUFSIZE 4096\r
9777 \r
9778   HANDLE hChildStdinRd, hChildStdinWr,\r
9779     hChildStdoutRd, hChildStdoutWr;\r
9780   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9781   SECURITY_ATTRIBUTES saAttr;\r
9782   BOOL fSuccess;\r
9783   PROCESS_INFORMATION piProcInfo;\r
9784   STARTUPINFO siStartInfo;\r
9785   ChildProc *cp;\r
9786   char buf[MSG_SIZ];\r
9787   DWORD err;\r
9788 \r
9789   if (appData.debugMode) {\r
9790     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9791   }\r
9792 \r
9793   *pr = NoProc;\r
9794 \r
9795   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9796   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9797   saAttr.bInheritHandle = TRUE;\r
9798   saAttr.lpSecurityDescriptor = NULL;\r
9799 \r
9800   /*\r
9801    * The steps for redirecting child's STDOUT:\r
9802    *     1. Create anonymous pipe to be STDOUT for child.\r
9803    *     2. Create a noninheritable duplicate of read handle,\r
9804    *         and close the inheritable read handle.\r
9805    */\r
9806 \r
9807   /* Create a pipe for the child's STDOUT. */\r
9808   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9809     return GetLastError();\r
9810   }\r
9811 \r
9812   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9813   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9814                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9815                              FALSE,     /* not inherited */\r
9816                              DUPLICATE_SAME_ACCESS);\r
9817   if (! fSuccess) {\r
9818     return GetLastError();\r
9819   }\r
9820   CloseHandle(hChildStdoutRd);\r
9821 \r
9822   /*\r
9823    * The steps for redirecting child's STDIN:\r
9824    *     1. Create anonymous pipe to be STDIN for child.\r
9825    *     2. Create a noninheritable duplicate of write handle,\r
9826    *         and close the inheritable write handle.\r
9827    */\r
9828 \r
9829   /* Create a pipe for the child's STDIN. */\r
9830   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9831     return GetLastError();\r
9832   }\r
9833 \r
9834   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9835   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9836                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9837                              FALSE,     /* not inherited */\r
9838                              DUPLICATE_SAME_ACCESS);\r
9839   if (! fSuccess) {\r
9840     return GetLastError();\r
9841   }\r
9842   CloseHandle(hChildStdinWr);\r
9843 \r
9844   /* Arrange to (1) look in dir for the child .exe file, and\r
9845    * (2) have dir be the child's working directory.  Interpret\r
9846    * dir relative to the directory WinBoard loaded from. */\r
9847   GetCurrentDirectory(MSG_SIZ, buf);\r
9848   SetCurrentDirectory(installDir);\r
9849   SetCurrentDirectory(dir);\r
9850 \r
9851   /* Now create the child process. */\r
9852 \r
9853   siStartInfo.cb = sizeof(STARTUPINFO);\r
9854   siStartInfo.lpReserved = NULL;\r
9855   siStartInfo.lpDesktop = NULL;\r
9856   siStartInfo.lpTitle = NULL;\r
9857   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9858   siStartInfo.cbReserved2 = 0;\r
9859   siStartInfo.lpReserved2 = NULL;\r
9860   siStartInfo.hStdInput = hChildStdinRd;\r
9861   siStartInfo.hStdOutput = hChildStdoutWr;\r
9862   siStartInfo.hStdError = hChildStdoutWr;\r
9863 \r
9864   fSuccess = CreateProcess(NULL,\r
9865                            cmdLine,        /* command line */\r
9866                            NULL,           /* process security attributes */\r
9867                            NULL,           /* primary thread security attrs */\r
9868                            TRUE,           /* handles are inherited */\r
9869                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9870                            NULL,           /* use parent's environment */\r
9871                            NULL,\r
9872                            &siStartInfo, /* STARTUPINFO pointer */\r
9873                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9874 \r
9875   err = GetLastError();\r
9876   SetCurrentDirectory(buf); /* return to prev directory */\r
9877   if (! fSuccess) {\r
9878     return err;\r
9879   }\r
9880 \r
9881   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9882     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9883     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9884   }\r
9885 \r
9886   /* Close the handles we don't need in the parent */\r
9887   CloseHandle(piProcInfo.hThread);\r
9888   CloseHandle(hChildStdinRd);\r
9889   CloseHandle(hChildStdoutWr);\r
9890 \r
9891   /* Prepare return value */\r
9892   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9893   cp->kind = CPReal;\r
9894   cp->hProcess = piProcInfo.hProcess;\r
9895   cp->pid = piProcInfo.dwProcessId;\r
9896   cp->hFrom = hChildStdoutRdDup;\r
9897   cp->hTo = hChildStdinWrDup;\r
9898 \r
9899   *pr = (void *) cp;\r
9900 \r
9901   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9902      2000 where engines sometimes don't see the initial command(s)\r
9903      from WinBoard and hang.  I don't understand how that can happen,\r
9904      but the Sleep is harmless, so I've put it in.  Others have also\r
9905      reported what may be the same problem, so hopefully this will fix\r
9906      it for them too.  */\r
9907   Sleep(500);\r
9908 \r
9909   return NO_ERROR;\r
9910 }\r
9911 \r
9912 \r
9913 void\r
9914 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9915 {\r
9916   ChildProc *cp; int result;\r
9917 \r
9918   cp = (ChildProc *) pr;\r
9919   if (cp == NULL) return;\r
9920 \r
9921   switch (cp->kind) {\r
9922   case CPReal:\r
9923     /* TerminateProcess is considered harmful, so... */\r
9924     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9925     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9926     /* The following doesn't work because the chess program\r
9927        doesn't "have the same console" as WinBoard.  Maybe\r
9928        we could arrange for this even though neither WinBoard\r
9929        nor the chess program uses a console for stdio? */\r
9930     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9931 \r
9932     /* [AS] Special termination modes for misbehaving programs... */\r
9933     if( signal == 9 ) { \r
9934         result = TerminateProcess( cp->hProcess, 0 );\r
9935 \r
9936         if ( appData.debugMode) {\r
9937             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9938         }\r
9939     }\r
9940     else if( signal == 10 ) {\r
9941         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9942 \r
9943         if( dw != WAIT_OBJECT_0 ) {\r
9944             result = TerminateProcess( cp->hProcess, 0 );\r
9945 \r
9946             if ( appData.debugMode) {\r
9947                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9948             }\r
9949 \r
9950         }\r
9951     }\r
9952 \r
9953     CloseHandle(cp->hProcess);\r
9954     break;\r
9955 \r
9956   case CPComm:\r
9957     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9958     break;\r
9959 \r
9960   case CPSock:\r
9961     closesocket(cp->sock);\r
9962     WSACleanup();\r
9963     break;\r
9964 \r
9965   case CPRcmd:\r
9966     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9967     closesocket(cp->sock);\r
9968     closesocket(cp->sock2);\r
9969     WSACleanup();\r
9970     break;\r
9971   }\r
9972   free(cp);\r
9973 }\r
9974 \r
9975 void\r
9976 InterruptChildProcess(ProcRef pr)\r
9977 {\r
9978   ChildProc *cp;\r
9979 \r
9980   cp = (ChildProc *) pr;\r
9981   if (cp == NULL) return;\r
9982   switch (cp->kind) {\r
9983   case CPReal:\r
9984     /* The following doesn't work because the chess program\r
9985        doesn't "have the same console" as WinBoard.  Maybe\r
9986        we could arrange for this even though neither WinBoard\r
9987        nor the chess program uses a console for stdio */\r
9988     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9989     break;\r
9990 \r
9991   case CPComm:\r
9992   case CPSock:\r
9993     /* Can't interrupt */\r
9994     break;\r
9995 \r
9996   case CPRcmd:\r
9997     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9998     break;\r
9999   }\r
10000 }\r
10001 \r
10002 \r
10003 int\r
10004 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10005 {\r
10006   char cmdLine[MSG_SIZ];\r
10007 \r
10008   if (port[0] == NULLCHAR) {\r
10009     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10010   } else {\r
10011     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10012   }\r
10013   return StartChildProcess(cmdLine, "", pr);\r
10014 }\r
10015 \r
10016 \r
10017 /* Code to open TCP sockets */\r
10018 \r
10019 int\r
10020 OpenTCP(char *host, char *port, ProcRef *pr)\r
10021 {\r
10022   ChildProc *cp;\r
10023   int err;\r
10024   SOCKET s;\r
10025   struct sockaddr_in sa, mysa;\r
10026   struct hostent FAR *hp;\r
10027   unsigned short uport;\r
10028   WORD wVersionRequested;\r
10029   WSADATA wsaData;\r
10030 \r
10031   /* Initialize socket DLL */\r
10032   wVersionRequested = MAKEWORD(1, 1);\r
10033   err = WSAStartup(wVersionRequested, &wsaData);\r
10034   if (err != 0) return err;\r
10035 \r
10036   /* Make socket */\r
10037   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10038     err = WSAGetLastError();\r
10039     WSACleanup();\r
10040     return err;\r
10041   }\r
10042 \r
10043   /* Bind local address using (mostly) don't-care values.\r
10044    */\r
10045   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10046   mysa.sin_family = AF_INET;\r
10047   mysa.sin_addr.s_addr = INADDR_ANY;\r
10048   uport = (unsigned short) 0;\r
10049   mysa.sin_port = htons(uport);\r
10050   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10051       == SOCKET_ERROR) {\r
10052     err = WSAGetLastError();\r
10053     WSACleanup();\r
10054     return err;\r
10055   }\r
10056 \r
10057   /* Resolve remote host name */\r
10058   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10059   if (!(hp = gethostbyname(host))) {\r
10060     unsigned int b0, b1, b2, b3;\r
10061 \r
10062     err = WSAGetLastError();\r
10063 \r
10064     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10065       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10066       hp->h_addrtype = AF_INET;\r
10067       hp->h_length = 4;\r
10068       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10069       hp->h_addr_list[0] = (char *) malloc(4);\r
10070       hp->h_addr_list[0][0] = (char) b0;\r
10071       hp->h_addr_list[0][1] = (char) b1;\r
10072       hp->h_addr_list[0][2] = (char) b2;\r
10073       hp->h_addr_list[0][3] = (char) b3;\r
10074     } else {\r
10075       WSACleanup();\r
10076       return err;\r
10077     }\r
10078   }\r
10079   sa.sin_family = hp->h_addrtype;\r
10080   uport = (unsigned short) atoi(port);\r
10081   sa.sin_port = htons(uport);\r
10082   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10083 \r
10084   /* Make connection */\r
10085   if (connect(s, (struct sockaddr *) &sa,\r
10086               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10087     err = WSAGetLastError();\r
10088     WSACleanup();\r
10089     return err;\r
10090   }\r
10091 \r
10092   /* Prepare return value */\r
10093   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10094   cp->kind = CPSock;\r
10095   cp->sock = s;\r
10096   *pr = (ProcRef *) cp;\r
10097 \r
10098   return NO_ERROR;\r
10099 }\r
10100 \r
10101 int\r
10102 OpenCommPort(char *name, ProcRef *pr)\r
10103 {\r
10104   HANDLE h;\r
10105   COMMTIMEOUTS ct;\r
10106   ChildProc *cp;\r
10107   char fullname[MSG_SIZ];\r
10108 \r
10109   if (*name != '\\')\r
10110     sprintf(fullname, "\\\\.\\%s", name);\r
10111   else\r
10112     strcpy(fullname, name);\r
10113 \r
10114   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10115                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10116   if (h == (HANDLE) -1) {\r
10117     return GetLastError();\r
10118   }\r
10119   hCommPort = h;\r
10120 \r
10121   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10122 \r
10123   /* Accumulate characters until a 100ms pause, then parse */\r
10124   ct.ReadIntervalTimeout = 100;\r
10125   ct.ReadTotalTimeoutMultiplier = 0;\r
10126   ct.ReadTotalTimeoutConstant = 0;\r
10127   ct.WriteTotalTimeoutMultiplier = 0;\r
10128   ct.WriteTotalTimeoutConstant = 0;\r
10129   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10130 \r
10131   /* Prepare return value */\r
10132   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10133   cp->kind = CPComm;\r
10134   cp->hFrom = h;\r
10135   cp->hTo = h;\r
10136   *pr = (ProcRef *) cp;\r
10137 \r
10138   return NO_ERROR;\r
10139 }\r
10140 \r
10141 int\r
10142 OpenLoopback(ProcRef *pr)\r
10143 {\r
10144   DisplayFatalError("Not implemented", 0, 1);\r
10145   return NO_ERROR;\r
10146 }\r
10147 \r
10148 \r
10149 int\r
10150 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10151 {\r
10152   ChildProc *cp;\r
10153   int err;\r
10154   SOCKET s, s2, s3;\r
10155   struct sockaddr_in sa, mysa;\r
10156   struct hostent FAR *hp;\r
10157   unsigned short uport;\r
10158   WORD wVersionRequested;\r
10159   WSADATA wsaData;\r
10160   int fromPort;\r
10161   char stderrPortStr[MSG_SIZ];\r
10162 \r
10163   /* Initialize socket DLL */\r
10164   wVersionRequested = MAKEWORD(1, 1);\r
10165   err = WSAStartup(wVersionRequested, &wsaData);\r
10166   if (err != 0) return err;\r
10167 \r
10168   /* Resolve remote host name */\r
10169   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10170   if (!(hp = gethostbyname(host))) {\r
10171     unsigned int b0, b1, b2, b3;\r
10172 \r
10173     err = WSAGetLastError();\r
10174 \r
10175     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10176       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10177       hp->h_addrtype = AF_INET;\r
10178       hp->h_length = 4;\r
10179       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10180       hp->h_addr_list[0] = (char *) malloc(4);\r
10181       hp->h_addr_list[0][0] = (char) b0;\r
10182       hp->h_addr_list[0][1] = (char) b1;\r
10183       hp->h_addr_list[0][2] = (char) b2;\r
10184       hp->h_addr_list[0][3] = (char) b3;\r
10185     } else {\r
10186       WSACleanup();\r
10187       return err;\r
10188     }\r
10189   }\r
10190   sa.sin_family = hp->h_addrtype;\r
10191   uport = (unsigned short) 514;\r
10192   sa.sin_port = htons(uport);\r
10193   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10194 \r
10195   /* Bind local socket to unused "privileged" port address\r
10196    */\r
10197   s = INVALID_SOCKET;\r
10198   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10199   mysa.sin_family = AF_INET;\r
10200   mysa.sin_addr.s_addr = INADDR_ANY;\r
10201   for (fromPort = 1023;; fromPort--) {\r
10202     if (fromPort < 0) {\r
10203       WSACleanup();\r
10204       return WSAEADDRINUSE;\r
10205     }\r
10206     if (s == INVALID_SOCKET) {\r
10207       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10208         err = WSAGetLastError();\r
10209         WSACleanup();\r
10210         return err;\r
10211       }\r
10212     }\r
10213     uport = (unsigned short) fromPort;\r
10214     mysa.sin_port = htons(uport);\r
10215     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10216         == SOCKET_ERROR) {\r
10217       err = WSAGetLastError();\r
10218       if (err == WSAEADDRINUSE) continue;\r
10219       WSACleanup();\r
10220       return err;\r
10221     }\r
10222     if (connect(s, (struct sockaddr *) &sa,\r
10223       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10224       err = WSAGetLastError();\r
10225       if (err == WSAEADDRINUSE) {\r
10226         closesocket(s);\r
10227         s = -1;\r
10228         continue;\r
10229       }\r
10230       WSACleanup();\r
10231       return err;\r
10232     }\r
10233     break;\r
10234   }\r
10235 \r
10236   /* Bind stderr local socket to unused "privileged" port address\r
10237    */\r
10238   s2 = INVALID_SOCKET;\r
10239   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10240   mysa.sin_family = AF_INET;\r
10241   mysa.sin_addr.s_addr = INADDR_ANY;\r
10242   for (fromPort = 1023;; fromPort--) {\r
10243     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10244     if (fromPort < 0) {\r
10245       (void) closesocket(s);\r
10246       WSACleanup();\r
10247       return WSAEADDRINUSE;\r
10248     }\r
10249     if (s2 == INVALID_SOCKET) {\r
10250       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10251         err = WSAGetLastError();\r
10252         closesocket(s);\r
10253         WSACleanup();\r
10254         return err;\r
10255       }\r
10256     }\r
10257     uport = (unsigned short) fromPort;\r
10258     mysa.sin_port = htons(uport);\r
10259     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10260         == SOCKET_ERROR) {\r
10261       err = WSAGetLastError();\r
10262       if (err == WSAEADDRINUSE) continue;\r
10263       (void) closesocket(s);\r
10264       WSACleanup();\r
10265       return err;\r
10266     }\r
10267     if (listen(s2, 1) == SOCKET_ERROR) {\r
10268       err = WSAGetLastError();\r
10269       if (err == WSAEADDRINUSE) {\r
10270         closesocket(s2);\r
10271         s2 = INVALID_SOCKET;\r
10272         continue;\r
10273       }\r
10274       (void) closesocket(s);\r
10275       (void) closesocket(s2);\r
10276       WSACleanup();\r
10277       return err;\r
10278     }\r
10279     break;\r
10280   }\r
10281   prevStderrPort = fromPort; // remember port used\r
10282   sprintf(stderrPortStr, "%d", fromPort);\r
10283 \r
10284   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10285     err = WSAGetLastError();\r
10286     (void) closesocket(s);\r
10287     (void) closesocket(s2);\r
10288     WSACleanup();\r
10289     return err;\r
10290   }\r
10291 \r
10292   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10293     err = WSAGetLastError();\r
10294     (void) closesocket(s);\r
10295     (void) closesocket(s2);\r
10296     WSACleanup();\r
10297     return err;\r
10298   }\r
10299   if (*user == NULLCHAR) user = UserName();\r
10300   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10301     err = WSAGetLastError();\r
10302     (void) closesocket(s);\r
10303     (void) closesocket(s2);\r
10304     WSACleanup();\r
10305     return err;\r
10306   }\r
10307   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10308     err = WSAGetLastError();\r
10309     (void) closesocket(s);\r
10310     (void) closesocket(s2);\r
10311     WSACleanup();\r
10312     return err;\r
10313   }\r
10314 \r
10315   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10316     err = WSAGetLastError();\r
10317     (void) closesocket(s);\r
10318     (void) closesocket(s2);\r
10319     WSACleanup();\r
10320     return err;\r
10321   }\r
10322   (void) closesocket(s2);  /* Stop listening */\r
10323 \r
10324   /* Prepare return value */\r
10325   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10326   cp->kind = CPRcmd;\r
10327   cp->sock = s;\r
10328   cp->sock2 = s3;\r
10329   *pr = (ProcRef *) cp;\r
10330 \r
10331   return NO_ERROR;\r
10332 }\r
10333 \r
10334 \r
10335 InputSourceRef\r
10336 AddInputSource(ProcRef pr, int lineByLine,\r
10337                InputCallback func, VOIDSTAR closure)\r
10338 {\r
10339   InputSource *is, *is2 = NULL;\r
10340   ChildProc *cp = (ChildProc *) pr;\r
10341 \r
10342   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10343   is->lineByLine = lineByLine;\r
10344   is->func = func;\r
10345   is->closure = closure;\r
10346   is->second = NULL;\r
10347   is->next = is->buf;\r
10348   if (pr == NoProc) {\r
10349     is->kind = CPReal;\r
10350     consoleInputSource = is;\r
10351   } else {\r
10352     is->kind = cp->kind;\r
10353     /* \r
10354         [AS] Try to avoid a race condition if the thread is given control too early:\r
10355         we create all threads suspended so that the is->hThread variable can be\r
10356         safely assigned, then let the threads start with ResumeThread.\r
10357     */\r
10358     switch (cp->kind) {\r
10359     case CPReal:\r
10360       is->hFile = cp->hFrom;\r
10361       cp->hFrom = NULL; /* now owned by InputThread */\r
10362       is->hThread =\r
10363         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10364                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10365       break;\r
10366 \r
10367     case CPComm:\r
10368       is->hFile = cp->hFrom;\r
10369       cp->hFrom = NULL; /* now owned by InputThread */\r
10370       is->hThread =\r
10371         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10372                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10373       break;\r
10374 \r
10375     case CPSock:\r
10376       is->sock = cp->sock;\r
10377       is->hThread =\r
10378         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10379                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10380       break;\r
10381 \r
10382     case CPRcmd:\r
10383       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10384       *is2 = *is;\r
10385       is->sock = cp->sock;\r
10386       is->second = is2;\r
10387       is2->sock = cp->sock2;\r
10388       is2->second = is2;\r
10389       is->hThread =\r
10390         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10391                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10392       is2->hThread =\r
10393         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10394                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10395       break;\r
10396     }\r
10397 \r
10398     if( is->hThread != NULL ) {\r
10399         ResumeThread( is->hThread );\r
10400     }\r
10401 \r
10402     if( is2 != NULL && is2->hThread != NULL ) {\r
10403         ResumeThread( is2->hThread );\r
10404     }\r
10405   }\r
10406 \r
10407   return (InputSourceRef) is;\r
10408 }\r
10409 \r
10410 void\r
10411 RemoveInputSource(InputSourceRef isr)\r
10412 {\r
10413   InputSource *is;\r
10414 \r
10415   is = (InputSource *) isr;\r
10416   is->hThread = NULL;  /* tell thread to stop */\r
10417   CloseHandle(is->hThread);\r
10418   if (is->second != NULL) {\r
10419     is->second->hThread = NULL;\r
10420     CloseHandle(is->second->hThread);\r
10421   }\r
10422 }\r
10423 \r
10424 \r
10425 int\r
10426 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10427 {\r
10428   DWORD dOutCount;\r
10429   int outCount = SOCKET_ERROR;\r
10430   ChildProc *cp = (ChildProc *) pr;\r
10431   static OVERLAPPED ovl;\r
10432 \r
10433   if (pr == NoProc) {\r
10434     ConsoleOutput(message, count, FALSE);\r
10435     return count;\r
10436   } \r
10437 \r
10438   if (ovl.hEvent == NULL) {\r
10439     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10440   }\r
10441   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10442 \r
10443   switch (cp->kind) {\r
10444   case CPSock:\r
10445   case CPRcmd:\r
10446     outCount = send(cp->sock, message, count, 0);\r
10447     if (outCount == SOCKET_ERROR) {\r
10448       *outError = WSAGetLastError();\r
10449     } else {\r
10450       *outError = NO_ERROR;\r
10451     }\r
10452     break;\r
10453 \r
10454   case CPReal:\r
10455     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10456                   &dOutCount, NULL)) {\r
10457       *outError = NO_ERROR;\r
10458       outCount = (int) dOutCount;\r
10459     } else {\r
10460       *outError = GetLastError();\r
10461     }\r
10462     break;\r
10463 \r
10464   case CPComm:\r
10465     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10466                             &dOutCount, &ovl);\r
10467     if (*outError == NO_ERROR) {\r
10468       outCount = (int) dOutCount;\r
10469     }\r
10470     break;\r
10471   }\r
10472   return outCount;\r
10473 }\r
10474 \r
10475 int\r
10476 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10477                        long msdelay)\r
10478 {\r
10479   /* Ignore delay, not implemented for WinBoard */\r
10480   return OutputToProcess(pr, message, count, outError);\r
10481 }\r
10482 \r
10483 \r
10484 void\r
10485 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10486                         char *buf, int count, int error)\r
10487 {\r
10488   DisplayFatalError("Not implemented", 0, 1);\r
10489 }\r
10490 \r
10491 /* see wgamelist.c for Game List functions */\r
10492 /* see wedittags.c for Edit Tags functions */\r
10493 \r
10494 \r
10495 VOID\r
10496 ICSInitScript()\r
10497 {\r
10498   FILE *f;\r
10499   char buf[MSG_SIZ];\r
10500   char *dummy;\r
10501 \r
10502   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10503     f = fopen(buf, "r");\r
10504     if (f != NULL) {\r
10505       ProcessICSInitScript(f);\r
10506       fclose(f);\r
10507     }\r
10508   }\r
10509 }\r
10510 \r
10511 \r
10512 VOID\r
10513 StartAnalysisClock()\r
10514 {\r
10515   if (analysisTimerEvent) return;\r
10516   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10517                                         (UINT) 2000, NULL);\r
10518 }\r
10519 \r
10520 LRESULT CALLBACK\r
10521 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10522 {\r
10523   static HANDLE hwndText;\r
10524   RECT rect;\r
10525   static int sizeX, sizeY;\r
10526   int newSizeX, newSizeY, flags;\r
10527   MINMAXINFO *mmi;\r
10528 \r
10529   switch (message) {\r
10530   case WM_INITDIALOG: /* message: initialize dialog box */\r
10531     /* Initialize the dialog items */\r
10532     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10533     SetWindowText(hDlg, analysisTitle);\r
10534     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10535     /* Size and position the dialog */\r
10536     if (!analysisDialog) {\r
10537       analysisDialog = hDlg;\r
10538       flags = SWP_NOZORDER;\r
10539       GetClientRect(hDlg, &rect);\r
10540       sizeX = rect.right;\r
10541       sizeY = rect.bottom;\r
10542       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10543           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10544         WINDOWPLACEMENT wp;\r
10545         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10546         wp.length = sizeof(WINDOWPLACEMENT);\r
10547         wp.flags = 0;\r
10548         wp.showCmd = SW_SHOW;\r
10549         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10550         wp.rcNormalPosition.left = analysisX;\r
10551         wp.rcNormalPosition.right = analysisX + analysisW;\r
10552         wp.rcNormalPosition.top = analysisY;\r
10553         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10554         SetWindowPlacement(hDlg, &wp);\r
10555 \r
10556         GetClientRect(hDlg, &rect);\r
10557         newSizeX = rect.right;\r
10558         newSizeY = rect.bottom;\r
10559         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10560                               newSizeX, newSizeY);\r
10561         sizeX = newSizeX;\r
10562         sizeY = newSizeY;\r
10563       }\r
10564     }\r
10565     return FALSE;\r
10566 \r
10567   case WM_COMMAND: /* message: received a command */\r
10568     switch (LOWORD(wParam)) {\r
10569     case IDCANCEL:\r
10570       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10571           ExitAnalyzeMode();\r
10572           ModeHighlight();\r
10573           return TRUE;\r
10574       }\r
10575       EditGameEvent();\r
10576       return TRUE;\r
10577     default:\r
10578       break;\r
10579     }\r
10580     break;\r
10581 \r
10582   case WM_SIZE:\r
10583     newSizeX = LOWORD(lParam);\r
10584     newSizeY = HIWORD(lParam);\r
10585     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10586     sizeX = newSizeX;\r
10587     sizeY = newSizeY;\r
10588     break;\r
10589 \r
10590   case WM_GETMINMAXINFO:\r
10591     /* Prevent resizing window too small */\r
10592     mmi = (MINMAXINFO *) lParam;\r
10593     mmi->ptMinTrackSize.x = 100;\r
10594     mmi->ptMinTrackSize.y = 100;\r
10595     break;\r
10596   }\r
10597   return FALSE;\r
10598 }\r
10599 \r
10600 VOID\r
10601 AnalysisPopUp(char* title, char* str)\r
10602 {\r
10603   FARPROC lpProc;\r
10604   char *p, *q;\r
10605 \r
10606   /* [AS] */\r
10607   EngineOutputPopUp();\r
10608   return;\r
10609 \r
10610   if (str == NULL) str = "";\r
10611   p = (char *) malloc(2 * strlen(str) + 2);\r
10612   q = p;\r
10613   while (*str) {\r
10614     if (*str == '\n') *q++ = '\r';\r
10615     *q++ = *str++;\r
10616   }\r
10617   *q = NULLCHAR;\r
10618   if (analysisText != NULL) free(analysisText);\r
10619   analysisText = p;\r
10620 \r
10621   if (analysisDialog) {\r
10622     SetWindowText(analysisDialog, title);\r
10623     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10624     ShowWindow(analysisDialog, SW_SHOW);\r
10625   } else {\r
10626     analysisTitle = title;\r
10627     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10628     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10629                  hwndMain, (DLGPROC)lpProc);\r
10630     FreeProcInstance(lpProc);\r
10631   }\r
10632   analysisDialogUp = TRUE;  \r
10633 }\r
10634 \r
10635 VOID\r
10636 AnalysisPopDown()\r
10637 {\r
10638   if (analysisDialog) {\r
10639     ShowWindow(analysisDialog, SW_HIDE);\r
10640   }\r
10641   analysisDialogUp = FALSE;  \r
10642 }\r
10643 \r
10644 \r
10645 VOID\r
10646 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10647 {\r
10648   highlightInfo.sq[0].x = fromX;\r
10649   highlightInfo.sq[0].y = fromY;\r
10650   highlightInfo.sq[1].x = toX;\r
10651   highlightInfo.sq[1].y = toY;\r
10652 }\r
10653 \r
10654 VOID\r
10655 ClearHighlights()\r
10656 {\r
10657   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10658     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10659 }\r
10660 \r
10661 VOID\r
10662 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10663 {\r
10664   premoveHighlightInfo.sq[0].x = fromX;\r
10665   premoveHighlightInfo.sq[0].y = fromY;\r
10666   premoveHighlightInfo.sq[1].x = toX;\r
10667   premoveHighlightInfo.sq[1].y = toY;\r
10668 }\r
10669 \r
10670 VOID\r
10671 ClearPremoveHighlights()\r
10672 {\r
10673   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10674     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10675 }\r
10676 \r
10677 VOID\r
10678 ShutDownFrontEnd()\r
10679 {\r
10680   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10681   DeleteClipboardTempFiles();\r
10682 }\r
10683 \r
10684 void\r
10685 BoardToTop()\r
10686 {\r
10687     if (IsIconic(hwndMain))\r
10688       ShowWindow(hwndMain, SW_RESTORE);\r
10689 \r
10690     SetActiveWindow(hwndMain);\r
10691 }\r
10692 \r
10693 /*\r
10694  * Prototypes for animation support routines\r
10695  */\r
10696 static void ScreenSquare(int column, int row, POINT * pt);\r
10697 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10698      POINT frames[], int * nFrames);\r
10699 \r
10700 \r
10701 void\r
10702 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10703 {       // [HGM] atomic: animate blast wave\r
10704         int i;\r
10705 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10706         explodeInfo.fromX = fromX;\r
10707         explodeInfo.fromY = fromY;\r
10708         explodeInfo.toX = toX;\r
10709         explodeInfo.toY = toY;\r
10710         for(i=1; i<nFrames; i++) {\r
10711             explodeInfo.radius = (i*180)/(nFrames-1);\r
10712             DrawPosition(FALSE, NULL);\r
10713             Sleep(appData.animSpeed);\r
10714         }\r
10715         explodeInfo.radius = 0;\r
10716         DrawPosition(TRUE, NULL);\r
10717 }\r
10718 \r
10719 #define kFactor 4\r
10720 \r
10721 void\r
10722 AnimateMove(board, fromX, fromY, toX, toY)\r
10723      Board board;\r
10724      int fromX;\r
10725      int fromY;\r
10726      int toX;\r
10727      int toY;\r
10728 {\r
10729   ChessSquare piece;\r
10730   POINT start, finish, mid;\r
10731   POINT frames[kFactor * 2 + 1];\r
10732   int nFrames, n;\r
10733 \r
10734   if (!appData.animate) return;\r
10735   if (doingSizing) return;\r
10736   if (fromY < 0 || fromX < 0) return;\r
10737   piece = board[fromY][fromX];\r
10738   if (piece >= EmptySquare) return;\r
10739 \r
10740   ScreenSquare(fromX, fromY, &start);\r
10741   ScreenSquare(toX, toY, &finish);\r
10742 \r
10743   /* All pieces except knights move in straight line */\r
10744   if (piece != WhiteKnight && piece != BlackKnight) {\r
10745     mid.x = start.x + (finish.x - start.x) / 2;\r
10746     mid.y = start.y + (finish.y - start.y) / 2;\r
10747   } else {\r
10748     /* Knight: make diagonal movement then straight */\r
10749     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10750        mid.x = start.x + (finish.x - start.x) / 2;\r
10751        mid.y = finish.y;\r
10752      } else {\r
10753        mid.x = finish.x;\r
10754        mid.y = start.y + (finish.y - start.y) / 2;\r
10755      }\r
10756   }\r
10757   \r
10758   /* Don't use as many frames for very short moves */\r
10759   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10760     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10761   else\r
10762     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10763 \r
10764   animInfo.from.x = fromX;\r
10765   animInfo.from.y = fromY;\r
10766   animInfo.to.x = toX;\r
10767   animInfo.to.y = toY;\r
10768   animInfo.lastpos = start;\r
10769   animInfo.piece = piece;\r
10770   for (n = 0; n < nFrames; n++) {\r
10771     animInfo.pos = frames[n];\r
10772     DrawPosition(FALSE, NULL);\r
10773     animInfo.lastpos = animInfo.pos;\r
10774     Sleep(appData.animSpeed);\r
10775   }\r
10776   animInfo.pos = finish;\r
10777   DrawPosition(FALSE, NULL);\r
10778   animInfo.piece = EmptySquare;\r
10779   if(gameInfo.variant == VariantAtomic && \r
10780      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10781         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10782 }\r
10783 \r
10784 /*      Convert board position to corner of screen rect and color       */\r
10785 \r
10786 static void\r
10787 ScreenSquare(column, row, pt)\r
10788      int column; int row; POINT * pt;\r
10789 {\r
10790   if (flipView) {\r
10791     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10792     pt->y = lineGap + row * (squareSize + lineGap);\r
10793   } else {\r
10794     pt->x = lineGap + column * (squareSize + lineGap);\r
10795     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10796   }\r
10797 }\r
10798 \r
10799 /*      Generate a series of frame coords from start->mid->finish.\r
10800         The movement rate doubles until the half way point is\r
10801         reached, then halves back down to the final destination,\r
10802         which gives a nice slow in/out effect. The algorithmn\r
10803         may seem to generate too many intermediates for short\r
10804         moves, but remember that the purpose is to attract the\r
10805         viewers attention to the piece about to be moved and\r
10806         then to where it ends up. Too few frames would be less\r
10807         noticeable.                                             */\r
10808 \r
10809 static void\r
10810 Tween(start, mid, finish, factor, frames, nFrames)\r
10811      POINT * start; POINT * mid;\r
10812      POINT * finish; int factor;\r
10813      POINT frames[]; int * nFrames;\r
10814 {\r
10815   int n, fraction = 1, count = 0;\r
10816 \r
10817   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10818   for (n = 0; n < factor; n++)\r
10819     fraction *= 2;\r
10820   for (n = 0; n < factor; n++) {\r
10821     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10822     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10823     count ++;\r
10824     fraction = fraction / 2;\r
10825   }\r
10826   \r
10827   /* Midpoint */\r
10828   frames[count] = *mid;\r
10829   count ++;\r
10830   \r
10831   /* Slow out, stepping 1/2, then 1/4, ... */\r
10832   fraction = 2;\r
10833   for (n = 0; n < factor; n++) {\r
10834     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10835     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10836     count ++;\r
10837     fraction = fraction * 2;\r
10838   }\r
10839   *nFrames = count;\r
10840 }\r
10841 \r
10842 void\r
10843 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10844 {\r
10845 #if 0\r
10846     char buf[256];\r
10847 \r
10848     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10849         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10850 \r
10851     OutputDebugString( buf );\r
10852 #endif\r
10853 \r
10854     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10855 \r
10856     EvalGraphSet( first, last, current, pvInfoList );\r
10857 }\r
10858 \r
10859 void SetProgramStats( FrontEndProgramStats * stats )\r
10860 {\r
10861 #if 0\r
10862     char buf[1024];\r
10863 \r
10864     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10865         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10866 \r
10867     OutputDebugString( buf );\r
10868 #endif\r
10869 \r
10870     EngineOutputUpdate( stats );\r
10871 }\r