new zippy option zippyShortGame
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
107 \r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int fromX, fromY, toX, toY, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo;\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "ICS Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
156 static int squareSize, lineGap, minorSize;\r
157 static int winWidth, winHeight;\r
158 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
159 static int logoHeight = 0;\r
160 static char messageText[MESSAGE_TEXT_MAX];\r
161 static int clockTimerEvent = 0;\r
162 static int loadGameTimerEvent = 0;\r
163 static int analysisTimerEvent = 0;\r
164 static DelayedEventCallback delayedTimerCallback;\r
165 static int delayedTimerEvent = 0;\r
166 static int buttonCount = 2;\r
167 char *icsTextMenuString;\r
168 char *icsNames;\r
169 char *firstChessProgramNames;\r
170 char *secondChessProgramNames;\r
171 \r
172 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
173 \r
174 #define PALETTESIZE 256\r
175 \r
176 HINSTANCE hInst;          /* current instance */\r
177 HWND hwndMain = NULL;        /* root window*/\r
178 HWND hwndConsole = NULL;\r
179 BOOLEAN alwaysOnTop = FALSE;\r
180 RECT boardRect;\r
181 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
182   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
183 HPALETTE hPal;\r
184 ColorClass currentColorClass;\r
185 \r
186 HWND hCommPort = NULL;    /* currently open comm port */\r
187 static HWND hwndPause;    /* pause button */\r
188 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
189 static HBRUSH lightSquareBrush, darkSquareBrush,\r
190   blackSquareBrush, /* [HGM] for band between board and holdings */\r
191   explodeBrush,     /* [HGM] atomic */\r
192   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
193 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
194 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
195 static HPEN gridPen = NULL;\r
196 static HPEN highlightPen = NULL;\r
197 static HPEN premovePen = NULL;\r
198 static NPLOGPALETTE pLogPal;\r
199 static BOOL paletteChanged = FALSE;\r
200 static HICON iconWhite, iconBlack, iconCurrent;\r
201 static int doingSizing = FALSE;\r
202 static int lastSizing = 0;\r
203 static int prevStderrPort;\r
204 \r
205 /* [AS] Support for background textures */\r
206 #define BACK_TEXTURE_MODE_DISABLED      0\r
207 #define BACK_TEXTURE_MODE_PLAIN         1\r
208 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
209 \r
210 static HBITMAP liteBackTexture = NULL;\r
211 static HBITMAP darkBackTexture = NULL;\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int backTextureSquareSize = 0;\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
216 \r
217 #if __GNUC__ && !defined(_winmajor)\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
219 #else\r
220 #define oldDialog (_winmajor < 4)\r
221 #endif\r
222 \r
223 char *defaultTextAttribs[] = \r
224 {\r
225   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
226   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
227   COLOR_NONE\r
228 };\r
229 \r
230 typedef struct {\r
231   char *name;\r
232   int squareSize;\r
233   int lineGap;\r
234   int smallLayout;\r
235   int tinyLayout;\r
236   int cliWidth, cliHeight;\r
237 } SizeInfo;\r
238 \r
239 SizeInfo sizeInfo[] = \r
240 {\r
241   { "tiny",     21, 0, 1, 1, 0, 0 },\r
242   { "teeny",    25, 1, 1, 1, 0, 0 },\r
243   { "dinky",    29, 1, 1, 1, 0, 0 },\r
244   { "petite",   33, 1, 1, 1, 0, 0 },\r
245   { "slim",     37, 2, 1, 0, 0, 0 },\r
246   { "small",    40, 2, 1, 0, 0, 0 },\r
247   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
248   { "middling", 49, 2, 0, 0, 0, 0 },\r
249   { "average",  54, 2, 0, 0, 0, 0 },\r
250   { "moderate", 58, 3, 0, 0, 0, 0 },\r
251   { "medium",   64, 3, 0, 0, 0, 0 },\r
252   { "bulky",    72, 3, 0, 0, 0, 0 },\r
253   { "large",    80, 3, 0, 0, 0, 0 },\r
254   { "big",      87, 3, 0, 0, 0, 0 },\r
255   { "huge",     95, 3, 0, 0, 0, 0 },\r
256   { "giant",    108, 3, 0, 0, 0, 0 },\r
257   { "colossal", 116, 4, 0, 0, 0, 0 },\r
258   { "titanic",  129, 4, 0, 0, 0, 0 },\r
259   { NULL, 0, 0, 0, 0, 0, 0 }\r
260 };\r
261 \r
262 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
263 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
264 {\r
265   { 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
266   { 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
267   { 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
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283 };\r
284 \r
285 MyFont *font[NUM_SIZES][NUM_FONTS];\r
286 \r
287 typedef struct {\r
288   char *label;\r
289   int id;\r
290   HWND hwnd;\r
291   WNDPROC wndproc;\r
292 } MyButtonDesc;\r
293 \r
294 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
295 #define N_BUTTONS 5\r
296 \r
297 MyButtonDesc buttonDesc[N_BUTTONS] =\r
298 {\r
299   {"<<", IDM_ToStart, NULL, NULL},\r
300   {"<", IDM_Backward, NULL, NULL},\r
301   {"P", IDM_Pause, NULL, NULL},\r
302   {">", IDM_Forward, NULL, NULL},\r
303   {">>", IDM_ToEnd, NULL, NULL},\r
304 };\r
305 \r
306 int tinyLayout = 0, smallLayout = 0;\r
307 #define MENU_BAR_ITEMS 6\r
308 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
309   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
310   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
311 };\r
312 \r
313 \r
314 MySound sounds[(int)NSoundClasses];\r
315 MyTextAttribs textAttribs[(int)NColorClasses];\r
316 \r
317 MyColorizeAttribs colorizeAttribs[] = {\r
318   { (COLORREF)0, 0, "Shout Text" },\r
319   { (COLORREF)0, 0, "SShout/CShout" },\r
320   { (COLORREF)0, 0, "Channel 1 Text" },\r
321   { (COLORREF)0, 0, "Channel Text" },\r
322   { (COLORREF)0, 0, "Kibitz Text" },\r
323   { (COLORREF)0, 0, "Tell Text" },\r
324   { (COLORREF)0, 0, "Challenge Text" },\r
325   { (COLORREF)0, 0, "Request Text" },\r
326   { (COLORREF)0, 0, "Seek Text" },\r
327   { (COLORREF)0, 0, "Normal Text" },\r
328   { (COLORREF)0, 0, "None" }\r
329 };\r
330 \r
331 \r
332 \r
333 static char *commentTitle;\r
334 static char *commentText;\r
335 static int commentIndex;\r
336 static Boolean editComment = FALSE;\r
337 HWND commentDialog = NULL;\r
338 BOOLEAN commentDialogUp = FALSE;\r
339 static int commentX, commentY, commentH, commentW;\r
340 \r
341 static char *analysisTitle;\r
342 static char *analysisText;\r
343 HWND analysisDialog = NULL;\r
344 BOOLEAN analysisDialogUp = FALSE;\r
345 static int analysisX, analysisY, analysisH, analysisW;\r
346 \r
347 char errorTitle[MSG_SIZ];\r
348 char errorMessage[2*MSG_SIZ];\r
349 HWND errorDialog = NULL;\r
350 BOOLEAN moveErrorMessageUp = FALSE;\r
351 BOOLEAN consoleEcho = TRUE;\r
352 CHARFORMAT consoleCF;\r
353 COLORREF consoleBackgroundColor;\r
354 \r
355 char *programVersion;\r
356 \r
357 #define CPReal 1\r
358 #define CPComm 2\r
359 #define CPSock 3\r
360 #define CPRcmd 4\r
361 typedef int CPKind;\r
362 \r
363 typedef struct {\r
364   CPKind kind;\r
365   HANDLE hProcess;\r
366   DWORD pid;\r
367   HANDLE hTo;\r
368   HANDLE hFrom;\r
369   SOCKET sock;\r
370   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
371 } ChildProc;\r
372 \r
373 #define INPUT_SOURCE_BUF_SIZE 4096\r
374 \r
375 typedef struct _InputSource {\r
376   CPKind kind;\r
377   HANDLE hFile;\r
378   SOCKET sock;\r
379   int lineByLine;\r
380   HANDLE hThread;\r
381   DWORD id;\r
382   char buf[INPUT_SOURCE_BUF_SIZE];\r
383   char *next;\r
384   DWORD count;\r
385   int error;\r
386   InputCallback func;\r
387   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
388   VOIDSTAR closure;\r
389 } InputSource;\r
390 \r
391 InputSource *consoleInputSource;\r
392 \r
393 DCB dcb;\r
394 \r
395 /* forward */\r
396 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
397 VOID ConsoleCreate();\r
398 LRESULT CALLBACK\r
399   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
400 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
401 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
402 VOID ParseCommSettings(char *arg, DCB *dcb);\r
403 LRESULT CALLBACK\r
404   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
406 void ParseIcsTextMenu(char *icsTextMenuString);\r
407 VOID PopUpMoveDialog(char firstchar);\r
408 VOID PopUpNameDialog(char firstchar);\r
409 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
410 \r
411 /* [AS] */\r
412 int NewGameFRC();\r
413 int GameListOptions();\r
414 \r
415 HWND moveHistoryDialog = NULL;\r
416 BOOLEAN moveHistoryDialogUp = FALSE;\r
417 \r
418 WindowPlacement wpMoveHistory;\r
419 \r
420 HWND evalGraphDialog = NULL;\r
421 BOOLEAN evalGraphDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpEvalGraph;\r
424 \r
425 HWND engineOutputDialog = NULL;\r
426 BOOLEAN engineOutputDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEngineOutput;\r
429 \r
430 VOID MoveHistoryPopUp();\r
431 VOID MoveHistoryPopDown();\r
432 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
433 BOOL MoveHistoryIsUp();\r
434 \r
435 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
436 VOID EvalGraphPopUp();\r
437 VOID EvalGraphPopDown();\r
438 BOOL EvalGraphIsUp();\r
439 \r
440 VOID EngineOutputPopUp();\r
441 VOID EngineOutputPopDown();\r
442 BOOL EngineOutputIsUp();\r
443 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
444 \r
445 VOID GothicPopUp(char *title, VariantClass variant);\r
446 /*\r
447  * Setting "frozen" should disable all user input other than deleting\r
448  * the window.  We do this while engines are initializing themselves.\r
449  */\r
450 static int frozen = 0;\r
451 static int oldMenuItemState[MENU_BAR_ITEMS];\r
452 void FreezeUI()\r
453 {\r
454   HMENU hmenu;\r
455   int i;\r
456 \r
457   if (frozen) return;\r
458   frozen = 1;\r
459   hmenu = GetMenu(hwndMain);\r
460   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
461     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
462   }\r
463   DrawMenuBar(hwndMain);\r
464 }\r
465 \r
466 /* Undo a FreezeUI */\r
467 void ThawUI()\r
468 {\r
469   HMENU hmenu;\r
470   int i;\r
471 \r
472   if (!frozen) return;\r
473   frozen = 0;\r
474   hmenu = GetMenu(hwndMain);\r
475   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
476     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
477   }\r
478   DrawMenuBar(hwndMain);\r
479 }\r
480 \r
481 /*---------------------------------------------------------------------------*\\r
482  *\r
483  * WinMain\r
484  *\r
485 \*---------------------------------------------------------------------------*/\r
486 \r
487 int APIENTRY\r
488 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
489         LPSTR lpCmdLine, int nCmdShow)\r
490 {\r
491   MSG msg;\r
492   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
493 //  INITCOMMONCONTROLSEX ex;\r
494 \r
495   debugFP = stderr;\r
496 \r
497   LoadLibrary("RICHED32.DLL");\r
498   consoleCF.cbSize = sizeof(CHARFORMAT);\r
499 \r
500   if (!InitApplication(hInstance)) {\r
501     return (FALSE);\r
502   }\r
503   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
504     return (FALSE);\r
505   }\r
506 \r
507 //  InitCommonControlsEx(&ex);\r
508   InitCommonControls();\r
509 \r
510   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
511   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
512   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
513 \r
514   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
515 \r
516   while (GetMessage(&msg, /* message structure */\r
517                     NULL, /* handle of window receiving the message */\r
518                     0,    /* lowest message to examine */\r
519                     0))   /* highest message to examine */\r
520     {\r
521       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
522           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
523           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
524           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
525           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
526           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
527           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
528           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
529           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
530           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
531         TranslateMessage(&msg); /* Translates virtual key codes */\r
532         DispatchMessage(&msg);  /* Dispatches message to window */\r
533       }\r
534     }\r
535 \r
536 \r
537   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
538 }\r
539 \r
540 /*---------------------------------------------------------------------------*\\r
541  *\r
542  * Initialization functions\r
543  *\r
544 \*---------------------------------------------------------------------------*/\r
545 \r
546 BOOL\r
547 InitApplication(HINSTANCE hInstance)\r
548 {\r
549   WNDCLASS wc;\r
550 \r
551   /* Fill in window class structure with parameters that describe the */\r
552   /* main window. */\r
553 \r
554   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
555   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
556   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
557   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
558   wc.hInstance     = hInstance;         /* Owner of this class */\r
559   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
560   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
561   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
562   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
563   wc.lpszClassName = szAppName;                 /* Name to register as */\r
564 \r
565   /* Register the window class and return success/failure code. */\r
566   if (!RegisterClass(&wc)) return FALSE;\r
567 \r
568   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
569   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
570   wc.cbClsExtra    = 0;\r
571   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
572   wc.hInstance     = hInstance;\r
573   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
574   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
575   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
576   wc.lpszMenuName  = NULL;\r
577   wc.lpszClassName = szConsoleName;\r
578 \r
579   if (!RegisterClass(&wc)) return FALSE;\r
580   return TRUE;\r
581 }\r
582 \r
583 \r
584 /* Set by InitInstance, used by EnsureOnScreen */\r
585 int screenHeight, screenWidth;\r
586 \r
587 void\r
588 EnsureOnScreen(int *x, int *y)\r
589 {\r
590 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
591   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
592   if (*x > screenWidth - 32) *x = 0;\r
593   if (*y > screenHeight - 32) *y = 0;\r
594   if (*x < 0) *x = 0;\r
595   if (*y < 0) *y = 0;\r
596 //  if (*x < 10) *x = 10;\r
597 //  if (*y < gap) *y = gap;\r
598 }\r
599 \r
600 BOOL\r
601 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
602 {\r
603   HWND hwnd; /* Main window handle. */\r
604   int ibs;\r
605   WINDOWPLACEMENT wp;\r
606   char *filepart;\r
607 \r
608   hInst = hInstance;    /* Store instance handle in our global variable */\r
609 \r
610   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
611     *filepart = NULLCHAR;\r
612   } else {\r
613     GetCurrentDirectory(MSG_SIZ, installDir);\r
614   }\r
615   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
616   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
617   if (appData.debugMode) {\r
618     debugFP = fopen(appData.nameOfDebugFile, "w");\r
619     setbuf(debugFP, NULL);\r
620   }\r
621 \r
622   InitBackEnd1();\r
623 \r
624 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
625 //  InitEngineUCI( installDir, &second );\r
626 \r
627   /* Create a main window for this application instance. */\r
628   hwnd = CreateWindow(szAppName, szTitle,\r
629                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
630                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
631                       NULL, NULL, hInstance, NULL);\r
632   hwndMain = hwnd;\r
633 \r
634   /* If window could not be created, return "failure" */\r
635   if (!hwnd) {\r
636     return (FALSE);\r
637   }\r
638 \r
639   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
640   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
641       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
642 \r
643       if (first.programLogo == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
645       }\r
646   } else if(appData.autoLogo) {\r
647       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
648         char buf[MSG_SIZ];\r
649         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
650         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
651       }\r
652   }\r
653 \r
654   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
655       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
656 \r
657       if (second.programLogo == NULL && appData.debugMode) {\r
658           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
659       }\r
660   } else if(appData.autoLogo) {\r
661       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
662         char buf[MSG_SIZ];\r
663         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
664         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
665       }\r
666   }\r
667 \r
668   iconWhite = LoadIcon(hInstance, "icon_white");\r
669   iconBlack = LoadIcon(hInstance, "icon_black");\r
670   iconCurrent = iconWhite;\r
671   InitDrawingColors();\r
672   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
673   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
674   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
675     /* Compute window size for each board size, and use the largest\r
676        size that fits on this screen as the default. */\r
677     InitDrawingSizes((BoardSize)ibs, 0);\r
678     if (boardSize == (BoardSize)-1 &&\r
679         winHeight <= screenHeight\r
680            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
681         && winWidth <= screenWidth) {\r
682       boardSize = (BoardSize)ibs;\r
683     }\r
684   }\r
685 \r
686   InitDrawingSizes(boardSize, 0);\r
687   InitMenuChecks();\r
688   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
689 \r
690   /* [AS] Load textures if specified */\r
691   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
692   \r
693   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
694       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
695       liteBackTextureMode = appData.liteBackTextureMode;\r
696 \r
697       if (liteBackTexture == NULL && appData.debugMode) {\r
698           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
699       }\r
700   }\r
701   \r
702   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
703       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
704       darkBackTextureMode = appData.darkBackTextureMode;\r
705 \r
706       if (darkBackTexture == NULL && appData.debugMode) {\r
707           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
708       }\r
709   }\r
710 \r
711   mysrandom( (unsigned) time(NULL) );\r
712 \r
713   /* [AS] Restore layout */\r
714   if( wpMoveHistory.visible ) {\r
715       MoveHistoryPopUp();\r
716   }\r
717 \r
718   if( wpEvalGraph.visible ) {\r
719       EvalGraphPopUp();\r
720   }\r
721 \r
722   if( wpEngineOutput.visible ) {\r
723       EngineOutputPopUp();\r
724   }\r
725 \r
726   InitBackEnd2();\r
727 \r
728   /* Make the window visible; update its client area; and return "success" */\r
729   EnsureOnScreen(&boardX, &boardY);\r
730   wp.length = sizeof(WINDOWPLACEMENT);\r
731   wp.flags = 0;\r
732   wp.showCmd = nCmdShow;\r
733   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
734   wp.rcNormalPosition.left = boardX;\r
735   wp.rcNormalPosition.right = boardX + winWidth;\r
736   wp.rcNormalPosition.top = boardY;\r
737   wp.rcNormalPosition.bottom = boardY + winHeight;\r
738   SetWindowPlacement(hwndMain, &wp);\r
739 \r
740   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
741                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
742 \r
743 #if 0\r
744   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
745   if( gameInfo.variant != VariantFischeRandom ) {\r
746       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
747   }\r
748 #endif\r
749   if (hwndConsole) {\r
750 #if AOT_CONSOLE\r
751     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
752                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
753 #endif\r
754     ShowWindow(hwndConsole, nCmdShow);\r
755   }\r
756   UpdateWindow(hwnd);\r
757 \r
758   return TRUE;\r
759 \r
760 }\r
761 \r
762 \r
763 typedef enum {\r
764   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
765   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
766   ArgSettingsFilename\r
767 } ArgType;\r
768 \r
769 typedef struct {\r
770   char *argName;\r
771   ArgType argType;\r
772   /***\r
773   union {\r
774     String *pString;       // ArgString\r
775     int *pInt;             // ArgInt\r
776     float *pFloat;         // ArgFloat\r
777     Boolean *pBoolean;     // ArgBoolean\r
778     COLORREF *pColor;      // ArgColor\r
779     ColorClass cc;         // ArgAttribs\r
780     String *pFilename;     // ArgFilename\r
781     BoardSize *pBoardSize; // ArgBoardSize\r
782     int whichFont;         // ArgFont\r
783     DCB *pDCB;             // ArgCommSettings\r
784     String *pFilename;     // ArgSettingsFilename\r
785   } argLoc;\r
786   ***/\r
787   LPVOID argLoc;\r
788   BOOL save;\r
789 } ArgDescriptor;\r
790 \r
791 int junk;\r
792 ArgDescriptor argDescriptors[] = {\r
793   /* positional arguments */\r
794   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
795   { "", ArgNone, NULL },\r
796   /* keyword arguments */\r
797   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
798   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
799   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
800   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
801   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
802   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
803   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
804   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
805   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
806   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
807   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
808   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
809   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
810   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
811   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
812   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
813   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
814   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
815     FALSE },\r
816   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
817     FALSE },\r
818   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
819     FALSE },\r
820   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
821   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
822     FALSE },\r
823   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
824   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
825   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
826   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
827   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
828   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
829   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
830   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
831   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
832   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
833   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
834   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
835   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
836   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
837   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
838   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
839   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
840   /*!!bitmapDirectory?*/\r
841   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
842   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
843   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
844   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
845   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
846   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
847   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
848   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
849   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
850   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
851   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
852   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
853   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
854   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
855   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
856   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
857   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
858   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
859   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
860   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
861   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
862   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
863   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
864   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
865   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
866   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
867   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
868   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
869   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
870   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
871   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
872   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
873   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
874   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
875   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
876   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
877   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
878   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
879   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
880   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
881   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
882   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
883   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
884   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
885   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
886   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
887   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
888   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
889   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
890   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
891   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
892   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
893   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
894   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
895   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
896   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
897   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
898   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
899   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
900   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
901   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
902   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
903   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
904   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
905   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
906   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
907   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
908   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
909   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
910   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
911   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
912   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
913   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
914   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
915   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
916   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
917   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
918   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
919   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
920   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
921   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
922   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
923   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
924   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
925   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
926   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
927   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
928   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
929   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
930   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
931   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
932   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
933   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
934   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
935     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
936   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
937   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
938   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
939   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
940   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
941   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
942   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
943   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
944     TRUE }, /* must come after all fonts */\r
945   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
946   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
947     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
948   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
949   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
950   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
951   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
952   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
953   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
954   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
955   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
956   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
957   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
958   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
959   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
960   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
961   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
962   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
963   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
964   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
965   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
966   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
967   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
968   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
969   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
970   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
971   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
972   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
973   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
974   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
975   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
976 #if 0\r
977   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
978   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
979 #endif\r
980   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
981   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
982   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
983   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
984   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
985   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
986   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
987   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
988   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
989   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
990   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
991   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
992   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
993   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
994   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
995   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
996   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
997   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
998   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
999   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1000   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1001   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1002   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1003   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1004   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1005   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1006   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1007   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1008   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1009   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1010   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1011   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1012   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1013   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1014   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1015   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1016   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1017   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1018   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1019   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1020   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1021   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1022   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1023   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1024   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1025   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1026   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1027   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1028   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1029   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1030   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1031   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1032   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1033   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1034   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1035   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1036   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1037   { "highlightLastMove", ArgBoolean,\r
1038     (LPVOID) &appData.highlightLastMove, TRUE },\r
1039   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1040   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1041   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1042   { "highlightDragging", ArgBoolean,\r
1043     (LPVOID) &appData.highlightDragging, TRUE },\r
1044   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1045   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1046   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1047   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1048   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1049   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1050   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1051   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1052   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1053   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1054   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1055   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1056   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1057   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1058   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1059   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1060   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1061   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1062   { "soundShout", ArgFilename,\r
1063     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1064   { "soundSShout", ArgFilename,\r
1065     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1066   { "soundChannel1", ArgFilename,\r
1067     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1068   { "soundChannel", ArgFilename,\r
1069     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1070   { "soundKibitz", ArgFilename,\r
1071     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1072   { "soundTell", ArgFilename,\r
1073     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1074   { "soundChallenge", ArgFilename,\r
1075     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1076   { "soundRequest", ArgFilename,\r
1077     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1078   { "soundSeek", ArgFilename,\r
1079     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1080   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1081   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1082   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1083   { "soundIcsLoss", ArgFilename, \r
1084     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1085   { "soundIcsDraw", ArgFilename, \r
1086     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1087   { "soundIcsUnfinished", ArgFilename, \r
1088     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1089   { "soundIcsAlarm", ArgFilename, \r
1090     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1091   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1092   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1093   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1094   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1095   { "reuseChessPrograms", ArgBoolean,\r
1096     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1097   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1098   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1099   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1100   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1101   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1102   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1103   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1104   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1105   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1106   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1107   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1108   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1109   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1110   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1111   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1112   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1113   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1114   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1115   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1116   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1117   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1118   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1119   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1120   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1121   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1122   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1123   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1124   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1125   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1126   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1127   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1128   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1129   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1130   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1131   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1132   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1133   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1134     TRUE },\r
1135   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1136     TRUE },\r
1137   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1138   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1139   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1140   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1141   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1142   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1143   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1144   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1145   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1146   /* [AS] New features */\r
1147   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1148   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1149   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1150   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1151   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1152   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1153   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1154   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1155   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1156   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1157   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1158   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1159   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1160   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1161   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1162   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1163   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1164   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1165   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1166   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1167   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1168   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1169   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1170   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1171   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1172   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1173   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1174   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1175   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1176   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1177   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1178   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1179   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1180   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1181   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1182   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1183   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1184   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1185   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1186   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1187   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1188   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1189   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1190   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1191   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1192   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1193   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1194   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1195   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1196   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1197 \r
1198   /* [AS] Layout stuff */\r
1199   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1200   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1201   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1202   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1203   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1204 \r
1205   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1206   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1207   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1208   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1209   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1210 \r
1211   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1212   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1213   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1214   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1215   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1216 \r
1217   /* [HGM] board-size, adjudication and misc. options */\r
1218   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1219   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1220   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1221   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1222   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1223   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1224   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1225   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1226   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1227   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1228   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1229   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1230   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1231   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1232   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1233   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1234   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1235   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1236   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1237   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1238   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1239   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1240   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1241   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1242   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1243   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1244   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1245   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1246   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1247   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1248   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1249 \r
1250 #ifdef ZIPPY\r
1251   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1252   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1253   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1254   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1255   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1256   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1257   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1258   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1259   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1260   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1261   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1262   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1263   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1264     FALSE },\r
1265   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1266   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1267   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1268   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1269   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1270   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1271   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1272     FALSE },\r
1273   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1274   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1275   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1276   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1277   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1278   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1279   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1280   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1281   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1282   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1283   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1284   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1285   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1286   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1287   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1288   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1289   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1290   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1291   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1292 #endif\r
1293   /* [HGM] options for broadcasting and time odds */\r
1294   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1295   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1296   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1297   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1298   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1299   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1300   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1301   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1302   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1303   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1304   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1305   { NULL, ArgNone, NULL, FALSE }\r
1306 };\r
1307 \r
1308 \r
1309 /* Kludge for indirection files on command line */\r
1310 char* lastIndirectionFilename;\r
1311 ArgDescriptor argDescriptorIndirection =\r
1312 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1313 \r
1314 \r
1315 VOID\r
1316 ExitArgError(char *msg, char *badArg)\r
1317 {\r
1318   char buf[MSG_SIZ];\r
1319 \r
1320   sprintf(buf, "%s %s", msg, badArg);\r
1321   DisplayFatalError(buf, 0, 2);\r
1322   exit(2);\r
1323 }\r
1324 \r
1325 /* Command line font name parser.  NULL name means do nothing.\r
1326    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1327    For backward compatibility, syntax without the colon is also\r
1328    accepted, but font names with digits in them won't work in that case.\r
1329 */\r
1330 VOID\r
1331 ParseFontName(char *name, MyFontParams *mfp)\r
1332 {\r
1333   char *p, *q;\r
1334   if (name == NULL) return;\r
1335   p = name;\r
1336   q = strchr(p, ':');\r
1337   if (q) {\r
1338     if (q - p >= sizeof(mfp->faceName))\r
1339       ExitArgError("Font name too long:", name);\r
1340     memcpy(mfp->faceName, p, q - p);\r
1341     mfp->faceName[q - p] = NULLCHAR;\r
1342     p = q + 1;\r
1343   } else {\r
1344     q = mfp->faceName;\r
1345     while (*p && !isdigit(*p)) {\r
1346       *q++ = *p++;\r
1347       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1348         ExitArgError("Font name too long:", name);\r
1349     }\r
1350     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1351     *q = NULLCHAR;\r
1352   }\r
1353   if (!*p) ExitArgError("Font point size missing:", name);\r
1354   mfp->pointSize = (float) atof(p);\r
1355   mfp->bold = (strchr(p, 'b') != NULL);\r
1356   mfp->italic = (strchr(p, 'i') != NULL);\r
1357   mfp->underline = (strchr(p, 'u') != NULL);\r
1358   mfp->strikeout = (strchr(p, 's') != NULL);\r
1359 }\r
1360 \r
1361 /* Color name parser.\r
1362    X version accepts X color names, but this one\r
1363    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1364 COLORREF\r
1365 ParseColorName(char *name)\r
1366 {\r
1367   int red, green, blue, count;\r
1368   char buf[MSG_SIZ];\r
1369 \r
1370   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1371   if (count != 3) {\r
1372     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1373       &red, &green, &blue);\r
1374   }\r
1375   if (count != 3) {\r
1376     sprintf(buf, "Can't parse color name %s", name);\r
1377     DisplayError(buf, 0);\r
1378     return RGB(0, 0, 0);\r
1379   }\r
1380   return PALETTERGB(red, green, blue);\r
1381 }\r
1382 \r
1383 \r
1384 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1385 {\r
1386   char *e = argValue;\r
1387   int eff = 0;\r
1388 \r
1389   while (*e) {\r
1390     if (*e == 'b')      eff |= CFE_BOLD;\r
1391     else if (*e == 'i') eff |= CFE_ITALIC;\r
1392     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1393     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1394     else if (*e == '#' || isdigit(*e)) break;\r
1395     e++;\r
1396   }\r
1397   *effects = eff;\r
1398   *color   = ParseColorName(e);\r
1399 }\r
1400 \r
1401 \r
1402 BoardSize\r
1403 ParseBoardSize(char *name)\r
1404 {\r
1405   BoardSize bs = SizeTiny;\r
1406   while (sizeInfo[bs].name != NULL) {\r
1407     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1408     bs++;\r
1409   }\r
1410   ExitArgError("Unrecognized board size value", name);\r
1411   return bs; /* not reached */\r
1412 }\r
1413 \r
1414 \r
1415 char\r
1416 StringGet(void *getClosure)\r
1417 {\r
1418   char **p = (char **) getClosure;\r
1419   return *((*p)++);\r
1420 }\r
1421 \r
1422 char\r
1423 FileGet(void *getClosure)\r
1424 {\r
1425   int c;\r
1426   FILE* f = (FILE*) getClosure;\r
1427 \r
1428   c = getc(f);\r
1429   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1430   if (c == EOF)\r
1431     return NULLCHAR;\r
1432   else\r
1433     return (char) c;\r
1434 }\r
1435 \r
1436 /* Parse settings file named "name". If file found, return the\r
1437    full name in fullname and return TRUE; else return FALSE */\r
1438 BOOLEAN\r
1439 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1440 {\r
1441   char *dummy;\r
1442   FILE *f;\r
1443   int ok; char buf[MSG_SIZ];\r
1444 \r
1445   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1446   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1447     sprintf(buf, "%s.ini", name);\r
1448     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1449   }\r
1450   if (ok) {\r
1451     f = fopen(fullname, "r");\r
1452     if (f != NULL) {\r
1453       ParseArgs(FileGet, f);\r
1454       fclose(f);\r
1455       return TRUE;\r
1456     }\r
1457   }\r
1458   return FALSE;\r
1459 }\r
1460 \r
1461 VOID\r
1462 ParseArgs(GetFunc get, void *cl)\r
1463 {\r
1464   char argName[ARG_MAX];\r
1465   char argValue[ARG_MAX];\r
1466   ArgDescriptor *ad;\r
1467   char start;\r
1468   char *q;\r
1469   int i, octval;\r
1470   char ch;\r
1471   int posarg = 0;\r
1472 \r
1473   ch = get(cl);\r
1474   for (;;) {\r
1475     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1476     if (ch == NULLCHAR) break;\r
1477     if (ch == ';') {\r
1478       /* Comment to end of line */\r
1479       ch = get(cl);\r
1480       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1481       continue;\r
1482     } else if (ch == '/' || ch == '-') {\r
1483       /* Switch */\r
1484       q = argName;\r
1485       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1486              ch != '\n' && ch != '\t') {\r
1487         *q++ = ch;\r
1488         ch = get(cl);\r
1489       }\r
1490       *q = NULLCHAR;\r
1491 \r
1492       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1493         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1494 \r
1495       if (ad->argName == NULL)\r
1496         ExitArgError("Unrecognized argument", argName);\r
1497 \r
1498     } else if (ch == '@') {\r
1499       /* Indirection file */\r
1500       ad = &argDescriptorIndirection;\r
1501       ch = get(cl);\r
1502     } else {\r
1503       /* Positional argument */\r
1504       ad = &argDescriptors[posarg++];\r
1505       strcpy(argName, ad->argName);\r
1506     }\r
1507 \r
1508     if (ad->argType == ArgTrue) {\r
1509       *(Boolean *) ad->argLoc = TRUE;\r
1510       continue;\r
1511     }\r
1512     if (ad->argType == ArgFalse) {\r
1513       *(Boolean *) ad->argLoc = FALSE;\r
1514       continue;\r
1515     }\r
1516 \r
1517     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1518     if (ch == NULLCHAR || ch == '\n') {\r
1519       ExitArgError("No value provided for argument", argName);\r
1520     }\r
1521     q = argValue;\r
1522     if (ch == '{') {\r
1523       // Quoting with { }.  No characters have to (or can) be escaped.\r
1524       // Thus the string cannot contain a '}' character.\r
1525       start = ch;\r
1526       ch = get(cl);\r
1527       while (start) {\r
1528         switch (ch) {\r
1529         case NULLCHAR:\r
1530           start = NULLCHAR;\r
1531           break;\r
1532           \r
1533         case '}':\r
1534           ch = get(cl);\r
1535           start = NULLCHAR;\r
1536           break;\r
1537 \r
1538         default:\r
1539           *q++ = ch;\r
1540           ch = get(cl);\r
1541           break;\r
1542         }\r
1543       }   \r
1544     } else if (ch == '\'' || ch == '"') {\r
1545       // Quoting with ' ' or " ", with \ as escape character.\r
1546       // Inconvenient for long strings that may contain Windows filenames.\r
1547       start = ch;\r
1548       ch = get(cl);\r
1549       while (start) {\r
1550         switch (ch) {\r
1551         case NULLCHAR:\r
1552           start = NULLCHAR;\r
1553           break;\r
1554 \r
1555         default:\r
1556         not_special:\r
1557           *q++ = ch;\r
1558           ch = get(cl);\r
1559           break;\r
1560 \r
1561         case '\'':\r
1562         case '\"':\r
1563           if (ch == start) {\r
1564             ch = get(cl);\r
1565             start = NULLCHAR;\r
1566             break;\r
1567           } else {\r
1568             goto not_special;\r
1569           }\r
1570 \r
1571         case '\\':\r
1572           if (ad->argType == ArgFilename\r
1573               || ad->argType == ArgSettingsFilename) {\r
1574               goto not_special;\r
1575           }\r
1576           ch = get(cl);\r
1577           switch (ch) {\r
1578           case NULLCHAR:\r
1579             ExitArgError("Incomplete \\ escape in value for", argName);\r
1580             break;\r
1581           case 'n':\r
1582             *q++ = '\n';\r
1583             ch = get(cl);\r
1584             break;\r
1585           case 'r':\r
1586             *q++ = '\r';\r
1587             ch = get(cl);\r
1588             break;\r
1589           case 't':\r
1590             *q++ = '\t';\r
1591             ch = get(cl);\r
1592             break;\r
1593           case 'b':\r
1594             *q++ = '\b';\r
1595             ch = get(cl);\r
1596             break;\r
1597           case 'f':\r
1598             *q++ = '\f';\r
1599             ch = get(cl);\r
1600             break;\r
1601           default:\r
1602             octval = 0;\r
1603             for (i = 0; i < 3; i++) {\r
1604               if (ch >= '0' && ch <= '7') {\r
1605                 octval = octval*8 + (ch - '0');\r
1606                 ch = get(cl);\r
1607               } else {\r
1608                 break;\r
1609               }\r
1610             }\r
1611             if (i > 0) {\r
1612               *q++ = (char) octval;\r
1613             } else {\r
1614               *q++ = ch;\r
1615               ch = get(cl);\r
1616             }\r
1617             break;\r
1618           }\r
1619           break;\r
1620         }\r
1621       }\r
1622     } else {\r
1623       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1624         *q++ = ch;\r
1625         ch = get(cl);\r
1626       }\r
1627     }\r
1628     *q = NULLCHAR;\r
1629 \r
1630     switch (ad->argType) {\r
1631     case ArgInt:\r
1632       *(int *) ad->argLoc = atoi(argValue);\r
1633       break;\r
1634 \r
1635     case ArgFloat:\r
1636       *(float *) ad->argLoc = (float) atof(argValue);\r
1637       break;\r
1638 \r
1639     case ArgString:\r
1640     case ArgFilename:\r
1641       *(char **) ad->argLoc = strdup(argValue);\r
1642       break;\r
1643 \r
1644     case ArgSettingsFilename:\r
1645       {\r
1646         char fullname[MSG_SIZ];\r
1647         if (ParseSettingsFile(argValue, fullname)) {\r
1648           if (ad->argLoc != NULL) {\r
1649             *(char **) ad->argLoc = strdup(fullname);\r
1650           }\r
1651         } else {\r
1652           if (ad->argLoc != NULL) {\r
1653           } else {\r
1654             ExitArgError("Failed to open indirection file", argValue);\r
1655           }\r
1656         }\r
1657       }\r
1658       break;\r
1659 \r
1660     case ArgBoolean:\r
1661       switch (argValue[0]) {\r
1662       case 't':\r
1663       case 'T':\r
1664         *(Boolean *) ad->argLoc = TRUE;\r
1665         break;\r
1666       case 'f':\r
1667       case 'F':\r
1668         *(Boolean *) ad->argLoc = FALSE;\r
1669         break;\r
1670       default:\r
1671         ExitArgError("Unrecognized boolean argument value", argValue);\r
1672         break;\r
1673       }\r
1674       break;\r
1675 \r
1676     case ArgColor:\r
1677       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1678       break;\r
1679 \r
1680     case ArgAttribs: {\r
1681       ColorClass cc = (ColorClass)ad->argLoc;\r
1682       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1683       }\r
1684       break;\r
1685       \r
1686     case ArgBoardSize:\r
1687       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1688       break;\r
1689 \r
1690     case ArgFont:\r
1691       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1692       break;\r
1693 \r
1694     case ArgCommSettings:\r
1695       ParseCommSettings(argValue, &dcb);\r
1696       break;\r
1697 \r
1698     case ArgNone:\r
1699       ExitArgError("Unrecognized argument", argValue);\r
1700       break;\r
1701     case ArgTrue:\r
1702     case ArgFalse: ;\r
1703     }\r
1704   }\r
1705 }\r
1706 \r
1707 VOID\r
1708 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1709 {\r
1710   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1711   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1712   DeleteDC(hdc);\r
1713   lf->lfWidth = 0;\r
1714   lf->lfEscapement = 0;\r
1715   lf->lfOrientation = 0;\r
1716   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1717   lf->lfItalic = mfp->italic;\r
1718   lf->lfUnderline = mfp->underline;\r
1719   lf->lfStrikeOut = mfp->strikeout;\r
1720   lf->lfCharSet = DEFAULT_CHARSET;\r
1721   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1722   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1723   lf->lfQuality = DEFAULT_QUALITY;\r
1724   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1725   strcpy(lf->lfFaceName, mfp->faceName);\r
1726 }\r
1727 \r
1728 VOID\r
1729 CreateFontInMF(MyFont *mf)\r
1730 {\r
1731   LFfromMFP(&mf->lf, &mf->mfp);\r
1732   if (mf->hf) DeleteObject(mf->hf);\r
1733   mf->hf = CreateFontIndirect(&mf->lf);\r
1734 }\r
1735 \r
1736 VOID\r
1737 SetDefaultTextAttribs()\r
1738 {\r
1739   ColorClass cc;\r
1740   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1741     ParseAttribs(&textAttribs[cc].color, \r
1742                  &textAttribs[cc].effects, \r
1743                  defaultTextAttribs[cc]);\r
1744   }\r
1745 }\r
1746 \r
1747 VOID\r
1748 SetDefaultSounds()\r
1749 {\r
1750   ColorClass cc;\r
1751   SoundClass sc;\r
1752   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1753     textAttribs[cc].sound.name = strdup("");\r
1754     textAttribs[cc].sound.data = NULL;\r
1755   }\r
1756   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1757     sounds[sc].name = strdup("");\r
1758     sounds[sc].data = NULL;\r
1759   }\r
1760   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1761 }\r
1762 \r
1763 VOID\r
1764 LoadAllSounds()\r
1765 {\r
1766   ColorClass cc;\r
1767   SoundClass sc;\r
1768   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1769     MyLoadSound(&textAttribs[cc].sound);\r
1770   }\r
1771   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1772     MyLoadSound(&sounds[sc]);\r
1773   }\r
1774 }\r
1775 \r
1776 VOID\r
1777 InitAppData(LPSTR lpCmdLine)\r
1778 {\r
1779   int i, j;\r
1780   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1781   char *dummy, *p;\r
1782 \r
1783   programName = szAppName;\r
1784 \r
1785   /* Initialize to defaults */\r
1786   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1787   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1788   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1789   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1790   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1791   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1792   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1793   SetDefaultTextAttribs();\r
1794   SetDefaultSounds();\r
1795   appData.movesPerSession = MOVES_PER_SESSION;\r
1796   appData.initString = INIT_STRING;\r
1797   appData.secondInitString = INIT_STRING;\r
1798   appData.firstComputerString = COMPUTER_STRING;\r
1799   appData.secondComputerString = COMPUTER_STRING;\r
1800   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1801   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1802   appData.firstPlaysBlack = FALSE;\r
1803   appData.noChessProgram = FALSE;\r
1804   chessProgram = FALSE;\r
1805   appData.firstHost = FIRST_HOST;\r
1806   appData.secondHost = SECOND_HOST;\r
1807   appData.firstDirectory = FIRST_DIRECTORY;\r
1808   appData.secondDirectory = SECOND_DIRECTORY;\r
1809   appData.bitmapDirectory = "";\r
1810   appData.remoteShell = REMOTE_SHELL;\r
1811   appData.remoteUser = "";\r
1812   appData.timeDelay = TIME_DELAY;\r
1813   appData.timeControl = TIME_CONTROL;\r
1814   appData.timeIncrement = TIME_INCREMENT;\r
1815   appData.icsActive = FALSE;\r
1816   appData.icsHost = "";\r
1817   appData.icsPort = ICS_PORT;\r
1818   appData.icsCommPort = ICS_COMM_PORT;\r
1819   appData.icsLogon = ICS_LOGON;\r
1820   appData.icsHelper = "";\r
1821   appData.useTelnet = FALSE;\r
1822   appData.telnetProgram = TELNET_PROGRAM;\r
1823   appData.gateway = "";\r
1824   appData.loadGameFile = "";\r
1825   appData.loadGameIndex = 0;\r
1826   appData.saveGameFile = "";\r
1827   appData.autoSaveGames = FALSE;\r
1828   appData.loadPositionFile = "";\r
1829   appData.loadPositionIndex = 1;\r
1830   appData.savePositionFile = "";\r
1831   appData.matchMode = FALSE;\r
1832   appData.matchGames = 0;\r
1833   appData.monoMode = FALSE;\r
1834   appData.debugMode = FALSE;\r
1835   appData.clockMode = TRUE;\r
1836   boardSize = (BoardSize) -1; /* determine by screen size */\r
1837   appData.Iconic = FALSE; /*unused*/\r
1838   appData.searchTime = "";\r
1839   appData.searchDepth = 0;\r
1840   appData.showCoords = FALSE;\r
1841   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1842   appData.autoCallFlag = FALSE;\r
1843   appData.flipView = FALSE;\r
1844   appData.autoFlipView = TRUE;\r
1845   appData.cmailGameName = "";\r
1846   appData.alwaysPromoteToQueen = FALSE;\r
1847   appData.oldSaveStyle = FALSE;\r
1848   appData.quietPlay = FALSE;\r
1849   appData.showThinking = FALSE;\r
1850   appData.ponderNextMove = TRUE;\r
1851   appData.periodicUpdates = TRUE;\r
1852   appData.popupExitMessage = TRUE;\r
1853   appData.popupMoveErrors = FALSE;\r
1854   appData.autoObserve = FALSE;\r
1855   appData.autoComment = FALSE;\r
1856   appData.animate = TRUE;\r
1857   appData.animSpeed = 10;\r
1858   appData.animateDragging = TRUE;\r
1859   appData.highlightLastMove = TRUE;\r
1860   appData.getMoveList = TRUE;\r
1861   appData.testLegality = TRUE;\r
1862   appData.premove = TRUE;\r
1863   appData.premoveWhite = FALSE;\r
1864   appData.premoveWhiteText = "";\r
1865   appData.premoveBlack = FALSE;\r
1866   appData.premoveBlackText = "";\r
1867   appData.icsAlarm = TRUE;\r
1868   appData.icsAlarmTime = 5000;\r
1869   appData.autoRaiseBoard = TRUE;\r
1870   appData.localLineEditing = TRUE;\r
1871   appData.colorize = TRUE;\r
1872   appData.reuseFirst = TRUE;\r
1873   appData.reuseSecond = TRUE;\r
1874   appData.blindfold = FALSE;\r
1875   appData.icsEngineAnalyze = FALSE;\r
1876   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1877   dcb.DCBlength = sizeof(DCB);\r
1878   dcb.BaudRate = 9600;\r
1879   dcb.fBinary = TRUE;\r
1880   dcb.fParity = FALSE;\r
1881   dcb.fOutxCtsFlow = FALSE;\r
1882   dcb.fOutxDsrFlow = FALSE;\r
1883   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1884   dcb.fDsrSensitivity = FALSE;\r
1885   dcb.fTXContinueOnXoff = TRUE;\r
1886   dcb.fOutX = FALSE;\r
1887   dcb.fInX = FALSE;\r
1888   dcb.fNull = FALSE;\r
1889   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1890   dcb.fAbortOnError = FALSE;\r
1891   dcb.ByteSize = 7;\r
1892   dcb.Parity = SPACEPARITY;\r
1893   dcb.StopBits = ONESTOPBIT;\r
1894   settingsFileName = SETTINGS_FILE;\r
1895   saveSettingsOnExit = TRUE;\r
1896   boardX = CW_USEDEFAULT;\r
1897   boardY = CW_USEDEFAULT;\r
1898   consoleX = CW_USEDEFAULT; \r
1899   consoleY = CW_USEDEFAULT; \r
1900   consoleW = CW_USEDEFAULT;\r
1901   consoleH = CW_USEDEFAULT;\r
1902   analysisX = CW_USEDEFAULT; \r
1903   analysisY = CW_USEDEFAULT; \r
1904   analysisW = CW_USEDEFAULT;\r
1905   analysisH = CW_USEDEFAULT;\r
1906   commentX = CW_USEDEFAULT; \r
1907   commentY = CW_USEDEFAULT; \r
1908   commentW = CW_USEDEFAULT;\r
1909   commentH = CW_USEDEFAULT;\r
1910   editTagsX = CW_USEDEFAULT; \r
1911   editTagsY = CW_USEDEFAULT; \r
1912   editTagsW = CW_USEDEFAULT;\r
1913   editTagsH = CW_USEDEFAULT;\r
1914   gameListX = CW_USEDEFAULT; \r
1915   gameListY = CW_USEDEFAULT; \r
1916   gameListW = CW_USEDEFAULT;\r
1917   gameListH = CW_USEDEFAULT;\r
1918   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1919   icsNames = ICS_NAMES;\r
1920   firstChessProgramNames = FCP_NAMES;\r
1921   secondChessProgramNames = SCP_NAMES;\r
1922   appData.initialMode = "";\r
1923   appData.variant = "normal";\r
1924   appData.firstProtocolVersion = PROTOVER;\r
1925   appData.secondProtocolVersion = PROTOVER;\r
1926   appData.showButtonBar = TRUE;\r
1927 \r
1928    /* [AS] New properties (see comments in header file) */\r
1929   appData.firstScoreIsAbsolute = FALSE;\r
1930   appData.secondScoreIsAbsolute = FALSE;\r
1931   appData.saveExtendedInfoInPGN = FALSE;\r
1932   appData.hideThinkingFromHuman = FALSE;\r
1933   appData.liteBackTextureFile = "";\r
1934   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1935   appData.darkBackTextureFile = "";\r
1936   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1937   appData.renderPiecesWithFont = "";\r
1938   appData.fontToPieceTable = "";\r
1939   appData.fontBackColorWhite = 0;\r
1940   appData.fontForeColorWhite = 0;\r
1941   appData.fontBackColorBlack = 0;\r
1942   appData.fontForeColorBlack = 0;\r
1943   appData.fontPieceSize = 80;\r
1944   appData.overrideLineGap = 1;\r
1945   appData.adjudicateLossThreshold = 0;\r
1946   appData.delayBeforeQuit = 0;\r
1947   appData.delayAfterQuit = 0;\r
1948   appData.nameOfDebugFile = "winboard.debug";\r
1949   appData.pgnEventHeader = "Computer Chess Game";\r
1950   appData.defaultFrcPosition = -1;\r
1951   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1952   appData.saveOutOfBookInfo = TRUE;\r
1953   appData.showEvalInMoveHistory = TRUE;\r
1954   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1955   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1956   appData.highlightMoveWithArrow = FALSE;\r
1957   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1958   appData.useStickyWindows = TRUE;\r
1959   appData.adjudicateDrawMoves = 0;\r
1960   appData.autoDisplayComment = TRUE;\r
1961   appData.autoDisplayTags = TRUE;\r
1962   appData.firstIsUCI = FALSE;\r
1963   appData.secondIsUCI = FALSE;\r
1964   appData.firstHasOwnBookUCI = TRUE;\r
1965   appData.secondHasOwnBookUCI = TRUE;\r
1966   appData.polyglotDir = "";\r
1967   appData.usePolyglotBook = FALSE;\r
1968   appData.polyglotBook = "";\r
1969   appData.defaultHashSize = 64;\r
1970   appData.defaultCacheSizeEGTB = 4;\r
1971   appData.defaultPathEGTB = "c:\\egtb";\r
1972   appData.firstOptions = "";\r
1973   appData.secondOptions = "";\r
1974 \r
1975   InitWindowPlacement( &wpMoveHistory );\r
1976   InitWindowPlacement( &wpEvalGraph );\r
1977   InitWindowPlacement( &wpEngineOutput );\r
1978 \r
1979   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1980   appData.NrFiles      = -1;\r
1981   appData.NrRanks      = -1;\r
1982   appData.holdingsSize = -1;\r
1983   appData.testClaims   = FALSE;\r
1984   appData.checkMates   = FALSE;\r
1985   appData.materialDraws= FALSE;\r
1986   appData.trivialDraws = FALSE;\r
1987   appData.ruleMoves    = 51;\r
1988   appData.drawRepeats  = 6;\r
1989   appData.matchPause   = 10000;\r
1990   appData.alphaRank    = FALSE;\r
1991   appData.allWhite     = FALSE;\r
1992   appData.upsideDown   = FALSE;\r
1993   appData.serverPause  = 15;\r
1994   appData.serverMovesName   = NULL;\r
1995   appData.suppressLoadMoves = FALSE;\r
1996   appData.firstTimeOdds  = 1;\r
1997   appData.secondTimeOdds = 1;\r
1998   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1999   appData.secondAccumulateTC = 1;\r
2000   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2001   appData.secondNPS = -1;\r
2002   appData.engineComments = 1;\r
2003   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2004   appData.egtFormats = "";\r
2005 \r
2006 #ifdef ZIPPY\r
2007   appData.zippyTalk = ZIPPY_TALK;\r
2008   appData.zippyPlay = ZIPPY_PLAY;\r
2009   appData.zippyLines = ZIPPY_LINES;\r
2010   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2011   appData.zippyPassword = ZIPPY_PASSWORD;\r
2012   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2013   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2014   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2015   appData.zippyUseI = ZIPPY_USE_I;\r
2016   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2017   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2018   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2019   appData.zippyGameStart = ZIPPY_GAME_START;\r
2020   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2021   appData.zippyAbort = ZIPPY_ABORT;\r
2022   appData.zippyVariants = ZIPPY_VARIANTS;\r
2023   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2024   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2025 #endif\r
2026 \r
2027   /* Point font array elements to structures and\r
2028      parse default font names */\r
2029   for (i=0; i<NUM_FONTS; i++) {\r
2030     for (j=0; j<NUM_SIZES; j++) {\r
2031       font[j][i] = &fontRec[j][i];\r
2032       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2033     }\r
2034   }\r
2035   \r
2036   /* Parse default settings file if any */\r
2037   if (ParseSettingsFile(settingsFileName, buf)) {\r
2038     settingsFileName = strdup(buf);\r
2039   }\r
2040 \r
2041   /* Parse command line */\r
2042   ParseArgs(StringGet, &lpCmdLine);\r
2043 \r
2044   /* [HGM] make sure board size is acceptable */\r
2045   if(appData.NrFiles > BOARD_SIZE ||\r
2046      appData.NrRanks > BOARD_SIZE   )\r
2047       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2048 \r
2049   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2050    * with options from the command line, we now make an even higher priority\r
2051    * overrule by WB options attached to the engine command line. This so that\r
2052    * tournament managers can use WB options (such as /timeOdds) that follow\r
2053    * the engines.\r
2054    */\r
2055   if(appData.firstChessProgram != NULL) {\r
2056       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2057       static char *f = "first";\r
2058       char buf[MSG_SIZ], *q = buf;\r
2059       if(p != NULL) { // engine command line contains WinBoard options\r
2060           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2061           ParseArgs(StringGet, &q);\r
2062           p[-1] = 0; // cut them offengine command line\r
2063       }\r
2064   }\r
2065   // now do same for second chess program\r
2066   if(appData.secondChessProgram != NULL) {\r
2067       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2068       static char *s = "second";\r
2069       char buf[MSG_SIZ], *q = buf;\r
2070       if(p != NULL) { // engine command line contains WinBoard options\r
2071           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2072           ParseArgs(StringGet, &q);\r
2073           p[-1] = 0; // cut them offengine command line\r
2074       }\r
2075   }\r
2076 \r
2077 \r
2078   /* Propagate options that affect others */\r
2079   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2080   if (appData.icsActive || appData.noChessProgram) {\r
2081      chessProgram = FALSE;  /* not local chess program mode */\r
2082   }\r
2083 \r
2084   /* Open startup dialog if needed */\r
2085   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2086       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2087       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2088                         *appData.secondChessProgram == NULLCHAR))) {\r
2089     FARPROC lpProc;\r
2090     \r
2091     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2092     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2093     FreeProcInstance(lpProc);\r
2094   }\r
2095 \r
2096   /* Make sure save files land in the right (?) directory */\r
2097   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2098     appData.saveGameFile = strdup(buf);\r
2099   }\r
2100   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2101     appData.savePositionFile = strdup(buf);\r
2102   }\r
2103 \r
2104   /* Finish initialization for fonts and sounds */\r
2105   for (i=0; i<NUM_FONTS; i++) {\r
2106     for (j=0; j<NUM_SIZES; j++) {\r
2107       CreateFontInMF(font[j][i]);\r
2108     }\r
2109   }\r
2110   /* xboard, and older WinBoards, controlled the move sound with the\r
2111      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2112      always turn the option on (so that the backend will call us),\r
2113      then let the user turn the sound off by setting it to silence if\r
2114      desired.  To accommodate old winboard.ini files saved by old\r
2115      versions of WinBoard, we also turn off the sound if the option\r
2116      was initially set to false. */\r
2117   if (!appData.ringBellAfterMoves) {\r
2118     sounds[(int)SoundMove].name = strdup("");\r
2119     appData.ringBellAfterMoves = TRUE;\r
2120   }\r
2121   GetCurrentDirectory(MSG_SIZ, currDir);\r
2122   SetCurrentDirectory(installDir);\r
2123   LoadAllSounds();\r
2124   SetCurrentDirectory(currDir);\r
2125 \r
2126   p = icsTextMenuString;\r
2127   if (p[0] == '@') {\r
2128     FILE* f = fopen(p + 1, "r");\r
2129     if (f == NULL) {\r
2130       DisplayFatalError(p + 1, errno, 2);\r
2131       return;\r
2132     }\r
2133     i = fread(buf, 1, sizeof(buf)-1, f);\r
2134     fclose(f);\r
2135     buf[i] = NULLCHAR;\r
2136     p = buf;\r
2137   }\r
2138   ParseIcsTextMenu(strdup(p));\r
2139 }\r
2140 \r
2141 \r
2142 VOID\r
2143 InitMenuChecks()\r
2144 {\r
2145   HMENU hmenu = GetMenu(hwndMain);\r
2146 \r
2147   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2148                         MF_BYCOMMAND|((appData.icsActive &&\r
2149                                        *appData.icsCommPort != NULLCHAR) ?\r
2150                                       MF_ENABLED : MF_GRAYED));\r
2151   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2152                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2153                                      MF_CHECKED : MF_UNCHECKED));\r
2154 }\r
2155 \r
2156 \r
2157 VOID\r
2158 SaveSettings(char* name)\r
2159 {\r
2160   FILE *f;\r
2161   ArgDescriptor *ad;\r
2162   WINDOWPLACEMENT wp;\r
2163   char dir[MSG_SIZ];\r
2164 \r
2165   if (!hwndMain) return;\r
2166 \r
2167   GetCurrentDirectory(MSG_SIZ, dir);\r
2168   SetCurrentDirectory(installDir);\r
2169   f = fopen(name, "w");\r
2170   SetCurrentDirectory(dir);\r
2171   if (f == NULL) {\r
2172     DisplayError(name, errno);\r
2173     return;\r
2174   }\r
2175   fprintf(f, ";\n");\r
2176   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2177   fprintf(f, ";\n");\r
2178   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2179   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2180   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2181   fprintf(f, ";\n");\r
2182 \r
2183   wp.length = sizeof(WINDOWPLACEMENT);\r
2184   GetWindowPlacement(hwndMain, &wp);\r
2185   boardX = wp.rcNormalPosition.left;\r
2186   boardY = wp.rcNormalPosition.top;\r
2187 \r
2188   if (hwndConsole) {\r
2189     GetWindowPlacement(hwndConsole, &wp);\r
2190     consoleX = wp.rcNormalPosition.left;\r
2191     consoleY = wp.rcNormalPosition.top;\r
2192     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2193     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2194   }\r
2195 \r
2196   if (analysisDialog) {\r
2197     GetWindowPlacement(analysisDialog, &wp);\r
2198     analysisX = wp.rcNormalPosition.left;\r
2199     analysisY = wp.rcNormalPosition.top;\r
2200     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2201     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2202   }\r
2203 \r
2204   if (commentDialog) {\r
2205     GetWindowPlacement(commentDialog, &wp);\r
2206     commentX = wp.rcNormalPosition.left;\r
2207     commentY = wp.rcNormalPosition.top;\r
2208     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2209     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2210   }\r
2211 \r
2212   if (editTagsDialog) {\r
2213     GetWindowPlacement(editTagsDialog, &wp);\r
2214     editTagsX = wp.rcNormalPosition.left;\r
2215     editTagsY = wp.rcNormalPosition.top;\r
2216     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2217     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2218   }\r
2219 \r
2220   if (gameListDialog) {\r
2221     GetWindowPlacement(gameListDialog, &wp);\r
2222     gameListX = wp.rcNormalPosition.left;\r
2223     gameListY = wp.rcNormalPosition.top;\r
2224     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2225     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2226   }\r
2227 \r
2228   /* [AS] Move history */\r
2229   wpMoveHistory.visible = MoveHistoryIsUp();\r
2230   \r
2231   if( moveHistoryDialog ) {\r
2232     GetWindowPlacement(moveHistoryDialog, &wp);\r
2233     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2234     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2235     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2236     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2237   }\r
2238 \r
2239   /* [AS] Eval graph */\r
2240   wpEvalGraph.visible = EvalGraphIsUp();\r
2241 \r
2242   if( evalGraphDialog ) {\r
2243     GetWindowPlacement(evalGraphDialog, &wp);\r
2244     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2245     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2246     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2247     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2248   }\r
2249 \r
2250   /* [AS] Engine output */\r
2251   wpEngineOutput.visible = EngineOutputIsUp();\r
2252 \r
2253   if( engineOutputDialog ) {\r
2254     GetWindowPlacement(engineOutputDialog, &wp);\r
2255     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2256     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2257     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2258     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2259   }\r
2260 \r
2261   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2262     if (!ad->save) continue;\r
2263     switch (ad->argType) {\r
2264     case ArgString:\r
2265       {\r
2266         char *p = *(char **)ad->argLoc;\r
2267         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2268           /* Quote multiline values or \-containing values\r
2269              with { } if possible */\r
2270           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2271         } else {\r
2272           /* Else quote with " " */\r
2273           fprintf(f, "/%s=\"", ad->argName);\r
2274           while (*p) {\r
2275             if (*p == '\n') fprintf(f, "\n");\r
2276             else if (*p == '\r') fprintf(f, "\\r");\r
2277             else if (*p == '\t') fprintf(f, "\\t");\r
2278             else if (*p == '\b') fprintf(f, "\\b");\r
2279             else if (*p == '\f') fprintf(f, "\\f");\r
2280             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2281             else if (*p == '\"') fprintf(f, "\\\"");\r
2282             else if (*p == '\\') fprintf(f, "\\\\");\r
2283             else putc(*p, f);\r
2284             p++;\r
2285           }\r
2286           fprintf(f, "\"\n");\r
2287         }\r
2288       }\r
2289       break;\r
2290     case ArgInt:\r
2291       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2292       break;\r
2293     case ArgFloat:\r
2294       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2295       break;\r
2296     case ArgBoolean:\r
2297       fprintf(f, "/%s=%s\n", ad->argName, \r
2298         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2299       break;\r
2300     case ArgTrue:\r
2301       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2302       break;\r
2303     case ArgFalse:\r
2304       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2305       break;\r
2306     case ArgColor:\r
2307       {\r
2308         COLORREF color = *(COLORREF *)ad->argLoc;\r
2309         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2310           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2311       }\r
2312       break;\r
2313     case ArgAttribs:\r
2314       {\r
2315         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2316         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2317           (ta->effects & CFE_BOLD) ? "b" : "",\r
2318           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2319           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2320           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2321           (ta->effects) ? " " : "",\r
2322           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2323       }\r
2324       break;\r
2325     case ArgFilename:\r
2326       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2327         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2328       } else {\r
2329         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2330       }\r
2331       break;\r
2332     case ArgBoardSize:\r
2333       fprintf(f, "/%s=%s\n", ad->argName,\r
2334               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2335       break;\r
2336     case ArgFont:\r
2337       {\r
2338         int bs;\r
2339         for (bs=0; bs<NUM_SIZES; bs++) {\r
2340           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2341           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2342           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2343             ad->argName, mfp->faceName, mfp->pointSize,\r
2344             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2345             mfp->bold ? "b" : "",\r
2346             mfp->italic ? "i" : "",\r
2347             mfp->underline ? "u" : "",\r
2348             mfp->strikeout ? "s" : "");\r
2349         }\r
2350       }\r
2351       break;\r
2352     case ArgCommSettings:\r
2353       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2354     case ArgNone:\r
2355     case ArgSettingsFilename: ;\r
2356     }\r
2357   }\r
2358   fclose(f);\r
2359 }\r
2360 \r
2361 \r
2362 \r
2363 /*---------------------------------------------------------------------------*\\r
2364  *\r
2365  * GDI board drawing routines\r
2366  *\r
2367 \*---------------------------------------------------------------------------*/\r
2368 \r
2369 /* [AS] Draw square using background texture */\r
2370 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2371 {\r
2372     XFORM   x;\r
2373 \r
2374     if( mode == 0 ) {\r
2375         return; /* Should never happen! */\r
2376     }\r
2377 \r
2378     SetGraphicsMode( dst, GM_ADVANCED );\r
2379 \r
2380     switch( mode ) {\r
2381     case 1:\r
2382         /* Identity */\r
2383         break;\r
2384     case 2:\r
2385         /* X reflection */\r
2386         x.eM11 = -1.0;\r
2387         x.eM12 = 0;\r
2388         x.eM21 = 0;\r
2389         x.eM22 = 1.0;\r
2390         x.eDx = (FLOAT) dw + dx - 1;\r
2391         x.eDy = 0;\r
2392         dx = 0;\r
2393         SetWorldTransform( dst, &x );\r
2394         break;\r
2395     case 3:\r
2396         /* Y reflection */\r
2397         x.eM11 = 1.0;\r
2398         x.eM12 = 0;\r
2399         x.eM21 = 0;\r
2400         x.eM22 = -1.0;\r
2401         x.eDx = 0;\r
2402         x.eDy = (FLOAT) dh + dy - 1;\r
2403         dy = 0;\r
2404         SetWorldTransform( dst, &x );\r
2405         break;\r
2406     case 4:\r
2407         /* X/Y flip */\r
2408         x.eM11 = 0;\r
2409         x.eM12 = 1.0;\r
2410         x.eM21 = 1.0;\r
2411         x.eM22 = 0;\r
2412         x.eDx = (FLOAT) dx;\r
2413         x.eDy = (FLOAT) dy;\r
2414         dx = 0;\r
2415         dy = 0;\r
2416         SetWorldTransform( dst, &x );\r
2417         break;\r
2418     }\r
2419 \r
2420     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2421 \r
2422     x.eM11 = 1.0;\r
2423     x.eM12 = 0;\r
2424     x.eM21 = 0;\r
2425     x.eM22 = 1.0;\r
2426     x.eDx = 0;\r
2427     x.eDy = 0;\r
2428     SetWorldTransform( dst, &x );\r
2429 \r
2430     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2431 }\r
2432 \r
2433 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2434 enum {\r
2435     PM_WP = (int) WhitePawn, \r
2436     PM_WN = (int) WhiteKnight, \r
2437     PM_WB = (int) WhiteBishop, \r
2438     PM_WR = (int) WhiteRook, \r
2439     PM_WQ = (int) WhiteQueen, \r
2440     PM_WF = (int) WhiteFerz, \r
2441     PM_WW = (int) WhiteWazir, \r
2442     PM_WE = (int) WhiteAlfil, \r
2443     PM_WM = (int) WhiteMan, \r
2444     PM_WO = (int) WhiteCannon, \r
2445     PM_WU = (int) WhiteUnicorn, \r
2446     PM_WH = (int) WhiteNightrider, \r
2447     PM_WA = (int) WhiteAngel, \r
2448     PM_WC = (int) WhiteMarshall, \r
2449     PM_WAB = (int) WhiteCardinal, \r
2450     PM_WD = (int) WhiteDragon, \r
2451     PM_WL = (int) WhiteLance, \r
2452     PM_WS = (int) WhiteCobra, \r
2453     PM_WV = (int) WhiteFalcon, \r
2454     PM_WSG = (int) WhiteSilver, \r
2455     PM_WG = (int) WhiteGrasshopper, \r
2456     PM_WK = (int) WhiteKing,\r
2457     PM_BP = (int) BlackPawn, \r
2458     PM_BN = (int) BlackKnight, \r
2459     PM_BB = (int) BlackBishop, \r
2460     PM_BR = (int) BlackRook, \r
2461     PM_BQ = (int) BlackQueen, \r
2462     PM_BF = (int) BlackFerz, \r
2463     PM_BW = (int) BlackWazir, \r
2464     PM_BE = (int) BlackAlfil, \r
2465     PM_BM = (int) BlackMan,\r
2466     PM_BO = (int) BlackCannon, \r
2467     PM_BU = (int) BlackUnicorn, \r
2468     PM_BH = (int) BlackNightrider, \r
2469     PM_BA = (int) BlackAngel, \r
2470     PM_BC = (int) BlackMarshall, \r
2471     PM_BG = (int) BlackGrasshopper, \r
2472     PM_BAB = (int) BlackCardinal,\r
2473     PM_BD = (int) BlackDragon,\r
2474     PM_BL = (int) BlackLance,\r
2475     PM_BS = (int) BlackCobra,\r
2476     PM_BV = (int) BlackFalcon,\r
2477     PM_BSG = (int) BlackSilver,\r
2478     PM_BK = (int) BlackKing\r
2479 };\r
2480 \r
2481 static HFONT hPieceFont = NULL;\r
2482 static HBITMAP hPieceMask[(int) EmptySquare];\r
2483 static HBITMAP hPieceFace[(int) EmptySquare];\r
2484 static int fontBitmapSquareSize = 0;\r
2485 static char pieceToFontChar[(int) EmptySquare] =\r
2486                               { 'p', 'n', 'b', 'r', 'q', \r
2487                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2488                       'k', 'o', 'm', 'v', 't', 'w', \r
2489                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2490                                                               'l' };\r
2491 \r
2492 extern BOOL SetCharTable( char *table, const char * map );\r
2493 /* [HGM] moved to backend.c */\r
2494 \r
2495 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2496 {\r
2497     HBRUSH hbrush;\r
2498     BYTE r1 = GetRValue( color );\r
2499     BYTE g1 = GetGValue( color );\r
2500     BYTE b1 = GetBValue( color );\r
2501     BYTE r2 = r1 / 2;\r
2502     BYTE g2 = g1 / 2;\r
2503     BYTE b2 = b1 / 2;\r
2504     RECT rc;\r
2505 \r
2506     /* Create a uniform background first */\r
2507     hbrush = CreateSolidBrush( color );\r
2508     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2509     FillRect( hdc, &rc, hbrush );\r
2510     DeleteObject( hbrush );\r
2511     \r
2512     if( mode == 1 ) {\r
2513         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2514         int steps = squareSize / 2;\r
2515         int i;\r
2516 \r
2517         for( i=0; i<steps; i++ ) {\r
2518             BYTE r = r1 - (r1-r2) * i / steps;\r
2519             BYTE g = g1 - (g1-g2) * i / steps;\r
2520             BYTE b = b1 - (b1-b2) * i / steps;\r
2521 \r
2522             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2523             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2524             FillRect( hdc, &rc, hbrush );\r
2525             DeleteObject(hbrush);\r
2526         }\r
2527     }\r
2528     else if( mode == 2 ) {\r
2529         /* Diagonal gradient, good more or less for every piece */\r
2530         POINT triangle[3];\r
2531         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2532         HBRUSH hbrush_old;\r
2533         int steps = squareSize;\r
2534         int i;\r
2535 \r
2536         triangle[0].x = squareSize - steps;\r
2537         triangle[0].y = squareSize;\r
2538         triangle[1].x = squareSize;\r
2539         triangle[1].y = squareSize;\r
2540         triangle[2].x = squareSize;\r
2541         triangle[2].y = squareSize - steps;\r
2542 \r
2543         for( i=0; i<steps; i++ ) {\r
2544             BYTE r = r1 - (r1-r2) * i / steps;\r
2545             BYTE g = g1 - (g1-g2) * i / steps;\r
2546             BYTE b = b1 - (b1-b2) * i / steps;\r
2547 \r
2548             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2549             hbrush_old = SelectObject( hdc, hbrush );\r
2550             Polygon( hdc, triangle, 3 );\r
2551             SelectObject( hdc, hbrush_old );\r
2552             DeleteObject(hbrush);\r
2553             triangle[0].x++;\r
2554             triangle[2].y++;\r
2555         }\r
2556 \r
2557         SelectObject( hdc, hpen );\r
2558     }\r
2559 }\r
2560 \r
2561 /*\r
2562     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2563     seems to work ok. The main problem here is to find the "inside" of a chess\r
2564     piece: follow the steps as explained below.\r
2565 */\r
2566 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2567 {\r
2568     HBITMAP hbm;\r
2569     HBITMAP hbm_old;\r
2570     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2571     RECT rc;\r
2572     SIZE sz;\r
2573     POINT pt;\r
2574     int backColor = whitePieceColor; \r
2575     int foreColor = blackPieceColor;\r
2576     \r
2577     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2578         backColor = appData.fontBackColorWhite;\r
2579         foreColor = appData.fontForeColorWhite;\r
2580     }\r
2581     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2582         backColor = appData.fontBackColorBlack;\r
2583         foreColor = appData.fontForeColorBlack;\r
2584     }\r
2585 \r
2586     /* Mask */\r
2587     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2588 \r
2589     hbm_old = SelectObject( hdc, hbm );\r
2590 \r
2591     rc.left = 0;\r
2592     rc.top = 0;\r
2593     rc.right = squareSize;\r
2594     rc.bottom = squareSize;\r
2595 \r
2596     /* Step 1: background is now black */\r
2597     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2598 \r
2599     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2600 \r
2601     pt.x = (squareSize - sz.cx) / 2;\r
2602     pt.y = (squareSize - sz.cy) / 2;\r
2603 \r
2604     SetBkMode( hdc, TRANSPARENT );\r
2605     SetTextColor( hdc, chroma );\r
2606     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2607     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2608 \r
2609     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2610     /* Step 3: the area outside the piece is filled with white */\r
2611 //    FloodFill( hdc, 0, 0, chroma );\r
2612     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2613     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2614     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2615     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2616     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2617     /* \r
2618         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2619         but if the start point is not inside the piece we're lost!\r
2620         There should be a better way to do this... if we could create a region or path\r
2621         from the fill operation we would be fine for example.\r
2622     */\r
2623 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2624     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2625 \r
2626     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2627         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2628         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2629 \r
2630         SelectObject( dc2, bm2 );\r
2631         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2632         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2633         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2634         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2635         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2636 \r
2637         DeleteDC( dc2 );\r
2638         DeleteObject( bm2 );\r
2639     }\r
2640 \r
2641     SetTextColor( hdc, 0 );\r
2642     /* \r
2643         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2644         draw the piece again in black for safety.\r
2645     */\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, hbm_old );\r
2649 \r
2650     if( hPieceMask[index] != NULL ) {\r
2651         DeleteObject( hPieceMask[index] );\r
2652     }\r
2653 \r
2654     hPieceMask[index] = hbm;\r
2655 \r
2656     /* Face */\r
2657     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2658 \r
2659     SelectObject( hdc, hbm );\r
2660 \r
2661     {\r
2662         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2663         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2664         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2665 \r
2666         SelectObject( dc1, hPieceMask[index] );\r
2667         SelectObject( dc2, bm2 );\r
2668         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2669         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2670         \r
2671         /* \r
2672             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2673             the piece background and deletes (makes transparent) the rest.\r
2674             Thanks to that mask, we are free to paint the background with the greates\r
2675             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2676             We use this, to make gradients and give the pieces a "roundish" look.\r
2677         */\r
2678         SetPieceBackground( hdc, backColor, 2 );\r
2679         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2680 \r
2681         DeleteDC( dc2 );\r
2682         DeleteDC( dc1 );\r
2683         DeleteObject( bm2 );\r
2684     }\r
2685 \r
2686     SetTextColor( hdc, foreColor );\r
2687     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2688 \r
2689     SelectObject( hdc, hbm_old );\r
2690 \r
2691     if( hPieceFace[index] != NULL ) {\r
2692         DeleteObject( hPieceFace[index] );\r
2693     }\r
2694 \r
2695     hPieceFace[index] = hbm;\r
2696 }\r
2697 \r
2698 static int TranslatePieceToFontPiece( int piece )\r
2699 {\r
2700     switch( piece ) {\r
2701     case BlackPawn:\r
2702         return PM_BP;\r
2703     case BlackKnight:\r
2704         return PM_BN;\r
2705     case BlackBishop:\r
2706         return PM_BB;\r
2707     case BlackRook:\r
2708         return PM_BR;\r
2709     case BlackQueen:\r
2710         return PM_BQ;\r
2711     case BlackKing:\r
2712         return PM_BK;\r
2713     case WhitePawn:\r
2714         return PM_WP;\r
2715     case WhiteKnight:\r
2716         return PM_WN;\r
2717     case WhiteBishop:\r
2718         return PM_WB;\r
2719     case WhiteRook:\r
2720         return PM_WR;\r
2721     case WhiteQueen:\r
2722         return PM_WQ;\r
2723     case WhiteKing:\r
2724         return PM_WK;\r
2725 \r
2726     case BlackAngel:\r
2727         return PM_BA;\r
2728     case BlackMarshall:\r
2729         return PM_BC;\r
2730     case BlackFerz:\r
2731         return PM_BF;\r
2732     case BlackNightrider:\r
2733         return PM_BH;\r
2734     case BlackAlfil:\r
2735         return PM_BE;\r
2736     case BlackWazir:\r
2737         return PM_BW;\r
2738     case BlackUnicorn:\r
2739         return PM_BU;\r
2740     case BlackCannon:\r
2741         return PM_BO;\r
2742     case BlackGrasshopper:\r
2743         return PM_BG;\r
2744     case BlackMan:\r
2745         return PM_BM;\r
2746     case BlackSilver:\r
2747         return PM_BSG;\r
2748     case BlackLance:\r
2749         return PM_BL;\r
2750     case BlackFalcon:\r
2751         return PM_BV;\r
2752     case BlackCobra:\r
2753         return PM_BS;\r
2754     case BlackCardinal:\r
2755         return PM_BAB;\r
2756     case BlackDragon:\r
2757         return PM_BD;\r
2758 \r
2759     case WhiteAngel:\r
2760         return PM_WA;\r
2761     case WhiteMarshall:\r
2762         return PM_WC;\r
2763     case WhiteFerz:\r
2764         return PM_WF;\r
2765     case WhiteNightrider:\r
2766         return PM_WH;\r
2767     case WhiteAlfil:\r
2768         return PM_WE;\r
2769     case WhiteWazir:\r
2770         return PM_WW;\r
2771     case WhiteUnicorn:\r
2772         return PM_WU;\r
2773     case WhiteCannon:\r
2774         return PM_WO;\r
2775     case WhiteGrasshopper:\r
2776         return PM_WG;\r
2777     case WhiteMan:\r
2778         return PM_WM;\r
2779     case WhiteSilver:\r
2780         return PM_WSG;\r
2781     case WhiteLance:\r
2782         return PM_WL;\r
2783     case WhiteFalcon:\r
2784         return PM_WV;\r
2785     case WhiteCobra:\r
2786         return PM_WS;\r
2787     case WhiteCardinal:\r
2788         return PM_WAB;\r
2789     case WhiteDragon:\r
2790         return PM_WD;\r
2791     }\r
2792 \r
2793     return 0;\r
2794 }\r
2795 \r
2796 void CreatePiecesFromFont()\r
2797 {\r
2798     LOGFONT lf;\r
2799     HDC hdc_window = NULL;\r
2800     HDC hdc = NULL;\r
2801     HFONT hfont_old;\r
2802     int fontHeight;\r
2803     int i;\r
2804 \r
2805     if( fontBitmapSquareSize < 0 ) {\r
2806         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2807         return;\r
2808     }\r
2809 \r
2810     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2811         fontBitmapSquareSize = -1;\r
2812         return;\r
2813     }\r
2814 \r
2815     if( fontBitmapSquareSize != squareSize ) {\r
2816         hdc_window = GetDC( hwndMain );\r
2817         hdc = CreateCompatibleDC( hdc_window );\r
2818 \r
2819         if( hPieceFont != NULL ) {\r
2820             DeleteObject( hPieceFont );\r
2821         }\r
2822         else {\r
2823             for( i=0; i<=(int)BlackKing; i++ ) {\r
2824                 hPieceMask[i] = NULL;\r
2825                 hPieceFace[i] = NULL;\r
2826             }\r
2827         }\r
2828 \r
2829         fontHeight = 75;\r
2830 \r
2831         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2832             fontHeight = appData.fontPieceSize;\r
2833         }\r
2834 \r
2835         fontHeight = (fontHeight * squareSize) / 100;\r
2836 \r
2837         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2838         lf.lfWidth = 0;\r
2839         lf.lfEscapement = 0;\r
2840         lf.lfOrientation = 0;\r
2841         lf.lfWeight = FW_NORMAL;\r
2842         lf.lfItalic = 0;\r
2843         lf.lfUnderline = 0;\r
2844         lf.lfStrikeOut = 0;\r
2845         lf.lfCharSet = DEFAULT_CHARSET;\r
2846         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2847         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2848         lf.lfQuality = PROOF_QUALITY;\r
2849         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2850         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2851         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2852 \r
2853         hPieceFont = CreateFontIndirect( &lf );\r
2854 \r
2855         if( hPieceFont == NULL ) {\r
2856             fontBitmapSquareSize = -2;\r
2857         }\r
2858         else {\r
2859             /* Setup font-to-piece character table */\r
2860             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2861                 /* No (or wrong) global settings, try to detect the font */\r
2862                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2863                     /* Alpha */\r
2864                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2865                 }\r
2866                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2867                     /* DiagramTT* family */\r
2868                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2869                 }\r
2870                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2871                     /* Fairy symbols */\r
2872                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2873                 }\r
2874                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2875                     /* Good Companion (Some characters get warped as literal :-( */\r
2876                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2877                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2878                     SetCharTable(pieceToFontChar, s);\r
2879                 }\r
2880                 else {\r
2881                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2882                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2883                 }\r
2884             }\r
2885 \r
2886             /* Create bitmaps */\r
2887             hfont_old = SelectObject( hdc, hPieceFont );\r
2888 #if 0\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2901 \r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2923             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2931             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2932             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2933             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2934 #else\r
2935             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2936                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2937                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2938 #endif\r
2939             SelectObject( hdc, hfont_old );\r
2940 \r
2941             fontBitmapSquareSize = squareSize;\r
2942         }\r
2943     }\r
2944 \r
2945     if( hdc != NULL ) {\r
2946         DeleteDC( hdc );\r
2947     }\r
2948 \r
2949     if( hdc_window != NULL ) {\r
2950         ReleaseDC( hwndMain, hdc_window );\r
2951     }\r
2952 }\r
2953 \r
2954 HBITMAP\r
2955 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2956 {\r
2957   char name[128];\r
2958 \r
2959   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2960   if (gameInfo.event &&\r
2961       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2962       strcmp(name, "k80s") == 0) {\r
2963     strcpy(name, "tim");\r
2964   }\r
2965   return LoadBitmap(hinst, name);\r
2966 }\r
2967 \r
2968 \r
2969 /* Insert a color into the program's logical palette\r
2970    structure.  This code assumes the given color is\r
2971    the result of the RGB or PALETTERGB macro, and it\r
2972    knows how those macros work (which is documented).\r
2973 */\r
2974 VOID\r
2975 InsertInPalette(COLORREF color)\r
2976 {\r
2977   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2978 \r
2979   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2980     DisplayFatalError("Too many colors", 0, 1);\r
2981     pLogPal->palNumEntries--;\r
2982     return;\r
2983   }\r
2984 \r
2985   pe->peFlags = (char) 0;\r
2986   pe->peRed = (char) (0xFF & color);\r
2987   pe->peGreen = (char) (0xFF & (color >> 8));\r
2988   pe->peBlue = (char) (0xFF & (color >> 16));\r
2989   return;\r
2990 }\r
2991 \r
2992 \r
2993 VOID\r
2994 InitDrawingColors()\r
2995 {\r
2996   if (pLogPal == NULL) {\r
2997     /* Allocate enough memory for a logical palette with\r
2998      * PALETTESIZE entries and set the size and version fields\r
2999      * of the logical palette structure.\r
3000      */\r
3001     pLogPal = (NPLOGPALETTE)\r
3002       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3003                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3004     pLogPal->palVersion    = 0x300;\r
3005   }\r
3006   pLogPal->palNumEntries = 0;\r
3007 \r
3008   InsertInPalette(lightSquareColor);\r
3009   InsertInPalette(darkSquareColor);\r
3010   InsertInPalette(whitePieceColor);\r
3011   InsertInPalette(blackPieceColor);\r
3012   InsertInPalette(highlightSquareColor);\r
3013   InsertInPalette(premoveHighlightColor);\r
3014 \r
3015   /*  create a logical color palette according the information\r
3016    *  in the LOGPALETTE structure.\r
3017    */\r
3018   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3019 \r
3020   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3021   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3022   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3023   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3024   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3025   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3026   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3027   /* [AS] Force rendering of the font-based pieces */\r
3028   if( fontBitmapSquareSize > 0 ) {\r
3029     fontBitmapSquareSize = 0;\r
3030   }\r
3031 }\r
3032 \r
3033 \r
3034 int\r
3035 BoardWidth(int boardSize, int n)\r
3036 { /* [HGM] argument n added to allow different width and height */\r
3037   int lineGap = sizeInfo[boardSize].lineGap;\r
3038 \r
3039   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3040       lineGap = appData.overrideLineGap;\r
3041   }\r
3042 \r
3043   return (n + 1) * lineGap +\r
3044           n * sizeInfo[boardSize].squareSize;\r
3045 }\r
3046 \r
3047 /* Respond to board resize by dragging edge */\r
3048 VOID\r
3049 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3050 {\r
3051   BoardSize newSize = NUM_SIZES - 1;\r
3052   static int recurse = 0;\r
3053   if (IsIconic(hwndMain)) return;\r
3054   if (recurse > 0) return;\r
3055   recurse++;\r
3056   while (newSize > 0) {\r
3057         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3058         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3059            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3060     newSize--;\r
3061   } \r
3062   boardSize = newSize;\r
3063   InitDrawingSizes(boardSize, flags);\r
3064   recurse--;\r
3065 }\r
3066 \r
3067 \r
3068 \r
3069 VOID\r
3070 InitDrawingSizes(BoardSize boardSize, int flags)\r
3071 {\r
3072   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3073   ChessSquare piece;\r
3074   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3075   HDC hdc;\r
3076   SIZE clockSize, messageSize;\r
3077   HFONT oldFont;\r
3078   char buf[MSG_SIZ];\r
3079   char *str;\r
3080   HMENU hmenu = GetMenu(hwndMain);\r
3081   RECT crect, wrect;\r
3082   int offby;\r
3083   LOGBRUSH logbrush;\r
3084 \r
3085   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3086   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3087 \r
3088   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3089   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3090 \r
3091   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3092   smallLayout = sizeInfo[boardSize].smallLayout;\r
3093   squareSize = sizeInfo[boardSize].squareSize;\r
3094   lineGap = sizeInfo[boardSize].lineGap;\r
3095   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3096 \r
3097   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3098       lineGap = appData.overrideLineGap;\r
3099   }\r
3100 \r
3101   if (tinyLayout != oldTinyLayout) {\r
3102     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3103     if (tinyLayout) {\r
3104       style &= ~WS_SYSMENU;\r
3105       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3106                  "&Minimize\tCtrl+F4");\r
3107     } else {\r
3108       style |= WS_SYSMENU;\r
3109       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3110     }\r
3111     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3112 \r
3113     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3114       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3115         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3116     }\r
3117     DrawMenuBar(hwndMain);\r
3118   }\r
3119 \r
3120   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3121   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3122 \r
3123   /* Get text area sizes */\r
3124   hdc = GetDC(hwndMain);\r
3125   if (appData.clockMode) {\r
3126     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3127   } else {\r
3128     sprintf(buf, "White");\r
3129   }\r
3130   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3131   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3132   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3133   str = "We only care about the height here";\r
3134   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3135   SelectObject(hdc, oldFont);\r
3136   ReleaseDC(hwndMain, hdc);\r
3137 \r
3138   /* Compute where everything goes */\r
3139   if(first.programLogo || second.programLogo) {\r
3140         /* [HGM] logo: if either logo is on, reserve space for it */\r
3141         logoHeight =  2*clockSize.cy;\r
3142         leftLogoRect.left   = OUTER_MARGIN;\r
3143         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3144         leftLogoRect.top    = OUTER_MARGIN;\r
3145         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3146 \r
3147         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3148         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3149         rightLogoRect.top    = OUTER_MARGIN;\r
3150         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3151 \r
3152 \r
3153     blackRect.left = leftLogoRect.right;\r
3154     blackRect.right = rightLogoRect.left;\r
3155     blackRect.top = OUTER_MARGIN;\r
3156     blackRect.bottom = blackRect.top + clockSize.cy;\r
3157 \r
3158     whiteRect.left = blackRect.left ;\r
3159     whiteRect.right = blackRect.right;\r
3160     whiteRect.top = blackRect.bottom;\r
3161     whiteRect.bottom = leftLogoRect.bottom;\r
3162   } else {\r
3163     whiteRect.left = OUTER_MARGIN;\r
3164     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3165     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3166     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3167 \r
3168     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3169     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3170     blackRect.top = whiteRect.top;\r
3171     blackRect.bottom = whiteRect.bottom;\r
3172   }\r
3173 \r
3174   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3175   if (appData.showButtonBar) {\r
3176     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3177       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3178   } else {\r
3179     messageRect.right = OUTER_MARGIN + boardWidth;\r
3180   }\r
3181   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3182   messageRect.bottom = messageRect.top + messageSize.cy;\r
3183 \r
3184   boardRect.left = OUTER_MARGIN;\r
3185   boardRect.right = boardRect.left + boardWidth;\r
3186   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3187   boardRect.bottom = boardRect.top + boardHeight;\r
3188 \r
3189   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3190   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3191   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3192   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3193   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3194     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3195   GetWindowRect(hwndMain, &wrect);\r
3196   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3197                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3198   /* compensate if menu bar wrapped */\r
3199   GetClientRect(hwndMain, &crect);\r
3200   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3201   winHeight += offby;\r
3202   switch (flags) {\r
3203   case WMSZ_TOPLEFT:\r
3204     SetWindowPos(hwndMain, NULL, \r
3205                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3206                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3207     break;\r
3208 \r
3209   case WMSZ_TOPRIGHT:\r
3210   case WMSZ_TOP:\r
3211     SetWindowPos(hwndMain, NULL, \r
3212                  wrect.left, wrect.bottom - winHeight, \r
3213                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3214     break;\r
3215 \r
3216   case WMSZ_BOTTOMLEFT:\r
3217   case WMSZ_LEFT:\r
3218     SetWindowPos(hwndMain, NULL, \r
3219                  wrect.right - winWidth, wrect.top, \r
3220                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3221     break;\r
3222 \r
3223   case WMSZ_BOTTOMRIGHT:\r
3224   case WMSZ_BOTTOM:\r
3225   case WMSZ_RIGHT:\r
3226   default:\r
3227     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3228                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3229     break;\r
3230   }\r
3231 \r
3232   hwndPause = NULL;\r
3233   for (i = 0; i < N_BUTTONS; i++) {\r
3234     if (buttonDesc[i].hwnd != NULL) {\r
3235       DestroyWindow(buttonDesc[i].hwnd);\r
3236       buttonDesc[i].hwnd = NULL;\r
3237     }\r
3238     if (appData.showButtonBar) {\r
3239       buttonDesc[i].hwnd =\r
3240         CreateWindow("BUTTON", buttonDesc[i].label,\r
3241                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3242                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3243                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3244                      (HMENU) buttonDesc[i].id,\r
3245                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3246       if (tinyLayout) {\r
3247         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3248                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3249                     MAKELPARAM(FALSE, 0));\r
3250       }\r
3251       if (buttonDesc[i].id == IDM_Pause)\r
3252         hwndPause = buttonDesc[i].hwnd;\r
3253       buttonDesc[i].wndproc = (WNDPROC)\r
3254         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3255     }\r
3256   }\r
3257   if (gridPen != NULL) DeleteObject(gridPen);\r
3258   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3259   if (premovePen != NULL) DeleteObject(premovePen);\r
3260   if (lineGap != 0) {\r
3261     logbrush.lbStyle = BS_SOLID;\r
3262     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3263     gridPen =\r
3264       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3265                    lineGap, &logbrush, 0, NULL);\r
3266     logbrush.lbColor = highlightSquareColor;\r
3267     highlightPen =\r
3268       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3269                    lineGap, &logbrush, 0, NULL);\r
3270 \r
3271     logbrush.lbColor = premoveHighlightColor; \r
3272     premovePen =\r
3273       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3274                    lineGap, &logbrush, 0, NULL);\r
3275 \r
3276     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3277     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3278       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3279       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3280         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3281       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3282         BOARD_WIDTH * (squareSize + lineGap);\r
3283       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3284     }\r
3285     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3286       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3287       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3288         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3289         lineGap / 2 + (i * (squareSize + lineGap));\r
3290       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3291         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3292       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3293     }\r
3294   }\r
3295 \r
3296   /* [HGM] Licensing requirement */\r
3297 #ifdef GOTHIC\r
3298   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3299 #endif\r
3300 #ifdef FALCON\r
3301   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3302 #endif\r
3303   GothicPopUp( "", VariantNormal);\r
3304 \r
3305 \r
3306 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3307   oldBoardSize = boardSize;\r
3308   oldTinyLayout = tinyLayout;\r
3309 \r
3310   /* Load piece bitmaps for this board size */\r
3311   for (i=0; i<=2; i++) {\r
3312     for (piece = WhitePawn;\r
3313          (int) piece < (int) BlackPawn;\r
3314          piece = (ChessSquare) ((int) piece + 1)) {\r
3315       if (pieceBitmap[i][piece] != NULL)\r
3316         DeleteObject(pieceBitmap[i][piece]);\r
3317     }\r
3318   }\r
3319 \r
3320   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3321   // Orthodox Chess pieces\r
3322   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3323   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3324   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3325   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3326   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3327   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3328   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3329   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3330   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3331   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3332   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3333   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3334   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3335   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3336   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3337   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3338     // in Shogi, Hijack the unused Queen for Lance\r
3339     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3340     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3341     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3342   } else {\r
3343     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3344     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3345     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3346   }\r
3347 \r
3348   if(squareSize <= 72 && squareSize >= 33) { \r
3349     /* A & C are available in most sizes now */\r
3350     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3351       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3352       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3353       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3354       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3355       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3356       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3357       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3358       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3359       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3360       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3361       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3362       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3363     } else { // Smirf-like\r
3364       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3365       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3366       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3367     }\r
3368     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3369       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3370       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3371       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3372     } else { // WinBoard standard\r
3373       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3374       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3375       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3376     }\r
3377   }\r
3378 \r
3379 \r
3380   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3381     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3382     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3383     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3384     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3385     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3386     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3387     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3388     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3389     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3390     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3391     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3392     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3393     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3394     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3395     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3396     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3397     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3398     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3399     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3400     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3401     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3402     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3403     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3404     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3405     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3406     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3407     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3408     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3409     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3410     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3411 \r
3412     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3413       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3414       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3415       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3416       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3417       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3418       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3419       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3420       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3421       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3422       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3423       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3424       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3425     } else {\r
3426       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3427       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3428       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3429       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3430       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3431       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3432       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3433       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3434       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3435       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3436       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3437       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3438     }\r
3439 \r
3440   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3441     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3442     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3443     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3444     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3445     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3446     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3447     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3448     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3449     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3450     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3451     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3452     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3453     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3454     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3455   }\r
3456 \r
3457 \r
3458   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3459   /* special Shogi support in this size */\r
3460   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3461       for (piece = WhitePawn;\r
3462            (int) piece < (int) BlackPawn;\r
3463            piece = (ChessSquare) ((int) piece + 1)) {\r
3464         if (pieceBitmap[i][piece] != NULL)\r
3465           DeleteObject(pieceBitmap[i][piece]);\r
3466       }\r
3467     }\r
3468   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3469   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3470   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3471   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3472   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3473   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3474   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3475   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3476   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3477   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3478   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3479   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3480   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3481   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3482   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3483   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3484   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3485   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3486   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3487   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3488   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3489   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3490   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3491   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3492   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3493   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3494   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3495   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3496   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3497   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3498   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3499   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3500   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3501   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3502   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3503   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3504   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3505   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3506   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3507   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3508   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3509   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3510   minorSize = 0;\r
3511   }\r
3512 }\r
3513 \r
3514 HBITMAP\r
3515 PieceBitmap(ChessSquare p, int kind)\r
3516 {\r
3517   if ((int) p >= (int) BlackPawn)\r
3518     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3519 \r
3520   return pieceBitmap[kind][(int) p];\r
3521 }\r
3522 \r
3523 /***************************************************************/\r
3524 \r
3525 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3526 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3527 /*\r
3528 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3529 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3530 */\r
3531 \r
3532 VOID\r
3533 SquareToPos(int row, int column, int * x, int * y)\r
3534 {\r
3535   if (flipView) {\r
3536     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3537     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3538   } else {\r
3539     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3540     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3541   }\r
3542 }\r
3543 \r
3544 VOID\r
3545 DrawCoordsOnDC(HDC hdc)\r
3546 {\r
3547   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
3548   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
3549   char str[2] = { NULLCHAR, NULLCHAR };\r
3550   int oldMode, oldAlign, x, y, start, i;\r
3551   HFONT oldFont;\r
3552   HBRUSH oldBrush;\r
3553 \r
3554   if (!appData.showCoords)\r
3555     return;\r
3556 \r
3557   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3558 \r
3559   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3560   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3561   oldAlign = GetTextAlign(hdc);\r
3562   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3563 \r
3564   y = boardRect.top + lineGap;\r
3565   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3566 \r
3567   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3568   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3569     str[0] = files[start + i];\r
3570     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3571     y += squareSize + lineGap;\r
3572   }\r
3573 \r
3574   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3575 \r
3576   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3577   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3578     str[0] = ranks[start + i];\r
3579     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3580     x += squareSize + lineGap;\r
3581   }    \r
3582 \r
3583   SelectObject(hdc, oldBrush);\r
3584   SetBkMode(hdc, oldMode);\r
3585   SetTextAlign(hdc, oldAlign);\r
3586   SelectObject(hdc, oldFont);\r
3587 }\r
3588 \r
3589 VOID\r
3590 DrawGridOnDC(HDC hdc)\r
3591 {\r
3592   HPEN oldPen;\r
3593  \r
3594   if (lineGap != 0) {\r
3595     oldPen = SelectObject(hdc, gridPen);\r
3596     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3597     SelectObject(hdc, oldPen);\r
3598   }\r
3599 }\r
3600 \r
3601 #define HIGHLIGHT_PEN 0\r
3602 #define PREMOVE_PEN   1\r
3603 \r
3604 VOID\r
3605 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3606 {\r
3607   int x1, y1;\r
3608   HPEN oldPen, hPen;\r
3609   if (lineGap == 0) return;\r
3610   if (flipView) {\r
3611     x1 = boardRect.left +\r
3612       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3613     y1 = boardRect.top +\r
3614       lineGap/2 + y * (squareSize + lineGap);\r
3615   } else {\r
3616     x1 = boardRect.left +\r
3617       lineGap/2 + x * (squareSize + lineGap);\r
3618     y1 = boardRect.top +\r
3619       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3620   }\r
3621   hPen = pen ? premovePen : highlightPen;\r
3622   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3623   MoveToEx(hdc, x1, y1, NULL);\r
3624   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3625   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3626   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3627   LineTo(hdc, x1, y1);\r
3628   SelectObject(hdc, oldPen);\r
3629 }\r
3630 \r
3631 VOID\r
3632 DrawHighlightsOnDC(HDC hdc)\r
3633 {\r
3634   int i;\r
3635   for (i=0; i<2; i++) {\r
3636     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3637       DrawHighlightOnDC(hdc, TRUE,\r
3638                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3639                         HIGHLIGHT_PEN);\r
3640   }\r
3641   for (i=0; i<2; i++) {\r
3642     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3643         premoveHighlightInfo.sq[i].y >= 0) {\r
3644         DrawHighlightOnDC(hdc, TRUE,\r
3645                           premoveHighlightInfo.sq[i].x, \r
3646                           premoveHighlightInfo.sq[i].y,\r
3647                           PREMOVE_PEN);\r
3648     }\r
3649   }\r
3650 }\r
3651 \r
3652 /* Note: sqcolor is used only in monoMode */\r
3653 /* Note that this code is largely duplicated in woptions.c,\r
3654    function DrawSampleSquare, so that needs to be updated too */\r
3655 VOID\r
3656 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3657 {\r
3658   HBITMAP oldBitmap;\r
3659   HBRUSH oldBrush;\r
3660   int tmpSize;\r
3661 \r
3662   if (appData.blindfold) return;\r
3663 \r
3664   /* [AS] Use font-based pieces if needed */\r
3665   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3666     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3667     CreatePiecesFromFont();\r
3668 \r
3669     if( fontBitmapSquareSize == squareSize ) {\r
3670         int index = TranslatePieceToFontPiece(piece);\r
3671 \r
3672         SelectObject( tmphdc, hPieceMask[ index ] );\r
3673 \r
3674         BitBlt( hdc,\r
3675             x, y,\r
3676             squareSize, squareSize,\r
3677             tmphdc,\r
3678             0, 0,\r
3679             SRCAND );\r
3680 \r
3681         SelectObject( tmphdc, hPieceFace[ index ] );\r
3682 \r
3683         BitBlt( hdc,\r
3684             x, y,\r
3685             squareSize, squareSize,\r
3686             tmphdc,\r
3687             0, 0,\r
3688             SRCPAINT );\r
3689 \r
3690         return;\r
3691     }\r
3692   }\r
3693 \r
3694   if (appData.monoMode) {\r
3695     SelectObject(tmphdc, PieceBitmap(piece, \r
3696       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3697     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3698            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3699   } else {\r
3700     tmpSize = squareSize;\r
3701     if(minorSize &&\r
3702         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3703          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3704       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3705       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3706       x += (squareSize - minorSize)>>1;\r
3707       y += squareSize - minorSize - 2;\r
3708       tmpSize = minorSize;\r
3709     }\r
3710     if (color || appData.allWhite ) {\r
3711       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3712       if( color )\r
3713               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3714       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3715       if(appData.upsideDown && color==flipView)\r
3716         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3717       else\r
3718         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3719 #if 0\r
3720       /* Use black piece color for outline of white pieces */\r
3721       /* Not sure this looks really good (though xboard does it).\r
3722          Maybe better to have another selectable color, default black */\r
3723       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3724       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3725       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3726 #else\r
3727       /* Use black for outline of white pieces */\r
3728       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3729       if(appData.upsideDown && color==flipView)\r
3730         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3731       else\r
3732         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3733 #endif\r
3734     } else {\r
3735 #if 0\r
3736       /* Use white piece color for details of black pieces */\r
3737       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3738          WHITE_PIECE ones aren't always the right shape. */\r
3739       /* Not sure this looks really good (though xboard does it).\r
3740          Maybe better to have another selectable color, default medium gray? */\r
3741       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3742       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3743       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3744       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3745       SelectObject(hdc, blackPieceBrush);\r
3746       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3747 #else\r
3748       /* Use square color for details of black pieces */\r
3749       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3750       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3751       if(appData.upsideDown && !flipView)\r
3752         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3753       else\r
3754         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3755 #endif\r
3756     }\r
3757     SelectObject(hdc, oldBrush);\r
3758     SelectObject(tmphdc, oldBitmap);\r
3759   }\r
3760 }\r
3761 \r
3762 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3763 int GetBackTextureMode( int algo )\r
3764 {\r
3765     int result = BACK_TEXTURE_MODE_DISABLED;\r
3766 \r
3767     switch( algo ) \r
3768     {\r
3769         case BACK_TEXTURE_MODE_PLAIN:\r
3770             result = 1; /* Always use identity map */\r
3771             break;\r
3772         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3773             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3774             break;\r
3775     }\r
3776 \r
3777     return result;\r
3778 }\r
3779 \r
3780 /* \r
3781     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3782     to handle redraws cleanly (as random numbers would always be different).\r
3783 */\r
3784 VOID RebuildTextureSquareInfo()\r
3785 {\r
3786     BITMAP bi;\r
3787     int lite_w = 0;\r
3788     int lite_h = 0;\r
3789     int dark_w = 0;\r
3790     int dark_h = 0;\r
3791     int row;\r
3792     int col;\r
3793 \r
3794     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3795 \r
3796     if( liteBackTexture != NULL ) {\r
3797         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3798             lite_w = bi.bmWidth;\r
3799             lite_h = bi.bmHeight;\r
3800         }\r
3801     }\r
3802 \r
3803     if( darkBackTexture != NULL ) {\r
3804         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3805             dark_w = bi.bmWidth;\r
3806             dark_h = bi.bmHeight;\r
3807         }\r
3808     }\r
3809 \r
3810     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3811         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3812             if( (col + row) & 1 ) {\r
3813                 /* Lite square */\r
3814                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3815                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3816                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3817                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3818                 }\r
3819             }\r
3820             else {\r
3821                 /* Dark square */\r
3822                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3823                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3824                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3825                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3826                 }\r
3827             }\r
3828         }\r
3829     }\r
3830 }\r
3831 \r
3832 /* [AS] Arrow highlighting support */\r
3833 \r
3834 static int A_WIDTH = 5; /* Width of arrow body */\r
3835 \r
3836 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3837 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3838 \r
3839 static double Sqr( double x )\r
3840 {\r
3841     return x*x;\r
3842 }\r
3843 \r
3844 static int Round( double x )\r
3845 {\r
3846     return (int) (x + 0.5);\r
3847 }\r
3848 \r
3849 /* Draw an arrow between two points using current settings */\r
3850 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3851 {\r
3852     POINT arrow[7];\r
3853     double dx, dy, j, k, x, y;\r
3854 \r
3855     if( d_x == s_x ) {\r
3856         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3857 \r
3858         arrow[0].x = s_x + A_WIDTH;\r
3859         arrow[0].y = s_y;\r
3860 \r
3861         arrow[1].x = s_x + A_WIDTH;\r
3862         arrow[1].y = d_y - h;\r
3863 \r
3864         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3865         arrow[2].y = d_y - h;\r
3866 \r
3867         arrow[3].x = d_x;\r
3868         arrow[3].y = d_y;\r
3869 \r
3870         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3871         arrow[4].y = d_y - h;\r
3872 \r
3873         arrow[5].x = s_x - A_WIDTH;\r
3874         arrow[5].y = d_y - h;\r
3875 \r
3876         arrow[6].x = s_x - A_WIDTH;\r
3877         arrow[6].y = s_y;\r
3878     }\r
3879     else if( d_y == s_y ) {\r
3880         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3881 \r
3882         arrow[0].x = s_x;\r
3883         arrow[0].y = s_y + A_WIDTH;\r
3884 \r
3885         arrow[1].x = d_x - w;\r
3886         arrow[1].y = s_y + A_WIDTH;\r
3887 \r
3888         arrow[2].x = d_x - w;\r
3889         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3890 \r
3891         arrow[3].x = d_x;\r
3892         arrow[3].y = d_y;\r
3893 \r
3894         arrow[4].x = d_x - w;\r
3895         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3896 \r
3897         arrow[5].x = d_x - w;\r
3898         arrow[5].y = s_y - A_WIDTH;\r
3899 \r
3900         arrow[6].x = s_x;\r
3901         arrow[6].y = s_y - A_WIDTH;\r
3902     }\r
3903     else {\r
3904         /* [AS] Needed a lot of paper for this! :-) */\r
3905         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3906         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3907   \r
3908         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3909 \r
3910         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3911 \r
3912         x = s_x;\r
3913         y = s_y;\r
3914 \r
3915         arrow[0].x = Round(x - j);\r
3916         arrow[0].y = Round(y + j*dx);\r
3917 \r
3918         arrow[1].x = Round(x + j);\r
3919         arrow[1].y = Round(y - j*dx);\r
3920 \r
3921         if( d_x > s_x ) {\r
3922             x = (double) d_x - k;\r
3923             y = (double) d_y - k*dy;\r
3924         }\r
3925         else {\r
3926             x = (double) d_x + k;\r
3927             y = (double) d_y + k*dy;\r
3928         }\r
3929 \r
3930         arrow[2].x = Round(x + j);\r
3931         arrow[2].y = Round(y - j*dx);\r
3932 \r
3933         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3934         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3935 \r
3936         arrow[4].x = d_x;\r
3937         arrow[4].y = d_y;\r
3938 \r
3939         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3940         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3941 \r
3942         arrow[6].x = Round(x - j);\r
3943         arrow[6].y = Round(y + j*dx);\r
3944     }\r
3945 \r
3946     Polygon( hdc, arrow, 7 );\r
3947 }\r
3948 \r
3949 /* [AS] Draw an arrow between two squares */\r
3950 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3951 {\r
3952     int s_x, s_y, d_x, d_y;\r
3953     HPEN hpen;\r
3954     HPEN holdpen;\r
3955     HBRUSH hbrush;\r
3956     HBRUSH holdbrush;\r
3957     LOGBRUSH stLB;\r
3958 \r
3959     if( s_col == d_col && s_row == d_row ) {\r
3960         return;\r
3961     }\r
3962 \r
3963     /* Get source and destination points */\r
3964     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3965     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3966 \r
3967     if( d_y > s_y ) {\r
3968         d_y += squareSize / 4;\r
3969     }\r
3970     else if( d_y < s_y ) {\r
3971         d_y += 3 * squareSize / 4;\r
3972     }\r
3973     else {\r
3974         d_y += squareSize / 2;\r
3975     }\r
3976 \r
3977     if( d_x > s_x ) {\r
3978         d_x += squareSize / 4;\r
3979     }\r
3980     else if( d_x < s_x ) {\r
3981         d_x += 3 * squareSize / 4;\r
3982     }\r
3983     else {\r
3984         d_x += squareSize / 2;\r
3985     }\r
3986 \r
3987     s_x += squareSize / 2;\r
3988     s_y += squareSize / 2;\r
3989 \r
3990     /* Adjust width */\r
3991     A_WIDTH = squareSize / 14;\r
3992 \r
3993     /* Draw */\r
3994     stLB.lbStyle = BS_SOLID;\r
3995     stLB.lbColor = appData.highlightArrowColor;\r
3996     stLB.lbHatch = 0;\r
3997 \r
3998     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3999     holdpen = SelectObject( hdc, hpen );\r
4000     hbrush = CreateBrushIndirect( &stLB );\r
4001     holdbrush = SelectObject( hdc, hbrush );\r
4002 \r
4003     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4004 \r
4005     SelectObject( hdc, holdpen );\r
4006     SelectObject( hdc, holdbrush );\r
4007     DeleteObject( hpen );\r
4008     DeleteObject( hbrush );\r
4009 }\r
4010 \r
4011 BOOL HasHighlightInfo()\r
4012 {\r
4013     BOOL result = FALSE;\r
4014 \r
4015     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4016         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4017     {\r
4018         result = TRUE;\r
4019     }\r
4020 \r
4021     return result;\r
4022 }\r
4023 \r
4024 BOOL IsDrawArrowEnabled()\r
4025 {\r
4026     BOOL result = FALSE;\r
4027 \r
4028     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4029         result = TRUE;\r
4030     }\r
4031 \r
4032     return result;\r
4033 }\r
4034 \r
4035 VOID DrawArrowHighlight( HDC hdc )\r
4036 {\r
4037     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4038         DrawArrowBetweenSquares( hdc,\r
4039             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4040             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4041     }\r
4042 }\r
4043 \r
4044 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4045 {\r
4046     HRGN result = NULL;\r
4047 \r
4048     if( HasHighlightInfo() ) {\r
4049         int x1, y1, x2, y2;\r
4050         int sx, sy, dx, dy;\r
4051 \r
4052         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4053         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4054 \r
4055         sx = MIN( x1, x2 );\r
4056         sy = MIN( y1, y2 );\r
4057         dx = MAX( x1, x2 ) + squareSize;\r
4058         dy = MAX( y1, y2 ) + squareSize;\r
4059 \r
4060         result = CreateRectRgn( sx, sy, dx, dy );\r
4061     }\r
4062 \r
4063     return result;\r
4064 }\r
4065 \r
4066 /*\r
4067     Warning: this function modifies the behavior of several other functions. \r
4068     \r
4069     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4070     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4071     repaint is scattered all over the place, which is not good for features such as\r
4072     "arrow highlighting" that require a full repaint of the board.\r
4073 \r
4074     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4075     user interaction, when speed is not so important) but especially to avoid errors\r
4076     in the displayed graphics.\r
4077 \r
4078     In such patched places, I always try refer to this function so there is a single\r
4079     place to maintain knowledge.\r
4080     \r
4081     To restore the original behavior, just return FALSE unconditionally.\r
4082 */\r
4083 BOOL IsFullRepaintPreferrable()\r
4084 {\r
4085     BOOL result = FALSE;\r
4086 \r
4087     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4088         /* Arrow may appear on the board */\r
4089         result = TRUE;\r
4090     }\r
4091 \r
4092     return result;\r
4093 }\r
4094 \r
4095 /* \r
4096     This function is called by DrawPosition to know whether a full repaint must\r
4097     be forced or not.\r
4098 \r
4099     Only DrawPosition may directly call this function, which makes use of \r
4100     some state information. Other function should call DrawPosition specifying \r
4101     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4102 */\r
4103 BOOL DrawPositionNeedsFullRepaint()\r
4104 {\r
4105     BOOL result = FALSE;\r
4106 \r
4107     /* \r
4108         Probably a slightly better policy would be to trigger a full repaint\r
4109         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4110         but animation is fast enough that it's difficult to notice.\r
4111     */\r
4112     if( animInfo.piece == EmptySquare ) {\r
4113         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4114             result = TRUE;\r
4115         }\r
4116     }\r
4117 \r
4118     return result;\r
4119 }\r
4120 \r
4121 VOID\r
4122 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4123 {\r
4124   int row, column, x, y, square_color, piece_color;\r
4125   ChessSquare piece;\r
4126   HBRUSH oldBrush;\r
4127   HDC texture_hdc = NULL;\r
4128 \r
4129   /* [AS] Initialize background textures if needed */\r
4130   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4131       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4132       if( backTextureSquareSize != squareSize \r
4133        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4134           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4135           backTextureSquareSize = squareSize;\r
4136           RebuildTextureSquareInfo();\r
4137       }\r
4138 \r
4139       texture_hdc = CreateCompatibleDC( hdc );\r
4140   }\r
4141 \r
4142   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4143     for (column = 0; column < BOARD_WIDTH; column++) {\r
4144   \r
4145       SquareToPos(row, column, &x, &y);\r
4146 \r
4147       piece = board[row][column];\r
4148 \r
4149       square_color = ((column + row) % 2) == 1;\r
4150       if( gameInfo.variant == VariantXiangqi ) {\r
4151           square_color = !InPalace(row, column);\r
4152           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4153           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4154       }\r
4155       piece_color = (int) piece < (int) BlackPawn;\r
4156 \r
4157 \r
4158       /* [HGM] holdings file: light square or black */\r
4159       if(column == BOARD_LEFT-2) {\r
4160             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4161                 square_color = 1;\r
4162             else {\r
4163                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4164                 continue;\r
4165             }\r
4166       } else\r
4167       if(column == BOARD_RGHT + 1 ) {\r
4168             if( row < gameInfo.holdingsSize )\r
4169                 square_color = 1;\r
4170             else {\r
4171                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4172                 continue;\r
4173             }\r
4174       }\r
4175       if(column == BOARD_LEFT-1 ) /* left align */\r
4176             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4177       else if( column == BOARD_RGHT) /* right align */\r
4178             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4179       else\r
4180       if (appData.monoMode) {\r
4181         if (piece == EmptySquare) {\r
4182           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4183                  square_color ? WHITENESS : BLACKNESS);\r
4184         } else {\r
4185           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4186         }\r
4187       } \r
4188       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4189           /* [AS] Draw the square using a texture bitmap */\r
4190           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4191           int r = row, c = column; // [HGM] do not flip board in flipView\r
4192           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4193 \r
4194           DrawTile( x, y, \r
4195               squareSize, squareSize, \r
4196               hdc, \r
4197               texture_hdc,\r
4198               backTextureSquareInfo[r][c].mode,\r
4199               backTextureSquareInfo[r][c].x,\r
4200               backTextureSquareInfo[r][c].y );\r
4201 \r
4202           SelectObject( texture_hdc, hbm );\r
4203 \r
4204           if (piece != EmptySquare) {\r
4205               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4206           }\r
4207       }\r
4208       else {\r
4209         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4210 \r
4211         oldBrush = SelectObject(hdc, brush );\r
4212         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4213         SelectObject(hdc, oldBrush);\r
4214         if (piece != EmptySquare)\r
4215           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4216       }\r
4217     }\r
4218   }\r
4219 \r
4220   if( texture_hdc != NULL ) {\r
4221     DeleteDC( texture_hdc );\r
4222   }\r
4223 }\r
4224 \r
4225 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4226 void fputDW(FILE *f, int x)\r
4227 {\r
4228         fputc(x     & 255, f);\r
4229         fputc(x>>8  & 255, f);\r
4230         fputc(x>>16 & 255, f);\r
4231         fputc(x>>24 & 255, f);\r
4232 }\r
4233 \r
4234 #define MAX_CLIPS 200   /* more than enough */\r
4235 \r
4236 VOID\r
4237 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4238 {\r
4239 //  HBITMAP bufferBitmap;\r
4240   BITMAP bi;\r
4241 //  RECT Rect;\r
4242   HDC tmphdc;\r
4243   HBITMAP hbm;\r
4244   int w = 100, h = 50;\r
4245 \r
4246   if(cps->programLogo == NULL) return;\r
4247 //  GetClientRect(hwndMain, &Rect);\r
4248 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4249 //                                      Rect.bottom-Rect.top+1);\r
4250   tmphdc = CreateCompatibleDC(hdc);\r
4251   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4252   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4253             w = bi.bmWidth;\r
4254             h = bi.bmHeight;\r
4255   }\r
4256   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4257                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4258   SelectObject(tmphdc, hbm);\r
4259   DeleteDC(tmphdc);\r
4260 }\r
4261 \r
4262 VOID\r
4263 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4264 {\r
4265   static Board lastReq, lastDrawn;\r
4266   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4267   static int lastDrawnFlipView = 0;\r
4268   static int lastReqValid = 0, lastDrawnValid = 0;\r
4269   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4270   HDC tmphdc;\r
4271   HDC hdcmem;\r
4272   HBITMAP bufferBitmap;\r
4273   HBITMAP oldBitmap;\r
4274   RECT Rect;\r
4275   HRGN clips[MAX_CLIPS];\r
4276   ChessSquare dragged_piece = EmptySquare;\r
4277 \r
4278   /* I'm undecided on this - this function figures out whether a full\r
4279    * repaint is necessary on its own, so there's no real reason to have the\r
4280    * caller tell it that.  I think this can safely be set to FALSE - but\r
4281    * if we trust the callers not to request full repaints unnessesarily, then\r
4282    * we could skip some clipping work.  In other words, only request a full\r
4283    * redraw when the majority of pieces have changed positions (ie. flip, \r
4284    * gamestart and similar)  --Hawk\r
4285    */\r
4286   Boolean fullrepaint = repaint;\r
4287 \r
4288   if( DrawPositionNeedsFullRepaint() ) {\r
4289       fullrepaint = TRUE;\r
4290   }\r
4291 \r
4292 #if 0\r
4293   if( fullrepaint ) {\r
4294       static int repaint_count = 0;\r
4295       char buf[128];\r
4296 \r
4297       repaint_count++;\r
4298       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4299       OutputDebugString( buf );\r
4300   }\r
4301 #endif\r
4302 \r
4303   if (board == NULL) {\r
4304     if (!lastReqValid) {\r
4305       return;\r
4306     }\r
4307     board = lastReq;\r
4308   } else {\r
4309     CopyBoard(lastReq, board);\r
4310     lastReqValid = 1;\r
4311   }\r
4312 \r
4313   if (doingSizing) {\r
4314     return;\r
4315   }\r
4316 \r
4317   if (IsIconic(hwndMain)) {\r
4318     return;\r
4319   }\r
4320 \r
4321   if (hdc == NULL) {\r
4322     hdc = GetDC(hwndMain);\r
4323     if (!appData.monoMode) {\r
4324       SelectPalette(hdc, hPal, FALSE);\r
4325       RealizePalette(hdc);\r
4326     }\r
4327     releaseDC = TRUE;\r
4328   } else {\r
4329     releaseDC = FALSE;\r
4330   }\r
4331 \r
4332 #if 0\r
4333   fprintf(debugFP, "*******************************\n"\r
4334                    "repaint = %s\n"\r
4335                    "dragInfo.from (%d,%d)\n"\r
4336                    "dragInfo.start (%d,%d)\n"\r
4337                    "dragInfo.pos (%d,%d)\n"\r
4338                    "dragInfo.lastpos (%d,%d)\n", \r
4339                     repaint ? "TRUE" : "FALSE",\r
4340                     dragInfo.from.x, dragInfo.from.y, \r
4341                     dragInfo.start.x, dragInfo.start.y,\r
4342                     dragInfo.pos.x, dragInfo.pos.y,\r
4343                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4344   fprintf(debugFP, "prev:  ");\r
4345   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4346     for (column = 0; column < BOARD_WIDTH; column++) {\r
4347       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4348     }\r
4349   }\r
4350   fprintf(debugFP, "\n");\r
4351   fprintf(debugFP, "board: ");\r
4352   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4353     for (column = 0; column < BOARD_WIDTH; column++) {\r
4354       fprintf(debugFP, "%d ", board[row][column]);\r
4355     }\r
4356   }\r
4357   fprintf(debugFP, "\n");\r
4358   fflush(debugFP);\r
4359 #endif\r
4360 \r
4361   /* Create some work-DCs */\r
4362   hdcmem = CreateCompatibleDC(hdc);\r
4363   tmphdc = CreateCompatibleDC(hdc);\r
4364 \r
4365   /* If dragging is in progress, we temporarely remove the piece */\r
4366   /* [HGM] or temporarily decrease count if stacked              */\r
4367   /*       !! Moved to before board compare !!                   */\r
4368   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4369     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4370     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4371             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4372         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4373     } else \r
4374     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4375             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4376         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4377     } else \r
4378         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4379   }\r
4380 \r
4381   /* Figure out which squares need updating by comparing the \r
4382    * newest board with the last drawn board and checking if\r
4383    * flipping has changed.\r
4384    */\r
4385   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4386     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4387       for (column = 0; column < BOARD_WIDTH; column++) {\r
4388         if (lastDrawn[row][column] != board[row][column]) {\r
4389           SquareToPos(row, column, &x, &y);\r
4390           clips[num_clips++] =\r
4391             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4392         }\r
4393       }\r
4394     }\r
4395     for (i=0; i<2; i++) {\r
4396       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4397           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4398         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4399             lastDrawnHighlight.sq[i].y >= 0) {\r
4400           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4401                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4402           clips[num_clips++] =\r
4403             CreateRectRgn(x - lineGap, y - lineGap, \r
4404                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4405         }\r
4406         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4407           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4408           clips[num_clips++] =\r
4409             CreateRectRgn(x - lineGap, y - lineGap, \r
4410                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4411         }\r
4412       }\r
4413     }\r
4414     for (i=0; i<2; i++) {\r
4415       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4416           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4417         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4418             lastDrawnPremove.sq[i].y >= 0) {\r
4419           SquareToPos(lastDrawnPremove.sq[i].y,\r
4420                       lastDrawnPremove.sq[i].x, &x, &y);\r
4421           clips[num_clips++] =\r
4422             CreateRectRgn(x - lineGap, y - lineGap, \r
4423                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4424         }\r
4425         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4426             premoveHighlightInfo.sq[i].y >= 0) {\r
4427           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4428                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4429           clips[num_clips++] =\r
4430             CreateRectRgn(x - lineGap, y - lineGap, \r
4431                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4432         }\r
4433       }\r
4434     }\r
4435   } else {\r
4436     fullrepaint = TRUE;\r
4437   }\r
4438 \r
4439   /* Create a buffer bitmap - this is the actual bitmap\r
4440    * being written to.  When all the work is done, we can\r
4441    * copy it to the real DC (the screen).  This avoids\r
4442    * the problems with flickering.\r
4443    */\r
4444   GetClientRect(hwndMain, &Rect);\r
4445   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4446                                         Rect.bottom-Rect.top+1);\r
4447   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4448   if (!appData.monoMode) {\r
4449     SelectPalette(hdcmem, hPal, FALSE);\r
4450   }\r
4451 \r
4452   /* Create clips for dragging */\r
4453   if (!fullrepaint) {\r
4454     if (dragInfo.from.x >= 0) {\r
4455       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4456       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4457     }\r
4458     if (dragInfo.start.x >= 0) {\r
4459       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4460       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4461     }\r
4462     if (dragInfo.pos.x >= 0) {\r
4463       x = dragInfo.pos.x - squareSize / 2;\r
4464       y = dragInfo.pos.y - squareSize / 2;\r
4465       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4466     }\r
4467     if (dragInfo.lastpos.x >= 0) {\r
4468       x = dragInfo.lastpos.x - squareSize / 2;\r
4469       y = dragInfo.lastpos.y - squareSize / 2;\r
4470       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4471     }\r
4472   }\r
4473 \r
4474   /* Are we animating a move?  \r
4475    * If so, \r
4476    *   - remove the piece from the board (temporarely)\r
4477    *   - calculate the clipping region\r
4478    */\r
4479   if (!fullrepaint) {\r
4480     if (animInfo.piece != EmptySquare) {\r
4481       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4482       x = boardRect.left + animInfo.lastpos.x;\r
4483       y = boardRect.top + animInfo.lastpos.y;\r
4484       x2 = boardRect.left + animInfo.pos.x;\r
4485       y2 = boardRect.top + animInfo.pos.y;\r
4486       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4487       /* Slight kludge.  The real problem is that after AnimateMove is\r
4488          done, the position on the screen does not match lastDrawn.\r
4489          This currently causes trouble only on e.p. captures in\r
4490          atomic, where the piece moves to an empty square and then\r
4491          explodes.  The old and new positions both had an empty square\r
4492          at the destination, but animation has drawn a piece there and\r
4493          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4494       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4495     }\r
4496   }\r
4497 \r
4498   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4499   if (num_clips == 0)\r
4500     fullrepaint = TRUE;\r
4501 \r
4502   /* Set clipping on the memory DC */\r
4503   if (!fullrepaint) {\r
4504     SelectClipRgn(hdcmem, clips[0]);\r
4505     for (x = 1; x < num_clips; x++) {\r
4506       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4507         abort();  // this should never ever happen!\r
4508     }\r
4509   }\r
4510 \r
4511   /* Do all the drawing to the memory DC */\r
4512   if(explodeInfo.radius) { // [HGM] atomic\r
4513         HBRUSH oldBrush;\r
4514         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4515         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4516         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4517         x += squareSize/2;\r
4518         y += squareSize/2;\r
4519         if(!fullrepaint) {\r
4520           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4521           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4522         }\r
4523         DrawGridOnDC(hdcmem);\r
4524         DrawHighlightsOnDC(hdcmem);\r
4525         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4526         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4527         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4528         SelectObject(hdcmem, oldBrush);\r
4529   } else {\r
4530     DrawGridOnDC(hdcmem);\r
4531     DrawHighlightsOnDC(hdcmem);\r
4532     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4533   }\r
4534   if(logoHeight) {\r
4535         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4536         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4537   }\r
4538 \r
4539   if( appData.highlightMoveWithArrow ) {\r
4540     DrawArrowHighlight(hdcmem);\r
4541   }\r
4542 \r
4543   DrawCoordsOnDC(hdcmem);\r
4544 \r
4545   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4546                  /* to make sure lastDrawn contains what is actually drawn */\r
4547 \r
4548   /* Put the dragged piece back into place and draw it (out of place!) */\r
4549     if (dragged_piece != EmptySquare) {\r
4550     /* [HGM] or restack */\r
4551     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4552                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4553     else\r
4554     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4555                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4556     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4557     x = dragInfo.pos.x - squareSize / 2;\r
4558     y = dragInfo.pos.y - squareSize / 2;\r
4559     DrawPieceOnDC(hdcmem, dragged_piece,\r
4560                   ((int) dragged_piece < (int) BlackPawn), \r
4561                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4562   }   \r
4563   \r
4564   /* Put the animated piece back into place and draw it */\r
4565   if (animInfo.piece != EmptySquare) {\r
4566     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4567     x = boardRect.left + animInfo.pos.x;\r
4568     y = boardRect.top + animInfo.pos.y;\r
4569     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4570                   ((int) animInfo.piece < (int) BlackPawn),\r
4571                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4572   }\r
4573 \r
4574   /* Release the bufferBitmap by selecting in the old bitmap \r
4575    * and delete the memory DC\r
4576    */\r
4577   SelectObject(hdcmem, oldBitmap);\r
4578   DeleteDC(hdcmem);\r
4579 \r
4580   /* Set clipping on the target DC */\r
4581   if (!fullrepaint) {\r
4582     SelectClipRgn(hdc, clips[0]);\r
4583     for (x = 1; x < num_clips; x++) {\r
4584       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4585         abort();   // this should never ever happen!\r
4586     } \r
4587   }\r
4588 \r
4589   /* Copy the new bitmap onto the screen in one go.\r
4590    * This way we avoid any flickering\r
4591    */\r
4592   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4593   BitBlt(hdc, boardRect.left, boardRect.top,\r
4594          boardRect.right - boardRect.left,\r
4595          boardRect.bottom - boardRect.top,\r
4596          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4597   if(saveDiagFlag) { \r
4598     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4599     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4600 \r
4601     GetObject(bufferBitmap, sizeof(b), &b);\r
4602     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4603         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4604         bih.biWidth = b.bmWidth;\r
4605         bih.biHeight = b.bmHeight;\r
4606         bih.biPlanes = 1;\r
4607         bih.biBitCount = b.bmBitsPixel;\r
4608         bih.biCompression = 0;\r
4609         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4610         bih.biXPelsPerMeter = 0;\r
4611         bih.biYPelsPerMeter = 0;\r
4612         bih.biClrUsed = 0;\r
4613         bih.biClrImportant = 0;\r
4614 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4615 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4616         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4617 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4618 \r
4619 #if 1\r
4620         wb = b.bmWidthBytes;\r
4621         // count colors\r
4622         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4623                 int k = ((int*) pData)[i];\r
4624                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4625                 if(j >= 16) break;\r
4626                 color[j] = k;\r
4627                 if(j >= nrColors) nrColors = j+1;\r
4628         }\r
4629         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4630                 INT p = 0;\r
4631                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4632                     for(w=0; w<(wb>>2); w+=2) {\r
4633                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4634                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4635                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4636                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4637                         pData[p++] = m | j<<4;\r
4638                     }\r
4639                     while(p&3) pData[p++] = 0;\r
4640                 }\r
4641                 fac = 3;\r
4642                 wb = ((wb+31)>>5)<<2;\r
4643         }\r
4644         // write BITMAPFILEHEADER\r
4645         fprintf(diagFile, "BM");\r
4646         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4647         fputDW(diagFile, 0);\r
4648         fputDW(diagFile, 0x36 + (fac?64:0));\r
4649         // write BITMAPINFOHEADER\r
4650         fputDW(diagFile, 40);\r
4651         fputDW(diagFile, b.bmWidth);\r
4652         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4653         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4654         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4655         fputDW(diagFile, 0);\r
4656         fputDW(diagFile, 0);\r
4657         fputDW(diagFile, 0);\r
4658         fputDW(diagFile, 0);\r
4659         fputDW(diagFile, 0);\r
4660         fputDW(diagFile, 0);\r
4661         // write color table\r
4662         if(fac)\r
4663         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4664         // write bitmap data\r
4665         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4666                 fputc(pData[i], diagFile);\r
4667 #endif\r
4668      }\r
4669   }\r
4670 \r
4671   SelectObject(tmphdc, oldBitmap);\r
4672 \r
4673   /* Massive cleanup */\r
4674   for (x = 0; x < num_clips; x++)\r
4675     DeleteObject(clips[x]);\r
4676 \r
4677   DeleteDC(tmphdc);\r
4678   DeleteObject(bufferBitmap);\r
4679 \r
4680   if (releaseDC) \r
4681     ReleaseDC(hwndMain, hdc);\r
4682   \r
4683   if (lastDrawnFlipView != flipView) {\r
4684     if (flipView)\r
4685       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4686     else\r
4687       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4688   }\r
4689 \r
4690 /*  CopyBoard(lastDrawn, board);*/\r
4691   lastDrawnHighlight = highlightInfo;\r
4692   lastDrawnPremove   = premoveHighlightInfo;\r
4693   lastDrawnFlipView = flipView;\r
4694   lastDrawnValid = 1;\r
4695 }\r
4696 \r
4697 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4698 int\r
4699 SaveDiagram(f)\r
4700      FILE *f;\r
4701 {\r
4702     saveDiagFlag = 1; diagFile = f;\r
4703     HDCDrawPosition(NULL, TRUE, NULL);\r
4704 \r
4705     saveDiagFlag = 0;\r
4706 \r
4707 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4708     \r
4709     fclose(f);\r
4710     return TRUE;\r
4711 }\r
4712 \r
4713 \r
4714 /*---------------------------------------------------------------------------*\\r
4715 | CLIENT PAINT PROCEDURE\r
4716 |   This is the main event-handler for the WM_PAINT message.\r
4717 |\r
4718 \*---------------------------------------------------------------------------*/\r
4719 VOID\r
4720 PaintProc(HWND hwnd)\r
4721 {\r
4722   HDC         hdc;\r
4723   PAINTSTRUCT ps;\r
4724   HFONT       oldFont;\r
4725 \r
4726   if((hdc = BeginPaint(hwnd, &ps))) {\r
4727     if (IsIconic(hwnd)) {\r
4728       DrawIcon(hdc, 2, 2, iconCurrent);\r
4729     } else {\r
4730       if (!appData.monoMode) {\r
4731         SelectPalette(hdc, hPal, FALSE);\r
4732         RealizePalette(hdc);\r
4733       }\r
4734       HDCDrawPosition(hdc, 1, NULL);\r
4735       oldFont =\r
4736         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4737       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4738                  ETO_CLIPPED|ETO_OPAQUE,\r
4739                  &messageRect, messageText, strlen(messageText), NULL);\r
4740       SelectObject(hdc, oldFont);\r
4741       DisplayBothClocks();\r
4742     }\r
4743     EndPaint(hwnd,&ps);\r
4744   }\r
4745 \r
4746   return;\r
4747 }\r
4748 \r
4749 \r
4750 /*\r
4751  * If the user selects on a border boundary, return -1; if off the board,\r
4752  *   return -2.  Otherwise map the event coordinate to the square.\r
4753  * The offset boardRect.left or boardRect.top must already have been\r
4754  *   subtracted from x.\r
4755  */\r
4756 int\r
4757 EventToSquare(int x)\r
4758 {\r
4759   if (x <= 0)\r
4760     return -2;\r
4761   if (x < lineGap)\r
4762     return -1;\r
4763   x -= lineGap;\r
4764   if ((x % (squareSize + lineGap)) >= squareSize)\r
4765     return -1;\r
4766   x /= (squareSize + lineGap);\r
4767   if (x >= BOARD_SIZE)\r
4768     return -2;\r
4769   return x;\r
4770 }\r
4771 \r
4772 typedef struct {\r
4773   char piece;\r
4774   int command;\r
4775   char* name;\r
4776 } DropEnable;\r
4777 \r
4778 DropEnable dropEnables[] = {\r
4779   { 'P', DP_Pawn, "Pawn" },\r
4780   { 'N', DP_Knight, "Knight" },\r
4781   { 'B', DP_Bishop, "Bishop" },\r
4782   { 'R', DP_Rook, "Rook" },\r
4783   { 'Q', DP_Queen, "Queen" },\r
4784 };\r
4785 \r
4786 VOID\r
4787 SetupDropMenu(HMENU hmenu)\r
4788 {\r
4789   int i, count, enable;\r
4790   char *p;\r
4791   extern char white_holding[], black_holding[];\r
4792   char item[MSG_SIZ];\r
4793 \r
4794   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4795     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4796                dropEnables[i].piece);\r
4797     count = 0;\r
4798     while (p && *p++ == dropEnables[i].piece) count++;\r
4799     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4800     enable = count > 0 || !appData.testLegality\r
4801       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4802                       && !appData.icsActive);\r
4803     ModifyMenu(hmenu, dropEnables[i].command,\r
4804                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4805                dropEnables[i].command, item);\r
4806   }\r
4807 }\r
4808 \r
4809 static int fromX = -1, fromY = -1, toX, toY;\r
4810 \r
4811 /* Event handler for mouse messages */\r
4812 VOID\r
4813 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4814 {\r
4815   int x, y;\r
4816   POINT pt;\r
4817   static int recursive = 0;\r
4818   HMENU hmenu;\r
4819 //  BOOLEAN needsRedraw = FALSE;\r
4820   BOOLEAN saveAnimate;\r
4821   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4822   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4823   ChessMove moveType;\r
4824 \r
4825   if (recursive) {\r
4826     if (message == WM_MBUTTONUP) {\r
4827       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4828          to the middle button: we simulate pressing the left button too!\r
4829          */\r
4830       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4831       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4832     }\r
4833     return;\r
4834   }\r
4835   recursive++;\r
4836   \r
4837   pt.x = LOWORD(lParam);\r
4838   pt.y = HIWORD(lParam);\r
4839   x = EventToSquare(pt.x - boardRect.left);\r
4840   y = EventToSquare(pt.y - boardRect.top);\r
4841   if (!flipView && y >= 0) {\r
4842     y = BOARD_HEIGHT - 1 - y;\r
4843   }\r
4844   if (flipView && x >= 0) {\r
4845     x = BOARD_WIDTH - 1 - x;\r
4846   }\r
4847 \r
4848   switch (message) {\r
4849   case WM_LBUTTONDOWN:\r
4850     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4851         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4852         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4853         if(gameInfo.holdingsWidth && \r
4854                 (WhiteOnMove(currentMove) \r
4855                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4856                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4857             // click in right holdings, for determining promotion piece\r
4858             ChessSquare p = boards[currentMove][y][x];\r
4859             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4860             if(p != EmptySquare) {\r
4861                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4862                 fromX = fromY = -1;\r
4863                 break;\r
4864             }\r
4865         }\r
4866         DrawPosition(FALSE, boards[currentMove]);\r
4867         break;\r
4868     }\r
4869     ErrorPopDown();\r
4870     sameAgain = FALSE;\r
4871     if (y == -2) {\r
4872       /* Downclick vertically off board; check if on clock */\r
4873       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4874         if (gameMode == EditPosition) {\r
4875           SetWhiteToPlayEvent();\r
4876         } else if (gameMode == IcsPlayingBlack ||\r
4877                    gameMode == MachinePlaysWhite) {\r
4878           CallFlagEvent();\r
4879         } else if (gameMode == EditGame) {\r
4880           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4881         }\r
4882       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4883         if (gameMode == EditPosition) {\r
4884           SetBlackToPlayEvent();\r
4885         } else if (gameMode == IcsPlayingWhite ||\r
4886                    gameMode == MachinePlaysBlack) {\r
4887           CallFlagEvent();\r
4888         } else if (gameMode == EditGame) {\r
4889           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4890         }\r
4891       }\r
4892       if (!appData.highlightLastMove) {\r
4893         ClearHighlights();\r
4894         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4895       }\r
4896       fromX = fromY = -1;\r
4897       dragInfo.start.x = dragInfo.start.y = -1;\r
4898       dragInfo.from = dragInfo.start;\r
4899       break;\r
4900     } else if (x < 0 || y < 0\r
4901       /* [HGM] block clicks between board and holdings */\r
4902               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4903               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4904               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4905         /* EditPosition, empty square, or different color piece;\r
4906            click-click move is possible */\r
4907                                ) {\r
4908       break;\r
4909     } else if (fromX == x && fromY == y) {\r
4910       /* Downclick on same square again */\r
4911       ClearHighlights();\r
4912       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4913       sameAgain = TRUE;  \r
4914     } else if (fromX != -1 &&\r
4915                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4916                                                                         ) {\r
4917       /* Downclick on different square. */\r
4918       /* [HGM] if on holdings file, should count as new first click ! */\r
4919       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4920         toX = x;\r
4921         toY = y;\r
4922         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4923            to make sure move is legal before showing promotion popup */\r
4924         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4925         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4926                 fromX = fromY = -1; \r
4927                 ClearHighlights();\r
4928                 DrawPosition(FALSE, boards[currentMove]);\r
4929                 break; \r
4930         } else \r
4931         if(moveType != ImpossibleMove) {\r
4932           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4933           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4934             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4935               appData.alwaysPromoteToQueen)) {\r
4936                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4937                   if (!appData.highlightLastMove) {\r
4938                       ClearHighlights();\r
4939                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4940                   }\r
4941           } else\r
4942           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4943                   SetHighlights(fromX, fromY, toX, toY);\r
4944                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4945                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4946                      If promotion to Q is legal, all are legal! */\r
4947                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4948                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4949                     // kludge to temporarily execute move on display, wthout promotng yet\r
4950                     promotionChoice = TRUE;\r
4951                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4952                     boards[currentMove][toY][toX] = p;\r
4953                     DrawPosition(FALSE, boards[currentMove]);\r
4954                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4955                     boards[currentMove][toY][toX] = q;\r
4956                   } else\r
4957                   PromotionPopup(hwnd);\r
4958           } else {       /* not a promotion */\r
4959              if (appData.animate || appData.highlightLastMove) {\r
4960                  SetHighlights(fromX, fromY, toX, toY);\r
4961              } else {\r
4962                  ClearHighlights();\r
4963              }\r
4964              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4965              fromX = fromY = -1;\r
4966              if (appData.animate && !appData.highlightLastMove) {\r
4967                   ClearHighlights();\r
4968                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4969              }\r
4970           }\r
4971           break;\r
4972         }\r
4973         if (gotPremove) {\r
4974             /* [HGM] it seemed that braces were missing here */\r
4975             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4976             fromX = fromY = -1;\r
4977             break;\r
4978         }\r
4979       }\r
4980       ClearHighlights();\r
4981       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4982     }\r
4983     /* First downclick, or restart on a square with same color piece */\r
4984     if (!frozen && OKToStartUserMove(x, y)) {\r
4985       fromX = x;\r
4986       fromY = y;\r
4987       dragInfo.lastpos = pt;\r
4988       dragInfo.from.x = fromX;\r
4989       dragInfo.from.y = fromY;\r
4990       dragInfo.start = dragInfo.from;\r
4991       SetCapture(hwndMain);\r
4992     } else {\r
4993       fromX = fromY = -1;\r
4994       dragInfo.start.x = dragInfo.start.y = -1;\r
4995       dragInfo.from = dragInfo.start;\r
4996       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4997     }\r
4998     break;\r
4999 \r
5000   case WM_LBUTTONUP:\r
5001     ReleaseCapture();\r
5002     if (fromX == -1) break;\r
5003     if (x == fromX && y == fromY) {\r
5004       dragInfo.from.x = dragInfo.from.y = -1;\r
5005       /* Upclick on same square */\r
5006       if (sameAgain) {\r
5007         /* Clicked same square twice: abort click-click move */\r
5008         fromX = fromY = -1;\r
5009         gotPremove = 0;\r
5010         ClearPremoveHighlights();\r
5011       } else {\r
5012         /* First square clicked: start click-click move */\r
5013         SetHighlights(fromX, fromY, -1, -1);\r
5014       }\r
5015       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5016     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5017       /* Errant click; ignore */\r
5018       break;\r
5019     } else {\r
5020       /* Finish drag move. */\r
5021     if (appData.debugMode) {\r
5022         fprintf(debugFP, "release\n");\r
5023     }\r
5024       dragInfo.from.x = dragInfo.from.y = -1;\r
5025       toX = x;\r
5026       toY = y;\r
5027       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5028       appData.animate = appData.animate && !appData.animateDragging;\r
5029       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5030       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5031                 fromX = fromY = -1; \r
5032                 ClearHighlights();\r
5033                 DrawPosition(FALSE, boards[currentMove]);\r
5034                 break; \r
5035       } else \r
5036       if(moveType != ImpossibleMove) {\r
5037           /* [HGM] use move type to determine if move is promotion.\r
5038              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5039           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5040             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5041               appData.alwaysPromoteToQueen)) \r
5042                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5043           else \r
5044           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5045                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5046                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5047                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5048                     // kludge to temporarily execute move on display, wthout promotng yet\r
5049                     promotionChoice = TRUE;\r
5050                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5051                     boards[currentMove][toY][toX] = p;\r
5052                     DrawPosition(FALSE, boards[currentMove]);\r
5053                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5054                     boards[currentMove][toY][toX] = q;\r
5055                     break;\r
5056                   } else\r
5057                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5058           } else {\r
5059             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5060                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5061                                         moveType == WhiteCapturesEnPassant || \r
5062                                         moveType == BlackCapturesEnPassant   ) )\r
5063                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5064             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5065           }\r
5066       }\r
5067       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5068       appData.animate = saveAnimate;\r
5069       fromX = fromY = -1;\r
5070       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5071         ClearHighlights();\r
5072       }\r
5073       if (appData.animate || appData.animateDragging ||\r
5074           appData.highlightDragging || gotPremove) {\r
5075         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5076       }\r
5077     }\r
5078     dragInfo.start.x = dragInfo.start.y = -1; \r
5079     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5080     break;\r
5081 \r
5082   case WM_MOUSEMOVE:\r
5083     if ((appData.animateDragging || appData.highlightDragging)\r
5084         && (wParam & MK_LBUTTON)\r
5085         && dragInfo.from.x >= 0) \r
5086     {\r
5087       BOOL full_repaint = FALSE;\r
5088 \r
5089       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5090       if (appData.animateDragging) {\r
5091         dragInfo.pos = pt;\r
5092       }\r
5093       if (appData.highlightDragging) {\r
5094         SetHighlights(fromX, fromY, x, y);\r
5095         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5096             full_repaint = TRUE;\r
5097         }\r
5098       }\r
5099       \r
5100       DrawPosition( full_repaint, NULL);\r
5101       \r
5102       dragInfo.lastpos = dragInfo.pos;\r
5103     }\r
5104     break;\r
5105 \r
5106   case WM_MOUSEWHEEL: // [DM]\r
5107     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5108        /* Mouse Wheel is being rolled forward\r
5109         * Play moves forward\r
5110         */\r
5111        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5112                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5113        /* Mouse Wheel is being rolled backward\r
5114         * Play moves backward\r
5115         */\r
5116        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5117                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5118     }\r
5119     break;\r
5120 \r
5121   case WM_MBUTTONDOWN:\r
5122   case WM_RBUTTONDOWN:\r
5123     ErrorPopDown();\r
5124     ReleaseCapture();\r
5125     fromX = fromY = -1;\r
5126     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5127     dragInfo.start.x = dragInfo.start.y = -1;\r
5128     dragInfo.from = dragInfo.start;\r
5129     dragInfo.lastpos = dragInfo.pos;\r
5130     if (appData.highlightDragging) {\r
5131       ClearHighlights();\r
5132     }\r
5133     if(y == -2) {\r
5134       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5135       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5136           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5137       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5138           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5139       }\r
5140     }\r
5141     DrawPosition(TRUE, NULL);\r
5142 \r
5143     switch (gameMode) {\r
5144     case EditPosition:\r
5145     case IcsExamining:\r
5146       if (x < 0 || y < 0) break;\r
5147       fromX = x;\r
5148       fromY = y;\r
5149       if (message == WM_MBUTTONDOWN) {\r
5150         buttonCount = 3;  /* even if system didn't think so */\r
5151         if (wParam & MK_SHIFT) \r
5152           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5153         else\r
5154           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5155       } else { /* message == WM_RBUTTONDOWN */\r
5156 #if 0\r
5157         if (buttonCount == 3) {\r
5158           if (wParam & MK_SHIFT) \r
5159             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5160           else\r
5161             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5162         } else {\r
5163           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5164         }\r
5165 #else\r
5166         /* Just have one menu, on the right button.  Windows users don't\r
5167            think to try the middle one, and sometimes other software steals\r
5168            it, or it doesn't really exist. */\r
5169         if(gameInfo.variant != VariantShogi)\r
5170             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5171         else\r
5172             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5173 #endif\r
5174       }\r
5175       break;\r
5176     case IcsPlayingWhite:\r
5177     case IcsPlayingBlack:\r
5178     case EditGame:\r
5179     case MachinePlaysWhite:\r
5180     case MachinePlaysBlack:\r
5181       if (appData.testLegality &&\r
5182           gameInfo.variant != VariantBughouse &&\r
5183           gameInfo.variant != VariantCrazyhouse) break;\r
5184       if (x < 0 || y < 0) break;\r
5185       fromX = x;\r
5186       fromY = y;\r
5187       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5188       SetupDropMenu(hmenu);\r
5189       MenuPopup(hwnd, pt, hmenu, -1);\r
5190       break;\r
5191     default:\r
5192       break;\r
5193     }\r
5194     break;\r
5195   }\r
5196 \r
5197   recursive--;\r
5198 }\r
5199 \r
5200 /* Preprocess messages for buttons in main window */\r
5201 LRESULT CALLBACK\r
5202 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5203 {\r
5204   int id = GetWindowLong(hwnd, GWL_ID);\r
5205   int i, dir;\r
5206 \r
5207   for (i=0; i<N_BUTTONS; i++) {\r
5208     if (buttonDesc[i].id == id) break;\r
5209   }\r
5210   if (i == N_BUTTONS) return 0;\r
5211   switch (message) {\r
5212   case WM_KEYDOWN:\r
5213     switch (wParam) {\r
5214     case VK_LEFT:\r
5215     case VK_RIGHT:\r
5216       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5217       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5218       return TRUE;\r
5219     }\r
5220     break;\r
5221   case WM_CHAR:\r
5222     switch (wParam) {\r
5223     case '\r':\r
5224       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5225       return TRUE;\r
5226     case '\t':\r
5227       if (appData.icsActive) {\r
5228         if (GetKeyState(VK_SHIFT) < 0) {\r
5229           /* shifted */\r
5230           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5231           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5232           SetFocus(h);\r
5233         } else {\r
5234           /* unshifted */\r
5235           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5236           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5237           SetFocus(h);\r
5238         }\r
5239         return TRUE;\r
5240       }\r
5241       break;\r
5242     default:\r
5243       if (appData.icsActive) {\r
5244         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5245         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5246         SetFocus(h);\r
5247         SendMessage(h, WM_CHAR, wParam, lParam);\r
5248         return TRUE;\r
5249       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5250         PopUpMoveDialog((char)wParam);\r
5251       }\r
5252       break;\r
5253     }\r
5254     break;\r
5255   }\r
5256   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5257 }\r
5258 \r
5259 /* Process messages for Promotion dialog box */\r
5260 LRESULT CALLBACK\r
5261 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5262 {\r
5263   char promoChar;\r
5264 \r
5265   switch (message) {\r
5266   case WM_INITDIALOG: /* message: initialize dialog box */\r
5267     /* Center the dialog over the application window */\r
5268     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5269     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5270       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5271        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5272                SW_SHOW : SW_HIDE);\r
5273     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5274     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5275        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5276          PieceToChar(WhiteAngel) != '~') ||\r
5277         (PieceToChar(BlackAngel) >= 'A' &&\r
5278          PieceToChar(BlackAngel) != '~')   ) ?\r
5279                SW_SHOW : SW_HIDE);\r
5280     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5281        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5282          PieceToChar(WhiteMarshall) != '~') ||\r
5283         (PieceToChar(BlackMarshall) >= 'A' &&\r
5284          PieceToChar(BlackMarshall) != '~')   ) ?\r
5285                SW_SHOW : SW_HIDE);\r
5286     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5287     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5288        gameInfo.variant != VariantShogi ?\r
5289                SW_SHOW : SW_HIDE);\r
5290     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5291        gameInfo.variant != VariantShogi ?\r
5292                SW_SHOW : SW_HIDE);\r
5293     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5294        gameInfo.variant == VariantShogi ?\r
5295                SW_SHOW : SW_HIDE);\r
5296     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5297        gameInfo.variant == VariantShogi ?\r
5298                SW_SHOW : SW_HIDE);\r
5299     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5300        gameInfo.variant == VariantSuper ?\r
5301                SW_SHOW : SW_HIDE);\r
5302     return TRUE;\r
5303 \r
5304   case WM_COMMAND: /* message: received a command */\r
5305     switch (LOWORD(wParam)) {\r
5306     case IDCANCEL:\r
5307       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5308       ClearHighlights();\r
5309       DrawPosition(FALSE, NULL);\r
5310       return TRUE;\r
5311     case PB_King:\r
5312       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5313       break;\r
5314     case PB_Queen:\r
5315       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5316       break;\r
5317     case PB_Rook:\r
5318       promoChar = PieceToChar(BlackRook);\r
5319       break;\r
5320     case PB_Bishop:\r
5321       promoChar = PieceToChar(BlackBishop);\r
5322       break;\r
5323     case PB_Chancellor:\r
5324       promoChar = PieceToChar(BlackMarshall);\r
5325       break;\r
5326     case PB_Archbishop:\r
5327       promoChar = PieceToChar(BlackAngel);\r
5328       break;\r
5329     case PB_Knight:\r
5330       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5331       break;\r
5332     default:\r
5333       return FALSE;\r
5334     }\r
5335     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5336     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5337        only show the popup when we are already sure the move is valid or\r
5338        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5339        will figure out it is a promotion from the promoChar. */\r
5340     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5341     if (!appData.highlightLastMove) {\r
5342       ClearHighlights();\r
5343       DrawPosition(FALSE, NULL);\r
5344     }\r
5345     return TRUE;\r
5346   }\r
5347   return FALSE;\r
5348 }\r
5349 \r
5350 /* Pop up promotion dialog */\r
5351 VOID\r
5352 PromotionPopup(HWND hwnd)\r
5353 {\r
5354   FARPROC lpProc;\r
5355 \r
5356   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5357   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5358     hwnd, (DLGPROC)lpProc);\r
5359   FreeProcInstance(lpProc);\r
5360 }\r
5361 \r
5362 /* Toggle ShowThinking */\r
5363 VOID\r
5364 ToggleShowThinking()\r
5365 {\r
5366   appData.showThinking = !appData.showThinking;\r
5367   ShowThinkingEvent();\r
5368 }\r
5369 \r
5370 VOID\r
5371 LoadGameDialog(HWND hwnd, char* title)\r
5372 {\r
5373   UINT number = 0;\r
5374   FILE *f;\r
5375   char fileTitle[MSG_SIZ];\r
5376   f = OpenFileDialog(hwnd, "rb", "",\r
5377                      appData.oldSaveStyle ? "gam" : "pgn",\r
5378                      GAME_FILT,\r
5379                      title, &number, fileTitle, NULL);\r
5380   if (f != NULL) {\r
5381     cmailMsgLoaded = FALSE;\r
5382     if (number == 0) {\r
5383       int error = GameListBuild(f);\r
5384       if (error) {\r
5385         DisplayError("Cannot build game list", error);\r
5386       } else if (!ListEmpty(&gameList) &&\r
5387                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5388         GameListPopUp(f, fileTitle);\r
5389         return;\r
5390       }\r
5391       GameListDestroy();\r
5392       number = 1;\r
5393     }\r
5394     LoadGame(f, number, fileTitle, FALSE);\r
5395   }\r
5396 }\r
5397 \r
5398 VOID\r
5399 ChangedConsoleFont()\r
5400 {\r
5401   CHARFORMAT cfmt;\r
5402   CHARRANGE tmpsel, sel;\r
5403   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5404   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5405   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5406   PARAFORMAT paraf;\r
5407 \r
5408   cfmt.cbSize = sizeof(CHARFORMAT);\r
5409   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5410   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5411   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5412    * size.  This was undocumented in the version of MSVC++ that I had\r
5413    * when I wrote the code, but is apparently documented now.\r
5414    */\r
5415   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5416   cfmt.bCharSet = f->lf.lfCharSet;\r
5417   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5418   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5419   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5420   /* Why are the following seemingly needed too? */\r
5421   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5422   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5423   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5424   tmpsel.cpMin = 0;\r
5425   tmpsel.cpMax = -1; /*999999?*/\r
5426   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5427   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5428   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5429    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5430    */\r
5431   paraf.cbSize = sizeof(paraf);\r
5432   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5433   paraf.dxStartIndent = 0;\r
5434   paraf.dxOffset = WRAP_INDENT;\r
5435   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5436   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5437 }\r
5438 \r
5439 /*---------------------------------------------------------------------------*\\r
5440  *\r
5441  * Window Proc for main window\r
5442  *\r
5443 \*---------------------------------------------------------------------------*/\r
5444 \r
5445 /* Process messages for main window, etc. */\r
5446 LRESULT CALLBACK\r
5447 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5448 {\r
5449   FARPROC lpProc;\r
5450   int wmId, wmEvent;\r
5451   char *defName;\r
5452   FILE *f;\r
5453   UINT number;\r
5454   char fileTitle[MSG_SIZ];\r
5455   char buf[MSG_SIZ];\r
5456   static SnapData sd;\r
5457 \r
5458   switch (message) {\r
5459 \r
5460   case WM_PAINT: /* message: repaint portion of window */\r
5461     PaintProc(hwnd);\r
5462     break;\r
5463 \r
5464   case WM_ERASEBKGND:\r
5465     if (IsIconic(hwnd)) {\r
5466       /* Cheat; change the message */\r
5467       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5468     } else {\r
5469       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5470     }\r
5471     break;\r
5472 \r
5473   case WM_LBUTTONDOWN:\r
5474   case WM_MBUTTONDOWN:\r
5475   case WM_RBUTTONDOWN:\r
5476   case WM_LBUTTONUP:\r
5477   case WM_MBUTTONUP:\r
5478   case WM_RBUTTONUP:\r
5479   case WM_MOUSEMOVE:\r
5480   case WM_MOUSEWHEEL:\r
5481     MouseEvent(hwnd, message, wParam, lParam);\r
5482     break;\r
5483 \r
5484   case WM_CHAR:\r
5485     \r
5486     if (appData.icsActive) {\r
5487       if (wParam == '\t') {\r
5488         if (GetKeyState(VK_SHIFT) < 0) {\r
5489           /* shifted */\r
5490           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5491           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5492           SetFocus(h);\r
5493         } else {\r
5494           /* unshifted */\r
5495           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5496           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5497           SetFocus(h);\r
5498         }\r
5499       } else {\r
5500         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5501         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5502         SetFocus(h);\r
5503         SendMessage(h, message, wParam, lParam);\r
5504       }\r
5505     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5506       PopUpMoveDialog((char)wParam);\r
5507     }\r
5508     break;\r
5509 \r
5510   case WM_PALETTECHANGED:\r
5511     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5512       int nnew;\r
5513       HDC hdc = GetDC(hwndMain);\r
5514       SelectPalette(hdc, hPal, TRUE);\r
5515       nnew = RealizePalette(hdc);\r
5516       if (nnew > 0) {\r
5517         paletteChanged = TRUE;\r
5518 #if 0\r
5519         UpdateColors(hdc);\r
5520 #else\r
5521         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5522 #endif\r
5523       }\r
5524       ReleaseDC(hwnd, hdc);\r
5525     }\r
5526     break;\r
5527 \r
5528   case WM_QUERYNEWPALETTE:\r
5529     if (!appData.monoMode /*&& paletteChanged*/) {\r
5530       int nnew;\r
5531       HDC hdc = GetDC(hwndMain);\r
5532       paletteChanged = FALSE;\r
5533       SelectPalette(hdc, hPal, FALSE);\r
5534       nnew = RealizePalette(hdc);\r
5535       if (nnew > 0) {\r
5536         InvalidateRect(hwnd, &boardRect, FALSE);\r
5537       }\r
5538       ReleaseDC(hwnd, hdc);\r
5539       return TRUE;\r
5540     }\r
5541     return FALSE;\r
5542 \r
5543   case WM_COMMAND: /* message: command from application menu */\r
5544     wmId    = LOWORD(wParam);\r
5545     wmEvent = HIWORD(wParam);\r
5546 \r
5547     switch (wmId) {\r
5548     case IDM_NewGame:\r
5549       ResetGameEvent();\r
5550       AnalysisPopDown();\r
5551       break;\r
5552 \r
5553     case IDM_NewGameFRC:\r
5554       if( NewGameFRC() == 0 ) {\r
5555         ResetGameEvent();\r
5556         AnalysisPopDown();\r
5557       }\r
5558       break;\r
5559 \r
5560     case IDM_NewVariant:\r
5561       NewVariantPopup(hwnd);\r
5562       break;\r
5563 \r
5564     case IDM_LoadGame:\r
5565       LoadGameDialog(hwnd, "Load Game from File");\r
5566       break;\r
5567 \r
5568     case IDM_LoadNextGame:\r
5569       ReloadGame(1);\r
5570       break;\r
5571 \r
5572     case IDM_LoadPrevGame:\r
5573       ReloadGame(-1);\r
5574       break;\r
5575 \r
5576     case IDM_ReloadGame:\r
5577       ReloadGame(0);\r
5578       break;\r
5579 \r
5580     case IDM_LoadPosition:\r
5581       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5582         Reset(FALSE, TRUE);\r
5583       }\r
5584       number = 1;\r
5585       f = OpenFileDialog(hwnd, "rb", "",\r
5586                          appData.oldSaveStyle ? "pos" : "fen",\r
5587                          POSITION_FILT,\r
5588                          "Load Position from File", &number, fileTitle, NULL);\r
5589       if (f != NULL) {\r
5590         LoadPosition(f, number, fileTitle);\r
5591       }\r
5592       break;\r
5593 \r
5594     case IDM_LoadNextPosition:\r
5595       ReloadPosition(1);\r
5596       break;\r
5597 \r
5598     case IDM_LoadPrevPosition:\r
5599       ReloadPosition(-1);\r
5600       break;\r
5601 \r
5602     case IDM_ReloadPosition:\r
5603       ReloadPosition(0);\r
5604       break;\r
5605 \r
5606     case IDM_SaveGame:\r
5607       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5608       f = OpenFileDialog(hwnd, "a", defName,\r
5609                          appData.oldSaveStyle ? "gam" : "pgn",\r
5610                          GAME_FILT,\r
5611                          "Save Game to File", NULL, fileTitle, NULL);\r
5612       if (f != NULL) {\r
5613         SaveGame(f, 0, "");\r
5614       }\r
5615       break;\r
5616 \r
5617     case IDM_SavePosition:\r
5618       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5619       f = OpenFileDialog(hwnd, "a", defName,\r
5620                          appData.oldSaveStyle ? "pos" : "fen",\r
5621                          POSITION_FILT,\r
5622                          "Save Position to File", NULL, fileTitle, NULL);\r
5623       if (f != NULL) {\r
5624         SavePosition(f, 0, "");\r
5625       }\r
5626       break;\r
5627 \r
5628     case IDM_SaveDiagram:\r
5629       defName = "diagram";\r
5630       f = OpenFileDialog(hwnd, "wb", defName,\r
5631                          "bmp",\r
5632                          DIAGRAM_FILT,\r
5633                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5634       if (f != NULL) {\r
5635         SaveDiagram(f);\r
5636       }\r
5637       break;\r
5638 \r
5639     case IDM_CopyGame:\r
5640       CopyGameToClipboard();\r
5641       break;\r
5642 \r
5643     case IDM_PasteGame:\r
5644       PasteGameFromClipboard();\r
5645       break;\r
5646 \r
5647     case IDM_CopyGameListToClipboard:\r
5648       CopyGameListToClipboard();\r
5649       break;\r
5650 \r
5651     /* [AS] Autodetect FEN or PGN data */\r
5652     case IDM_PasteAny:\r
5653       PasteGameOrFENFromClipboard();\r
5654       break;\r
5655 \r
5656     /* [AS] Move history */\r
5657     case IDM_ShowMoveHistory:\r
5658         if( MoveHistoryIsUp() ) {\r
5659             MoveHistoryPopDown();\r
5660         }\r
5661         else {\r
5662             MoveHistoryPopUp();\r
5663         }\r
5664         break;\r
5665 \r
5666     /* [AS] Eval graph */\r
5667     case IDM_ShowEvalGraph:\r
5668         if( EvalGraphIsUp() ) {\r
5669             EvalGraphPopDown();\r
5670         }\r
5671         else {\r
5672             EvalGraphPopUp();\r
5673         }\r
5674         break;\r
5675 \r
5676     /* [AS] Engine output */\r
5677     case IDM_ShowEngineOutput:\r
5678         if( EngineOutputIsUp() ) {\r
5679             EngineOutputPopDown();\r
5680         }\r
5681         else {\r
5682             EngineOutputPopUp();\r
5683         }\r
5684         break;\r
5685 \r
5686     /* [AS] User adjudication */\r
5687     case IDM_UserAdjudication_White:\r
5688         UserAdjudicationEvent( +1 );\r
5689         break;\r
5690 \r
5691     case IDM_UserAdjudication_Black:\r
5692         UserAdjudicationEvent( -1 );\r
5693         break;\r
5694 \r
5695     case IDM_UserAdjudication_Draw:\r
5696         UserAdjudicationEvent( 0 );\r
5697         break;\r
5698 \r
5699     /* [AS] Game list options dialog */\r
5700     case IDM_GameListOptions:\r
5701       GameListOptions();\r
5702       break;\r
5703 \r
5704     case IDM_CopyPosition:\r
5705       CopyFENToClipboard();\r
5706       break;\r
5707 \r
5708     case IDM_PastePosition:\r
5709       PasteFENFromClipboard();\r
5710       break;\r
5711 \r
5712     case IDM_MailMove:\r
5713       MailMoveEvent();\r
5714       break;\r
5715 \r
5716     case IDM_ReloadCMailMsg:\r
5717       Reset(TRUE, TRUE);\r
5718       ReloadCmailMsgEvent(FALSE);\r
5719       break;\r
5720 \r
5721     case IDM_Minimize:\r
5722       ShowWindow(hwnd, SW_MINIMIZE);\r
5723       break;\r
5724 \r
5725     case IDM_Exit:\r
5726       ExitEvent(0);\r
5727       break;\r
5728 \r
5729     case IDM_MachineWhite:\r
5730       MachineWhiteEvent();\r
5731       /*\r
5732        * refresh the tags dialog only if it's visible\r
5733        */\r
5734       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5735           char *tags;\r
5736           tags = PGNTags(&gameInfo);\r
5737           TagsPopUp(tags, CmailMsg());\r
5738           free(tags);\r
5739       }\r
5740       break;\r
5741 \r
5742     case IDM_MachineBlack:\r
5743       MachineBlackEvent();\r
5744       /*\r
5745        * refresh the tags dialog only if it's visible\r
5746        */\r
5747       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5748           char *tags;\r
5749           tags = PGNTags(&gameInfo);\r
5750           TagsPopUp(tags, CmailMsg());\r
5751           free(tags);\r
5752       }\r
5753       break;\r
5754 \r
5755     case IDM_TwoMachines:\r
5756       TwoMachinesEvent();\r
5757       /*\r
5758        * refresh the tags dialog only if it's visible\r
5759        */\r
5760       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5761           char *tags;\r
5762           tags = PGNTags(&gameInfo);\r
5763           TagsPopUp(tags, CmailMsg());\r
5764           free(tags);\r
5765       }\r
5766       break;\r
5767 \r
5768     case IDM_AnalysisMode:\r
5769       if (!first.analysisSupport) {\r
5770         sprintf(buf, "%s does not support analysis", first.tidy);\r
5771         DisplayError(buf, 0);\r
5772       } else {\r
5773         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5774         if (appData.icsActive) {\r
5775                if (gameMode != IcsObserving) {\r
5776                        sprintf(buf, "You are not observing a game");\r
5777                        DisplayError(buf, 0);\r
5778                        /* secure check */\r
5779                        if (appData.icsEngineAnalyze) {\r
5780                                if (appData.debugMode) \r
5781                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5782                                ExitAnalyzeMode();\r
5783                                ModeHighlight();\r
5784                                break;\r
5785                        }\r
5786                        break;\r
5787                } else {\r
5788                        /* if enable, user want disable icsEngineAnalyze */\r
5789                        if (appData.icsEngineAnalyze) {\r
5790                                ExitAnalyzeMode();\r
5791                                ModeHighlight();\r
5792                                break;\r
5793                        }\r
5794                        appData.icsEngineAnalyze = TRUE;\r
5795                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5796                }\r
5797         } \r
5798         if (!appData.showThinking) ToggleShowThinking();\r
5799         AnalyzeModeEvent();\r
5800       }\r
5801       break;\r
5802 \r
5803     case IDM_AnalyzeFile:\r
5804       if (!first.analysisSupport) {\r
5805         char buf[MSG_SIZ];\r
5806         sprintf(buf, "%s does not support analysis", first.tidy);\r
5807         DisplayError(buf, 0);\r
5808       } else {\r
5809         if (!appData.showThinking) ToggleShowThinking();\r
5810         AnalyzeFileEvent();\r
5811         LoadGameDialog(hwnd, "Analyze Game from File");\r
5812         AnalysisPeriodicEvent(1);\r
5813       }\r
5814       break;\r
5815 \r
5816     case IDM_IcsClient:\r
5817       IcsClientEvent();\r
5818       break;\r
5819 \r
5820     case IDM_EditGame:\r
5821       EditGameEvent();\r
5822       break;\r
5823 \r
5824     case IDM_EditPosition:\r
5825       EditPositionEvent();\r
5826       break;\r
5827 \r
5828     case IDM_Training:\r
5829       TrainingEvent();\r
5830       break;\r
5831 \r
5832     case IDM_ShowGameList:\r
5833       ShowGameListProc();\r
5834       break;\r
5835 \r
5836     case IDM_EditTags:\r
5837       EditTagsProc();\r
5838       break;\r
5839 \r
5840     case IDM_EditComment:\r
5841       if (commentDialogUp && editComment) {\r
5842         CommentPopDown();\r
5843       } else {\r
5844         EditCommentEvent();\r
5845       }\r
5846       break;\r
5847 \r
5848     case IDM_Pause:\r
5849       PauseEvent();\r
5850       break;\r
5851 \r
5852     case IDM_Accept:\r
5853       AcceptEvent();\r
5854       break;\r
5855 \r
5856     case IDM_Decline:\r
5857       DeclineEvent();\r
5858       break;\r
5859 \r
5860     case IDM_Rematch:\r
5861       RematchEvent();\r
5862       break;\r
5863 \r
5864     case IDM_CallFlag:\r
5865       CallFlagEvent();\r
5866       break;\r
5867 \r
5868     case IDM_Draw:\r
5869       DrawEvent();\r
5870       break;\r
5871 \r
5872     case IDM_Adjourn:\r
5873       AdjournEvent();\r
5874       break;\r
5875 \r
5876     case IDM_Abort:\r
5877       AbortEvent();\r
5878       break;\r
5879 \r
5880     case IDM_Resign:\r
5881       ResignEvent();\r
5882       break;\r
5883 \r
5884     case IDM_StopObserving:\r
5885       StopObservingEvent();\r
5886       break;\r
5887 \r
5888     case IDM_StopExamining:\r
5889       StopExaminingEvent();\r
5890       break;\r
5891 \r
5892     case IDM_TypeInMove:\r
5893       PopUpMoveDialog('\000');\r
5894       break;\r
5895 \r
5896     case IDM_TypeInName:\r
5897       PopUpNameDialog('\000');\r
5898       break;\r
5899 \r
5900     case IDM_Backward:\r
5901       BackwardEvent();\r
5902       SetFocus(hwndMain);\r
5903       break;\r
5904 \r
5905     case IDM_Forward:\r
5906       ForwardEvent();\r
5907       SetFocus(hwndMain);\r
5908       break;\r
5909 \r
5910     case IDM_ToStart:\r
5911       ToStartEvent();\r
5912       SetFocus(hwndMain);\r
5913       break;\r
5914 \r
5915     case IDM_ToEnd:\r
5916       ToEndEvent();\r
5917       SetFocus(hwndMain);\r
5918       break;\r
5919 \r
5920     case IDM_Revert:\r
5921       RevertEvent();\r
5922       break;\r
5923 \r
5924     case IDM_TruncateGame:\r
5925       TruncateGameEvent();\r
5926       break;\r
5927 \r
5928     case IDM_MoveNow:\r
5929       MoveNowEvent();\r
5930       break;\r
5931 \r
5932     case IDM_RetractMove:\r
5933       RetractMoveEvent();\r
5934       break;\r
5935 \r
5936     case IDM_FlipView:\r
5937       flipView = !flipView;\r
5938       DrawPosition(FALSE, NULL);\r
5939       break;\r
5940 \r
5941     case IDM_FlipClock:\r
5942       flipClock = !flipClock;\r
5943       DisplayBothClocks();\r
5944       break;\r
5945 \r
5946     case IDM_GeneralOptions:\r
5947       GeneralOptionsPopup(hwnd);\r
5948       DrawPosition(TRUE, NULL);\r
5949       break;\r
5950 \r
5951     case IDM_BoardOptions:\r
5952       BoardOptionsPopup(hwnd);\r
5953       break;\r
5954 \r
5955     case IDM_EnginePlayOptions:\r
5956       EnginePlayOptionsPopup(hwnd);\r
5957       break;\r
5958 \r
5959     case IDM_OptionsUCI:\r
5960       UciOptionsPopup(hwnd);\r
5961       break;\r
5962 \r
5963     case IDM_IcsOptions:\r
5964       IcsOptionsPopup(hwnd);\r
5965       break;\r
5966 \r
5967     case IDM_Fonts:\r
5968       FontsOptionsPopup(hwnd);\r
5969       break;\r
5970 \r
5971     case IDM_Sounds:\r
5972       SoundOptionsPopup(hwnd);\r
5973       break;\r
5974 \r
5975     case IDM_CommPort:\r
5976       CommPortOptionsPopup(hwnd);\r
5977       break;\r
5978 \r
5979     case IDM_LoadOptions:\r
5980       LoadOptionsPopup(hwnd);\r
5981       break;\r
5982 \r
5983     case IDM_SaveOptions:\r
5984       SaveOptionsPopup(hwnd);\r
5985       break;\r
5986 \r
5987     case IDM_TimeControl:\r
5988       TimeControlOptionsPopup(hwnd);\r
5989       break;\r
5990 \r
5991     case IDM_SaveSettings:\r
5992       SaveSettings(settingsFileName);\r
5993       break;\r
5994 \r
5995     case IDM_SaveSettingsOnExit:\r
5996       saveSettingsOnExit = !saveSettingsOnExit;\r
5997       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5998                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5999                                          MF_CHECKED : MF_UNCHECKED));\r
6000       break;\r
6001 \r
6002     case IDM_Hint:\r
6003       HintEvent();\r
6004       break;\r
6005 \r
6006     case IDM_Book:\r
6007       BookEvent();\r
6008       break;\r
6009 \r
6010     case IDM_AboutGame:\r
6011       AboutGameEvent();\r
6012       break;\r
6013 \r
6014     case IDM_Debug:\r
6015       appData.debugMode = !appData.debugMode;\r
6016       if (appData.debugMode) {\r
6017         char dir[MSG_SIZ];\r
6018         GetCurrentDirectory(MSG_SIZ, dir);\r
6019         SetCurrentDirectory(installDir);\r
6020         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6021         SetCurrentDirectory(dir);\r
6022         setbuf(debugFP, NULL);\r
6023       } else {\r
6024         fclose(debugFP);\r
6025         debugFP = NULL;\r
6026       }\r
6027       break;\r
6028 \r
6029     case IDM_HELPCONTENTS:\r
6030       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6031         MessageBox (GetFocus(),\r
6032                     "Unable to activate help",\r
6033                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6034       }\r
6035       break;\r
6036 \r
6037     case IDM_HELPSEARCH:\r
6038       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6039         MessageBox (GetFocus(),\r
6040                     "Unable to activate help",\r
6041                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6042       }\r
6043       break;\r
6044 \r
6045     case IDM_HELPHELP:\r
6046       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6047         MessageBox (GetFocus(),\r
6048                     "Unable to activate help",\r
6049                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6050       }\r
6051       break;\r
6052 \r
6053     case IDM_ABOUT:\r
6054       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6055       DialogBox(hInst, \r
6056         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6057         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6058       FreeProcInstance(lpProc);\r
6059       break;\r
6060 \r
6061     case IDM_DirectCommand1:\r
6062       AskQuestionEvent("Direct Command",\r
6063                        "Send to chess program:", "", "1");\r
6064       break;\r
6065     case IDM_DirectCommand2:\r
6066       AskQuestionEvent("Direct Command",\r
6067                        "Send to second chess program:", "", "2");\r
6068       break;\r
6069 \r
6070     case EP_WhitePawn:\r
6071       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6072       fromX = fromY = -1;\r
6073       break;\r
6074 \r
6075     case EP_WhiteKnight:\r
6076       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6077       fromX = fromY = -1;\r
6078       break;\r
6079 \r
6080     case EP_WhiteBishop:\r
6081       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6082       fromX = fromY = -1;\r
6083       break;\r
6084 \r
6085     case EP_WhiteRook:\r
6086       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6087       fromX = fromY = -1;\r
6088       break;\r
6089 \r
6090     case EP_WhiteQueen:\r
6091       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6092       fromX = fromY = -1;\r
6093       break;\r
6094 \r
6095     case EP_WhiteFerz:\r
6096       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6097       fromX = fromY = -1;\r
6098       break;\r
6099 \r
6100     case EP_WhiteWazir:\r
6101       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6102       fromX = fromY = -1;\r
6103       break;\r
6104 \r
6105     case EP_WhiteAlfil:\r
6106       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6107       fromX = fromY = -1;\r
6108       break;\r
6109 \r
6110     case EP_WhiteCannon:\r
6111       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6112       fromX = fromY = -1;\r
6113       break;\r
6114 \r
6115     case EP_WhiteCardinal:\r
6116       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6117       fromX = fromY = -1;\r
6118       break;\r
6119 \r
6120     case EP_WhiteMarshall:\r
6121       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6122       fromX = fromY = -1;\r
6123       break;\r
6124 \r
6125     case EP_WhiteKing:\r
6126       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6127       fromX = fromY = -1;\r
6128       break;\r
6129 \r
6130     case EP_BlackPawn:\r
6131       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6132       fromX = fromY = -1;\r
6133       break;\r
6134 \r
6135     case EP_BlackKnight:\r
6136       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6137       fromX = fromY = -1;\r
6138       break;\r
6139 \r
6140     case EP_BlackBishop:\r
6141       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6142       fromX = fromY = -1;\r
6143       break;\r
6144 \r
6145     case EP_BlackRook:\r
6146       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6147       fromX = fromY = -1;\r
6148       break;\r
6149 \r
6150     case EP_BlackQueen:\r
6151       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6152       fromX = fromY = -1;\r
6153       break;\r
6154 \r
6155     case EP_BlackFerz:\r
6156       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6157       fromX = fromY = -1;\r
6158       break;\r
6159 \r
6160     case EP_BlackWazir:\r
6161       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6162       fromX = fromY = -1;\r
6163       break;\r
6164 \r
6165     case EP_BlackAlfil:\r
6166       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6167       fromX = fromY = -1;\r
6168       break;\r
6169 \r
6170     case EP_BlackCannon:\r
6171       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6172       fromX = fromY = -1;\r
6173       break;\r
6174 \r
6175     case EP_BlackCardinal:\r
6176       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6177       fromX = fromY = -1;\r
6178       break;\r
6179 \r
6180     case EP_BlackMarshall:\r
6181       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6182       fromX = fromY = -1;\r
6183       break;\r
6184 \r
6185     case EP_BlackKing:\r
6186       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6187       fromX = fromY = -1;\r
6188       break;\r
6189 \r
6190     case EP_EmptySquare:\r
6191       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6192       fromX = fromY = -1;\r
6193       break;\r
6194 \r
6195     case EP_ClearBoard:\r
6196       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6197       fromX = fromY = -1;\r
6198       break;\r
6199 \r
6200     case EP_White:\r
6201       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6202       fromX = fromY = -1;\r
6203       break;\r
6204 \r
6205     case EP_Black:\r
6206       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6207       fromX = fromY = -1;\r
6208       break;\r
6209 \r
6210     case EP_Promote:\r
6211       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6212       fromX = fromY = -1;\r
6213       break;\r
6214 \r
6215     case EP_Demote:\r
6216       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6217       fromX = fromY = -1;\r
6218       break;\r
6219 \r
6220     case DP_Pawn:\r
6221       DropMenuEvent(WhitePawn, fromX, fromY);\r
6222       fromX = fromY = -1;\r
6223       break;\r
6224 \r
6225     case DP_Knight:\r
6226       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6227       fromX = fromY = -1;\r
6228       break;\r
6229 \r
6230     case DP_Bishop:\r
6231       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6232       fromX = fromY = -1;\r
6233       break;\r
6234 \r
6235     case DP_Rook:\r
6236       DropMenuEvent(WhiteRook, fromX, fromY);\r
6237       fromX = fromY = -1;\r
6238       break;\r
6239 \r
6240     case DP_Queen:\r
6241       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6242       fromX = fromY = -1;\r
6243       break;\r
6244 \r
6245     default:\r
6246       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6247     }\r
6248     break;\r
6249 \r
6250   case WM_TIMER:\r
6251     switch (wParam) {\r
6252     case CLOCK_TIMER_ID:\r
6253       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6254       clockTimerEvent = 0;\r
6255       DecrementClocks(); /* call into back end */\r
6256       break;\r
6257     case LOAD_GAME_TIMER_ID:\r
6258       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6259       loadGameTimerEvent = 0;\r
6260       AutoPlayGameLoop(); /* call into back end */\r
6261       break;\r
6262     case ANALYSIS_TIMER_ID:\r
6263       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6264                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6265         AnalysisPeriodicEvent(0);\r
6266       } else {\r
6267         KillTimer(hwnd, analysisTimerEvent);\r
6268         analysisTimerEvent = 0;\r
6269       }\r
6270       break;\r
6271     case DELAYED_TIMER_ID:\r
6272       KillTimer(hwnd, delayedTimerEvent);\r
6273       delayedTimerEvent = 0;\r
6274       delayedTimerCallback();\r
6275       break;\r
6276     }\r
6277     break;\r
6278 \r
6279   case WM_USER_Input:\r
6280     InputEvent(hwnd, message, wParam, lParam);\r
6281     break;\r
6282 \r
6283   /* [AS] Also move "attached" child windows */\r
6284   case WM_WINDOWPOSCHANGING:\r
6285 \r
6286     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6287         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6288 \r
6289         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6290             /* Window is moving */\r
6291             RECT rcMain;\r
6292 \r
6293 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6294             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6295             rcMain.right  = boardX + winWidth;\r
6296             rcMain.top    = boardY;\r
6297             rcMain.bottom = boardY + winHeight;\r
6298             \r
6299             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6300             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6301             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6302             boardX = lpwp->x;\r
6303             boardY = lpwp->y;\r
6304         }\r
6305     }\r
6306     break;\r
6307 \r
6308   /* [AS] Snapping */\r
6309   case WM_ENTERSIZEMOVE:\r
6310     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6311     if (hwnd == hwndMain) {\r
6312       doingSizing = TRUE;\r
6313       lastSizing = 0;\r
6314     }\r
6315     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6316     break;\r
6317 \r
6318   case WM_SIZING:\r
6319     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6320     if (hwnd == hwndMain) {\r
6321       lastSizing = wParam;\r
6322     }\r
6323     break;\r
6324 \r
6325   case WM_MOVING:\r
6326     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6327       return OnMoving( &sd, hwnd, wParam, lParam );\r
6328 \r
6329   case WM_EXITSIZEMOVE:\r
6330     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6331     if (hwnd == hwndMain) {\r
6332       RECT client;\r
6333       doingSizing = FALSE;\r
6334       InvalidateRect(hwnd, &boardRect, FALSE);\r
6335       GetClientRect(hwnd, &client);\r
6336       ResizeBoard(client.right, client.bottom, lastSizing);\r
6337       lastSizing = 0;\r
6338       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6339     }\r
6340     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6341     break;\r
6342 \r
6343   case WM_DESTROY: /* message: window being destroyed */\r
6344     PostQuitMessage(0);\r
6345     break;\r
6346 \r
6347   case WM_CLOSE:\r
6348     if (hwnd == hwndMain) {\r
6349       ExitEvent(0);\r
6350     }\r
6351     break;\r
6352 \r
6353   default:      /* Passes it on if unprocessed */\r
6354     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6355   }\r
6356   return 0;\r
6357 }\r
6358 \r
6359 /*---------------------------------------------------------------------------*\\r
6360  *\r
6361  * Misc utility routines\r
6362  *\r
6363 \*---------------------------------------------------------------------------*/\r
6364 \r
6365 /*\r
6366  * Decent random number generator, at least not as bad as Windows\r
6367  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6368  */\r
6369 unsigned int randstate;\r
6370 \r
6371 int\r
6372 myrandom(void)\r
6373 {\r
6374   randstate = randstate * 1664525 + 1013904223;\r
6375   return (int) randstate & 0x7fffffff;\r
6376 }\r
6377 \r
6378 void\r
6379 mysrandom(unsigned int seed)\r
6380 {\r
6381   randstate = seed;\r
6382 }\r
6383 \r
6384 \r
6385 /* \r
6386  * returns TRUE if user selects a different color, FALSE otherwise \r
6387  */\r
6388 \r
6389 BOOL\r
6390 ChangeColor(HWND hwnd, COLORREF *which)\r
6391 {\r
6392   static BOOL firstTime = TRUE;\r
6393   static DWORD customColors[16];\r
6394   CHOOSECOLOR cc;\r
6395   COLORREF newcolor;\r
6396   int i;\r
6397   ColorClass ccl;\r
6398 \r
6399   if (firstTime) {\r
6400     /* Make initial colors in use available as custom colors */\r
6401     /* Should we put the compiled-in defaults here instead? */\r
6402     i = 0;\r
6403     customColors[i++] = lightSquareColor & 0xffffff;\r
6404     customColors[i++] = darkSquareColor & 0xffffff;\r
6405     customColors[i++] = whitePieceColor & 0xffffff;\r
6406     customColors[i++] = blackPieceColor & 0xffffff;\r
6407     customColors[i++] = highlightSquareColor & 0xffffff;\r
6408     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6409 \r
6410     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6411       customColors[i++] = textAttribs[ccl].color;\r
6412     }\r
6413     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6414     firstTime = FALSE;\r
6415   }\r
6416 \r
6417   cc.lStructSize = sizeof(cc);\r
6418   cc.hwndOwner = hwnd;\r
6419   cc.hInstance = NULL;\r
6420   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6421   cc.lpCustColors = (LPDWORD) customColors;\r
6422   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6423 \r
6424   if (!ChooseColor(&cc)) return FALSE;\r
6425 \r
6426   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6427   if (newcolor == *which) return FALSE;\r
6428   *which = newcolor;\r
6429   return TRUE;\r
6430 \r
6431   /*\r
6432   InitDrawingColors();\r
6433   InvalidateRect(hwnd, &boardRect, FALSE);\r
6434   */\r
6435 }\r
6436 \r
6437 BOOLEAN\r
6438 MyLoadSound(MySound *ms)\r
6439 {\r
6440   BOOL ok = FALSE;\r
6441   struct stat st;\r
6442   FILE *f;\r
6443 \r
6444   if (ms->data) free(ms->data);\r
6445   ms->data = NULL;\r
6446 \r
6447   switch (ms->name[0]) {\r
6448   case NULLCHAR:\r
6449     /* Silence */\r
6450     ok = TRUE;\r
6451     break;\r
6452   case '$':\r
6453     /* System sound from Control Panel.  Don't preload here. */\r
6454     ok = TRUE;\r
6455     break;\r
6456   case '!':\r
6457     if (ms->name[1] == NULLCHAR) {\r
6458       /* "!" alone = silence */\r
6459       ok = TRUE;\r
6460     } else {\r
6461       /* Builtin wave resource.  Error if not found. */\r
6462       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6463       if (h == NULL) break;\r
6464       ms->data = (void *)LoadResource(hInst, h);\r
6465       if (h == NULL) break;\r
6466       ok = TRUE;\r
6467     }\r
6468     break;\r
6469   default:\r
6470     /* .wav file.  Error if not found. */\r
6471     f = fopen(ms->name, "rb");\r
6472     if (f == NULL) break;\r
6473     if (fstat(fileno(f), &st) < 0) break;\r
6474     ms->data = malloc(st.st_size);\r
6475     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6476     fclose(f);\r
6477     ok = TRUE;\r
6478     break;\r
6479   }\r
6480   if (!ok) {\r
6481     char buf[MSG_SIZ];\r
6482     sprintf(buf, "Error loading sound %s", ms->name);\r
6483     DisplayError(buf, GetLastError());\r
6484   }\r
6485   return ok;\r
6486 }\r
6487 \r
6488 BOOLEAN\r
6489 MyPlaySound(MySound *ms)\r
6490 {\r
6491   BOOLEAN ok = FALSE;\r
6492   switch (ms->name[0]) {\r
6493   case NULLCHAR:\r
6494     /* Silence */\r
6495     ok = TRUE;\r
6496     break;\r
6497   case '$':\r
6498     /* System sound from Control Panel (deprecated feature).\r
6499        "$" alone or an unset sound name gets default beep (still in use). */\r
6500     if (ms->name[1]) {\r
6501       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6502     }\r
6503     if (!ok) ok = MessageBeep(MB_OK);\r
6504     break; \r
6505   case '!':\r
6506     /* Builtin wave resource, or "!" alone for silence */\r
6507     if (ms->name[1]) {\r
6508       if (ms->data == NULL) return FALSE;\r
6509       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6510     } else {\r
6511       ok = TRUE;\r
6512     }\r
6513     break;\r
6514   default:\r
6515     /* .wav file.  Error if not found. */\r
6516     if (ms->data == NULL) return FALSE;\r
6517     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6518     break;\r
6519   }\r
6520   /* Don't print an error: this can happen innocently if the sound driver\r
6521      is busy; for instance, if another instance of WinBoard is playing\r
6522      a sound at about the same time. */\r
6523 #if 0\r
6524   if (!ok) {\r
6525     char buf[MSG_SIZ];\r
6526     sprintf(buf, "Error playing sound %s", ms->name);\r
6527     DisplayError(buf, GetLastError());\r
6528   }\r
6529 #endif\r
6530   return ok;\r
6531 }\r
6532 \r
6533 \r
6534 LRESULT CALLBACK\r
6535 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6536 {\r
6537   BOOL ok;\r
6538   OPENFILENAME *ofn;\r
6539   static UINT *number; /* gross that this is static */\r
6540 \r
6541   switch (message) {\r
6542   case WM_INITDIALOG: /* message: initialize dialog box */\r
6543     /* Center the dialog over the application window */\r
6544     ofn = (OPENFILENAME *) lParam;\r
6545     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6546       number = (UINT *) ofn->lCustData;\r
6547       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6548     } else {\r
6549       number = NULL;\r
6550     }\r
6551     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6552     return FALSE;  /* Allow for further processing */\r
6553 \r
6554   case WM_COMMAND:\r
6555     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6556       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6557     }\r
6558     return FALSE;  /* Allow for further processing */\r
6559   }\r
6560   return FALSE;\r
6561 }\r
6562 \r
6563 UINT APIENTRY\r
6564 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6565 {\r
6566   static UINT *number;\r
6567   OPENFILENAME *ofname;\r
6568   OFNOTIFY *ofnot;\r
6569   switch (uiMsg) {\r
6570   case WM_INITDIALOG:\r
6571     ofname = (OPENFILENAME *)lParam;\r
6572     number = (UINT *)(ofname->lCustData);\r
6573     break;\r
6574   case WM_NOTIFY:\r
6575     ofnot = (OFNOTIFY *)lParam;\r
6576     if (ofnot->hdr.code == CDN_FILEOK) {\r
6577       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6578     }\r
6579     break;\r
6580   }\r
6581   return 0;\r
6582 }\r
6583 \r
6584 \r
6585 FILE *\r
6586 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6587                char *nameFilt, char *dlgTitle, UINT *number,\r
6588                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6589 {\r
6590   OPENFILENAME openFileName;\r
6591   char buf1[MSG_SIZ];\r
6592   FILE *f;\r
6593 \r
6594   if (fileName == NULL) fileName = buf1;\r
6595   if (defName == NULL) {\r
6596     strcpy(fileName, "*.");\r
6597     strcat(fileName, defExt);\r
6598   } else {\r
6599     strcpy(fileName, defName);\r
6600   }\r
6601   if (fileTitle) strcpy(fileTitle, "");\r
6602   if (number) *number = 0;\r
6603 \r
6604   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6605   openFileName.hwndOwner         = hwnd;\r
6606   openFileName.hInstance         = (HANDLE) hInst;\r
6607   openFileName.lpstrFilter       = nameFilt;\r
6608   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6609   openFileName.nMaxCustFilter    = 0L;\r
6610   openFileName.nFilterIndex      = 1L;\r
6611   openFileName.lpstrFile         = fileName;\r
6612   openFileName.nMaxFile          = MSG_SIZ;\r
6613   openFileName.lpstrFileTitle    = fileTitle;\r
6614   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6615   openFileName.lpstrInitialDir   = NULL;\r
6616   openFileName.lpstrTitle        = dlgTitle;\r
6617   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6618     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6619     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6620     | (oldDialog ? 0 : OFN_EXPLORER);\r
6621   openFileName.nFileOffset       = 0;\r
6622   openFileName.nFileExtension    = 0;\r
6623   openFileName.lpstrDefExt       = defExt;\r
6624   openFileName.lCustData         = (LONG) number;\r
6625   openFileName.lpfnHook          = oldDialog ?\r
6626     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6627   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6628 \r
6629   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6630                         GetOpenFileName(&openFileName)) {\r
6631     /* open the file */\r
6632     f = fopen(openFileName.lpstrFile, write);\r
6633     if (f == NULL) {\r
6634       MessageBox(hwnd, "File open failed", NULL,\r
6635                  MB_OK|MB_ICONEXCLAMATION);\r
6636       return NULL;\r
6637     }\r
6638   } else {\r
6639     int err = CommDlgExtendedError();\r
6640     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6641     return FALSE;\r
6642   }\r
6643   return f;\r
6644 }\r
6645 \r
6646 \r
6647 \r
6648 VOID APIENTRY\r
6649 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6650 {\r
6651   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6652 \r
6653   /*\r
6654    * Get the first pop-up menu in the menu template. This is the\r
6655    * menu that TrackPopupMenu displays.\r
6656    */\r
6657   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6658 \r
6659   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6660 \r
6661   /*\r
6662    * TrackPopup uses screen coordinates, so convert the\r
6663    * coordinates of the mouse click to screen coordinates.\r
6664    */\r
6665   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6666 \r
6667   /* Draw and track the floating pop-up menu. */\r
6668   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6669                  pt.x, pt.y, 0, hwnd, NULL);\r
6670 \r
6671   /* Destroy the menu.*/\r
6672   DestroyMenu(hmenu);\r
6673 }\r
6674    \r
6675 typedef struct {\r
6676   HWND hDlg, hText;\r
6677   int sizeX, sizeY, newSizeX, newSizeY;\r
6678   HDWP hdwp;\r
6679 } ResizeEditPlusButtonsClosure;\r
6680 \r
6681 BOOL CALLBACK\r
6682 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6683 {\r
6684   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6685   RECT rect;\r
6686   POINT pt;\r
6687 \r
6688   if (hChild == cl->hText) return TRUE;\r
6689   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6690   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6691   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6692   ScreenToClient(cl->hDlg, &pt);\r
6693   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6694     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6695   return TRUE;\r
6696 }\r
6697 \r
6698 /* Resize a dialog that has a (rich) edit field filling most of\r
6699    the top, with a row of buttons below */\r
6700 VOID\r
6701 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6702 {\r
6703   RECT rectText;\r
6704   int newTextHeight, newTextWidth;\r
6705   ResizeEditPlusButtonsClosure cl;\r
6706   \r
6707   /*if (IsIconic(hDlg)) return;*/\r
6708   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6709   \r
6710   cl.hdwp = BeginDeferWindowPos(8);\r
6711 \r
6712   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6713   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6714   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6715   if (newTextHeight < 0) {\r
6716     newSizeY += -newTextHeight;\r
6717     newTextHeight = 0;\r
6718   }\r
6719   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6720     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6721 \r
6722   cl.hDlg = hDlg;\r
6723   cl.hText = hText;\r
6724   cl.sizeX = sizeX;\r
6725   cl.sizeY = sizeY;\r
6726   cl.newSizeX = newSizeX;\r
6727   cl.newSizeY = newSizeY;\r
6728   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6729 \r
6730   EndDeferWindowPos(cl.hdwp);\r
6731 }\r
6732 \r
6733 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6734 {\r
6735     RECT    rChild, rParent;\r
6736     int     wChild, hChild, wParent, hParent;\r
6737     int     wScreen, hScreen, xNew, yNew;\r
6738     HDC     hdc;\r
6739 \r
6740     /* Get the Height and Width of the child window */\r
6741     GetWindowRect (hwndChild, &rChild);\r
6742     wChild = rChild.right - rChild.left;\r
6743     hChild = rChild.bottom - rChild.top;\r
6744 \r
6745     /* Get the Height and Width of the parent window */\r
6746     GetWindowRect (hwndParent, &rParent);\r
6747     wParent = rParent.right - rParent.left;\r
6748     hParent = rParent.bottom - rParent.top;\r
6749 \r
6750     /* Get the display limits */\r
6751     hdc = GetDC (hwndChild);\r
6752     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6753     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6754     ReleaseDC(hwndChild, hdc);\r
6755 \r
6756     /* Calculate new X position, then adjust for screen */\r
6757     xNew = rParent.left + ((wParent - wChild) /2);\r
6758     if (xNew < 0) {\r
6759         xNew = 0;\r
6760     } else if ((xNew+wChild) > wScreen) {\r
6761         xNew = wScreen - wChild;\r
6762     }\r
6763 \r
6764     /* Calculate new Y position, then adjust for screen */\r
6765     if( mode == 0 ) {\r
6766         yNew = rParent.top  + ((hParent - hChild) /2);\r
6767     }\r
6768     else {\r
6769         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6770     }\r
6771 \r
6772     if (yNew < 0) {\r
6773         yNew = 0;\r
6774     } else if ((yNew+hChild) > hScreen) {\r
6775         yNew = hScreen - hChild;\r
6776     }\r
6777 \r
6778     /* Set it, and return */\r
6779     return SetWindowPos (hwndChild, NULL,\r
6780                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6781 }\r
6782 \r
6783 /* Center one window over another */\r
6784 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6785 {\r
6786     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6787 }\r
6788 \r
6789 /*---------------------------------------------------------------------------*\\r
6790  *\r
6791  * Startup Dialog functions\r
6792  *\r
6793 \*---------------------------------------------------------------------------*/\r
6794 void\r
6795 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6796 {\r
6797   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6798 \r
6799   while (*cd != NULL) {\r
6800     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6801     cd++;\r
6802   }\r
6803 }\r
6804 \r
6805 void\r
6806 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6807 {\r
6808   char buf1[ARG_MAX];\r
6809   int len;\r
6810 \r
6811   if (str[0] == '@') {\r
6812     FILE* f = fopen(str + 1, "r");\r
6813     if (f == NULL) {\r
6814       DisplayFatalError(str + 1, errno, 2);\r
6815       return;\r
6816     }\r
6817     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6818     fclose(f);\r
6819     buf1[len] = NULLCHAR;\r
6820     str = buf1;\r
6821   }\r
6822 \r
6823   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6824 \r
6825   for (;;) {\r
6826     char buf[MSG_SIZ];\r
6827     char *end = strchr(str, '\n');\r
6828     if (end == NULL) return;\r
6829     memcpy(buf, str, end - str);\r
6830     buf[end - str] = NULLCHAR;\r
6831     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6832     str = end + 1;\r
6833   }\r
6834 }\r
6835 \r
6836 void\r
6837 SetStartupDialogEnables(HWND hDlg)\r
6838 {\r
6839   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6840     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6841     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6842   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6843     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6844   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6845     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6846   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6847     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6848   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6849     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6850     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6851     IsDlgButtonChecked(hDlg, OPT_View));\r
6852 }\r
6853 \r
6854 char *\r
6855 QuoteForFilename(char *filename)\r
6856 {\r
6857   int dquote, space;\r
6858   dquote = strchr(filename, '"') != NULL;\r
6859   space = strchr(filename, ' ') != NULL;\r
6860   if (dquote || space) {\r
6861     if (dquote) {\r
6862       return "'";\r
6863     } else {\r
6864       return "\"";\r
6865     }\r
6866   } else {\r
6867     return "";\r
6868   }\r
6869 }\r
6870 \r
6871 VOID\r
6872 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6873 {\r
6874   char buf[MSG_SIZ];\r
6875   char *q;\r
6876 \r
6877   InitComboStringsFromOption(hwndCombo, nthnames);\r
6878   q = QuoteForFilename(nthcp);\r
6879   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6880   if (*nthdir != NULLCHAR) {\r
6881     q = QuoteForFilename(nthdir);\r
6882     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6883   }\r
6884   if (*nthcp == NULLCHAR) {\r
6885     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6886   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6887     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6888     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6889   }\r
6890 }\r
6891 \r
6892 LRESULT CALLBACK\r
6893 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6894 {\r
6895   char buf[MSG_SIZ];\r
6896   HANDLE hwndCombo;\r
6897   char *p;\r
6898 \r
6899   switch (message) {\r
6900   case WM_INITDIALOG:\r
6901     /* Center the dialog */\r
6902     CenterWindow (hDlg, GetDesktopWindow());\r
6903     /* Initialize the dialog items */\r
6904     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6905                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6906                   firstChessProgramNames);\r
6907     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6908                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6909                   secondChessProgramNames);\r
6910     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6911     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6912     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6913     if (*appData.icsHelper != NULLCHAR) {\r
6914       char *q = QuoteForFilename(appData.icsHelper);\r
6915       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6916     }\r
6917     if (*appData.icsHost == NULLCHAR) {\r
6918       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6919       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6920     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6921       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6922       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6923     }\r
6924 \r
6925     if (appData.icsActive) {\r
6926       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6927     }\r
6928     else if (appData.noChessProgram) {\r
6929       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6930     }\r
6931     else {\r
6932       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6933     }\r
6934 \r
6935     SetStartupDialogEnables(hDlg);\r
6936     return TRUE;\r
6937 \r
6938   case WM_COMMAND:\r
6939     switch (LOWORD(wParam)) {\r
6940     case IDOK:\r
6941       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6942         strcpy(buf, "/fcp=");\r
6943         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6944         p = buf;\r
6945         ParseArgs(StringGet, &p);\r
6946         strcpy(buf, "/scp=");\r
6947         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6948         p = buf;\r
6949         ParseArgs(StringGet, &p);\r
6950         appData.noChessProgram = FALSE;\r
6951         appData.icsActive = FALSE;\r
6952       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6953         strcpy(buf, "/ics /icshost=");\r
6954         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6955         p = buf;\r
6956         ParseArgs(StringGet, &p);\r
6957         if (appData.zippyPlay) {\r
6958           strcpy(buf, "/fcp=");\r
6959           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6960           p = buf;\r
6961           ParseArgs(StringGet, &p);\r
6962         }\r
6963       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6964         appData.noChessProgram = TRUE;\r
6965         appData.icsActive = FALSE;\r
6966       } else {\r
6967         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6968                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6969         return TRUE;\r
6970       }\r
6971       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6972         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6973         p = buf;\r
6974         ParseArgs(StringGet, &p);\r
6975       }\r
6976       EndDialog(hDlg, TRUE);\r
6977       return TRUE;\r
6978 \r
6979     case IDCANCEL:\r
6980       ExitEvent(0);\r
6981       return TRUE;\r
6982 \r
6983     case IDM_HELPCONTENTS:\r
6984       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6985         MessageBox (GetFocus(),\r
6986                     "Unable to activate help",\r
6987                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6988       }\r
6989       break;\r
6990 \r
6991     default:\r
6992       SetStartupDialogEnables(hDlg);\r
6993       break;\r
6994     }\r
6995     break;\r
6996   }\r
6997   return FALSE;\r
6998 }\r
6999 \r
7000 /*---------------------------------------------------------------------------*\\r
7001  *\r
7002  * About box dialog functions\r
7003  *\r
7004 \*---------------------------------------------------------------------------*/\r
7005 \r
7006 /* Process messages for "About" dialog box */\r
7007 LRESULT CALLBACK\r
7008 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7009 {\r
7010   switch (message) {\r
7011   case WM_INITDIALOG: /* message: initialize dialog box */\r
7012     /* Center the dialog over the application window */\r
7013     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7014     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7015     return (TRUE);\r
7016 \r
7017   case WM_COMMAND: /* message: received a command */\r
7018     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7019         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7020       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7021       return (TRUE);\r
7022     }\r
7023     break;\r
7024   }\r
7025   return (FALSE);\r
7026 }\r
7027 \r
7028 /*---------------------------------------------------------------------------*\\r
7029  *\r
7030  * Comment Dialog functions\r
7031  *\r
7032 \*---------------------------------------------------------------------------*/\r
7033 \r
7034 LRESULT CALLBACK\r
7035 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7036 {\r
7037   static HANDLE hwndText = NULL;\r
7038   int len, newSizeX, newSizeY, flags;\r
7039   static int sizeX, sizeY;\r
7040   char *str;\r
7041   RECT rect;\r
7042   MINMAXINFO *mmi;\r
7043 \r
7044   switch (message) {\r
7045   case WM_INITDIALOG: /* message: initialize dialog box */\r
7046     /* Initialize the dialog items */\r
7047     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7048     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7049     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7050     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7051     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7052     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7053     SetWindowText(hDlg, commentTitle);\r
7054     if (editComment) {\r
7055       SetFocus(hwndText);\r
7056     } else {\r
7057       SetFocus(GetDlgItem(hDlg, IDOK));\r
7058     }\r
7059     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7060                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7061                 MAKELPARAM(FALSE, 0));\r
7062     /* Size and position the dialog */\r
7063     if (!commentDialog) {\r
7064       commentDialog = hDlg;\r
7065       flags = SWP_NOZORDER;\r
7066       GetClientRect(hDlg, &rect);\r
7067       sizeX = rect.right;\r
7068       sizeY = rect.bottom;\r
7069       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7070           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7071         WINDOWPLACEMENT wp;\r
7072         EnsureOnScreen(&commentX, &commentY);\r
7073         wp.length = sizeof(WINDOWPLACEMENT);\r
7074         wp.flags = 0;\r
7075         wp.showCmd = SW_SHOW;\r
7076         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7077         wp.rcNormalPosition.left = commentX;\r
7078         wp.rcNormalPosition.right = commentX + commentW;\r
7079         wp.rcNormalPosition.top = commentY;\r
7080         wp.rcNormalPosition.bottom = commentY + commentH;\r
7081         SetWindowPlacement(hDlg, &wp);\r
7082 \r
7083         GetClientRect(hDlg, &rect);\r
7084         newSizeX = rect.right;\r
7085         newSizeY = rect.bottom;\r
7086         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7087                               newSizeX, newSizeY);\r
7088         sizeX = newSizeX;\r
7089         sizeY = newSizeY;\r
7090       }\r
7091     }\r
7092     return FALSE;\r
7093 \r
7094   case WM_COMMAND: /* message: received a command */\r
7095     switch (LOWORD(wParam)) {\r
7096     case IDOK:\r
7097       if (editComment) {\r
7098         char *p, *q;\r
7099         /* Read changed options from the dialog box */\r
7100         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7101         len = GetWindowTextLength(hwndText);\r
7102         str = (char *) malloc(len + 1);\r
7103         GetWindowText(hwndText, str, len + 1);\r
7104         p = q = str;\r
7105         while (*q) {\r
7106           if (*q == '\r')\r
7107             q++;\r
7108           else\r
7109             *p++ = *q++;\r
7110         }\r
7111         *p = NULLCHAR;\r
7112         ReplaceComment(commentIndex, str);\r
7113         free(str);\r
7114       }\r
7115       CommentPopDown();\r
7116       return TRUE;\r
7117 \r
7118     case IDCANCEL:\r
7119     case OPT_CancelComment:\r
7120       CommentPopDown();\r
7121       return TRUE;\r
7122 \r
7123     case OPT_ClearComment:\r
7124       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7125       break;\r
7126 \r
7127     case OPT_EditComment:\r
7128       EditCommentEvent();\r
7129       return TRUE;\r
7130 \r
7131     default:\r
7132       break;\r
7133     }\r
7134     break;\r
7135 \r
7136   case WM_SIZE:\r
7137     newSizeX = LOWORD(lParam);\r
7138     newSizeY = HIWORD(lParam);\r
7139     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7140     sizeX = newSizeX;\r
7141     sizeY = newSizeY;\r
7142     break;\r
7143 \r
7144   case WM_GETMINMAXINFO:\r
7145     /* Prevent resizing window too small */\r
7146     mmi = (MINMAXINFO *) lParam;\r
7147     mmi->ptMinTrackSize.x = 100;\r
7148     mmi->ptMinTrackSize.y = 100;\r
7149     break;\r
7150   }\r
7151   return FALSE;\r
7152 }\r
7153 \r
7154 VOID\r
7155 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7156 {\r
7157   FARPROC lpProc;\r
7158   char *p, *q;\r
7159 \r
7160   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7161 \r
7162   if (str == NULL) str = "";\r
7163   p = (char *) malloc(2 * strlen(str) + 2);\r
7164   q = p;\r
7165   while (*str) {\r
7166     if (*str == '\n') *q++ = '\r';\r
7167     *q++ = *str++;\r
7168   }\r
7169   *q = NULLCHAR;\r
7170   if (commentText != NULL) free(commentText);\r
7171 \r
7172   commentIndex = index;\r
7173   commentTitle = title;\r
7174   commentText = p;\r
7175   editComment = edit;\r
7176 \r
7177   if (commentDialog) {\r
7178     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7179     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7180   } else {\r
7181     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7182     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7183                  hwndMain, (DLGPROC)lpProc);\r
7184     FreeProcInstance(lpProc);\r
7185   }\r
7186   commentDialogUp = TRUE;\r
7187 }\r
7188 \r
7189 \r
7190 /*---------------------------------------------------------------------------*\\r
7191  *\r
7192  * Type-in move dialog functions\r
7193  * \r
7194 \*---------------------------------------------------------------------------*/\r
7195 \r
7196 LRESULT CALLBACK\r
7197 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7198 {\r
7199   char move[MSG_SIZ];\r
7200   HWND hInput;\r
7201   ChessMove moveType;\r
7202   int fromX, fromY, toX, toY;\r
7203   char promoChar;\r
7204 \r
7205   switch (message) {\r
7206   case WM_INITDIALOG:\r
7207     move[0] = (char) lParam;\r
7208     move[1] = NULLCHAR;\r
7209     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7210     hInput = GetDlgItem(hDlg, OPT_Move);\r
7211     SetWindowText(hInput, move);\r
7212     SetFocus(hInput);\r
7213     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7214     return FALSE;\r
7215 \r
7216   case WM_COMMAND:\r
7217     switch (LOWORD(wParam)) {\r
7218     case IDOK:\r
7219       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7220         gameMode != Training) {\r
7221         DisplayMoveError("Displayed move is not current");\r
7222       } else {\r
7223         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7224         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7225           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7226           if (gameMode != Training)\r
7227               forwardMostMove = currentMove;\r
7228           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7229         } else {\r
7230           DisplayMoveError("Could not parse move");\r
7231         }\r
7232       }\r
7233       EndDialog(hDlg, TRUE);\r
7234       return TRUE;\r
7235     case IDCANCEL:\r
7236       EndDialog(hDlg, FALSE);\r
7237       return TRUE;\r
7238     default:\r
7239       break;\r
7240     }\r
7241     break;\r
7242   }\r
7243   return FALSE;\r
7244 }\r
7245 \r
7246 VOID\r
7247 PopUpMoveDialog(char firstchar)\r
7248 {\r
7249     FARPROC lpProc;\r
7250     \r
7251     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7252         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7253         gameMode == AnalyzeMode || gameMode == EditGame || \r
7254         gameMode == EditPosition || gameMode == IcsExamining ||\r
7255         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7256         gameMode == Training) {\r
7257       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7258       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7259         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7260       FreeProcInstance(lpProc);\r
7261     }\r
7262 }\r
7263 \r
7264 /*---------------------------------------------------------------------------*\\r
7265  *\r
7266  * Type-in name dialog functions\r
7267  * \r
7268 \*---------------------------------------------------------------------------*/\r
7269 \r
7270 LRESULT CALLBACK\r
7271 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7272 {\r
7273   char move[MSG_SIZ];\r
7274   HWND hInput;\r
7275 \r
7276   switch (message) {\r
7277   case WM_INITDIALOG:\r
7278     move[0] = (char) lParam;\r
7279     move[1] = NULLCHAR;\r
7280     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7281     hInput = GetDlgItem(hDlg, OPT_Name);\r
7282     SetWindowText(hInput, move);\r
7283     SetFocus(hInput);\r
7284     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7285     return FALSE;\r
7286 \r
7287   case WM_COMMAND:\r
7288     switch (LOWORD(wParam)) {\r
7289     case IDOK:\r
7290       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7291       appData.userName = strdup(move);\r
7292 \r
7293       EndDialog(hDlg, TRUE);\r
7294       return TRUE;\r
7295     case IDCANCEL:\r
7296       EndDialog(hDlg, FALSE);\r
7297       return TRUE;\r
7298     default:\r
7299       break;\r
7300     }\r
7301     break;\r
7302   }\r
7303   return FALSE;\r
7304 }\r
7305 \r
7306 VOID\r
7307 PopUpNameDialog(char firstchar)\r
7308 {\r
7309     FARPROC lpProc;\r
7310     \r
7311       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7312       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7313         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7314       FreeProcInstance(lpProc);\r
7315 }\r
7316 \r
7317 /*---------------------------------------------------------------------------*\\r
7318  *\r
7319  *  Error dialogs\r
7320  * \r
7321 \*---------------------------------------------------------------------------*/\r
7322 \r
7323 /* Nonmodal error box */\r
7324 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7325                              WPARAM wParam, LPARAM lParam);\r
7326 \r
7327 VOID\r
7328 ErrorPopUp(char *title, char *content)\r
7329 {\r
7330   FARPROC lpProc;\r
7331   char *p, *q;\r
7332   BOOLEAN modal = hwndMain == NULL;\r
7333 \r
7334   p = content;\r
7335   q = errorMessage;\r
7336   while (*p) {\r
7337     if (*p == '\n') {\r
7338       if (modal) {\r
7339         *q++ = ' ';\r
7340         p++;\r
7341       } else {\r
7342         *q++ = '\r';\r
7343         *q++ = *p++;\r
7344       }\r
7345     } else {\r
7346       *q++ = *p++;\r
7347     }\r
7348   }\r
7349   *q = NULLCHAR;\r
7350   strncpy(errorTitle, title, sizeof(errorTitle));\r
7351   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7352   \r
7353   if (modal) {\r
7354     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7355   } else {\r
7356     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7357     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7358                  hwndMain, (DLGPROC)lpProc);\r
7359     FreeProcInstance(lpProc);\r
7360   }\r
7361 }\r
7362 \r
7363 VOID\r
7364 ErrorPopDown()\r
7365 {\r
7366   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7367   if (errorDialog == NULL) return;\r
7368   DestroyWindow(errorDialog);\r
7369   errorDialog = NULL;\r
7370 }\r
7371 \r
7372 LRESULT CALLBACK\r
7373 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7374 {\r
7375   HANDLE hwndText;\r
7376   RECT rChild;\r
7377 \r
7378   switch (message) {\r
7379   case WM_INITDIALOG:\r
7380     GetWindowRect(hDlg, &rChild);\r
7381 \r
7382     /*\r
7383     SetWindowPos(hDlg, NULL, rChild.left,\r
7384       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7385       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7386     */\r
7387 \r
7388     /* \r
7389         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7390         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7391         and it doesn't work when you resize the dialog.\r
7392         For now, just give it a default position.\r
7393     */\r
7394     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7395 \r
7396     errorDialog = hDlg;\r
7397     SetWindowText(hDlg, errorTitle);\r
7398     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7399     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7400     return FALSE;\r
7401 \r
7402   case WM_COMMAND:\r
7403     switch (LOWORD(wParam)) {\r
7404     case IDOK:\r
7405     case IDCANCEL:\r
7406       if (errorDialog == hDlg) errorDialog = NULL;\r
7407       DestroyWindow(hDlg);\r
7408       return TRUE;\r
7409 \r
7410     default:\r
7411       break;\r
7412     }\r
7413     break;\r
7414   }\r
7415   return FALSE;\r
7416 }\r
7417 \r
7418 #ifdef GOTHIC\r
7419 HWND gothicDialog = NULL;\r
7420 \r
7421 LRESULT CALLBACK\r
7422 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7423 {\r
7424   HANDLE hwndText;\r
7425   RECT rChild;\r
7426   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7427 \r
7428   switch (message) {\r
7429   case WM_INITDIALOG:\r
7430     GetWindowRect(hDlg, &rChild);\r
7431 \r
7432     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7433                                                              SWP_NOZORDER);\r
7434 \r
7435     /* \r
7436         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7437         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7438         and it doesn't work when you resize the dialog.\r
7439         For now, just give it a default position.\r
7440     */\r
7441     gothicDialog = hDlg;\r
7442     SetWindowText(hDlg, errorTitle);\r
7443     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7444     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7445     return FALSE;\r
7446 \r
7447   case WM_COMMAND:\r
7448     switch (LOWORD(wParam)) {\r
7449     case IDOK:\r
7450     case IDCANCEL:\r
7451       if (errorDialog == hDlg) errorDialog = NULL;\r
7452       DestroyWindow(hDlg);\r
7453       return TRUE;\r
7454 \r
7455     default:\r
7456       break;\r
7457     }\r
7458     break;\r
7459   }\r
7460   return FALSE;\r
7461 }\r
7462 \r
7463 VOID\r
7464 GothicPopUp(char *title, VariantClass variant)\r
7465 {\r
7466   FARPROC lpProc;\r
7467   static char *lastTitle;\r
7468 \r
7469   strncpy(errorTitle, title, sizeof(errorTitle));\r
7470   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7471 \r
7472   if(lastTitle != title && gothicDialog != NULL) {\r
7473     DestroyWindow(gothicDialog);\r
7474     gothicDialog = NULL;\r
7475   }\r
7476   if(variant != VariantNormal && gothicDialog == NULL) {\r
7477     title = lastTitle;\r
7478     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7479     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7480                  hwndMain, (DLGPROC)lpProc);\r
7481     FreeProcInstance(lpProc);\r
7482   }\r
7483 }\r
7484 #endif\r
7485 \r
7486 /*---------------------------------------------------------------------------*\\r
7487  *\r
7488  *  Ics Interaction console functions\r
7489  *\r
7490 \*---------------------------------------------------------------------------*/\r
7491 \r
7492 #define HISTORY_SIZE 64\r
7493 static char *history[HISTORY_SIZE];\r
7494 int histIn = 0, histP = 0;\r
7495 \r
7496 VOID\r
7497 SaveInHistory(char *cmd)\r
7498 {\r
7499   if (history[histIn] != NULL) {\r
7500     free(history[histIn]);\r
7501     history[histIn] = NULL;\r
7502   }\r
7503   if (*cmd == NULLCHAR) return;\r
7504   history[histIn] = StrSave(cmd);\r
7505   histIn = (histIn + 1) % HISTORY_SIZE;\r
7506   if (history[histIn] != NULL) {\r
7507     free(history[histIn]);\r
7508     history[histIn] = NULL;\r
7509   }\r
7510   histP = histIn;\r
7511 }\r
7512 \r
7513 char *\r
7514 PrevInHistory(char *cmd)\r
7515 {\r
7516   int newhp;\r
7517   if (histP == histIn) {\r
7518     if (history[histIn] != NULL) free(history[histIn]);\r
7519     history[histIn] = StrSave(cmd);\r
7520   }\r
7521   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7522   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7523   histP = newhp;\r
7524   return history[histP];\r
7525 }\r
7526 \r
7527 char *\r
7528 NextInHistory()\r
7529 {\r
7530   if (histP == histIn) return NULL;\r
7531   histP = (histP + 1) % HISTORY_SIZE;\r
7532   return history[histP];\r
7533 }\r
7534 \r
7535 typedef struct {\r
7536   char *item;\r
7537   char *command;\r
7538   BOOLEAN getname;\r
7539   BOOLEAN immediate;\r
7540 } IcsTextMenuEntry;\r
7541 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7542 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7543 \r
7544 void\r
7545 ParseIcsTextMenu(char *icsTextMenuString)\r
7546 {\r
7547 //  int flags = 0;\r
7548   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7549   char *p = icsTextMenuString;\r
7550   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7551     free(e->item);\r
7552     e->item = NULL;\r
7553     if (e->command != NULL) {\r
7554       free(e->command);\r
7555       e->command = NULL;\r
7556     }\r
7557     e++;\r
7558   }\r
7559   e = icsTextMenuEntry;\r
7560   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7561     if (*p == ';' || *p == '\n') {\r
7562       e->item = strdup("-");\r
7563       e->command = NULL;\r
7564       p++;\r
7565     } else if (*p == '-') {\r
7566       e->item = strdup("-");\r
7567       e->command = NULL;\r
7568       p++;\r
7569       if (*p) p++;\r
7570     } else {\r
7571       char *q, *r, *s, *t;\r
7572       char c;\r
7573       q = strchr(p, ',');\r
7574       if (q == NULL) break;\r
7575       *q = NULLCHAR;\r
7576       r = strchr(q + 1, ',');\r
7577       if (r == NULL) break;\r
7578       *r = NULLCHAR;\r
7579       s = strchr(r + 1, ',');\r
7580       if (s == NULL) break;\r
7581       *s = NULLCHAR;\r
7582       c = ';';\r
7583       t = strchr(s + 1, c);\r
7584       if (t == NULL) {\r
7585         c = '\n';\r
7586         t = strchr(s + 1, c);\r
7587       }\r
7588       if (t != NULL) *t = NULLCHAR;\r
7589       e->item = strdup(p);\r
7590       e->command = strdup(q + 1);\r
7591       e->getname = *(r + 1) != '0';\r
7592       e->immediate = *(s + 1) != '0';\r
7593       *q = ',';\r
7594       *r = ',';\r
7595       *s = ',';\r
7596       if (t == NULL) break;\r
7597       *t = c;\r
7598       p = t + 1;\r
7599     }\r
7600     e++;\r
7601   } \r
7602 }\r
7603 \r
7604 HMENU\r
7605 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7606 {\r
7607   HMENU hmenu, h;\r
7608   int i = 0;\r
7609   hmenu = LoadMenu(hInst, "TextMenu");\r
7610   h = GetSubMenu(hmenu, 0);\r
7611   while (e->item) {\r
7612     if (strcmp(e->item, "-") == 0) {\r
7613       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7614     } else {\r
7615       if (e->item[0] == '|') {\r
7616         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7617                    IDM_CommandX + i, &e->item[1]);\r
7618       } else {\r
7619         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7620       }\r
7621     }\r
7622     e++;\r
7623     i++;\r
7624   } \r
7625   return hmenu;\r
7626 }\r
7627 \r
7628 WNDPROC consoleTextWindowProc;\r
7629 \r
7630 void\r
7631 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7632 {\r
7633   char buf[MSG_SIZ], name[MSG_SIZ];\r
7634   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7635   CHARRANGE sel;\r
7636 \r
7637   if (!getname) {\r
7638     SetWindowText(hInput, command);\r
7639     if (immediate) {\r
7640       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7641     } else {\r
7642       sel.cpMin = 999999;\r
7643       sel.cpMax = 999999;\r
7644       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7645       SetFocus(hInput);\r
7646     }\r
7647     return;\r
7648   }    \r
7649   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7650   if (sel.cpMin == sel.cpMax) {\r
7651     /* Expand to surrounding word */\r
7652     TEXTRANGE tr;\r
7653     do {\r
7654       tr.chrg.cpMax = sel.cpMin;\r
7655       tr.chrg.cpMin = --sel.cpMin;\r
7656       if (sel.cpMin < 0) break;\r
7657       tr.lpstrText = name;\r
7658       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7659     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7660     sel.cpMin++;\r
7661 \r
7662     do {\r
7663       tr.chrg.cpMin = sel.cpMax;\r
7664       tr.chrg.cpMax = ++sel.cpMax;\r
7665       tr.lpstrText = name;\r
7666       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7667     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7668     sel.cpMax--;\r
7669 \r
7670     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7671       MessageBeep(MB_ICONEXCLAMATION);\r
7672       return;\r
7673     }\r
7674     tr.chrg = sel;\r
7675     tr.lpstrText = name;\r
7676     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7677   } else {\r
7678     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7679       MessageBeep(MB_ICONEXCLAMATION);\r
7680       return;\r
7681     }\r
7682     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7683   }\r
7684   if (immediate) {\r
7685     sprintf(buf, "%s %s", command, name);\r
7686     SetWindowText(hInput, buf);\r
7687     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7688   } else {\r
7689     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7690     SetWindowText(hInput, buf);\r
7691     sel.cpMin = 999999;\r
7692     sel.cpMax = 999999;\r
7693     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7694     SetFocus(hInput);\r
7695   }\r
7696 }\r
7697 \r
7698 LRESULT CALLBACK \r
7699 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7700 {\r
7701   HWND hInput;\r
7702   CHARRANGE sel;\r
7703 \r
7704   switch (message) {\r
7705   case WM_KEYDOWN:\r
7706     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7707     switch (wParam) {\r
7708     case VK_PRIOR:\r
7709       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7710       return 0;\r
7711     case VK_NEXT:\r
7712       sel.cpMin = 999999;\r
7713       sel.cpMax = 999999;\r
7714       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7715       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7716       return 0;\r
7717     }\r
7718     break;\r
7719   case WM_CHAR:\r
7720     if (wParam == '\t') {\r
7721       if (GetKeyState(VK_SHIFT) < 0) {\r
7722         /* shifted */\r
7723         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7724         if (buttonDesc[0].hwnd) {\r
7725           SetFocus(buttonDesc[0].hwnd);\r
7726         } else {\r
7727           SetFocus(hwndMain);\r
7728         }\r
7729       } else {\r
7730         /* unshifted */\r
7731         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7732       }\r
7733     } else {\r
7734       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7735       SetFocus(hInput);\r
7736       SendMessage(hInput, message, wParam, lParam);\r
7737     }\r
7738     return 0;\r
7739   case WM_PASTE:\r
7740     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7741     SetFocus(hInput);\r
7742     return SendMessage(hInput, message, wParam, lParam);\r
7743   case WM_MBUTTONDOWN:\r
7744     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7745   case WM_RBUTTONDOWN:\r
7746     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7747       /* Move selection here if it was empty */\r
7748       POINT pt;\r
7749       pt.x = LOWORD(lParam);\r
7750       pt.y = HIWORD(lParam);\r
7751       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7752       if (sel.cpMin == sel.cpMax) {\r
7753         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7754         sel.cpMax = sel.cpMin;\r
7755         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7756       }\r
7757       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7758     }\r
7759     return 0;\r
7760   case WM_RBUTTONUP:\r
7761     if (GetKeyState(VK_SHIFT) & ~1) {\r
7762       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7763         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7764     } else {\r
7765       POINT pt;\r
7766       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7767       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7768       if (sel.cpMin == sel.cpMax) {\r
7769         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7770         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7771       }\r
7772       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7773         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7774       }\r
7775       pt.x = LOWORD(lParam);\r
7776       pt.y = HIWORD(lParam);\r
7777       MenuPopup(hwnd, pt, hmenu, -1);\r
7778     }\r
7779     return 0;\r
7780   case WM_COMMAND:\r
7781     switch (LOWORD(wParam)) {\r
7782     case IDM_QuickPaste:\r
7783       {\r
7784         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7785         if (sel.cpMin == sel.cpMax) {\r
7786           MessageBeep(MB_ICONEXCLAMATION);\r
7787           return 0;\r
7788         }\r
7789         SendMessage(hwnd, WM_COPY, 0, 0);\r
7790         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7791         SendMessage(hInput, WM_PASTE, 0, 0);\r
7792         SetFocus(hInput);\r
7793         return 0;\r
7794       }\r
7795     case IDM_Cut:\r
7796       SendMessage(hwnd, WM_CUT, 0, 0);\r
7797       return 0;\r
7798     case IDM_Paste:\r
7799       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7800       return 0;\r
7801     case IDM_Copy:\r
7802       SendMessage(hwnd, WM_COPY, 0, 0);\r
7803       return 0;\r
7804     default:\r
7805       {\r
7806         int i = LOWORD(wParam) - IDM_CommandX;\r
7807         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7808             icsTextMenuEntry[i].command != NULL) {\r
7809           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7810                    icsTextMenuEntry[i].getname,\r
7811                    icsTextMenuEntry[i].immediate);\r
7812           return 0;\r
7813         }\r
7814       }\r
7815       break;\r
7816     }\r
7817     break;\r
7818   }\r
7819   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7820 }\r
7821 \r
7822 WNDPROC consoleInputWindowProc;\r
7823 \r
7824 LRESULT CALLBACK\r
7825 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7826 {\r
7827   char buf[MSG_SIZ];\r
7828   char *p;\r
7829   static BOOL sendNextChar = FALSE;\r
7830   static BOOL quoteNextChar = FALSE;\r
7831   InputSource *is = consoleInputSource;\r
7832   CHARFORMAT cf;\r
7833   CHARRANGE sel;\r
7834 \r
7835   switch (message) {\r
7836   case WM_CHAR:\r
7837     if (!appData.localLineEditing || sendNextChar) {\r
7838       is->buf[0] = (CHAR) wParam;\r
7839       is->count = 1;\r
7840       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7841       sendNextChar = FALSE;\r
7842       return 0;\r
7843     }\r
7844     if (quoteNextChar) {\r
7845       buf[0] = (char) wParam;\r
7846       buf[1] = NULLCHAR;\r
7847       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7848       quoteNextChar = FALSE;\r
7849       return 0;\r
7850     }\r
7851     switch (wParam) {\r
7852     case '\r':   /* Enter key */\r
7853       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7854       if (consoleEcho) SaveInHistory(is->buf);\r
7855       is->buf[is->count++] = '\n';\r
7856       is->buf[is->count] = NULLCHAR;\r
7857       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7858       if (consoleEcho) {\r
7859         ConsoleOutput(is->buf, is->count, TRUE);\r
7860       } else if (appData.localLineEditing) {\r
7861         ConsoleOutput("\n", 1, TRUE);\r
7862       }\r
7863       /* fall thru */\r
7864     case '\033': /* Escape key */\r
7865       SetWindowText(hwnd, "");\r
7866       cf.cbSize = sizeof(CHARFORMAT);\r
7867       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7868       if (consoleEcho) {\r
7869         cf.crTextColor = textAttribs[ColorNormal].color;\r
7870       } else {\r
7871         cf.crTextColor = COLOR_ECHOOFF;\r
7872       }\r
7873       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7874       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7875       return 0;\r
7876     case '\t':   /* Tab key */\r
7877       if (GetKeyState(VK_SHIFT) < 0) {\r
7878         /* shifted */\r
7879         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7880       } else {\r
7881         /* unshifted */\r
7882         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7883         if (buttonDesc[0].hwnd) {\r
7884           SetFocus(buttonDesc[0].hwnd);\r
7885         } else {\r
7886           SetFocus(hwndMain);\r
7887         }\r
7888       }\r
7889       return 0;\r
7890     case '\023': /* Ctrl+S */\r
7891       sendNextChar = TRUE;\r
7892       return 0;\r
7893     case '\021': /* Ctrl+Q */\r
7894       quoteNextChar = TRUE;\r
7895       return 0;\r
7896     default:\r
7897       break;\r
7898     }\r
7899     break;\r
7900   case WM_KEYDOWN:\r
7901     switch (wParam) {\r
7902     case VK_UP:\r
7903       GetWindowText(hwnd, buf, MSG_SIZ);\r
7904       p = PrevInHistory(buf);\r
7905       if (p != NULL) {\r
7906         SetWindowText(hwnd, p);\r
7907         sel.cpMin = 999999;\r
7908         sel.cpMax = 999999;\r
7909         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7910         return 0;\r
7911       }\r
7912       break;\r
7913     case VK_DOWN:\r
7914       p = NextInHistory();\r
7915       if (p != NULL) {\r
7916         SetWindowText(hwnd, p);\r
7917         sel.cpMin = 999999;\r
7918         sel.cpMax = 999999;\r
7919         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7920         return 0;\r
7921       }\r
7922       break;\r
7923     case VK_HOME:\r
7924     case VK_END:\r
7925       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7926       /* fall thru */\r
7927     case VK_PRIOR:\r
7928     case VK_NEXT:\r
7929       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7930       return 0;\r
7931     }\r
7932     break;\r
7933   case WM_MBUTTONDOWN:\r
7934     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7935       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7936     break;\r
7937   case WM_RBUTTONUP:\r
7938     if (GetKeyState(VK_SHIFT) & ~1) {\r
7939       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7940         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7941     } else {\r
7942       POINT pt;\r
7943       HMENU hmenu;\r
7944       hmenu = LoadMenu(hInst, "InputMenu");\r
7945       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7946       if (sel.cpMin == sel.cpMax) {\r
7947         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7948         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7949       }\r
7950       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7951         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7952       }\r
7953       pt.x = LOWORD(lParam);\r
7954       pt.y = HIWORD(lParam);\r
7955       MenuPopup(hwnd, pt, hmenu, -1);\r
7956     }\r
7957     return 0;\r
7958   case WM_COMMAND:\r
7959     switch (LOWORD(wParam)) { \r
7960     case IDM_Undo:\r
7961       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7962       return 0;\r
7963     case IDM_SelectAll:\r
7964       sel.cpMin = 0;\r
7965       sel.cpMax = -1; /*999999?*/\r
7966       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7967       return 0;\r
7968     case IDM_Cut:\r
7969       SendMessage(hwnd, WM_CUT, 0, 0);\r
7970       return 0;\r
7971     case IDM_Paste:\r
7972       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7973       return 0;\r
7974     case IDM_Copy:\r
7975       SendMessage(hwnd, WM_COPY, 0, 0);\r
7976       return 0;\r
7977     }\r
7978     break;\r
7979   }\r
7980   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7981 }\r
7982 \r
7983 #define CO_MAX  100000\r
7984 #define CO_TRIM   1000\r
7985 \r
7986 LRESULT CALLBACK\r
7987 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7988 {\r
7989   static SnapData sd;\r
7990   static HWND hText, hInput /*, hFocus*/;\r
7991 //  InputSource *is = consoleInputSource;\r
7992   RECT rect;\r
7993   static int sizeX, sizeY;\r
7994   int newSizeX, newSizeY;\r
7995   MINMAXINFO *mmi;\r
7996 \r
7997   switch (message) {\r
7998   case WM_INITDIALOG: /* message: initialize dialog box */\r
7999     hwndConsole = hDlg;\r
8000     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8001     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8002     SetFocus(hInput);\r
8003     consoleTextWindowProc = (WNDPROC)\r
8004       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8005     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8006     consoleInputWindowProc = (WNDPROC)\r
8007       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8008     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8009     Colorize(ColorNormal, TRUE);\r
8010     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8011     ChangedConsoleFont();\r
8012     GetClientRect(hDlg, &rect);\r
8013     sizeX = rect.right;\r
8014     sizeY = rect.bottom;\r
8015     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8016         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8017       WINDOWPLACEMENT wp;\r
8018       EnsureOnScreen(&consoleX, &consoleY);\r
8019       wp.length = sizeof(WINDOWPLACEMENT);\r
8020       wp.flags = 0;\r
8021       wp.showCmd = SW_SHOW;\r
8022       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8023       wp.rcNormalPosition.left = consoleX;\r
8024       wp.rcNormalPosition.right = consoleX + consoleW;\r
8025       wp.rcNormalPosition.top = consoleY;\r
8026       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8027       SetWindowPlacement(hDlg, &wp);\r
8028     }\r
8029 #if 0 \r
8030    // [HGM] Chessknight's change 2004-07-13\r
8031    else { /* Determine Defaults */\r
8032        WINDOWPLACEMENT wp;\r
8033        consoleX = winWidth + 1;\r
8034        consoleY = boardY;\r
8035        consoleW = screenWidth -  winWidth;\r
8036        consoleH = winHeight;\r
8037        EnsureOnScreen(&consoleX, &consoleY);\r
8038        wp.length = sizeof(WINDOWPLACEMENT);\r
8039        wp.flags = 0;\r
8040        wp.showCmd = SW_SHOW;\r
8041        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8042        wp.rcNormalPosition.left = consoleX;\r
8043        wp.rcNormalPosition.right = consoleX + consoleW;\r
8044        wp.rcNormalPosition.top = consoleY;\r
8045        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8046        SetWindowPlacement(hDlg, &wp);\r
8047     }\r
8048 #endif\r
8049     return FALSE;\r
8050 \r
8051   case WM_SETFOCUS:\r
8052     SetFocus(hInput);\r
8053     return 0;\r
8054 \r
8055   case WM_CLOSE:\r
8056     ExitEvent(0);\r
8057     /* not reached */\r
8058     break;\r
8059 \r
8060   case WM_SIZE:\r
8061     if (IsIconic(hDlg)) break;\r
8062     newSizeX = LOWORD(lParam);\r
8063     newSizeY = HIWORD(lParam);\r
8064     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8065       RECT rectText, rectInput;\r
8066       POINT pt;\r
8067       int newTextHeight, newTextWidth;\r
8068       GetWindowRect(hText, &rectText);\r
8069       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8070       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8071       if (newTextHeight < 0) {\r
8072         newSizeY += -newTextHeight;\r
8073         newTextHeight = 0;\r
8074       }\r
8075       SetWindowPos(hText, NULL, 0, 0,\r
8076         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8077       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8078       pt.x = rectInput.left;\r
8079       pt.y = rectInput.top + newSizeY - sizeY;\r
8080       ScreenToClient(hDlg, &pt);\r
8081       SetWindowPos(hInput, NULL, \r
8082         pt.x, pt.y, /* needs client coords */   \r
8083         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8084         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8085     }\r
8086     sizeX = newSizeX;\r
8087     sizeY = newSizeY;\r
8088     break;\r
8089 \r
8090   case WM_GETMINMAXINFO:\r
8091     /* Prevent resizing window too small */\r
8092     mmi = (MINMAXINFO *) lParam;\r
8093     mmi->ptMinTrackSize.x = 100;\r
8094     mmi->ptMinTrackSize.y = 100;\r
8095     break;\r
8096 \r
8097   /* [AS] Snapping */\r
8098   case WM_ENTERSIZEMOVE:\r
8099     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8100 \r
8101   case WM_SIZING:\r
8102     return OnSizing( &sd, hDlg, wParam, lParam );\r
8103 \r
8104   case WM_MOVING:\r
8105     return OnMoving( &sd, hDlg, wParam, lParam );\r
8106 \r
8107   case WM_EXITSIZEMOVE:\r
8108     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8109   }\r
8110 \r
8111   return DefWindowProc(hDlg, message, wParam, lParam);\r
8112 }\r
8113 \r
8114 \r
8115 VOID\r
8116 ConsoleCreate()\r
8117 {\r
8118   HWND hCons;\r
8119   if (hwndConsole) return;\r
8120   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8121   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8122 }\r
8123 \r
8124 \r
8125 VOID\r
8126 ConsoleOutput(char* data, int length, int forceVisible)\r
8127 {\r
8128   HWND hText;\r
8129   int trim, exlen;\r
8130   char *p, *q;\r
8131   char buf[CO_MAX+1];\r
8132   POINT pEnd;\r
8133   RECT rect;\r
8134   static int delayLF = 0;\r
8135   CHARRANGE savesel, sel;\r
8136 \r
8137   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8138   p = data;\r
8139   q = buf;\r
8140   if (delayLF) {\r
8141     *q++ = '\r';\r
8142     *q++ = '\n';\r
8143     delayLF = 0;\r
8144   }\r
8145   while (length--) {\r
8146     if (*p == '\n') {\r
8147       if (*++p) {\r
8148         *q++ = '\r';\r
8149         *q++ = '\n';\r
8150       } else {\r
8151         delayLF = 1;\r
8152       }\r
8153     } else if (*p == '\007') {\r
8154        MyPlaySound(&sounds[(int)SoundBell]);\r
8155        p++;\r
8156     } else {\r
8157       *q++ = *p++;\r
8158     }\r
8159   }\r
8160   *q = NULLCHAR;\r
8161   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8162   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8163   /* Save current selection */\r
8164   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8165   exlen = GetWindowTextLength(hText);\r
8166   /* Find out whether current end of text is visible */\r
8167   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8168   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8169   /* Trim existing text if it's too long */\r
8170   if (exlen + (q - buf) > CO_MAX) {\r
8171     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8172     sel.cpMin = 0;\r
8173     sel.cpMax = trim;\r
8174     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8175     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8176     exlen -= trim;\r
8177     savesel.cpMin -= trim;\r
8178     savesel.cpMax -= trim;\r
8179     if (exlen < 0) exlen = 0;\r
8180     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8181     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8182   }\r
8183   /* Append the new text */\r
8184   sel.cpMin = exlen;\r
8185   sel.cpMax = exlen;\r
8186   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8187   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8188   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8189   if (forceVisible || exlen == 0 ||\r
8190       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8191        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8192     /* Scroll to make new end of text visible if old end of text\r
8193        was visible or new text is an echo of user typein */\r
8194     sel.cpMin = 9999999;\r
8195     sel.cpMax = 9999999;\r
8196     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8197     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8198     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8199     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8200   }\r
8201   if (savesel.cpMax == exlen || forceVisible) {\r
8202     /* Move insert point to new end of text if it was at the old\r
8203        end of text or if the new text is an echo of user typein */\r
8204     sel.cpMin = 9999999;\r
8205     sel.cpMax = 9999999;\r
8206     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8207   } else {\r
8208     /* Restore previous selection */\r
8209     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8210   }\r
8211   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8212 }\r
8213 \r
8214 /*---------*/\r
8215 \r
8216 \r
8217 void\r
8218 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8219 {\r
8220   char buf[100];\r
8221   char *str;\r
8222   COLORREF oldFg, oldBg;\r
8223   HFONT oldFont;\r
8224   RECT rect;\r
8225 \r
8226   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8227 \r
8228   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8229   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8230   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8231 \r
8232   rect.left = x;\r
8233   rect.right = x + squareSize;\r
8234   rect.top  = y;\r
8235   rect.bottom = y + squareSize;\r
8236   str = buf;\r
8237 \r
8238   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8239                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8240              y, ETO_CLIPPED|ETO_OPAQUE,\r
8241              &rect, str, strlen(str), NULL);\r
8242 \r
8243   (void) SetTextColor(hdc, oldFg);\r
8244   (void) SetBkColor(hdc, oldBg);\r
8245   (void) SelectObject(hdc, oldFont);\r
8246 }\r
8247 \r
8248 void\r
8249 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8250               RECT *rect, char *color, char *flagFell)\r
8251 {\r
8252   char buf[100];\r
8253   char *str;\r
8254   COLORREF oldFg, oldBg;\r
8255   HFONT oldFont;\r
8256 \r
8257   if (appData.clockMode) {\r
8258     if (tinyLayout)\r
8259       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8260     else\r
8261       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8262     str = buf;\r
8263   } else {\r
8264     str = color;\r
8265   }\r
8266 \r
8267   if (highlight) {\r
8268     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8269     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8270   } else {\r
8271     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8272     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8273   }\r
8274   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8275 \r
8276   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8277              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8278              rect, str, strlen(str), NULL);\r
8279 \r
8280   (void) SetTextColor(hdc, oldFg);\r
8281   (void) SetBkColor(hdc, oldBg);\r
8282   (void) SelectObject(hdc, oldFont);\r
8283 }\r
8284 \r
8285 \r
8286 int\r
8287 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8288            OVERLAPPED *ovl)\r
8289 {\r
8290   int ok, err;\r
8291 \r
8292   /* [AS]  */\r
8293   if( count <= 0 ) {\r
8294     if (appData.debugMode) {\r
8295       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8296     }\r
8297 \r
8298     return ERROR_INVALID_USER_BUFFER;\r
8299   }\r
8300 \r
8301   ResetEvent(ovl->hEvent);\r
8302   ovl->Offset = ovl->OffsetHigh = 0;\r
8303   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8304   if (ok) {\r
8305     err = NO_ERROR;\r
8306   } else {\r
8307     err = GetLastError();\r
8308     if (err == ERROR_IO_PENDING) {\r
8309       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8310       if (ok)\r
8311         err = NO_ERROR;\r
8312       else\r
8313         err = GetLastError();\r
8314     }\r
8315   }\r
8316   return err;\r
8317 }\r
8318 \r
8319 int\r
8320 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8321             OVERLAPPED *ovl)\r
8322 {\r
8323   int ok, err;\r
8324 \r
8325   ResetEvent(ovl->hEvent);\r
8326   ovl->Offset = ovl->OffsetHigh = 0;\r
8327   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8328   if (ok) {\r
8329     err = NO_ERROR;\r
8330   } else {\r
8331     err = GetLastError();\r
8332     if (err == ERROR_IO_PENDING) {\r
8333       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8334       if (ok)\r
8335         err = NO_ERROR;\r
8336       else\r
8337         err = GetLastError();\r
8338     }\r
8339   }\r
8340   return err;\r
8341 }\r
8342 \r
8343 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8344 void CheckForInputBufferFull( InputSource * is )\r
8345 {\r
8346     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8347         /* Look for end of line */\r
8348         char * p = is->buf;\r
8349         \r
8350         while( p < is->next && *p != '\n' ) {\r
8351             p++;\r
8352         }\r
8353 \r
8354         if( p >= is->next ) {\r
8355             if (appData.debugMode) {\r
8356                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8357             }\r
8358 \r
8359             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8360             is->count = (DWORD) -1;\r
8361             is->next = is->buf;\r
8362         }\r
8363     }\r
8364 }\r
8365 \r
8366 DWORD\r
8367 InputThread(LPVOID arg)\r
8368 {\r
8369   InputSource *is;\r
8370   OVERLAPPED ovl;\r
8371 \r
8372   is = (InputSource *) arg;\r
8373   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8374   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8375   while (is->hThread != NULL) {\r
8376     is->error = DoReadFile(is->hFile, is->next,\r
8377                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8378                            &is->count, &ovl);\r
8379     if (is->error == NO_ERROR) {\r
8380       is->next += is->count;\r
8381     } else {\r
8382       if (is->error == ERROR_BROKEN_PIPE) {\r
8383         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8384         is->count = 0;\r
8385       } else {\r
8386         is->count = (DWORD) -1;\r
8387         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8388         break; \r
8389       }\r
8390     }\r
8391 \r
8392     CheckForInputBufferFull( is );\r
8393 \r
8394     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8395 \r
8396     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8397 \r
8398     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8399   }\r
8400 \r
8401   CloseHandle(ovl.hEvent);\r
8402   CloseHandle(is->hFile);\r
8403 \r
8404   if (appData.debugMode) {\r
8405     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8406   }\r
8407 \r
8408   return 0;\r
8409 }\r
8410 \r
8411 \r
8412 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8413 DWORD\r
8414 NonOvlInputThread(LPVOID arg)\r
8415 {\r
8416   InputSource *is;\r
8417   char *p, *q;\r
8418   int i;\r
8419   char prev;\r
8420 \r
8421   is = (InputSource *) arg;\r
8422   while (is->hThread != NULL) {\r
8423     is->error = ReadFile(is->hFile, is->next,\r
8424                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8425                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8426     if (is->error == NO_ERROR) {\r
8427       /* Change CRLF to LF */\r
8428       if (is->next > is->buf) {\r
8429         p = is->next - 1;\r
8430         i = is->count + 1;\r
8431       } else {\r
8432         p = is->next;\r
8433         i = is->count;\r
8434       }\r
8435       q = p;\r
8436       prev = NULLCHAR;\r
8437       while (i > 0) {\r
8438         if (prev == '\r' && *p == '\n') {\r
8439           *(q-1) = '\n';\r
8440           is->count--;\r
8441         } else { \r
8442           *q++ = *p;\r
8443         }\r
8444         prev = *p++;\r
8445         i--;\r
8446       }\r
8447       *q = NULLCHAR;\r
8448       is->next = q;\r
8449     } else {\r
8450       if (is->error == ERROR_BROKEN_PIPE) {\r
8451         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8452         is->count = 0; \r
8453       } else {\r
8454         is->count = (DWORD) -1;\r
8455       }\r
8456     }\r
8457 \r
8458     CheckForInputBufferFull( is );\r
8459 \r
8460     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8461 \r
8462     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8463 \r
8464     if (is->count < 0) break;  /* Quit on error */\r
8465   }\r
8466   CloseHandle(is->hFile);\r
8467   return 0;\r
8468 }\r
8469 \r
8470 DWORD\r
8471 SocketInputThread(LPVOID arg)\r
8472 {\r
8473   InputSource *is;\r
8474 \r
8475   is = (InputSource *) arg;\r
8476   while (is->hThread != NULL) {\r
8477     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8478     if ((int)is->count == SOCKET_ERROR) {\r
8479       is->count = (DWORD) -1;\r
8480       is->error = WSAGetLastError();\r
8481     } else {\r
8482       is->error = NO_ERROR;\r
8483       is->next += is->count;\r
8484       if (is->count == 0 && is->second == is) {\r
8485         /* End of file on stderr; quit with no message */\r
8486         break;\r
8487       }\r
8488     }\r
8489     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8490 \r
8491     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8492 \r
8493     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8494   }\r
8495   return 0;\r
8496 }\r
8497 \r
8498 VOID\r
8499 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8500 {\r
8501   InputSource *is;\r
8502 \r
8503   is = (InputSource *) lParam;\r
8504   if (is->lineByLine) {\r
8505     /* Feed in lines one by one */\r
8506     char *p = is->buf;\r
8507     char *q = p;\r
8508     while (q < is->next) {\r
8509       if (*q++ == '\n') {\r
8510         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8511         p = q;\r
8512       }\r
8513     }\r
8514     \r
8515     /* Move any partial line to the start of the buffer */\r
8516     q = is->buf;\r
8517     while (p < is->next) {\r
8518       *q++ = *p++;\r
8519     }\r
8520     is->next = q;\r
8521 \r
8522     if (is->error != NO_ERROR || is->count == 0) {\r
8523       /* Notify backend of the error.  Note: If there was a partial\r
8524          line at the end, it is not flushed through. */\r
8525       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8526     }\r
8527   } else {\r
8528     /* Feed in the whole chunk of input at once */\r
8529     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8530     is->next = is->buf;\r
8531   }\r
8532 }\r
8533 \r
8534 /*---------------------------------------------------------------------------*\\r
8535  *\r
8536  *  Menu enables. Used when setting various modes.\r
8537  *\r
8538 \*---------------------------------------------------------------------------*/\r
8539 \r
8540 typedef struct {\r
8541   int item;\r
8542   int flags;\r
8543 } Enables;\r
8544 \r
8545 VOID\r
8546 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8547 {\r
8548   while (enab->item > 0) {\r
8549     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8550     enab++;\r
8551   }\r
8552 }\r
8553 \r
8554 Enables gnuEnables[] = {\r
8555   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8560   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8561   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8565   { -1, -1 }\r
8566 };\r
8567 \r
8568 Enables icsEnables[] = {\r
8569   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8570   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8574   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8575   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8576   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8578   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8580   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8581   { -1, -1 }\r
8582 };\r
8583 \r
8584 #ifdef ZIPPY\r
8585 Enables zippyEnables[] = {\r
8586   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8587   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8588   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8589   { -1, -1 }\r
8590 };\r
8591 #endif\r
8592 \r
8593 Enables ncpEnables[] = {\r
8594   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8595   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8596   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8597   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8598   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8602   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8603   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8609   { -1, -1 }\r
8610 };\r
8611 \r
8612 Enables trainingOnEnables[] = {\r
8613   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8616   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8617   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8618   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8619   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8620   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8621   { -1, -1 }\r
8622 };\r
8623 \r
8624 Enables trainingOffEnables[] = {\r
8625   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8627   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8628   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8631   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8632   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8633   { -1, -1 }\r
8634 };\r
8635 \r
8636 /* These modify either ncpEnables or gnuEnables */\r
8637 Enables cmailEnables[] = {\r
8638   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8639   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8640   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8641   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8642   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8643   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8644   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8645   { -1, -1 }\r
8646 };\r
8647 \r
8648 Enables machineThinkingEnables[] = {\r
8649   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8651   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8652   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8653   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8654   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8656   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8657   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8658   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8659   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8660   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8661   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8662   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8663   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8664   { -1, -1 }\r
8665 };\r
8666 \r
8667 Enables userThinkingEnables[] = {\r
8668   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8669   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8670   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8671   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8672   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8673   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8674   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8675   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8676   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8677   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8678   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8679   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8680   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8681   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8682   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8683   { -1, -1 }\r
8684 };\r
8685 \r
8686 /*---------------------------------------------------------------------------*\\r
8687  *\r
8688  *  Front-end interface functions exported by XBoard.\r
8689  *  Functions appear in same order as prototypes in frontend.h.\r
8690  * \r
8691 \*---------------------------------------------------------------------------*/\r
8692 VOID\r
8693 ModeHighlight()\r
8694 {\r
8695   static UINT prevChecked = 0;\r
8696   static int prevPausing = 0;\r
8697   UINT nowChecked;\r
8698 \r
8699   if (pausing != prevPausing) {\r
8700     prevPausing = pausing;\r
8701     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8702                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8703     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8704   }\r
8705 \r
8706   switch (gameMode) {\r
8707   case BeginningOfGame:\r
8708     if (appData.icsActive)\r
8709       nowChecked = IDM_IcsClient;\r
8710     else if (appData.noChessProgram)\r
8711       nowChecked = IDM_EditGame;\r
8712     else\r
8713       nowChecked = IDM_MachineBlack;\r
8714     break;\r
8715   case MachinePlaysBlack:\r
8716     nowChecked = IDM_MachineBlack;\r
8717     break;\r
8718   case MachinePlaysWhite:\r
8719     nowChecked = IDM_MachineWhite;\r
8720     break;\r
8721   case TwoMachinesPlay:\r
8722     nowChecked = IDM_TwoMachines;\r
8723     break;\r
8724   case AnalyzeMode:\r
8725     nowChecked = IDM_AnalysisMode;\r
8726     break;\r
8727   case AnalyzeFile:\r
8728     nowChecked = IDM_AnalyzeFile;\r
8729     break;\r
8730   case EditGame:\r
8731     nowChecked = IDM_EditGame;\r
8732     break;\r
8733   case PlayFromGameFile:\r
8734     nowChecked = IDM_LoadGame;\r
8735     break;\r
8736   case EditPosition:\r
8737     nowChecked = IDM_EditPosition;\r
8738     break;\r
8739   case Training:\r
8740     nowChecked = IDM_Training;\r
8741     break;\r
8742   case IcsPlayingWhite:\r
8743   case IcsPlayingBlack:\r
8744   case IcsObserving:\r
8745   case IcsIdle:\r
8746     nowChecked = IDM_IcsClient;\r
8747     break;\r
8748   default:\r
8749   case EndOfGame:\r
8750     nowChecked = 0;\r
8751     break;\r
8752   }\r
8753   if (prevChecked != 0)\r
8754     (void) CheckMenuItem(GetMenu(hwndMain),\r
8755                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8756   if (nowChecked != 0)\r
8757     (void) CheckMenuItem(GetMenu(hwndMain),\r
8758                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8759 \r
8760   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8761     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8762                           MF_BYCOMMAND|MF_ENABLED);\r
8763   } else {\r
8764     (void) EnableMenuItem(GetMenu(hwndMain), \r
8765                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8766   }\r
8767 \r
8768   prevChecked = nowChecked;\r
8769 \r
8770   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8771   if (appData.icsActive) {\r
8772        if (appData.icsEngineAnalyze) {\r
8773                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8774                        MF_BYCOMMAND|MF_CHECKED);\r
8775        } else {\r
8776                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8777                        MF_BYCOMMAND|MF_UNCHECKED);\r
8778        }\r
8779   }\r
8780 }\r
8781 \r
8782 VOID\r
8783 SetICSMode()\r
8784 {\r
8785   HMENU hmenu = GetMenu(hwndMain);\r
8786   SetMenuEnables(hmenu, icsEnables);\r
8787   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8788     MF_BYPOSITION|MF_ENABLED);\r
8789 #ifdef ZIPPY\r
8790   if (appData.zippyPlay) {\r
8791     SetMenuEnables(hmenu, zippyEnables);\r
8792     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8793          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8794           MF_BYCOMMAND|MF_ENABLED);\r
8795   }\r
8796 #endif\r
8797 }\r
8798 \r
8799 VOID\r
8800 SetGNUMode()\r
8801 {\r
8802   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8803 }\r
8804 \r
8805 VOID\r
8806 SetNCPMode()\r
8807 {\r
8808   HMENU hmenu = GetMenu(hwndMain);\r
8809   SetMenuEnables(hmenu, ncpEnables);\r
8810   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8811     MF_BYPOSITION|MF_GRAYED);\r
8812     DrawMenuBar(hwndMain);\r
8813 }\r
8814 \r
8815 VOID\r
8816 SetCmailMode()\r
8817 {\r
8818   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8819 }\r
8820 \r
8821 VOID \r
8822 SetTrainingModeOn()\r
8823 {\r
8824   int i;\r
8825   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8826   for (i = 0; i < N_BUTTONS; i++) {\r
8827     if (buttonDesc[i].hwnd != NULL)\r
8828       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8829   }\r
8830   CommentPopDown();\r
8831 }\r
8832 \r
8833 VOID SetTrainingModeOff()\r
8834 {\r
8835   int i;\r
8836   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8837   for (i = 0; i < N_BUTTONS; i++) {\r
8838     if (buttonDesc[i].hwnd != NULL)\r
8839       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8840   }\r
8841 }\r
8842 \r
8843 \r
8844 VOID\r
8845 SetUserThinkingEnables()\r
8846 {\r
8847   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8848 }\r
8849 \r
8850 VOID\r
8851 SetMachineThinkingEnables()\r
8852 {\r
8853   HMENU hMenu = GetMenu(hwndMain);\r
8854   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8855 \r
8856   SetMenuEnables(hMenu, machineThinkingEnables);\r
8857 \r
8858   if (gameMode == MachinePlaysBlack) {\r
8859     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8860   } else if (gameMode == MachinePlaysWhite) {\r
8861     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8862   } else if (gameMode == TwoMachinesPlay) {\r
8863     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8864   }\r
8865 }\r
8866 \r
8867 \r
8868 VOID\r
8869 DisplayTitle(char *str)\r
8870 {\r
8871   char title[MSG_SIZ], *host;\r
8872   if (str[0] != NULLCHAR) {\r
8873     strcpy(title, str);\r
8874   } else if (appData.icsActive) {\r
8875     if (appData.icsCommPort[0] != NULLCHAR)\r
8876       host = "ICS";\r
8877     else \r
8878       host = appData.icsHost;\r
8879     sprintf(title, "%s: %s", szTitle, host);\r
8880   } else if (appData.noChessProgram) {\r
8881     strcpy(title, szTitle);\r
8882   } else {\r
8883     strcpy(title, szTitle);\r
8884     strcat(title, ": ");\r
8885     strcat(title, first.tidy);\r
8886   }\r
8887   SetWindowText(hwndMain, title);\r
8888 }\r
8889 \r
8890 \r
8891 VOID\r
8892 DisplayMessage(char *str1, char *str2)\r
8893 {\r
8894   HDC hdc;\r
8895   HFONT oldFont;\r
8896   int remain = MESSAGE_TEXT_MAX - 1;\r
8897   int len;\r
8898 \r
8899   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8900   messageText[0] = NULLCHAR;\r
8901   if (*str1) {\r
8902     len = strlen(str1);\r
8903     if (len > remain) len = remain;\r
8904     strncpy(messageText, str1, len);\r
8905     messageText[len] = NULLCHAR;\r
8906     remain -= len;\r
8907   }\r
8908   if (*str2 && remain >= 2) {\r
8909     if (*str1) {\r
8910       strcat(messageText, "  ");\r
8911       remain -= 2;\r
8912     }\r
8913     len = strlen(str2);\r
8914     if (len > remain) len = remain;\r
8915     strncat(messageText, str2, len);\r
8916   }\r
8917   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8918 \r
8919   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8920   hdc = GetDC(hwndMain);\r
8921   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8922   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8923              &messageRect, messageText, strlen(messageText), NULL);\r
8924   (void) SelectObject(hdc, oldFont);\r
8925   (void) ReleaseDC(hwndMain, hdc);\r
8926 }\r
8927 \r
8928 VOID\r
8929 DisplayError(char *str, int error)\r
8930 {\r
8931   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8932   int len;\r
8933 \r
8934   if (error == 0) {\r
8935     strcpy(buf, str);\r
8936   } else {\r
8937     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8938                         NULL, error, LANG_NEUTRAL,\r
8939                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8940     if (len > 0) {\r
8941       sprintf(buf, "%s:\n%s", str, buf2);\r
8942     } else {\r
8943       ErrorMap *em = errmap;\r
8944       while (em->err != 0 && em->err != error) em++;\r
8945       if (em->err != 0) {\r
8946         sprintf(buf, "%s:\n%s", str, em->msg);\r
8947       } else {\r
8948         sprintf(buf, "%s:\nError code %d", str, error);\r
8949       }\r
8950     }\r
8951   }\r
8952   \r
8953   ErrorPopUp("Error", buf);\r
8954 }\r
8955 \r
8956 \r
8957 VOID\r
8958 DisplayMoveError(char *str)\r
8959 {\r
8960   fromX = fromY = -1;\r
8961   ClearHighlights();\r
8962   DrawPosition(FALSE, NULL);\r
8963   if (appData.popupMoveErrors) {\r
8964     ErrorPopUp("Error", str);\r
8965   } else {\r
8966     DisplayMessage(str, "");\r
8967     moveErrorMessageUp = TRUE;\r
8968   }\r
8969 }\r
8970 \r
8971 VOID\r
8972 DisplayFatalError(char *str, int error, int exitStatus)\r
8973 {\r
8974   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8975   int len;\r
8976   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8977 \r
8978   if (error != 0) {\r
8979     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8980                         NULL, error, LANG_NEUTRAL,\r
8981                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8982     if (len > 0) {\r
8983       sprintf(buf, "%s:\n%s", str, buf2);\r
8984     } else {\r
8985       ErrorMap *em = errmap;\r
8986       while (em->err != 0 && em->err != error) em++;\r
8987       if (em->err != 0) {\r
8988         sprintf(buf, "%s:\n%s", str, em->msg);\r
8989       } else {\r
8990         sprintf(buf, "%s:\nError code %d", str, error);\r
8991       }\r
8992     }\r
8993     str = buf;\r
8994   }\r
8995   if (appData.debugMode) {\r
8996     fprintf(debugFP, "%s: %s\n", label, str);\r
8997   }\r
8998   if (appData.popupExitMessage) {\r
8999     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9000                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9001   }\r
9002   ExitEvent(exitStatus);\r
9003 }\r
9004 \r
9005 \r
9006 VOID\r
9007 DisplayInformation(char *str)\r
9008 {\r
9009   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9010 }\r
9011 \r
9012 \r
9013 VOID\r
9014 DisplayNote(char *str)\r
9015 {\r
9016   ErrorPopUp("Note", str);\r
9017 }\r
9018 \r
9019 \r
9020 typedef struct {\r
9021   char *title, *question, *replyPrefix;\r
9022   ProcRef pr;\r
9023 } QuestionParams;\r
9024 \r
9025 LRESULT CALLBACK\r
9026 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9027 {\r
9028   static QuestionParams *qp;\r
9029   char reply[MSG_SIZ];\r
9030   int len, err;\r
9031 \r
9032   switch (message) {\r
9033   case WM_INITDIALOG:\r
9034     qp = (QuestionParams *) lParam;\r
9035     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9036     SetWindowText(hDlg, qp->title);\r
9037     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9038     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9039     return FALSE;\r
9040 \r
9041   case WM_COMMAND:\r
9042     switch (LOWORD(wParam)) {\r
9043     case IDOK:\r
9044       strcpy(reply, qp->replyPrefix);\r
9045       if (*reply) strcat(reply, " ");\r
9046       len = strlen(reply);\r
9047       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9048       strcat(reply, "\n");\r
9049       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9050       EndDialog(hDlg, TRUE);\r
9051       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9052       return TRUE;\r
9053     case IDCANCEL:\r
9054       EndDialog(hDlg, FALSE);\r
9055       return TRUE;\r
9056     default:\r
9057       break;\r
9058     }\r
9059     break;\r
9060   }\r
9061   return FALSE;\r
9062 }\r
9063 \r
9064 VOID\r
9065 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9066 {\r
9067     QuestionParams qp;\r
9068     FARPROC lpProc;\r
9069     \r
9070     qp.title = title;\r
9071     qp.question = question;\r
9072     qp.replyPrefix = replyPrefix;\r
9073     qp.pr = pr;\r
9074     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9075     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9076       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9077     FreeProcInstance(lpProc);\r
9078 }\r
9079 \r
9080 /* [AS] Pick FRC position */\r
9081 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9082 {\r
9083     static int * lpIndexFRC;\r
9084     BOOL index_is_ok;\r
9085     char buf[16];\r
9086 \r
9087     switch( message )\r
9088     {\r
9089     case WM_INITDIALOG:\r
9090         lpIndexFRC = (int *) lParam;\r
9091 \r
9092         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9093 \r
9094         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9095         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9096         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9097         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9098 \r
9099         break;\r
9100 \r
9101     case WM_COMMAND:\r
9102         switch( LOWORD(wParam) ) {\r
9103         case IDOK:\r
9104             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9105             EndDialog( hDlg, 0 );\r
9106             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9107             return TRUE;\r
9108         case IDCANCEL:\r
9109             EndDialog( hDlg, 1 );   \r
9110             return TRUE;\r
9111         case IDC_NFG_Edit:\r
9112             if( HIWORD(wParam) == EN_CHANGE ) {\r
9113                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9114 \r
9115                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9116             }\r
9117             return TRUE;\r
9118         case IDC_NFG_Random:\r
9119             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9120             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9121             return TRUE;\r
9122         }\r
9123 \r
9124         break;\r
9125     }\r
9126 \r
9127     return FALSE;\r
9128 }\r
9129 \r
9130 int NewGameFRC()\r
9131 {\r
9132     int result;\r
9133     int index = appData.defaultFrcPosition;\r
9134     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9135 \r
9136     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9137 \r
9138     if( result == 0 ) {\r
9139         appData.defaultFrcPosition = index;\r
9140     }\r
9141 \r
9142     return result;\r
9143 }\r
9144 \r
9145 /* [AS] Game list options */\r
9146 typedef struct {\r
9147     char id;\r
9148     char * name;\r
9149 } GLT_Item;\r
9150 \r
9151 static GLT_Item GLT_ItemInfo[] = {\r
9152     { GLT_EVENT,      "Event" },\r
9153     { GLT_SITE,       "Site" },\r
9154     { GLT_DATE,       "Date" },\r
9155     { GLT_ROUND,      "Round" },\r
9156     { GLT_PLAYERS,    "Players" },\r
9157     { GLT_RESULT,     "Result" },\r
9158     { GLT_WHITE_ELO,  "White Rating" },\r
9159     { GLT_BLACK_ELO,  "Black Rating" },\r
9160     { GLT_TIME_CONTROL,"Time Control" },\r
9161     { GLT_VARIANT,    "Variant" },\r
9162     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9163     { 0, 0 }\r
9164 };\r
9165 \r
9166 const char * GLT_FindItem( char id )\r
9167 {\r
9168     const char * result = 0;\r
9169 \r
9170     GLT_Item * list = GLT_ItemInfo;\r
9171 \r
9172     while( list->id != 0 ) {\r
9173         if( list->id == id ) {\r
9174             result = list->name;\r
9175             break;\r
9176         }\r
9177 \r
9178         list++;\r
9179     }\r
9180 \r
9181     return result;\r
9182 }\r
9183 \r
9184 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9185 {\r
9186     const char * name = GLT_FindItem( id );\r
9187 \r
9188     if( name != 0 ) {\r
9189         if( index >= 0 ) {\r
9190             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9191         }\r
9192         else {\r
9193             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9194         }\r
9195     }\r
9196 }\r
9197 \r
9198 void GLT_TagsToList( HWND hDlg, char * tags )\r
9199 {\r
9200     char * pc = tags;\r
9201 \r
9202     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9203 \r
9204     while( *pc ) {\r
9205         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9206         pc++;\r
9207     }\r
9208 \r
9209     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9210 \r
9211     pc = GLT_ALL_TAGS;\r
9212 \r
9213     while( *pc ) {\r
9214         if( strchr( tags, *pc ) == 0 ) {\r
9215             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9216         }\r
9217         pc++;\r
9218     }\r
9219 \r
9220     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9221 }\r
9222 \r
9223 char GLT_ListItemToTag( HWND hDlg, int index )\r
9224 {\r
9225     char result = '\0';\r
9226     char name[128];\r
9227 \r
9228     GLT_Item * list = GLT_ItemInfo;\r
9229 \r
9230     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9231         while( list->id != 0 ) {\r
9232             if( strcmp( list->name, name ) == 0 ) {\r
9233                 result = list->id;\r
9234                 break;\r
9235             }\r
9236 \r
9237             list++;\r
9238         }\r
9239     }\r
9240 \r
9241     return result;\r
9242 }\r
9243 \r
9244 void GLT_MoveSelection( HWND hDlg, int delta )\r
9245 {\r
9246     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9247     int idx2 = idx1 + delta;\r
9248     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9249 \r
9250     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9251         char buf[128];\r
9252 \r
9253         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9254         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9255         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9256         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9257     }\r
9258 }\r
9259 \r
9260 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9261 {\r
9262     static char glt[64];\r
9263     static char * lpUserGLT;\r
9264 \r
9265     switch( message )\r
9266     {\r
9267     case WM_INITDIALOG:\r
9268         lpUserGLT = (char *) lParam;\r
9269         \r
9270         strcpy( glt, lpUserGLT );\r
9271 \r
9272         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9273 \r
9274         /* Initialize list */\r
9275         GLT_TagsToList( hDlg, glt );\r
9276 \r
9277         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9278 \r
9279         break;\r
9280 \r
9281     case WM_COMMAND:\r
9282         switch( LOWORD(wParam) ) {\r
9283         case IDOK:\r
9284             {\r
9285                 char * pc = lpUserGLT;\r
9286                 int idx = 0;\r
9287 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9288                 char id;\r
9289 \r
9290                 do {\r
9291                     id = GLT_ListItemToTag( hDlg, idx );\r
9292 \r
9293                     *pc++ = id;\r
9294                     idx++;\r
9295                 } while( id != '\0' );\r
9296             }\r
9297             EndDialog( hDlg, 0 );\r
9298             return TRUE;\r
9299         case IDCANCEL:\r
9300             EndDialog( hDlg, 1 );\r
9301             return TRUE;\r
9302 \r
9303         case IDC_GLT_Default:\r
9304             strcpy( glt, GLT_DEFAULT_TAGS );\r
9305             GLT_TagsToList( hDlg, glt );\r
9306             return TRUE;\r
9307 \r
9308         case IDC_GLT_Restore:\r
9309             strcpy( glt, lpUserGLT );\r
9310             GLT_TagsToList( hDlg, glt );\r
9311             return TRUE;\r
9312 \r
9313         case IDC_GLT_Up:\r
9314             GLT_MoveSelection( hDlg, -1 );\r
9315             return TRUE;\r
9316 \r
9317         case IDC_GLT_Down:\r
9318             GLT_MoveSelection( hDlg, +1 );\r
9319             return TRUE;\r
9320         }\r
9321 \r
9322         break;\r
9323     }\r
9324 \r
9325     return FALSE;\r
9326 }\r
9327 \r
9328 int GameListOptions()\r
9329 {\r
9330     char glt[64];\r
9331     int result;\r
9332     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9333 \r
9334     strcpy( glt, appData.gameListTags );\r
9335 \r
9336     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9337 \r
9338     if( result == 0 ) {\r
9339         /* [AS] Memory leak here! */\r
9340         appData.gameListTags = strdup( glt ); \r
9341     }\r
9342 \r
9343     return result;\r
9344 }\r
9345 \r
9346 \r
9347 VOID\r
9348 DisplayIcsInteractionTitle(char *str)\r
9349 {\r
9350   char consoleTitle[MSG_SIZ];\r
9351 \r
9352   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9353   SetWindowText(hwndConsole, consoleTitle);\r
9354 }\r
9355 \r
9356 void\r
9357 DrawPosition(int fullRedraw, Board board)\r
9358 {\r
9359   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9360 }\r
9361 \r
9362 \r
9363 VOID\r
9364 ResetFrontEnd()\r
9365 {\r
9366   fromX = fromY = -1;\r
9367   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9368     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9369     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9370     dragInfo.lastpos = dragInfo.pos;\r
9371     dragInfo.start.x = dragInfo.start.y = -1;\r
9372     dragInfo.from = dragInfo.start;\r
9373     ReleaseCapture();\r
9374     DrawPosition(TRUE, NULL);\r
9375   }\r
9376 }\r
9377 \r
9378 \r
9379 VOID\r
9380 CommentPopUp(char *title, char *str)\r
9381 {\r
9382   HWND hwnd = GetActiveWindow();\r
9383   EitherCommentPopUp(0, title, str, FALSE);\r
9384   SetActiveWindow(hwnd);\r
9385 }\r
9386 \r
9387 VOID\r
9388 CommentPopDown(void)\r
9389 {\r
9390   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9391   if (commentDialog) {\r
9392     ShowWindow(commentDialog, SW_HIDE);\r
9393   }\r
9394   commentDialogUp = FALSE;\r
9395 }\r
9396 \r
9397 VOID\r
9398 EditCommentPopUp(int index, char *title, char *str)\r
9399 {\r
9400   EitherCommentPopUp(index, title, str, TRUE);\r
9401 }\r
9402 \r
9403 \r
9404 VOID\r
9405 RingBell()\r
9406 {\r
9407   MyPlaySound(&sounds[(int)SoundMove]);\r
9408 }\r
9409 \r
9410 VOID PlayIcsWinSound()\r
9411 {\r
9412   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9413 }\r
9414 \r
9415 VOID PlayIcsLossSound()\r
9416 {\r
9417   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9418 }\r
9419 \r
9420 VOID PlayIcsDrawSound()\r
9421 {\r
9422   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9423 }\r
9424 \r
9425 VOID PlayIcsUnfinishedSound()\r
9426 {\r
9427   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9428 }\r
9429 \r
9430 VOID\r
9431 PlayAlarmSound()\r
9432 {\r
9433   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9434 }\r
9435 \r
9436 \r
9437 VOID\r
9438 EchoOn()\r
9439 {\r
9440   HWND hInput;\r
9441   consoleEcho = TRUE;\r
9442   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9443   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9444   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9445 }\r
9446 \r
9447 \r
9448 VOID\r
9449 EchoOff()\r
9450 {\r
9451   CHARFORMAT cf;\r
9452   HWND hInput;\r
9453   consoleEcho = FALSE;\r
9454   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9455   /* This works OK: set text and background both to the same color */\r
9456   cf = consoleCF;\r
9457   cf.crTextColor = COLOR_ECHOOFF;\r
9458   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9459   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9460 }\r
9461 \r
9462 /* No Raw()...? */\r
9463 \r
9464 void Colorize(ColorClass cc, int continuation)\r
9465 {\r
9466   currentColorClass = cc;\r
9467   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9468   consoleCF.crTextColor = textAttribs[cc].color;\r
9469   consoleCF.dwEffects = textAttribs[cc].effects;\r
9470   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9471 }\r
9472 \r
9473 char *\r
9474 UserName()\r
9475 {\r
9476   static char buf[MSG_SIZ];\r
9477   DWORD bufsiz = MSG_SIZ;\r
9478 \r
9479   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9480         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9481   }\r
9482   if (!GetUserName(buf, &bufsiz)) {\r
9483     /*DisplayError("Error getting user name", GetLastError());*/\r
9484     strcpy(buf, "User");\r
9485   }\r
9486   return buf;\r
9487 }\r
9488 \r
9489 char *\r
9490 HostName()\r
9491 {\r
9492   static char buf[MSG_SIZ];\r
9493   DWORD bufsiz = MSG_SIZ;\r
9494 \r
9495   if (!GetComputerName(buf, &bufsiz)) {\r
9496     /*DisplayError("Error getting host name", GetLastError());*/\r
9497     strcpy(buf, "Unknown");\r
9498   }\r
9499   return buf;\r
9500 }\r
9501 \r
9502 \r
9503 int\r
9504 ClockTimerRunning()\r
9505 {\r
9506   return clockTimerEvent != 0;\r
9507 }\r
9508 \r
9509 int\r
9510 StopClockTimer()\r
9511 {\r
9512   if (clockTimerEvent == 0) return FALSE;\r
9513   KillTimer(hwndMain, clockTimerEvent);\r
9514   clockTimerEvent = 0;\r
9515   return TRUE;\r
9516 }\r
9517 \r
9518 void\r
9519 StartClockTimer(long millisec)\r
9520 {\r
9521   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9522                              (UINT) millisec, NULL);\r
9523 }\r
9524 \r
9525 void\r
9526 DisplayWhiteClock(long timeRemaining, int highlight)\r
9527 {\r
9528   HDC hdc;\r
9529   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9530 \r
9531   if(appData.noGUI) return;\r
9532   hdc = GetDC(hwndMain);\r
9533   if (!IsIconic(hwndMain)) {\r
9534     DisplayAClock(hdc, timeRemaining, highlight, \r
9535                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9536   }\r
9537   if (highlight && iconCurrent == iconBlack) {\r
9538     iconCurrent = iconWhite;\r
9539     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9540     if (IsIconic(hwndMain)) {\r
9541       DrawIcon(hdc, 2, 2, iconCurrent);\r
9542     }\r
9543   }\r
9544   (void) ReleaseDC(hwndMain, hdc);\r
9545   if (hwndConsole)\r
9546     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9547 }\r
9548 \r
9549 void\r
9550 DisplayBlackClock(long timeRemaining, int highlight)\r
9551 {\r
9552   HDC hdc;\r
9553   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9554 \r
9555   if(appData.noGUI) return;\r
9556   hdc = GetDC(hwndMain);\r
9557   if (!IsIconic(hwndMain)) {\r
9558     DisplayAClock(hdc, timeRemaining, highlight, \r
9559                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9560   }\r
9561   if (highlight && iconCurrent == iconWhite) {\r
9562     iconCurrent = iconBlack;\r
9563     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9564     if (IsIconic(hwndMain)) {\r
9565       DrawIcon(hdc, 2, 2, iconCurrent);\r
9566     }\r
9567   }\r
9568   (void) ReleaseDC(hwndMain, hdc);\r
9569   if (hwndConsole)\r
9570     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9571 }\r
9572 \r
9573 \r
9574 int\r
9575 LoadGameTimerRunning()\r
9576 {\r
9577   return loadGameTimerEvent != 0;\r
9578 }\r
9579 \r
9580 int\r
9581 StopLoadGameTimer()\r
9582 {\r
9583   if (loadGameTimerEvent == 0) return FALSE;\r
9584   KillTimer(hwndMain, loadGameTimerEvent);\r
9585   loadGameTimerEvent = 0;\r
9586   return TRUE;\r
9587 }\r
9588 \r
9589 void\r
9590 StartLoadGameTimer(long millisec)\r
9591 {\r
9592   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9593                                 (UINT) millisec, NULL);\r
9594 }\r
9595 \r
9596 void\r
9597 AutoSaveGame()\r
9598 {\r
9599   char *defName;\r
9600   FILE *f;\r
9601   char fileTitle[MSG_SIZ];\r
9602 \r
9603   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9604   f = OpenFileDialog(hwndMain, "a", defName,\r
9605                      appData.oldSaveStyle ? "gam" : "pgn",\r
9606                      GAME_FILT, \r
9607                      "Save Game to File", NULL, fileTitle, NULL);\r
9608   if (f != NULL) {\r
9609     SaveGame(f, 0, "");\r
9610     fclose(f);\r
9611   }\r
9612 }\r
9613 \r
9614 \r
9615 void\r
9616 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9617 {\r
9618   if (delayedTimerEvent != 0) {\r
9619     if (appData.debugMode) {\r
9620       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9621     }\r
9622     KillTimer(hwndMain, delayedTimerEvent);\r
9623     delayedTimerEvent = 0;\r
9624     delayedTimerCallback();\r
9625   }\r
9626   delayedTimerCallback = cb;\r
9627   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9628                                 (UINT) millisec, NULL);\r
9629 }\r
9630 \r
9631 DelayedEventCallback\r
9632 GetDelayedEvent()\r
9633 {\r
9634   if (delayedTimerEvent) {\r
9635     return delayedTimerCallback;\r
9636   } else {\r
9637     return NULL;\r
9638   }\r
9639 }\r
9640 \r
9641 void\r
9642 CancelDelayedEvent()\r
9643 {\r
9644   if (delayedTimerEvent) {\r
9645     KillTimer(hwndMain, delayedTimerEvent);\r
9646     delayedTimerEvent = 0;\r
9647   }\r
9648 }\r
9649 \r
9650 DWORD GetWin32Priority(int nice)\r
9651 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9652 /*\r
9653 REALTIME_PRIORITY_CLASS     0x00000100\r
9654 HIGH_PRIORITY_CLASS         0x00000080\r
9655 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9656 NORMAL_PRIORITY_CLASS       0x00000020\r
9657 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9658 IDLE_PRIORITY_CLASS         0x00000040\r
9659 */\r
9660         if (nice < -15) return 0x00000080;\r
9661         if (nice < 0)   return 0x00008000;\r
9662         if (nice == 0)  return 0x00000020;\r
9663         if (nice < 15)  return 0x00004000;\r
9664         return 0x00000040;\r
9665 }\r
9666 \r
9667 /* Start a child process running the given program.\r
9668    The process's standard output can be read from "from", and its\r
9669    standard input can be written to "to".\r
9670    Exit with fatal error if anything goes wrong.\r
9671    Returns an opaque pointer that can be used to destroy the process\r
9672    later.\r
9673 */\r
9674 int\r
9675 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9676 {\r
9677 #define BUFSIZE 4096\r
9678 \r
9679   HANDLE hChildStdinRd, hChildStdinWr,\r
9680     hChildStdoutRd, hChildStdoutWr;\r
9681   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9682   SECURITY_ATTRIBUTES saAttr;\r
9683   BOOL fSuccess;\r
9684   PROCESS_INFORMATION piProcInfo;\r
9685   STARTUPINFO siStartInfo;\r
9686   ChildProc *cp;\r
9687   char buf[MSG_SIZ];\r
9688   DWORD err;\r
9689 \r
9690   if (appData.debugMode) {\r
9691     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9692   }\r
9693 \r
9694   *pr = NoProc;\r
9695 \r
9696   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9697   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9698   saAttr.bInheritHandle = TRUE;\r
9699   saAttr.lpSecurityDescriptor = NULL;\r
9700 \r
9701   /*\r
9702    * The steps for redirecting child's STDOUT:\r
9703    *     1. Create anonymous pipe to be STDOUT for child.\r
9704    *     2. Create a noninheritable duplicate of read handle,\r
9705    *         and close the inheritable read handle.\r
9706    */\r
9707 \r
9708   /* Create a pipe for the child's STDOUT. */\r
9709   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9710     return GetLastError();\r
9711   }\r
9712 \r
9713   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9714   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9715                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9716                              FALSE,     /* not inherited */\r
9717                              DUPLICATE_SAME_ACCESS);\r
9718   if (! fSuccess) {\r
9719     return GetLastError();\r
9720   }\r
9721   CloseHandle(hChildStdoutRd);\r
9722 \r
9723   /*\r
9724    * The steps for redirecting child's STDIN:\r
9725    *     1. Create anonymous pipe to be STDIN for child.\r
9726    *     2. Create a noninheritable duplicate of write handle,\r
9727    *         and close the inheritable write handle.\r
9728    */\r
9729 \r
9730   /* Create a pipe for the child's STDIN. */\r
9731   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9732     return GetLastError();\r
9733   }\r
9734 \r
9735   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9736   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9737                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9738                              FALSE,     /* not inherited */\r
9739                              DUPLICATE_SAME_ACCESS);\r
9740   if (! fSuccess) {\r
9741     return GetLastError();\r
9742   }\r
9743   CloseHandle(hChildStdinWr);\r
9744 \r
9745   /* Arrange to (1) look in dir for the child .exe file, and\r
9746    * (2) have dir be the child's working directory.  Interpret\r
9747    * dir relative to the directory WinBoard loaded from. */\r
9748   GetCurrentDirectory(MSG_SIZ, buf);\r
9749   SetCurrentDirectory(installDir);\r
9750   SetCurrentDirectory(dir);\r
9751 \r
9752   /* Now create the child process. */\r
9753 \r
9754   siStartInfo.cb = sizeof(STARTUPINFO);\r
9755   siStartInfo.lpReserved = NULL;\r
9756   siStartInfo.lpDesktop = NULL;\r
9757   siStartInfo.lpTitle = NULL;\r
9758   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9759   siStartInfo.cbReserved2 = 0;\r
9760   siStartInfo.lpReserved2 = NULL;\r
9761   siStartInfo.hStdInput = hChildStdinRd;\r
9762   siStartInfo.hStdOutput = hChildStdoutWr;\r
9763   siStartInfo.hStdError = hChildStdoutWr;\r
9764 \r
9765   fSuccess = CreateProcess(NULL,\r
9766                            cmdLine,        /* command line */\r
9767                            NULL,           /* process security attributes */\r
9768                            NULL,           /* primary thread security attrs */\r
9769                            TRUE,           /* handles are inherited */\r
9770                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9771                            NULL,           /* use parent's environment */\r
9772                            NULL,\r
9773                            &siStartInfo, /* STARTUPINFO pointer */\r
9774                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9775 \r
9776   err = GetLastError();\r
9777   SetCurrentDirectory(buf); /* return to prev directory */\r
9778   if (! fSuccess) {\r
9779     return err;\r
9780   }\r
9781 \r
9782   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9783     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9784     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9785   }\r
9786 \r
9787   /* Close the handles we don't need in the parent */\r
9788   CloseHandle(piProcInfo.hThread);\r
9789   CloseHandle(hChildStdinRd);\r
9790   CloseHandle(hChildStdoutWr);\r
9791 \r
9792   /* Prepare return value */\r
9793   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9794   cp->kind = CPReal;\r
9795   cp->hProcess = piProcInfo.hProcess;\r
9796   cp->pid = piProcInfo.dwProcessId;\r
9797   cp->hFrom = hChildStdoutRdDup;\r
9798   cp->hTo = hChildStdinWrDup;\r
9799 \r
9800   *pr = (void *) cp;\r
9801 \r
9802   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9803      2000 where engines sometimes don't see the initial command(s)\r
9804      from WinBoard and hang.  I don't understand how that can happen,\r
9805      but the Sleep is harmless, so I've put it in.  Others have also\r
9806      reported what may be the same problem, so hopefully this will fix\r
9807      it for them too.  */\r
9808   Sleep(500);\r
9809 \r
9810   return NO_ERROR;\r
9811 }\r
9812 \r
9813 \r
9814 void\r
9815 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9816 {\r
9817   ChildProc *cp; int result;\r
9818 \r
9819   cp = (ChildProc *) pr;\r
9820   if (cp == NULL) return;\r
9821 \r
9822   switch (cp->kind) {\r
9823   case CPReal:\r
9824     /* TerminateProcess is considered harmful, so... */\r
9825     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9826     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9827     /* The following doesn't work because the chess program\r
9828        doesn't "have the same console" as WinBoard.  Maybe\r
9829        we could arrange for this even though neither WinBoard\r
9830        nor the chess program uses a console for stdio? */\r
9831     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9832 \r
9833     /* [AS] Special termination modes for misbehaving programs... */\r
9834     if( signal == 9 ) { \r
9835         result = TerminateProcess( cp->hProcess, 0 );\r
9836 \r
9837         if ( appData.debugMode) {\r
9838             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9839         }\r
9840     }\r
9841     else if( signal == 10 ) {\r
9842         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9843 \r
9844         if( dw != WAIT_OBJECT_0 ) {\r
9845             result = TerminateProcess( cp->hProcess, 0 );\r
9846 \r
9847             if ( appData.debugMode) {\r
9848                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9849             }\r
9850 \r
9851         }\r
9852     }\r
9853 \r
9854     CloseHandle(cp->hProcess);\r
9855     break;\r
9856 \r
9857   case CPComm:\r
9858     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9859     break;\r
9860 \r
9861   case CPSock:\r
9862     closesocket(cp->sock);\r
9863     WSACleanup();\r
9864     break;\r
9865 \r
9866   case CPRcmd:\r
9867     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9868     closesocket(cp->sock);\r
9869     closesocket(cp->sock2);\r
9870     WSACleanup();\r
9871     break;\r
9872   }\r
9873   free(cp);\r
9874 }\r
9875 \r
9876 void\r
9877 InterruptChildProcess(ProcRef pr)\r
9878 {\r
9879   ChildProc *cp;\r
9880 \r
9881   cp = (ChildProc *) pr;\r
9882   if (cp == NULL) return;\r
9883   switch (cp->kind) {\r
9884   case CPReal:\r
9885     /* The following doesn't work because the chess program\r
9886        doesn't "have the same console" as WinBoard.  Maybe\r
9887        we could arrange for this even though neither WinBoard\r
9888        nor the chess program uses a console for stdio */\r
9889     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9890     break;\r
9891 \r
9892   case CPComm:\r
9893   case CPSock:\r
9894     /* Can't interrupt */\r
9895     break;\r
9896 \r
9897   case CPRcmd:\r
9898     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9899     break;\r
9900   }\r
9901 }\r
9902 \r
9903 \r
9904 int\r
9905 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9906 {\r
9907   char cmdLine[MSG_SIZ];\r
9908 \r
9909   if (port[0] == NULLCHAR) {\r
9910     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9911   } else {\r
9912     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9913   }\r
9914   return StartChildProcess(cmdLine, "", pr);\r
9915 }\r
9916 \r
9917 \r
9918 /* Code to open TCP sockets */\r
9919 \r
9920 int\r
9921 OpenTCP(char *host, char *port, ProcRef *pr)\r
9922 {\r
9923   ChildProc *cp;\r
9924   int err;\r
9925   SOCKET s;\r
9926   struct sockaddr_in sa, mysa;\r
9927   struct hostent FAR *hp;\r
9928   unsigned short uport;\r
9929   WORD wVersionRequested;\r
9930   WSADATA wsaData;\r
9931 \r
9932   /* Initialize socket DLL */\r
9933   wVersionRequested = MAKEWORD(1, 1);\r
9934   err = WSAStartup(wVersionRequested, &wsaData);\r
9935   if (err != 0) return err;\r
9936 \r
9937   /* Make socket */\r
9938   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9939     err = WSAGetLastError();\r
9940     WSACleanup();\r
9941     return err;\r
9942   }\r
9943 \r
9944   /* Bind local address using (mostly) don't-care values.\r
9945    */\r
9946   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9947   mysa.sin_family = AF_INET;\r
9948   mysa.sin_addr.s_addr = INADDR_ANY;\r
9949   uport = (unsigned short) 0;\r
9950   mysa.sin_port = htons(uport);\r
9951   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9952       == SOCKET_ERROR) {\r
9953     err = WSAGetLastError();\r
9954     WSACleanup();\r
9955     return err;\r
9956   }\r
9957 \r
9958   /* Resolve remote host name */\r
9959   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9960   if (!(hp = gethostbyname(host))) {\r
9961     unsigned int b0, b1, b2, b3;\r
9962 \r
9963     err = WSAGetLastError();\r
9964 \r
9965     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9966       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9967       hp->h_addrtype = AF_INET;\r
9968       hp->h_length = 4;\r
9969       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9970       hp->h_addr_list[0] = (char *) malloc(4);\r
9971       hp->h_addr_list[0][0] = (char) b0;\r
9972       hp->h_addr_list[0][1] = (char) b1;\r
9973       hp->h_addr_list[0][2] = (char) b2;\r
9974       hp->h_addr_list[0][3] = (char) b3;\r
9975     } else {\r
9976       WSACleanup();\r
9977       return err;\r
9978     }\r
9979   }\r
9980   sa.sin_family = hp->h_addrtype;\r
9981   uport = (unsigned short) atoi(port);\r
9982   sa.sin_port = htons(uport);\r
9983   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9984 \r
9985   /* Make connection */\r
9986   if (connect(s, (struct sockaddr *) &sa,\r
9987               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9988     err = WSAGetLastError();\r
9989     WSACleanup();\r
9990     return err;\r
9991   }\r
9992 \r
9993   /* Prepare return value */\r
9994   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9995   cp->kind = CPSock;\r
9996   cp->sock = s;\r
9997   *pr = (ProcRef *) cp;\r
9998 \r
9999   return NO_ERROR;\r
10000 }\r
10001 \r
10002 int\r
10003 OpenCommPort(char *name, ProcRef *pr)\r
10004 {\r
10005   HANDLE h;\r
10006   COMMTIMEOUTS ct;\r
10007   ChildProc *cp;\r
10008   char fullname[MSG_SIZ];\r
10009 \r
10010   if (*name != '\\')\r
10011     sprintf(fullname, "\\\\.\\%s", name);\r
10012   else\r
10013     strcpy(fullname, name);\r
10014 \r
10015   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10016                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10017   if (h == (HANDLE) -1) {\r
10018     return GetLastError();\r
10019   }\r
10020   hCommPort = h;\r
10021 \r
10022   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10023 \r
10024   /* Accumulate characters until a 100ms pause, then parse */\r
10025   ct.ReadIntervalTimeout = 100;\r
10026   ct.ReadTotalTimeoutMultiplier = 0;\r
10027   ct.ReadTotalTimeoutConstant = 0;\r
10028   ct.WriteTotalTimeoutMultiplier = 0;\r
10029   ct.WriteTotalTimeoutConstant = 0;\r
10030   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10031 \r
10032   /* Prepare return value */\r
10033   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10034   cp->kind = CPComm;\r
10035   cp->hFrom = h;\r
10036   cp->hTo = h;\r
10037   *pr = (ProcRef *) cp;\r
10038 \r
10039   return NO_ERROR;\r
10040 }\r
10041 \r
10042 int\r
10043 OpenLoopback(ProcRef *pr)\r
10044 {\r
10045   DisplayFatalError("Not implemented", 0, 1);\r
10046   return NO_ERROR;\r
10047 }\r
10048 \r
10049 \r
10050 int\r
10051 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10052 {\r
10053   ChildProc *cp;\r
10054   int err;\r
10055   SOCKET s, s2, s3;\r
10056   struct sockaddr_in sa, mysa;\r
10057   struct hostent FAR *hp;\r
10058   unsigned short uport;\r
10059   WORD wVersionRequested;\r
10060   WSADATA wsaData;\r
10061   int fromPort;\r
10062   char stderrPortStr[MSG_SIZ];\r
10063 \r
10064   /* Initialize socket DLL */\r
10065   wVersionRequested = MAKEWORD(1, 1);\r
10066   err = WSAStartup(wVersionRequested, &wsaData);\r
10067   if (err != 0) return err;\r
10068 \r
10069   /* Resolve remote host name */\r
10070   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10071   if (!(hp = gethostbyname(host))) {\r
10072     unsigned int b0, b1, b2, b3;\r
10073 \r
10074     err = WSAGetLastError();\r
10075 \r
10076     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10077       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10078       hp->h_addrtype = AF_INET;\r
10079       hp->h_length = 4;\r
10080       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10081       hp->h_addr_list[0] = (char *) malloc(4);\r
10082       hp->h_addr_list[0][0] = (char) b0;\r
10083       hp->h_addr_list[0][1] = (char) b1;\r
10084       hp->h_addr_list[0][2] = (char) b2;\r
10085       hp->h_addr_list[0][3] = (char) b3;\r
10086     } else {\r
10087       WSACleanup();\r
10088       return err;\r
10089     }\r
10090   }\r
10091   sa.sin_family = hp->h_addrtype;\r
10092   uport = (unsigned short) 514;\r
10093   sa.sin_port = htons(uport);\r
10094   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10095 \r
10096   /* Bind local socket to unused "privileged" port address\r
10097    */\r
10098   s = INVALID_SOCKET;\r
10099   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10100   mysa.sin_family = AF_INET;\r
10101   mysa.sin_addr.s_addr = INADDR_ANY;\r
10102   for (fromPort = 1023;; fromPort--) {\r
10103     if (fromPort < 0) {\r
10104       WSACleanup();\r
10105       return WSAEADDRINUSE;\r
10106     }\r
10107     if (s == INVALID_SOCKET) {\r
10108       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10109         err = WSAGetLastError();\r
10110         WSACleanup();\r
10111         return err;\r
10112       }\r
10113     }\r
10114     uport = (unsigned short) fromPort;\r
10115     mysa.sin_port = htons(uport);\r
10116     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10117         == SOCKET_ERROR) {\r
10118       err = WSAGetLastError();\r
10119       if (err == WSAEADDRINUSE) continue;\r
10120       WSACleanup();\r
10121       return err;\r
10122     }\r
10123     if (connect(s, (struct sockaddr *) &sa,\r
10124       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10125       err = WSAGetLastError();\r
10126       if (err == WSAEADDRINUSE) {\r
10127         closesocket(s);\r
10128         s = -1;\r
10129         continue;\r
10130       }\r
10131       WSACleanup();\r
10132       return err;\r
10133     }\r
10134     break;\r
10135   }\r
10136 \r
10137   /* Bind stderr local socket to unused "privileged" port address\r
10138    */\r
10139   s2 = INVALID_SOCKET;\r
10140   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10141   mysa.sin_family = AF_INET;\r
10142   mysa.sin_addr.s_addr = INADDR_ANY;\r
10143   for (fromPort = 1023;; fromPort--) {\r
10144     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10145     if (fromPort < 0) {\r
10146       (void) closesocket(s);\r
10147       WSACleanup();\r
10148       return WSAEADDRINUSE;\r
10149     }\r
10150     if (s2 == INVALID_SOCKET) {\r
10151       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10152         err = WSAGetLastError();\r
10153         closesocket(s);\r
10154         WSACleanup();\r
10155         return err;\r
10156       }\r
10157     }\r
10158     uport = (unsigned short) fromPort;\r
10159     mysa.sin_port = htons(uport);\r
10160     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10161         == SOCKET_ERROR) {\r
10162       err = WSAGetLastError();\r
10163       if (err == WSAEADDRINUSE) continue;\r
10164       (void) closesocket(s);\r
10165       WSACleanup();\r
10166       return err;\r
10167     }\r
10168     if (listen(s2, 1) == SOCKET_ERROR) {\r
10169       err = WSAGetLastError();\r
10170       if (err == WSAEADDRINUSE) {\r
10171         closesocket(s2);\r
10172         s2 = INVALID_SOCKET;\r
10173         continue;\r
10174       }\r
10175       (void) closesocket(s);\r
10176       (void) closesocket(s2);\r
10177       WSACleanup();\r
10178       return err;\r
10179     }\r
10180     break;\r
10181   }\r
10182   prevStderrPort = fromPort; // remember port used\r
10183   sprintf(stderrPortStr, "%d", fromPort);\r
10184 \r
10185   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10186     err = WSAGetLastError();\r
10187     (void) closesocket(s);\r
10188     (void) closesocket(s2);\r
10189     WSACleanup();\r
10190     return err;\r
10191   }\r
10192 \r
10193   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10194     err = WSAGetLastError();\r
10195     (void) closesocket(s);\r
10196     (void) closesocket(s2);\r
10197     WSACleanup();\r
10198     return err;\r
10199   }\r
10200   if (*user == NULLCHAR) user = UserName();\r
10201   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10202     err = WSAGetLastError();\r
10203     (void) closesocket(s);\r
10204     (void) closesocket(s2);\r
10205     WSACleanup();\r
10206     return err;\r
10207   }\r
10208   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10209     err = WSAGetLastError();\r
10210     (void) closesocket(s);\r
10211     (void) closesocket(s2);\r
10212     WSACleanup();\r
10213     return err;\r
10214   }\r
10215 \r
10216   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10217     err = WSAGetLastError();\r
10218     (void) closesocket(s);\r
10219     (void) closesocket(s2);\r
10220     WSACleanup();\r
10221     return err;\r
10222   }\r
10223   (void) closesocket(s2);  /* Stop listening */\r
10224 \r
10225   /* Prepare return value */\r
10226   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10227   cp->kind = CPRcmd;\r
10228   cp->sock = s;\r
10229   cp->sock2 = s3;\r
10230   *pr = (ProcRef *) cp;\r
10231 \r
10232   return NO_ERROR;\r
10233 }\r
10234 \r
10235 \r
10236 InputSourceRef\r
10237 AddInputSource(ProcRef pr, int lineByLine,\r
10238                InputCallback func, VOIDSTAR closure)\r
10239 {\r
10240   InputSource *is, *is2 = NULL;\r
10241   ChildProc *cp = (ChildProc *) pr;\r
10242 \r
10243   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10244   is->lineByLine = lineByLine;\r
10245   is->func = func;\r
10246   is->closure = closure;\r
10247   is->second = NULL;\r
10248   is->next = is->buf;\r
10249   if (pr == NoProc) {\r
10250     is->kind = CPReal;\r
10251     consoleInputSource = is;\r
10252   } else {\r
10253     is->kind = cp->kind;\r
10254     /* \r
10255         [AS] Try to avoid a race condition if the thread is given control too early:\r
10256         we create all threads suspended so that the is->hThread variable can be\r
10257         safely assigned, then let the threads start with ResumeThread.\r
10258     */\r
10259     switch (cp->kind) {\r
10260     case CPReal:\r
10261       is->hFile = cp->hFrom;\r
10262       cp->hFrom = NULL; /* now owned by InputThread */\r
10263       is->hThread =\r
10264         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10265                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10266       break;\r
10267 \r
10268     case CPComm:\r
10269       is->hFile = cp->hFrom;\r
10270       cp->hFrom = NULL; /* now owned by InputThread */\r
10271       is->hThread =\r
10272         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10273                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10274       break;\r
10275 \r
10276     case CPSock:\r
10277       is->sock = cp->sock;\r
10278       is->hThread =\r
10279         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10280                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10281       break;\r
10282 \r
10283     case CPRcmd:\r
10284       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10285       *is2 = *is;\r
10286       is->sock = cp->sock;\r
10287       is->second = is2;\r
10288       is2->sock = cp->sock2;\r
10289       is2->second = is2;\r
10290       is->hThread =\r
10291         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10292                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10293       is2->hThread =\r
10294         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10295                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10296       break;\r
10297     }\r
10298 \r
10299     if( is->hThread != NULL ) {\r
10300         ResumeThread( is->hThread );\r
10301     }\r
10302 \r
10303     if( is2 != NULL && is2->hThread != NULL ) {\r
10304         ResumeThread( is2->hThread );\r
10305     }\r
10306   }\r
10307 \r
10308   return (InputSourceRef) is;\r
10309 }\r
10310 \r
10311 void\r
10312 RemoveInputSource(InputSourceRef isr)\r
10313 {\r
10314   InputSource *is;\r
10315 \r
10316   is = (InputSource *) isr;\r
10317   is->hThread = NULL;  /* tell thread to stop */\r
10318   CloseHandle(is->hThread);\r
10319   if (is->second != NULL) {\r
10320     is->second->hThread = NULL;\r
10321     CloseHandle(is->second->hThread);\r
10322   }\r
10323 }\r
10324 \r
10325 \r
10326 int\r
10327 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10328 {\r
10329   DWORD dOutCount;\r
10330   int outCount = SOCKET_ERROR;\r
10331   ChildProc *cp = (ChildProc *) pr;\r
10332   static OVERLAPPED ovl;\r
10333 \r
10334   if (pr == NoProc) {\r
10335     ConsoleOutput(message, count, FALSE);\r
10336     return count;\r
10337   } \r
10338 \r
10339   if (ovl.hEvent == NULL) {\r
10340     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10341   }\r
10342   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10343 \r
10344   switch (cp->kind) {\r
10345   case CPSock:\r
10346   case CPRcmd:\r
10347     outCount = send(cp->sock, message, count, 0);\r
10348     if (outCount == SOCKET_ERROR) {\r
10349       *outError = WSAGetLastError();\r
10350     } else {\r
10351       *outError = NO_ERROR;\r
10352     }\r
10353     break;\r
10354 \r
10355   case CPReal:\r
10356     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10357                   &dOutCount, NULL)) {\r
10358       *outError = NO_ERROR;\r
10359       outCount = (int) dOutCount;\r
10360     } else {\r
10361       *outError = GetLastError();\r
10362     }\r
10363     break;\r
10364 \r
10365   case CPComm:\r
10366     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10367                             &dOutCount, &ovl);\r
10368     if (*outError == NO_ERROR) {\r
10369       outCount = (int) dOutCount;\r
10370     }\r
10371     break;\r
10372   }\r
10373   return outCount;\r
10374 }\r
10375 \r
10376 int\r
10377 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10378                        long msdelay)\r
10379 {\r
10380   /* Ignore delay, not implemented for WinBoard */\r
10381   return OutputToProcess(pr, message, count, outError);\r
10382 }\r
10383 \r
10384 \r
10385 void\r
10386 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10387                         char *buf, int count, int error)\r
10388 {\r
10389   DisplayFatalError("Not implemented", 0, 1);\r
10390 }\r
10391 \r
10392 /* see wgamelist.c for Game List functions */\r
10393 /* see wedittags.c for Edit Tags functions */\r
10394 \r
10395 \r
10396 VOID\r
10397 ICSInitScript()\r
10398 {\r
10399   FILE *f;\r
10400   char buf[MSG_SIZ];\r
10401   char *dummy;\r
10402 \r
10403   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10404     f = fopen(buf, "r");\r
10405     if (f != NULL) {\r
10406       ProcessICSInitScript(f);\r
10407       fclose(f);\r
10408     }\r
10409   }\r
10410 }\r
10411 \r
10412 \r
10413 VOID\r
10414 StartAnalysisClock()\r
10415 {\r
10416   if (analysisTimerEvent) return;\r
10417   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10418                                         (UINT) 2000, NULL);\r
10419 }\r
10420 \r
10421 LRESULT CALLBACK\r
10422 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10423 {\r
10424   static HANDLE hwndText;\r
10425   RECT rect;\r
10426   static int sizeX, sizeY;\r
10427   int newSizeX, newSizeY, flags;\r
10428   MINMAXINFO *mmi;\r
10429 \r
10430   switch (message) {\r
10431   case WM_INITDIALOG: /* message: initialize dialog box */\r
10432     /* Initialize the dialog items */\r
10433     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10434     SetWindowText(hDlg, analysisTitle);\r
10435     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10436     /* Size and position the dialog */\r
10437     if (!analysisDialog) {\r
10438       analysisDialog = hDlg;\r
10439       flags = SWP_NOZORDER;\r
10440       GetClientRect(hDlg, &rect);\r
10441       sizeX = rect.right;\r
10442       sizeY = rect.bottom;\r
10443       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10444           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10445         WINDOWPLACEMENT wp;\r
10446         EnsureOnScreen(&analysisX, &analysisY);\r
10447         wp.length = sizeof(WINDOWPLACEMENT);\r
10448         wp.flags = 0;\r
10449         wp.showCmd = SW_SHOW;\r
10450         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10451         wp.rcNormalPosition.left = analysisX;\r
10452         wp.rcNormalPosition.right = analysisX + analysisW;\r
10453         wp.rcNormalPosition.top = analysisY;\r
10454         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10455         SetWindowPlacement(hDlg, &wp);\r
10456 \r
10457         GetClientRect(hDlg, &rect);\r
10458         newSizeX = rect.right;\r
10459         newSizeY = rect.bottom;\r
10460         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10461                               newSizeX, newSizeY);\r
10462         sizeX = newSizeX;\r
10463         sizeY = newSizeY;\r
10464       }\r
10465     }\r
10466     return FALSE;\r
10467 \r
10468   case WM_COMMAND: /* message: received a command */\r
10469     switch (LOWORD(wParam)) {\r
10470     case IDCANCEL:\r
10471       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10472           ExitAnalyzeMode();\r
10473           ModeHighlight();\r
10474           return TRUE;\r
10475       }\r
10476       EditGameEvent();\r
10477       return TRUE;\r
10478     default:\r
10479       break;\r
10480     }\r
10481     break;\r
10482 \r
10483   case WM_SIZE:\r
10484     newSizeX = LOWORD(lParam);\r
10485     newSizeY = HIWORD(lParam);\r
10486     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10487     sizeX = newSizeX;\r
10488     sizeY = newSizeY;\r
10489     break;\r
10490 \r
10491   case WM_GETMINMAXINFO:\r
10492     /* Prevent resizing window too small */\r
10493     mmi = (MINMAXINFO *) lParam;\r
10494     mmi->ptMinTrackSize.x = 100;\r
10495     mmi->ptMinTrackSize.y = 100;\r
10496     break;\r
10497   }\r
10498   return FALSE;\r
10499 }\r
10500 \r
10501 VOID\r
10502 AnalysisPopUp(char* title, char* str)\r
10503 {\r
10504   FARPROC lpProc;\r
10505   char *p, *q;\r
10506 \r
10507   /* [AS] */\r
10508   EngineOutputPopUp();\r
10509   return;\r
10510 \r
10511   if (str == NULL) str = "";\r
10512   p = (char *) malloc(2 * strlen(str) + 2);\r
10513   q = p;\r
10514   while (*str) {\r
10515     if (*str == '\n') *q++ = '\r';\r
10516     *q++ = *str++;\r
10517   }\r
10518   *q = NULLCHAR;\r
10519   if (analysisText != NULL) free(analysisText);\r
10520   analysisText = p;\r
10521 \r
10522   if (analysisDialog) {\r
10523     SetWindowText(analysisDialog, title);\r
10524     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10525     ShowWindow(analysisDialog, SW_SHOW);\r
10526   } else {\r
10527     analysisTitle = title;\r
10528     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10529     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10530                  hwndMain, (DLGPROC)lpProc);\r
10531     FreeProcInstance(lpProc);\r
10532   }\r
10533   analysisDialogUp = TRUE;  \r
10534 }\r
10535 \r
10536 VOID\r
10537 AnalysisPopDown()\r
10538 {\r
10539   if (analysisDialog) {\r
10540     ShowWindow(analysisDialog, SW_HIDE);\r
10541   }\r
10542   analysisDialogUp = FALSE;  \r
10543 }\r
10544 \r
10545 \r
10546 VOID\r
10547 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10548 {\r
10549   highlightInfo.sq[0].x = fromX;\r
10550   highlightInfo.sq[0].y = fromY;\r
10551   highlightInfo.sq[1].x = toX;\r
10552   highlightInfo.sq[1].y = toY;\r
10553 }\r
10554 \r
10555 VOID\r
10556 ClearHighlights()\r
10557 {\r
10558   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10559     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10560 }\r
10561 \r
10562 VOID\r
10563 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10564 {\r
10565   premoveHighlightInfo.sq[0].x = fromX;\r
10566   premoveHighlightInfo.sq[0].y = fromY;\r
10567   premoveHighlightInfo.sq[1].x = toX;\r
10568   premoveHighlightInfo.sq[1].y = toY;\r
10569 }\r
10570 \r
10571 VOID\r
10572 ClearPremoveHighlights()\r
10573 {\r
10574   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10575     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10576 }\r
10577 \r
10578 VOID\r
10579 ShutDownFrontEnd()\r
10580 {\r
10581   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10582   DeleteClipboardTempFiles();\r
10583 }\r
10584 \r
10585 void\r
10586 BoardToTop()\r
10587 {\r
10588     if (IsIconic(hwndMain))\r
10589       ShowWindow(hwndMain, SW_RESTORE);\r
10590 \r
10591     SetActiveWindow(hwndMain);\r
10592 }\r
10593 \r
10594 /*\r
10595  * Prototypes for animation support routines\r
10596  */\r
10597 static void ScreenSquare(int column, int row, POINT * pt);\r
10598 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10599      POINT frames[], int * nFrames);\r
10600 \r
10601 \r
10602 void\r
10603 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10604 {       // [HGM] atomic: animate blast wave\r
10605         int i;\r
10606 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10607         explodeInfo.fromX = fromX;\r
10608         explodeInfo.fromY = fromY;\r
10609         explodeInfo.toX = toX;\r
10610         explodeInfo.toY = toY;\r
10611         for(i=1; i<nFrames; i++) {\r
10612             explodeInfo.radius = (i*180)/(nFrames-1);\r
10613             DrawPosition(FALSE, NULL);\r
10614             Sleep(appData.animSpeed);\r
10615         }\r
10616         explodeInfo.radius = 0;\r
10617         DrawPosition(TRUE, NULL);\r
10618 }\r
10619 \r
10620 #define kFactor 4\r
10621 \r
10622 void\r
10623 AnimateMove(board, fromX, fromY, toX, toY)\r
10624      Board board;\r
10625      int fromX;\r
10626      int fromY;\r
10627      int toX;\r
10628      int toY;\r
10629 {\r
10630   ChessSquare piece;\r
10631   POINT start, finish, mid;\r
10632   POINT frames[kFactor * 2 + 1];\r
10633   int nFrames, n;\r
10634 \r
10635   if (!appData.animate) return;\r
10636   if (doingSizing) return;\r
10637   if (fromY < 0 || fromX < 0) return;\r
10638   piece = board[fromY][fromX];\r
10639   if (piece >= EmptySquare) return;\r
10640 \r
10641   ScreenSquare(fromX, fromY, &start);\r
10642   ScreenSquare(toX, toY, &finish);\r
10643 \r
10644   /* All pieces except knights move in straight line */\r
10645   if (piece != WhiteKnight && piece != BlackKnight) {\r
10646     mid.x = start.x + (finish.x - start.x) / 2;\r
10647     mid.y = start.y + (finish.y - start.y) / 2;\r
10648   } else {\r
10649     /* Knight: make diagonal movement then straight */\r
10650     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10651        mid.x = start.x + (finish.x - start.x) / 2;\r
10652        mid.y = finish.y;\r
10653      } else {\r
10654        mid.x = finish.x;\r
10655        mid.y = start.y + (finish.y - start.y) / 2;\r
10656      }\r
10657   }\r
10658   \r
10659   /* Don't use as many frames for very short moves */\r
10660   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10661     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10662   else\r
10663     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10664 \r
10665   animInfo.from.x = fromX;\r
10666   animInfo.from.y = fromY;\r
10667   animInfo.to.x = toX;\r
10668   animInfo.to.y = toY;\r
10669   animInfo.lastpos = start;\r
10670   animInfo.piece = piece;\r
10671   for (n = 0; n < nFrames; n++) {\r
10672     animInfo.pos = frames[n];\r
10673     DrawPosition(FALSE, NULL);\r
10674     animInfo.lastpos = animInfo.pos;\r
10675     Sleep(appData.animSpeed);\r
10676   }\r
10677   animInfo.pos = finish;\r
10678   DrawPosition(FALSE, NULL);\r
10679   animInfo.piece = EmptySquare;\r
10680   if(gameInfo.variant == VariantAtomic && \r
10681      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10682         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10683 }\r
10684 \r
10685 /*      Convert board position to corner of screen rect and color       */\r
10686 \r
10687 static void\r
10688 ScreenSquare(column, row, pt)\r
10689      int column; int row; POINT * pt;\r
10690 {\r
10691   if (flipView) {\r
10692     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10693     pt->y = lineGap + row * (squareSize + lineGap);\r
10694   } else {\r
10695     pt->x = lineGap + column * (squareSize + lineGap);\r
10696     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10697   }\r
10698 }\r
10699 \r
10700 /*      Generate a series of frame coords from start->mid->finish.\r
10701         The movement rate doubles until the half way point is\r
10702         reached, then halves back down to the final destination,\r
10703         which gives a nice slow in/out effect. The algorithmn\r
10704         may seem to generate too many intermediates for short\r
10705         moves, but remember that the purpose is to attract the\r
10706         viewers attention to the piece about to be moved and\r
10707         then to where it ends up. Too few frames would be less\r
10708         noticeable.                                             */\r
10709 \r
10710 static void\r
10711 Tween(start, mid, finish, factor, frames, nFrames)\r
10712      POINT * start; POINT * mid;\r
10713      POINT * finish; int factor;\r
10714      POINT frames[]; int * nFrames;\r
10715 {\r
10716   int n, fraction = 1, count = 0;\r
10717 \r
10718   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10719   for (n = 0; n < factor; n++)\r
10720     fraction *= 2;\r
10721   for (n = 0; n < factor; n++) {\r
10722     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10723     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10724     count ++;\r
10725     fraction = fraction / 2;\r
10726   }\r
10727   \r
10728   /* Midpoint */\r
10729   frames[count] = *mid;\r
10730   count ++;\r
10731   \r
10732   /* Slow out, stepping 1/2, then 1/4, ... */\r
10733   fraction = 2;\r
10734   for (n = 0; n < factor; n++) {\r
10735     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10736     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10737     count ++;\r
10738     fraction = fraction * 2;\r
10739   }\r
10740   *nFrames = count;\r
10741 }\r
10742 \r
10743 void\r
10744 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10745 {\r
10746 #if 0\r
10747     char buf[256];\r
10748 \r
10749     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10750         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10751 \r
10752     OutputDebugString( buf );\r
10753 #endif\r
10754 \r
10755     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10756 \r
10757     EvalGraphSet( first, last, current, pvInfoList );\r
10758 }\r
10759 \r
10760 void SetProgramStats( FrontEndProgramStats * stats )\r
10761 {\r
10762 #if 0\r
10763     char buf[1024];\r
10764 \r
10765     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10766         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10767 \r
10768     OutputDebugString( buf );\r
10769 #endif\r
10770 \r
10771     EngineOutputUpdate( stats );\r
10772 }\r