Fixed bug in atomic patch that interfered with non-atomic variants
[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 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 x, y, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo = {0, 0, 0};\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 \r
1248 #ifdef ZIPPY\r
1249   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1250   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1251   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1252   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1253   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1254   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1255   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1256   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1257   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1258   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1259   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1260   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1261   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1262     FALSE },\r
1263   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1264   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1265   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1266   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1267   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1268   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1269   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1270     FALSE },\r
1271   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1272   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1273   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1274   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1275   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1276   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1277   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1278   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1279   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1280   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1281   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1282   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1283   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1284   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1285   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1286   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1287   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1288   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1289 #endif\r
1290   /* [HGM] options for broadcasting and time odds */\r
1291   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1292   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1293   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1294   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1295   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1296   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1297   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1298   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1299   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1300   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1301   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1302   { NULL, ArgNone, NULL, FALSE }\r
1303 };\r
1304 \r
1305 \r
1306 /* Kludge for indirection files on command line */\r
1307 char* lastIndirectionFilename;\r
1308 ArgDescriptor argDescriptorIndirection =\r
1309 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1310 \r
1311 \r
1312 VOID\r
1313 ExitArgError(char *msg, char *badArg)\r
1314 {\r
1315   char buf[MSG_SIZ];\r
1316 \r
1317   sprintf(buf, "%s %s", msg, badArg);\r
1318   DisplayFatalError(buf, 0, 2);\r
1319   exit(2);\r
1320 }\r
1321 \r
1322 /* Command line font name parser.  NULL name means do nothing.\r
1323    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1324    For backward compatibility, syntax without the colon is also\r
1325    accepted, but font names with digits in them won't work in that case.\r
1326 */\r
1327 VOID\r
1328 ParseFontName(char *name, MyFontParams *mfp)\r
1329 {\r
1330   char *p, *q;\r
1331   if (name == NULL) return;\r
1332   p = name;\r
1333   q = strchr(p, ':');\r
1334   if (q) {\r
1335     if (q - p >= sizeof(mfp->faceName))\r
1336       ExitArgError("Font name too long:", name);\r
1337     memcpy(mfp->faceName, p, q - p);\r
1338     mfp->faceName[q - p] = NULLCHAR;\r
1339     p = q + 1;\r
1340   } else {\r
1341     q = mfp->faceName;\r
1342     while (*p && !isdigit(*p)) {\r
1343       *q++ = *p++;\r
1344       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1345         ExitArgError("Font name too long:", name);\r
1346     }\r
1347     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1348     *q = NULLCHAR;\r
1349   }\r
1350   if (!*p) ExitArgError("Font point size missing:", name);\r
1351   mfp->pointSize = (float) atof(p);\r
1352   mfp->bold = (strchr(p, 'b') != NULL);\r
1353   mfp->italic = (strchr(p, 'i') != NULL);\r
1354   mfp->underline = (strchr(p, 'u') != NULL);\r
1355   mfp->strikeout = (strchr(p, 's') != NULL);\r
1356 }\r
1357 \r
1358 /* Color name parser.\r
1359    X version accepts X color names, but this one\r
1360    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1361 COLORREF\r
1362 ParseColorName(char *name)\r
1363 {\r
1364   int red, green, blue, count;\r
1365   char buf[MSG_SIZ];\r
1366 \r
1367   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1368   if (count != 3) {\r
1369     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1370       &red, &green, &blue);\r
1371   }\r
1372   if (count != 3) {\r
1373     sprintf(buf, "Can't parse color name %s", name);\r
1374     DisplayError(buf, 0);\r
1375     return RGB(0, 0, 0);\r
1376   }\r
1377   return PALETTERGB(red, green, blue);\r
1378 }\r
1379 \r
1380 \r
1381 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1382 {\r
1383   char *e = argValue;\r
1384   int eff = 0;\r
1385 \r
1386   while (*e) {\r
1387     if (*e == 'b')      eff |= CFE_BOLD;\r
1388     else if (*e == 'i') eff |= CFE_ITALIC;\r
1389     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1390     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1391     else if (*e == '#' || isdigit(*e)) break;\r
1392     e++;\r
1393   }\r
1394   *effects = eff;\r
1395   *color   = ParseColorName(e);\r
1396 }\r
1397 \r
1398 \r
1399 BoardSize\r
1400 ParseBoardSize(char *name)\r
1401 {\r
1402   BoardSize bs = SizeTiny;\r
1403   while (sizeInfo[bs].name != NULL) {\r
1404     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1405     bs++;\r
1406   }\r
1407   ExitArgError("Unrecognized board size value", name);\r
1408   return bs; /* not reached */\r
1409 }\r
1410 \r
1411 \r
1412 char\r
1413 StringGet(void *getClosure)\r
1414 {\r
1415   char **p = (char **) getClosure;\r
1416   return *((*p)++);\r
1417 }\r
1418 \r
1419 char\r
1420 FileGet(void *getClosure)\r
1421 {\r
1422   int c;\r
1423   FILE* f = (FILE*) getClosure;\r
1424 \r
1425   c = getc(f);\r
1426   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1427   if (c == EOF)\r
1428     return NULLCHAR;\r
1429   else\r
1430     return (char) c;\r
1431 }\r
1432 \r
1433 /* Parse settings file named "name". If file found, return the\r
1434    full name in fullname and return TRUE; else return FALSE */\r
1435 BOOLEAN\r
1436 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1437 {\r
1438   char *dummy;\r
1439   FILE *f;\r
1440   int ok; char buf[MSG_SIZ];\r
1441 \r
1442   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1443   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1444     sprintf(buf, "%s.ini", name);\r
1445     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1446   }\r
1447   if (ok) {\r
1448     f = fopen(fullname, "r");\r
1449     if (f != NULL) {\r
1450       ParseArgs(FileGet, f);\r
1451       fclose(f);\r
1452       return TRUE;\r
1453     }\r
1454   }\r
1455   return FALSE;\r
1456 }\r
1457 \r
1458 VOID\r
1459 ParseArgs(GetFunc get, void *cl)\r
1460 {\r
1461   char argName[ARG_MAX];\r
1462   char argValue[ARG_MAX];\r
1463   ArgDescriptor *ad;\r
1464   char start;\r
1465   char *q;\r
1466   int i, octval;\r
1467   char ch;\r
1468   int posarg = 0;\r
1469 \r
1470   ch = get(cl);\r
1471   for (;;) {\r
1472     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1473     if (ch == NULLCHAR) break;\r
1474     if (ch == ';') {\r
1475       /* Comment to end of line */\r
1476       ch = get(cl);\r
1477       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1478       continue;\r
1479     } else if (ch == '/' || ch == '-') {\r
1480       /* Switch */\r
1481       q = argName;\r
1482       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1483              ch != '\n' && ch != '\t') {\r
1484         *q++ = ch;\r
1485         ch = get(cl);\r
1486       }\r
1487       *q = NULLCHAR;\r
1488 \r
1489       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1490         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1491 \r
1492       if (ad->argName == NULL)\r
1493         ExitArgError("Unrecognized argument", argName);\r
1494 \r
1495     } else if (ch == '@') {\r
1496       /* Indirection file */\r
1497       ad = &argDescriptorIndirection;\r
1498       ch = get(cl);\r
1499     } else {\r
1500       /* Positional argument */\r
1501       ad = &argDescriptors[posarg++];\r
1502       strcpy(argName, ad->argName);\r
1503     }\r
1504 \r
1505     if (ad->argType == ArgTrue) {\r
1506       *(Boolean *) ad->argLoc = TRUE;\r
1507       continue;\r
1508     }\r
1509     if (ad->argType == ArgFalse) {\r
1510       *(Boolean *) ad->argLoc = FALSE;\r
1511       continue;\r
1512     }\r
1513 \r
1514     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1515     if (ch == NULLCHAR || ch == '\n') {\r
1516       ExitArgError("No value provided for argument", argName);\r
1517     }\r
1518     q = argValue;\r
1519     if (ch == '{') {\r
1520       // Quoting with { }.  No characters have to (or can) be escaped.\r
1521       // Thus the string cannot contain a '}' character.\r
1522       start = ch;\r
1523       ch = get(cl);\r
1524       while (start) {\r
1525         switch (ch) {\r
1526         case NULLCHAR:\r
1527           start = NULLCHAR;\r
1528           break;\r
1529           \r
1530         case '}':\r
1531           ch = get(cl);\r
1532           start = NULLCHAR;\r
1533           break;\r
1534 \r
1535         default:\r
1536           *q++ = ch;\r
1537           ch = get(cl);\r
1538           break;\r
1539         }\r
1540       }   \r
1541     } else if (ch == '\'' || ch == '"') {\r
1542       // Quoting with ' ' or " ", with \ as escape character.\r
1543       // Inconvenient for long strings that may contain Windows filenames.\r
1544       start = ch;\r
1545       ch = get(cl);\r
1546       while (start) {\r
1547         switch (ch) {\r
1548         case NULLCHAR:\r
1549           start = NULLCHAR;\r
1550           break;\r
1551 \r
1552         default:\r
1553         not_special:\r
1554           *q++ = ch;\r
1555           ch = get(cl);\r
1556           break;\r
1557 \r
1558         case '\'':\r
1559         case '\"':\r
1560           if (ch == start) {\r
1561             ch = get(cl);\r
1562             start = NULLCHAR;\r
1563             break;\r
1564           } else {\r
1565             goto not_special;\r
1566           }\r
1567 \r
1568         case '\\':\r
1569           if (ad->argType == ArgFilename\r
1570               || ad->argType == ArgSettingsFilename) {\r
1571               goto not_special;\r
1572           }\r
1573           ch = get(cl);\r
1574           switch (ch) {\r
1575           case NULLCHAR:\r
1576             ExitArgError("Incomplete \\ escape in value for", argName);\r
1577             break;\r
1578           case 'n':\r
1579             *q++ = '\n';\r
1580             ch = get(cl);\r
1581             break;\r
1582           case 'r':\r
1583             *q++ = '\r';\r
1584             ch = get(cl);\r
1585             break;\r
1586           case 't':\r
1587             *q++ = '\t';\r
1588             ch = get(cl);\r
1589             break;\r
1590           case 'b':\r
1591             *q++ = '\b';\r
1592             ch = get(cl);\r
1593             break;\r
1594           case 'f':\r
1595             *q++ = '\f';\r
1596             ch = get(cl);\r
1597             break;\r
1598           default:\r
1599             octval = 0;\r
1600             for (i = 0; i < 3; i++) {\r
1601               if (ch >= '0' && ch <= '7') {\r
1602                 octval = octval*8 + (ch - '0');\r
1603                 ch = get(cl);\r
1604               } else {\r
1605                 break;\r
1606               }\r
1607             }\r
1608             if (i > 0) {\r
1609               *q++ = (char) octval;\r
1610             } else {\r
1611               *q++ = ch;\r
1612               ch = get(cl);\r
1613             }\r
1614             break;\r
1615           }\r
1616           break;\r
1617         }\r
1618       }\r
1619     } else {\r
1620       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1621         *q++ = ch;\r
1622         ch = get(cl);\r
1623       }\r
1624     }\r
1625     *q = NULLCHAR;\r
1626 \r
1627     switch (ad->argType) {\r
1628     case ArgInt:\r
1629       *(int *) ad->argLoc = atoi(argValue);\r
1630       break;\r
1631 \r
1632     case ArgFloat:\r
1633       *(float *) ad->argLoc = (float) atof(argValue);\r
1634       break;\r
1635 \r
1636     case ArgString:\r
1637     case ArgFilename:\r
1638       *(char **) ad->argLoc = strdup(argValue);\r
1639       break;\r
1640 \r
1641     case ArgSettingsFilename:\r
1642       {\r
1643         char fullname[MSG_SIZ];\r
1644         if (ParseSettingsFile(argValue, fullname)) {\r
1645           if (ad->argLoc != NULL) {\r
1646             *(char **) ad->argLoc = strdup(fullname);\r
1647           }\r
1648         } else {\r
1649           if (ad->argLoc != NULL) {\r
1650           } else {\r
1651             ExitArgError("Failed to open indirection file", argValue);\r
1652           }\r
1653         }\r
1654       }\r
1655       break;\r
1656 \r
1657     case ArgBoolean:\r
1658       switch (argValue[0]) {\r
1659       case 't':\r
1660       case 'T':\r
1661         *(Boolean *) ad->argLoc = TRUE;\r
1662         break;\r
1663       case 'f':\r
1664       case 'F':\r
1665         *(Boolean *) ad->argLoc = FALSE;\r
1666         break;\r
1667       default:\r
1668         ExitArgError("Unrecognized boolean argument value", argValue);\r
1669         break;\r
1670       }\r
1671       break;\r
1672 \r
1673     case ArgColor:\r
1674       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1675       break;\r
1676 \r
1677     case ArgAttribs: {\r
1678       ColorClass cc = (ColorClass)ad->argLoc;\r
1679       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1680       }\r
1681       break;\r
1682       \r
1683     case ArgBoardSize:\r
1684       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1685       break;\r
1686 \r
1687     case ArgFont:\r
1688       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1689       break;\r
1690 \r
1691     case ArgCommSettings:\r
1692       ParseCommSettings(argValue, &dcb);\r
1693       break;\r
1694 \r
1695     case ArgNone:\r
1696       ExitArgError("Unrecognized argument", argValue);\r
1697       break;\r
1698     case ArgTrue:\r
1699     case ArgFalse: ;\r
1700     }\r
1701   }\r
1702 }\r
1703 \r
1704 VOID\r
1705 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1706 {\r
1707   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1708   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1709   DeleteDC(hdc);\r
1710   lf->lfWidth = 0;\r
1711   lf->lfEscapement = 0;\r
1712   lf->lfOrientation = 0;\r
1713   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1714   lf->lfItalic = mfp->italic;\r
1715   lf->lfUnderline = mfp->underline;\r
1716   lf->lfStrikeOut = mfp->strikeout;\r
1717   lf->lfCharSet = DEFAULT_CHARSET;\r
1718   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1719   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1720   lf->lfQuality = DEFAULT_QUALITY;\r
1721   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1722   strcpy(lf->lfFaceName, mfp->faceName);\r
1723 }\r
1724 \r
1725 VOID\r
1726 CreateFontInMF(MyFont *mf)\r
1727 {\r
1728   LFfromMFP(&mf->lf, &mf->mfp);\r
1729   if (mf->hf) DeleteObject(mf->hf);\r
1730   mf->hf = CreateFontIndirect(&mf->lf);\r
1731 }\r
1732 \r
1733 VOID\r
1734 SetDefaultTextAttribs()\r
1735 {\r
1736   ColorClass cc;\r
1737   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1738     ParseAttribs(&textAttribs[cc].color, \r
1739                  &textAttribs[cc].effects, \r
1740                  defaultTextAttribs[cc]);\r
1741   }\r
1742 }\r
1743 \r
1744 VOID\r
1745 SetDefaultSounds()\r
1746 {\r
1747   ColorClass cc;\r
1748   SoundClass sc;\r
1749   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1750     textAttribs[cc].sound.name = strdup("");\r
1751     textAttribs[cc].sound.data = NULL;\r
1752   }\r
1753   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1754     sounds[sc].name = strdup("");\r
1755     sounds[sc].data = NULL;\r
1756   }\r
1757   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1758 }\r
1759 \r
1760 VOID\r
1761 LoadAllSounds()\r
1762 {\r
1763   ColorClass cc;\r
1764   SoundClass sc;\r
1765   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1766     MyLoadSound(&textAttribs[cc].sound);\r
1767   }\r
1768   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1769     MyLoadSound(&sounds[sc]);\r
1770   }\r
1771 }\r
1772 \r
1773 VOID\r
1774 InitAppData(LPSTR lpCmdLine)\r
1775 {\r
1776   int i, j;\r
1777   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1778   char *dummy, *p;\r
1779 \r
1780   programName = szAppName;\r
1781 \r
1782   /* Initialize to defaults */\r
1783   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1784   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1785   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1786   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1787   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1788   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1789   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1790   SetDefaultTextAttribs();\r
1791   SetDefaultSounds();\r
1792   appData.movesPerSession = MOVES_PER_SESSION;\r
1793   appData.initString = INIT_STRING;\r
1794   appData.secondInitString = INIT_STRING;\r
1795   appData.firstComputerString = COMPUTER_STRING;\r
1796   appData.secondComputerString = COMPUTER_STRING;\r
1797   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1798   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1799   appData.firstPlaysBlack = FALSE;\r
1800   appData.noChessProgram = FALSE;\r
1801   chessProgram = FALSE;\r
1802   appData.firstHost = FIRST_HOST;\r
1803   appData.secondHost = SECOND_HOST;\r
1804   appData.firstDirectory = FIRST_DIRECTORY;\r
1805   appData.secondDirectory = SECOND_DIRECTORY;\r
1806   appData.bitmapDirectory = "";\r
1807   appData.remoteShell = REMOTE_SHELL;\r
1808   appData.remoteUser = "";\r
1809   appData.timeDelay = TIME_DELAY;\r
1810   appData.timeControl = TIME_CONTROL;\r
1811   appData.timeIncrement = TIME_INCREMENT;\r
1812   appData.icsActive = FALSE;\r
1813   appData.icsHost = "";\r
1814   appData.icsPort = ICS_PORT;\r
1815   appData.icsCommPort = ICS_COMM_PORT;\r
1816   appData.icsLogon = ICS_LOGON;\r
1817   appData.icsHelper = "";\r
1818   appData.useTelnet = FALSE;\r
1819   appData.telnetProgram = TELNET_PROGRAM;\r
1820   appData.gateway = "";\r
1821   appData.loadGameFile = "";\r
1822   appData.loadGameIndex = 0;\r
1823   appData.saveGameFile = "";\r
1824   appData.autoSaveGames = FALSE;\r
1825   appData.loadPositionFile = "";\r
1826   appData.loadPositionIndex = 1;\r
1827   appData.savePositionFile = "";\r
1828   appData.matchMode = FALSE;\r
1829   appData.matchGames = 0;\r
1830   appData.monoMode = FALSE;\r
1831   appData.debugMode = FALSE;\r
1832   appData.clockMode = TRUE;\r
1833   boardSize = (BoardSize) -1; /* determine by screen size */\r
1834   appData.Iconic = FALSE; /*unused*/\r
1835   appData.searchTime = "";\r
1836   appData.searchDepth = 0;\r
1837   appData.showCoords = FALSE;\r
1838   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1839   appData.autoCallFlag = FALSE;\r
1840   appData.flipView = FALSE;\r
1841   appData.autoFlipView = TRUE;\r
1842   appData.cmailGameName = "";\r
1843   appData.alwaysPromoteToQueen = FALSE;\r
1844   appData.oldSaveStyle = FALSE;\r
1845   appData.quietPlay = FALSE;\r
1846   appData.showThinking = FALSE;\r
1847   appData.ponderNextMove = TRUE;\r
1848   appData.periodicUpdates = TRUE;\r
1849   appData.popupExitMessage = TRUE;\r
1850   appData.popupMoveErrors = FALSE;\r
1851   appData.autoObserve = FALSE;\r
1852   appData.autoComment = FALSE;\r
1853   appData.animate = TRUE;\r
1854   appData.animSpeed = 10;\r
1855   appData.animateDragging = TRUE;\r
1856   appData.highlightLastMove = TRUE;\r
1857   appData.getMoveList = TRUE;\r
1858   appData.testLegality = TRUE;\r
1859   appData.premove = TRUE;\r
1860   appData.premoveWhite = FALSE;\r
1861   appData.premoveWhiteText = "";\r
1862   appData.premoveBlack = FALSE;\r
1863   appData.premoveBlackText = "";\r
1864   appData.icsAlarm = TRUE;\r
1865   appData.icsAlarmTime = 5000;\r
1866   appData.autoRaiseBoard = TRUE;\r
1867   appData.localLineEditing = TRUE;\r
1868   appData.colorize = TRUE;\r
1869   appData.reuseFirst = TRUE;\r
1870   appData.reuseSecond = TRUE;\r
1871   appData.blindfold = FALSE;\r
1872   appData.icsEngineAnalyze = FALSE;\r
1873   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1874   dcb.DCBlength = sizeof(DCB);\r
1875   dcb.BaudRate = 9600;\r
1876   dcb.fBinary = TRUE;\r
1877   dcb.fParity = FALSE;\r
1878   dcb.fOutxCtsFlow = FALSE;\r
1879   dcb.fOutxDsrFlow = FALSE;\r
1880   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1881   dcb.fDsrSensitivity = FALSE;\r
1882   dcb.fTXContinueOnXoff = TRUE;\r
1883   dcb.fOutX = FALSE;\r
1884   dcb.fInX = FALSE;\r
1885   dcb.fNull = FALSE;\r
1886   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1887   dcb.fAbortOnError = FALSE;\r
1888   dcb.ByteSize = 7;\r
1889   dcb.Parity = SPACEPARITY;\r
1890   dcb.StopBits = ONESTOPBIT;\r
1891   settingsFileName = SETTINGS_FILE;\r
1892   saveSettingsOnExit = TRUE;\r
1893   boardX = CW_USEDEFAULT;\r
1894   boardY = CW_USEDEFAULT;\r
1895   consoleX = CW_USEDEFAULT; \r
1896   consoleY = CW_USEDEFAULT; \r
1897   consoleW = CW_USEDEFAULT;\r
1898   consoleH = CW_USEDEFAULT;\r
1899   analysisX = CW_USEDEFAULT; \r
1900   analysisY = CW_USEDEFAULT; \r
1901   analysisW = CW_USEDEFAULT;\r
1902   analysisH = CW_USEDEFAULT;\r
1903   commentX = CW_USEDEFAULT; \r
1904   commentY = CW_USEDEFAULT; \r
1905   commentW = CW_USEDEFAULT;\r
1906   commentH = CW_USEDEFAULT;\r
1907   editTagsX = CW_USEDEFAULT; \r
1908   editTagsY = CW_USEDEFAULT; \r
1909   editTagsW = CW_USEDEFAULT;\r
1910   editTagsH = CW_USEDEFAULT;\r
1911   gameListX = CW_USEDEFAULT; \r
1912   gameListY = CW_USEDEFAULT; \r
1913   gameListW = CW_USEDEFAULT;\r
1914   gameListH = CW_USEDEFAULT;\r
1915   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1916   icsNames = ICS_NAMES;\r
1917   firstChessProgramNames = FCP_NAMES;\r
1918   secondChessProgramNames = SCP_NAMES;\r
1919   appData.initialMode = "";\r
1920   appData.variant = "normal";\r
1921   appData.firstProtocolVersion = PROTOVER;\r
1922   appData.secondProtocolVersion = PROTOVER;\r
1923   appData.showButtonBar = TRUE;\r
1924 \r
1925    /* [AS] New properties (see comments in header file) */\r
1926   appData.firstScoreIsAbsolute = FALSE;\r
1927   appData.secondScoreIsAbsolute = FALSE;\r
1928   appData.saveExtendedInfoInPGN = FALSE;\r
1929   appData.hideThinkingFromHuman = FALSE;\r
1930   appData.liteBackTextureFile = "";\r
1931   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1932   appData.darkBackTextureFile = "";\r
1933   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1934   appData.renderPiecesWithFont = "";\r
1935   appData.fontToPieceTable = "";\r
1936   appData.fontBackColorWhite = 0;\r
1937   appData.fontForeColorWhite = 0;\r
1938   appData.fontBackColorBlack = 0;\r
1939   appData.fontForeColorBlack = 0;\r
1940   appData.fontPieceSize = 80;\r
1941   appData.overrideLineGap = 1;\r
1942   appData.adjudicateLossThreshold = 0;\r
1943   appData.delayBeforeQuit = 0;\r
1944   appData.delayAfterQuit = 0;\r
1945   appData.nameOfDebugFile = "winboard.debug";\r
1946   appData.pgnEventHeader = "Computer Chess Game";\r
1947   appData.defaultFrcPosition = -1;\r
1948   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1949   appData.saveOutOfBookInfo = TRUE;\r
1950   appData.showEvalInMoveHistory = TRUE;\r
1951   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1952   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1953   appData.highlightMoveWithArrow = FALSE;\r
1954   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1955   appData.useStickyWindows = TRUE;\r
1956   appData.adjudicateDrawMoves = 0;\r
1957   appData.autoDisplayComment = TRUE;\r
1958   appData.autoDisplayTags = TRUE;\r
1959   appData.firstIsUCI = FALSE;\r
1960   appData.secondIsUCI = FALSE;\r
1961   appData.firstHasOwnBookUCI = TRUE;\r
1962   appData.secondHasOwnBookUCI = TRUE;\r
1963   appData.polyglotDir = "";\r
1964   appData.usePolyglotBook = FALSE;\r
1965   appData.polyglotBook = "";\r
1966   appData.defaultHashSize = 64;\r
1967   appData.defaultCacheSizeEGTB = 4;\r
1968   appData.defaultPathEGTB = "c:\\egtb";\r
1969   appData.firstOptions = "";\r
1970   appData.secondOptions = "";\r
1971 \r
1972   InitWindowPlacement( &wpMoveHistory );\r
1973   InitWindowPlacement( &wpEvalGraph );\r
1974   InitWindowPlacement( &wpEngineOutput );\r
1975 \r
1976   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1977   appData.NrFiles      = -1;\r
1978   appData.NrRanks      = -1;\r
1979   appData.holdingsSize = -1;\r
1980   appData.testClaims   = FALSE;\r
1981   appData.checkMates   = FALSE;\r
1982   appData.materialDraws= FALSE;\r
1983   appData.trivialDraws = FALSE;\r
1984   appData.ruleMoves    = 51;\r
1985   appData.drawRepeats  = 6;\r
1986   appData.matchPause   = 10000;\r
1987   appData.alphaRank    = FALSE;\r
1988   appData.allWhite     = FALSE;\r
1989   appData.upsideDown   = FALSE;\r
1990   appData.serverPause  = 15;\r
1991   appData.serverMovesName   = NULL;\r
1992   appData.suppressLoadMoves = FALSE;\r
1993   appData.firstTimeOdds  = 1;\r
1994   appData.secondTimeOdds = 1;\r
1995   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1996   appData.secondAccumulateTC = 1;\r
1997   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1998   appData.secondNPS = -1;\r
1999   appData.engineComments = 1;\r
2000   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2001   appData.egtFormats = "";\r
2002 \r
2003 #ifdef ZIPPY\r
2004   appData.zippyTalk = ZIPPY_TALK;\r
2005   appData.zippyPlay = ZIPPY_PLAY;\r
2006   appData.zippyLines = ZIPPY_LINES;\r
2007   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2008   appData.zippyPassword = ZIPPY_PASSWORD;\r
2009   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2010   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2011   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2012   appData.zippyUseI = ZIPPY_USE_I;\r
2013   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2014   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2015   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2016   appData.zippyGameStart = ZIPPY_GAME_START;\r
2017   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2018   appData.zippyAbort = ZIPPY_ABORT;\r
2019   appData.zippyVariants = ZIPPY_VARIANTS;\r
2020   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2021   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2022 #endif\r
2023 \r
2024   /* Point font array elements to structures and\r
2025      parse default font names */\r
2026   for (i=0; i<NUM_FONTS; i++) {\r
2027     for (j=0; j<NUM_SIZES; j++) {\r
2028       font[j][i] = &fontRec[j][i];\r
2029       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2030     }\r
2031   }\r
2032   \r
2033   /* Parse default settings file if any */\r
2034   if (ParseSettingsFile(settingsFileName, buf)) {\r
2035     settingsFileName = strdup(buf);\r
2036   }\r
2037 \r
2038   /* Parse command line */\r
2039   ParseArgs(StringGet, &lpCmdLine);\r
2040 \r
2041   /* [HGM] make sure board size is acceptable */\r
2042   if(appData.NrFiles > BOARD_SIZE ||\r
2043      appData.NrRanks > BOARD_SIZE   )\r
2044       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2045 \r
2046   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2047    * with options from the command line, we now make an even higher priority\r
2048    * overrule by WB options attached to the engine command line. This so that\r
2049    * tournament managers can use WB options (such as /timeOdds) that follow\r
2050    * the engines.\r
2051    */\r
2052   if(appData.firstChessProgram != NULL) {\r
2053       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2054       static char *f = "first";\r
2055       char buf[MSG_SIZ], *q = buf;\r
2056       if(p != NULL) { // engine command line contains WinBoard options\r
2057           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2058           ParseArgs(StringGet, &q);\r
2059           p[-1] = 0; // cut them offengine command line\r
2060       }\r
2061   }\r
2062   // now do same for second chess program\r
2063   if(appData.secondChessProgram != NULL) {\r
2064       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2065       static char *s = "second";\r
2066       char buf[MSG_SIZ], *q = buf;\r
2067       if(p != NULL) { // engine command line contains WinBoard options\r
2068           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2069           ParseArgs(StringGet, &q);\r
2070           p[-1] = 0; // cut them offengine command line\r
2071       }\r
2072   }\r
2073 \r
2074 \r
2075   /* Propagate options that affect others */\r
2076   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2077   if (appData.icsActive || appData.noChessProgram) {\r
2078      chessProgram = FALSE;  /* not local chess program mode */\r
2079   }\r
2080 \r
2081   /* Open startup dialog if needed */\r
2082   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2083       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2084       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2085                         *appData.secondChessProgram == NULLCHAR))) {\r
2086     FARPROC lpProc;\r
2087     \r
2088     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2089     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2090     FreeProcInstance(lpProc);\r
2091   }\r
2092 \r
2093   /* Make sure save files land in the right (?) directory */\r
2094   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2095     appData.saveGameFile = strdup(buf);\r
2096   }\r
2097   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2098     appData.savePositionFile = strdup(buf);\r
2099   }\r
2100 \r
2101   /* Finish initialization for fonts and sounds */\r
2102   for (i=0; i<NUM_FONTS; i++) {\r
2103     for (j=0; j<NUM_SIZES; j++) {\r
2104       CreateFontInMF(font[j][i]);\r
2105     }\r
2106   }\r
2107   /* xboard, and older WinBoards, controlled the move sound with the\r
2108      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2109      always turn the option on (so that the backend will call us),\r
2110      then let the user turn the sound off by setting it to silence if\r
2111      desired.  To accommodate old winboard.ini files saved by old\r
2112      versions of WinBoard, we also turn off the sound if the option\r
2113      was initially set to false. */\r
2114   if (!appData.ringBellAfterMoves) {\r
2115     sounds[(int)SoundMove].name = strdup("");\r
2116     appData.ringBellAfterMoves = TRUE;\r
2117   }\r
2118   GetCurrentDirectory(MSG_SIZ, currDir);\r
2119   SetCurrentDirectory(installDir);\r
2120   LoadAllSounds();\r
2121   SetCurrentDirectory(currDir);\r
2122 \r
2123   p = icsTextMenuString;\r
2124   if (p[0] == '@') {\r
2125     FILE* f = fopen(p + 1, "r");\r
2126     if (f == NULL) {\r
2127       DisplayFatalError(p + 1, errno, 2);\r
2128       return;\r
2129     }\r
2130     i = fread(buf, 1, sizeof(buf)-1, f);\r
2131     fclose(f);\r
2132     buf[i] = NULLCHAR;\r
2133     p = buf;\r
2134   }\r
2135   ParseIcsTextMenu(strdup(p));\r
2136 }\r
2137 \r
2138 \r
2139 VOID\r
2140 InitMenuChecks()\r
2141 {\r
2142   HMENU hmenu = GetMenu(hwndMain);\r
2143 \r
2144   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2145                         MF_BYCOMMAND|((appData.icsActive &&\r
2146                                        *appData.icsCommPort != NULLCHAR) ?\r
2147                                       MF_ENABLED : MF_GRAYED));\r
2148   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2149                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2150                                      MF_CHECKED : MF_UNCHECKED));\r
2151 }\r
2152 \r
2153 \r
2154 VOID\r
2155 SaveSettings(char* name)\r
2156 {\r
2157   FILE *f;\r
2158   ArgDescriptor *ad;\r
2159   WINDOWPLACEMENT wp;\r
2160   char dir[MSG_SIZ];\r
2161 \r
2162   if (!hwndMain) return;\r
2163 \r
2164   GetCurrentDirectory(MSG_SIZ, dir);\r
2165   SetCurrentDirectory(installDir);\r
2166   f = fopen(name, "w");\r
2167   SetCurrentDirectory(dir);\r
2168   if (f == NULL) {\r
2169     DisplayError(name, errno);\r
2170     return;\r
2171   }\r
2172   fprintf(f, ";\n");\r
2173   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2174   fprintf(f, ";\n");\r
2175   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2176   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2177   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2178   fprintf(f, ";\n");\r
2179 \r
2180   wp.length = sizeof(WINDOWPLACEMENT);\r
2181   GetWindowPlacement(hwndMain, &wp);\r
2182   boardX = wp.rcNormalPosition.left;\r
2183   boardY = wp.rcNormalPosition.top;\r
2184 \r
2185   if (hwndConsole) {\r
2186     GetWindowPlacement(hwndConsole, &wp);\r
2187     consoleX = wp.rcNormalPosition.left;\r
2188     consoleY = wp.rcNormalPosition.top;\r
2189     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2190     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2191   }\r
2192 \r
2193   if (analysisDialog) {\r
2194     GetWindowPlacement(analysisDialog, &wp);\r
2195     analysisX = wp.rcNormalPosition.left;\r
2196     analysisY = wp.rcNormalPosition.top;\r
2197     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2198     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2199   }\r
2200 \r
2201   if (commentDialog) {\r
2202     GetWindowPlacement(commentDialog, &wp);\r
2203     commentX = wp.rcNormalPosition.left;\r
2204     commentY = wp.rcNormalPosition.top;\r
2205     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2206     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2207   }\r
2208 \r
2209   if (editTagsDialog) {\r
2210     GetWindowPlacement(editTagsDialog, &wp);\r
2211     editTagsX = wp.rcNormalPosition.left;\r
2212     editTagsY = wp.rcNormalPosition.top;\r
2213     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2214     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2215   }\r
2216 \r
2217   if (gameListDialog) {\r
2218     GetWindowPlacement(gameListDialog, &wp);\r
2219     gameListX = wp.rcNormalPosition.left;\r
2220     gameListY = wp.rcNormalPosition.top;\r
2221     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2222     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2223   }\r
2224 \r
2225   /* [AS] Move history */\r
2226   wpMoveHistory.visible = MoveHistoryIsUp();\r
2227   \r
2228   if( moveHistoryDialog ) {\r
2229     GetWindowPlacement(moveHistoryDialog, &wp);\r
2230     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2231     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2232     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2233     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2234   }\r
2235 \r
2236   /* [AS] Eval graph */\r
2237   wpEvalGraph.visible = EvalGraphIsUp();\r
2238 \r
2239   if( evalGraphDialog ) {\r
2240     GetWindowPlacement(evalGraphDialog, &wp);\r
2241     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2242     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2243     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2244     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2245   }\r
2246 \r
2247   /* [AS] Engine output */\r
2248   wpEngineOutput.visible = EngineOutputIsUp();\r
2249 \r
2250   if( engineOutputDialog ) {\r
2251     GetWindowPlacement(engineOutputDialog, &wp);\r
2252     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2253     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2254     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2255     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2256   }\r
2257 \r
2258   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2259     if (!ad->save) continue;\r
2260     switch (ad->argType) {\r
2261     case ArgString:\r
2262       {\r
2263         char *p = *(char **)ad->argLoc;\r
2264         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2265           /* Quote multiline values or \-containing values\r
2266              with { } if possible */\r
2267           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2268         } else {\r
2269           /* Else quote with " " */\r
2270           fprintf(f, "/%s=\"", ad->argName);\r
2271           while (*p) {\r
2272             if (*p == '\n') fprintf(f, "\n");\r
2273             else if (*p == '\r') fprintf(f, "\\r");\r
2274             else if (*p == '\t') fprintf(f, "\\t");\r
2275             else if (*p == '\b') fprintf(f, "\\b");\r
2276             else if (*p == '\f') fprintf(f, "\\f");\r
2277             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2278             else if (*p == '\"') fprintf(f, "\\\"");\r
2279             else if (*p == '\\') fprintf(f, "\\\\");\r
2280             else putc(*p, f);\r
2281             p++;\r
2282           }\r
2283           fprintf(f, "\"\n");\r
2284         }\r
2285       }\r
2286       break;\r
2287     case ArgInt:\r
2288       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2289       break;\r
2290     case ArgFloat:\r
2291       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2292       break;\r
2293     case ArgBoolean:\r
2294       fprintf(f, "/%s=%s\n", ad->argName, \r
2295         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2296       break;\r
2297     case ArgTrue:\r
2298       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2299       break;\r
2300     case ArgFalse:\r
2301       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2302       break;\r
2303     case ArgColor:\r
2304       {\r
2305         COLORREF color = *(COLORREF *)ad->argLoc;\r
2306         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2307           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2308       }\r
2309       break;\r
2310     case ArgAttribs:\r
2311       {\r
2312         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2313         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2314           (ta->effects & CFE_BOLD) ? "b" : "",\r
2315           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2316           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2317           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2318           (ta->effects) ? " " : "",\r
2319           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2320       }\r
2321       break;\r
2322     case ArgFilename:\r
2323       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2324         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2325       } else {\r
2326         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2327       }\r
2328       break;\r
2329     case ArgBoardSize:\r
2330       fprintf(f, "/%s=%s\n", ad->argName,\r
2331               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2332       break;\r
2333     case ArgFont:\r
2334       {\r
2335         int bs;\r
2336         for (bs=0; bs<NUM_SIZES; bs++) {\r
2337           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2338           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2339           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2340             ad->argName, mfp->faceName, mfp->pointSize,\r
2341             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2342             mfp->bold ? "b" : "",\r
2343             mfp->italic ? "i" : "",\r
2344             mfp->underline ? "u" : "",\r
2345             mfp->strikeout ? "s" : "");\r
2346         }\r
2347       }\r
2348       break;\r
2349     case ArgCommSettings:\r
2350       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2351     case ArgNone:\r
2352     case ArgSettingsFilename: ;\r
2353     }\r
2354   }\r
2355   fclose(f);\r
2356 }\r
2357 \r
2358 \r
2359 \r
2360 /*---------------------------------------------------------------------------*\\r
2361  *\r
2362  * GDI board drawing routines\r
2363  *\r
2364 \*---------------------------------------------------------------------------*/\r
2365 \r
2366 /* [AS] Draw square using background texture */\r
2367 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2368 {\r
2369     XFORM   x;\r
2370 \r
2371     if( mode == 0 ) {\r
2372         return; /* Should never happen! */\r
2373     }\r
2374 \r
2375     SetGraphicsMode( dst, GM_ADVANCED );\r
2376 \r
2377     switch( mode ) {\r
2378     case 1:\r
2379         /* Identity */\r
2380         break;\r
2381     case 2:\r
2382         /* X reflection */\r
2383         x.eM11 = -1.0;\r
2384         x.eM12 = 0;\r
2385         x.eM21 = 0;\r
2386         x.eM22 = 1.0;\r
2387         x.eDx = (FLOAT) dw + dx - 1;\r
2388         x.eDy = 0;\r
2389         dx = 0;\r
2390         SetWorldTransform( dst, &x );\r
2391         break;\r
2392     case 3:\r
2393         /* Y reflection */\r
2394         x.eM11 = 1.0;\r
2395         x.eM12 = 0;\r
2396         x.eM21 = 0;\r
2397         x.eM22 = -1.0;\r
2398         x.eDx = 0;\r
2399         x.eDy = (FLOAT) dh + dy - 1;\r
2400         dy = 0;\r
2401         SetWorldTransform( dst, &x );\r
2402         break;\r
2403     case 4:\r
2404         /* X/Y flip */\r
2405         x.eM11 = 0;\r
2406         x.eM12 = 1.0;\r
2407         x.eM21 = 1.0;\r
2408         x.eM22 = 0;\r
2409         x.eDx = (FLOAT) dx;\r
2410         x.eDy = (FLOAT) dy;\r
2411         dx = 0;\r
2412         dy = 0;\r
2413         SetWorldTransform( dst, &x );\r
2414         break;\r
2415     }\r
2416 \r
2417     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2418 \r
2419     x.eM11 = 1.0;\r
2420     x.eM12 = 0;\r
2421     x.eM21 = 0;\r
2422     x.eM22 = 1.0;\r
2423     x.eDx = 0;\r
2424     x.eDy = 0;\r
2425     SetWorldTransform( dst, &x );\r
2426 \r
2427     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2428 }\r
2429 \r
2430 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2431 enum {\r
2432     PM_WP = (int) WhitePawn, \r
2433     PM_WN = (int) WhiteKnight, \r
2434     PM_WB = (int) WhiteBishop, \r
2435     PM_WR = (int) WhiteRook, \r
2436     PM_WQ = (int) WhiteQueen, \r
2437     PM_WF = (int) WhiteFerz, \r
2438     PM_WW = (int) WhiteWazir, \r
2439     PM_WE = (int) WhiteAlfil, \r
2440     PM_WM = (int) WhiteMan, \r
2441     PM_WO = (int) WhiteCannon, \r
2442     PM_WU = (int) WhiteUnicorn, \r
2443     PM_WH = (int) WhiteNightrider, \r
2444     PM_WA = (int) WhiteAngel, \r
2445     PM_WC = (int) WhiteMarshall, \r
2446     PM_WAB = (int) WhiteCardinal, \r
2447     PM_WD = (int) WhiteDragon, \r
2448     PM_WL = (int) WhiteLance, \r
2449     PM_WS = (int) WhiteCobra, \r
2450     PM_WV = (int) WhiteFalcon, \r
2451     PM_WSG = (int) WhiteSilver, \r
2452     PM_WG = (int) WhiteGrasshopper, \r
2453     PM_WK = (int) WhiteKing,\r
2454     PM_BP = (int) BlackPawn, \r
2455     PM_BN = (int) BlackKnight, \r
2456     PM_BB = (int) BlackBishop, \r
2457     PM_BR = (int) BlackRook, \r
2458     PM_BQ = (int) BlackQueen, \r
2459     PM_BF = (int) BlackFerz, \r
2460     PM_BW = (int) BlackWazir, \r
2461     PM_BE = (int) BlackAlfil, \r
2462     PM_BM = (int) BlackMan,\r
2463     PM_BO = (int) BlackCannon, \r
2464     PM_BU = (int) BlackUnicorn, \r
2465     PM_BH = (int) BlackNightrider, \r
2466     PM_BA = (int) BlackAngel, \r
2467     PM_BC = (int) BlackMarshall, \r
2468     PM_BG = (int) BlackGrasshopper, \r
2469     PM_BAB = (int) BlackCardinal,\r
2470     PM_BD = (int) BlackDragon,\r
2471     PM_BL = (int) BlackLance,\r
2472     PM_BS = (int) BlackCobra,\r
2473     PM_BV = (int) BlackFalcon,\r
2474     PM_BSG = (int) BlackSilver,\r
2475     PM_BK = (int) BlackKing\r
2476 };\r
2477 \r
2478 static HFONT hPieceFont = NULL;\r
2479 static HBITMAP hPieceMask[(int) EmptySquare];\r
2480 static HBITMAP hPieceFace[(int) EmptySquare];\r
2481 static int fontBitmapSquareSize = 0;\r
2482 static char pieceToFontChar[(int) EmptySquare] =\r
2483                               { 'p', 'n', 'b', 'r', 'q', \r
2484                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2485                       'k', 'o', 'm', 'v', 't', 'w', \r
2486                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2487                                                               'l' };\r
2488 \r
2489 extern BOOL SetCharTable( char *table, const char * map );\r
2490 /* [HGM] moved to backend.c */\r
2491 \r
2492 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2493 {\r
2494     HBRUSH hbrush;\r
2495     BYTE r1 = GetRValue( color );\r
2496     BYTE g1 = GetGValue( color );\r
2497     BYTE b1 = GetBValue( color );\r
2498     BYTE r2 = r1 / 2;\r
2499     BYTE g2 = g1 / 2;\r
2500     BYTE b2 = b1 / 2;\r
2501     RECT rc;\r
2502 \r
2503     /* Create a uniform background first */\r
2504     hbrush = CreateSolidBrush( color );\r
2505     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2506     FillRect( hdc, &rc, hbrush );\r
2507     DeleteObject( hbrush );\r
2508     \r
2509     if( mode == 1 ) {\r
2510         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2511         int steps = squareSize / 2;\r
2512         int i;\r
2513 \r
2514         for( i=0; i<steps; i++ ) {\r
2515             BYTE r = r1 - (r1-r2) * i / steps;\r
2516             BYTE g = g1 - (g1-g2) * i / steps;\r
2517             BYTE b = b1 - (b1-b2) * i / steps;\r
2518 \r
2519             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2520             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2521             FillRect( hdc, &rc, hbrush );\r
2522             DeleteObject(hbrush);\r
2523         }\r
2524     }\r
2525     else if( mode == 2 ) {\r
2526         /* Diagonal gradient, good more or less for every piece */\r
2527         POINT triangle[3];\r
2528         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2529         HBRUSH hbrush_old;\r
2530         int steps = squareSize;\r
2531         int i;\r
2532 \r
2533         triangle[0].x = squareSize - steps;\r
2534         triangle[0].y = squareSize;\r
2535         triangle[1].x = squareSize;\r
2536         triangle[1].y = squareSize;\r
2537         triangle[2].x = squareSize;\r
2538         triangle[2].y = squareSize - steps;\r
2539 \r
2540         for( i=0; i<steps; i++ ) {\r
2541             BYTE r = r1 - (r1-r2) * i / steps;\r
2542             BYTE g = g1 - (g1-g2) * i / steps;\r
2543             BYTE b = b1 - (b1-b2) * i / steps;\r
2544 \r
2545             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2546             hbrush_old = SelectObject( hdc, hbrush );\r
2547             Polygon( hdc, triangle, 3 );\r
2548             SelectObject( hdc, hbrush_old );\r
2549             DeleteObject(hbrush);\r
2550             triangle[0].x++;\r
2551             triangle[2].y++;\r
2552         }\r
2553 \r
2554         SelectObject( hdc, hpen );\r
2555     }\r
2556 }\r
2557 \r
2558 /*\r
2559     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2560     seems to work ok. The main problem here is to find the "inside" of a chess\r
2561     piece: follow the steps as explained below.\r
2562 */\r
2563 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2564 {\r
2565     HBITMAP hbm;\r
2566     HBITMAP hbm_old;\r
2567     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2568     RECT rc;\r
2569     SIZE sz;\r
2570     POINT pt;\r
2571     int backColor = whitePieceColor; \r
2572     int foreColor = blackPieceColor;\r
2573     \r
2574     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2575         backColor = appData.fontBackColorWhite;\r
2576         foreColor = appData.fontForeColorWhite;\r
2577     }\r
2578     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2579         backColor = appData.fontBackColorBlack;\r
2580         foreColor = appData.fontForeColorBlack;\r
2581     }\r
2582 \r
2583     /* Mask */\r
2584     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2585 \r
2586     hbm_old = SelectObject( hdc, hbm );\r
2587 \r
2588     rc.left = 0;\r
2589     rc.top = 0;\r
2590     rc.right = squareSize;\r
2591     rc.bottom = squareSize;\r
2592 \r
2593     /* Step 1: background is now black */\r
2594     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2595 \r
2596     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2597 \r
2598     pt.x = (squareSize - sz.cx) / 2;\r
2599     pt.y = (squareSize - sz.cy) / 2;\r
2600 \r
2601     SetBkMode( hdc, TRANSPARENT );\r
2602     SetTextColor( hdc, chroma );\r
2603     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2604     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2605 \r
2606     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2607     /* Step 3: the area outside the piece is filled with white */\r
2608 //    FloodFill( hdc, 0, 0, chroma );\r
2609     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2610     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2611     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2612     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2613     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2614     /* \r
2615         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2616         but if the start point is not inside the piece we're lost!\r
2617         There should be a better way to do this... if we could create a region or path\r
2618         from the fill operation we would be fine for example.\r
2619     */\r
2620 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2621     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2622 \r
2623     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2624         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2625         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2626 \r
2627         SelectObject( dc2, bm2 );\r
2628         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2629         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2630         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2631         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2632         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2633 \r
2634         DeleteDC( dc2 );\r
2635         DeleteObject( bm2 );\r
2636     }\r
2637 \r
2638     SetTextColor( hdc, 0 );\r
2639     /* \r
2640         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2641         draw the piece again in black for safety.\r
2642     */\r
2643     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2644 \r
2645     SelectObject( hdc, hbm_old );\r
2646 \r
2647     if( hPieceMask[index] != NULL ) {\r
2648         DeleteObject( hPieceMask[index] );\r
2649     }\r
2650 \r
2651     hPieceMask[index] = hbm;\r
2652 \r
2653     /* Face */\r
2654     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2655 \r
2656     SelectObject( hdc, hbm );\r
2657 \r
2658     {\r
2659         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2660         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2661         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2662 \r
2663         SelectObject( dc1, hPieceMask[index] );\r
2664         SelectObject( dc2, bm2 );\r
2665         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2666         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2667         \r
2668         /* \r
2669             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2670             the piece background and deletes (makes transparent) the rest.\r
2671             Thanks to that mask, we are free to paint the background with the greates\r
2672             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2673             We use this, to make gradients and give the pieces a "roundish" look.\r
2674         */\r
2675         SetPieceBackground( hdc, backColor, 2 );\r
2676         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2677 \r
2678         DeleteDC( dc2 );\r
2679         DeleteDC( dc1 );\r
2680         DeleteObject( bm2 );\r
2681     }\r
2682 \r
2683     SetTextColor( hdc, foreColor );\r
2684     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2685 \r
2686     SelectObject( hdc, hbm_old );\r
2687 \r
2688     if( hPieceFace[index] != NULL ) {\r
2689         DeleteObject( hPieceFace[index] );\r
2690     }\r
2691 \r
2692     hPieceFace[index] = hbm;\r
2693 }\r
2694 \r
2695 static int TranslatePieceToFontPiece( int piece )\r
2696 {\r
2697     switch( piece ) {\r
2698     case BlackPawn:\r
2699         return PM_BP;\r
2700     case BlackKnight:\r
2701         return PM_BN;\r
2702     case BlackBishop:\r
2703         return PM_BB;\r
2704     case BlackRook:\r
2705         return PM_BR;\r
2706     case BlackQueen:\r
2707         return PM_BQ;\r
2708     case BlackKing:\r
2709         return PM_BK;\r
2710     case WhitePawn:\r
2711         return PM_WP;\r
2712     case WhiteKnight:\r
2713         return PM_WN;\r
2714     case WhiteBishop:\r
2715         return PM_WB;\r
2716     case WhiteRook:\r
2717         return PM_WR;\r
2718     case WhiteQueen:\r
2719         return PM_WQ;\r
2720     case WhiteKing:\r
2721         return PM_WK;\r
2722 \r
2723     case BlackAngel:\r
2724         return PM_BA;\r
2725     case BlackMarshall:\r
2726         return PM_BC;\r
2727     case BlackFerz:\r
2728         return PM_BF;\r
2729     case BlackNightrider:\r
2730         return PM_BH;\r
2731     case BlackAlfil:\r
2732         return PM_BE;\r
2733     case BlackWazir:\r
2734         return PM_BW;\r
2735     case BlackUnicorn:\r
2736         return PM_BU;\r
2737     case BlackCannon:\r
2738         return PM_BO;\r
2739     case BlackGrasshopper:\r
2740         return PM_BG;\r
2741     case BlackMan:\r
2742         return PM_BM;\r
2743     case BlackSilver:\r
2744         return PM_BSG;\r
2745     case BlackLance:\r
2746         return PM_BL;\r
2747     case BlackFalcon:\r
2748         return PM_BV;\r
2749     case BlackCobra:\r
2750         return PM_BS;\r
2751     case BlackCardinal:\r
2752         return PM_BAB;\r
2753     case BlackDragon:\r
2754         return PM_BD;\r
2755 \r
2756     case WhiteAngel:\r
2757         return PM_WA;\r
2758     case WhiteMarshall:\r
2759         return PM_WC;\r
2760     case WhiteFerz:\r
2761         return PM_WF;\r
2762     case WhiteNightrider:\r
2763         return PM_WH;\r
2764     case WhiteAlfil:\r
2765         return PM_WE;\r
2766     case WhiteWazir:\r
2767         return PM_WW;\r
2768     case WhiteUnicorn:\r
2769         return PM_WU;\r
2770     case WhiteCannon:\r
2771         return PM_WO;\r
2772     case WhiteGrasshopper:\r
2773         return PM_WG;\r
2774     case WhiteMan:\r
2775         return PM_WM;\r
2776     case WhiteSilver:\r
2777         return PM_WSG;\r
2778     case WhiteLance:\r
2779         return PM_WL;\r
2780     case WhiteFalcon:\r
2781         return PM_WV;\r
2782     case WhiteCobra:\r
2783         return PM_WS;\r
2784     case WhiteCardinal:\r
2785         return PM_WAB;\r
2786     case WhiteDragon:\r
2787         return PM_WD;\r
2788     }\r
2789 \r
2790     return 0;\r
2791 }\r
2792 \r
2793 void CreatePiecesFromFont()\r
2794 {\r
2795     LOGFONT lf;\r
2796     HDC hdc_window = NULL;\r
2797     HDC hdc = NULL;\r
2798     HFONT hfont_old;\r
2799     int fontHeight;\r
2800     int i;\r
2801 \r
2802     if( fontBitmapSquareSize < 0 ) {\r
2803         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2804         return;\r
2805     }\r
2806 \r
2807     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2808         fontBitmapSquareSize = -1;\r
2809         return;\r
2810     }\r
2811 \r
2812     if( fontBitmapSquareSize != squareSize ) {\r
2813         hdc_window = GetDC( hwndMain );\r
2814         hdc = CreateCompatibleDC( hdc_window );\r
2815 \r
2816         if( hPieceFont != NULL ) {\r
2817             DeleteObject( hPieceFont );\r
2818         }\r
2819         else {\r
2820             for( i=0; i<=(int)BlackKing; i++ ) {\r
2821                 hPieceMask[i] = NULL;\r
2822                 hPieceFace[i] = NULL;\r
2823             }\r
2824         }\r
2825 \r
2826         fontHeight = 75;\r
2827 \r
2828         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2829             fontHeight = appData.fontPieceSize;\r
2830         }\r
2831 \r
2832         fontHeight = (fontHeight * squareSize) / 100;\r
2833 \r
2834         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2835         lf.lfWidth = 0;\r
2836         lf.lfEscapement = 0;\r
2837         lf.lfOrientation = 0;\r
2838         lf.lfWeight = FW_NORMAL;\r
2839         lf.lfItalic = 0;\r
2840         lf.lfUnderline = 0;\r
2841         lf.lfStrikeOut = 0;\r
2842         lf.lfCharSet = DEFAULT_CHARSET;\r
2843         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2844         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2845         lf.lfQuality = PROOF_QUALITY;\r
2846         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2847         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2848         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2849 \r
2850         hPieceFont = CreateFontIndirect( &lf );\r
2851 \r
2852         if( hPieceFont == NULL ) {\r
2853             fontBitmapSquareSize = -2;\r
2854         }\r
2855         else {\r
2856             /* Setup font-to-piece character table */\r
2857             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2858                 /* No (or wrong) global settings, try to detect the font */\r
2859                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2860                     /* Alpha */\r
2861                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2862                 }\r
2863                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2864                     /* DiagramTT* family */\r
2865                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2866                 }\r
2867                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2868                     /* Fairy symbols */\r
2869                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2870                 }\r
2871                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2872                     /* Good Companion (Some characters get warped as literal :-( */\r
2873                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2874                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2875                     SetCharTable(pieceToFontChar, s);\r
2876                 }\r
2877                 else {\r
2878                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2879                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2880                 }\r
2881             }\r
2882 \r
2883             /* Create bitmaps */\r
2884             hfont_old = SelectObject( hdc, hPieceFont );\r
2885 #if 0\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2898 \r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2923             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2931 #else\r
2932             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2933                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2934                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2935 #endif\r
2936             SelectObject( hdc, hfont_old );\r
2937 \r
2938             fontBitmapSquareSize = squareSize;\r
2939         }\r
2940     }\r
2941 \r
2942     if( hdc != NULL ) {\r
2943         DeleteDC( hdc );\r
2944     }\r
2945 \r
2946     if( hdc_window != NULL ) {\r
2947         ReleaseDC( hwndMain, hdc_window );\r
2948     }\r
2949 }\r
2950 \r
2951 HBITMAP\r
2952 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2953 {\r
2954   char name[128];\r
2955 \r
2956   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2957   if (gameInfo.event &&\r
2958       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2959       strcmp(name, "k80s") == 0) {\r
2960     strcpy(name, "tim");\r
2961   }\r
2962   return LoadBitmap(hinst, name);\r
2963 }\r
2964 \r
2965 \r
2966 /* Insert a color into the program's logical palette\r
2967    structure.  This code assumes the given color is\r
2968    the result of the RGB or PALETTERGB macro, and it\r
2969    knows how those macros work (which is documented).\r
2970 */\r
2971 VOID\r
2972 InsertInPalette(COLORREF color)\r
2973 {\r
2974   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2975 \r
2976   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2977     DisplayFatalError("Too many colors", 0, 1);\r
2978     pLogPal->palNumEntries--;\r
2979     return;\r
2980   }\r
2981 \r
2982   pe->peFlags = (char) 0;\r
2983   pe->peRed = (char) (0xFF & color);\r
2984   pe->peGreen = (char) (0xFF & (color >> 8));\r
2985   pe->peBlue = (char) (0xFF & (color >> 16));\r
2986   return;\r
2987 }\r
2988 \r
2989 \r
2990 VOID\r
2991 InitDrawingColors()\r
2992 {\r
2993   if (pLogPal == NULL) {\r
2994     /* Allocate enough memory for a logical palette with\r
2995      * PALETTESIZE entries and set the size and version fields\r
2996      * of the logical palette structure.\r
2997      */\r
2998     pLogPal = (NPLOGPALETTE)\r
2999       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3000                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3001     pLogPal->palVersion    = 0x300;\r
3002   }\r
3003   pLogPal->palNumEntries = 0;\r
3004 \r
3005   InsertInPalette(lightSquareColor);\r
3006   InsertInPalette(darkSquareColor);\r
3007   InsertInPalette(whitePieceColor);\r
3008   InsertInPalette(blackPieceColor);\r
3009   InsertInPalette(highlightSquareColor);\r
3010   InsertInPalette(premoveHighlightColor);\r
3011 \r
3012   /*  create a logical color palette according the information\r
3013    *  in the LOGPALETTE structure.\r
3014    */\r
3015   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3016 \r
3017   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3018   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3019   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3020   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3021   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3022   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3023   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3024   /* [AS] Force rendering of the font-based pieces */\r
3025   if( fontBitmapSquareSize > 0 ) {\r
3026     fontBitmapSquareSize = 0;\r
3027   }\r
3028 }\r
3029 \r
3030 \r
3031 int\r
3032 BoardWidth(int boardSize, int n)\r
3033 { /* [HGM] argument n added to allow different width and height */\r
3034   int lineGap = sizeInfo[boardSize].lineGap;\r
3035 \r
3036   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3037       lineGap = appData.overrideLineGap;\r
3038   }\r
3039 \r
3040   return (n + 1) * lineGap +\r
3041           n * sizeInfo[boardSize].squareSize;\r
3042 }\r
3043 \r
3044 /* Respond to board resize by dragging edge */\r
3045 VOID\r
3046 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3047 {\r
3048   BoardSize newSize = NUM_SIZES - 1;\r
3049   static int recurse = 0;\r
3050   if (IsIconic(hwndMain)) return;\r
3051   if (recurse > 0) return;\r
3052   recurse++;\r
3053   while (newSize > 0) {\r
3054         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3055         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3056            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3057     newSize--;\r
3058   } \r
3059   boardSize = newSize;\r
3060   InitDrawingSizes(boardSize, flags);\r
3061   recurse--;\r
3062 }\r
3063 \r
3064 \r
3065 \r
3066 VOID\r
3067 InitDrawingSizes(BoardSize boardSize, int flags)\r
3068 {\r
3069   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3070   ChessSquare piece;\r
3071   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3072   HDC hdc;\r
3073   SIZE clockSize, messageSize;\r
3074   HFONT oldFont;\r
3075   char buf[MSG_SIZ];\r
3076   char *str;\r
3077   HMENU hmenu = GetMenu(hwndMain);\r
3078   RECT crect, wrect;\r
3079   int offby;\r
3080   LOGBRUSH logbrush;\r
3081 \r
3082   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3083   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3084 \r
3085   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3086   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3087 \r
3088   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3089   smallLayout = sizeInfo[boardSize].smallLayout;\r
3090   squareSize = sizeInfo[boardSize].squareSize;\r
3091   lineGap = sizeInfo[boardSize].lineGap;\r
3092   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3093 \r
3094   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3095       lineGap = appData.overrideLineGap;\r
3096   }\r
3097 \r
3098   if (tinyLayout != oldTinyLayout) {\r
3099     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3100     if (tinyLayout) {\r
3101       style &= ~WS_SYSMENU;\r
3102       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3103                  "&Minimize\tCtrl+F4");\r
3104     } else {\r
3105       style |= WS_SYSMENU;\r
3106       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3107     }\r
3108     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3109 \r
3110     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3111       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3112         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3113     }\r
3114     DrawMenuBar(hwndMain);\r
3115   }\r
3116 \r
3117   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3118   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3119 \r
3120   /* Get text area sizes */\r
3121   hdc = GetDC(hwndMain);\r
3122   if (appData.clockMode) {\r
3123     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3124   } else {\r
3125     sprintf(buf, "White");\r
3126   }\r
3127   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3128   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3129   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3130   str = "We only care about the height here";\r
3131   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3132   SelectObject(hdc, oldFont);\r
3133   ReleaseDC(hwndMain, hdc);\r
3134 \r
3135   /* Compute where everything goes */\r
3136   if(first.programLogo || second.programLogo) {\r
3137         /* [HGM] logo: if either logo is on, reserve space for it */\r
3138         logoHeight =  2*clockSize.cy;\r
3139         leftLogoRect.left   = OUTER_MARGIN;\r
3140         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3141         leftLogoRect.top    = OUTER_MARGIN;\r
3142         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3143 \r
3144         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3145         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3146         rightLogoRect.top    = OUTER_MARGIN;\r
3147         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3148 \r
3149 \r
3150     blackRect.left = leftLogoRect.right;\r
3151     blackRect.right = rightLogoRect.left;\r
3152     blackRect.top = OUTER_MARGIN;\r
3153     blackRect.bottom = blackRect.top + clockSize.cy;\r
3154 \r
3155     whiteRect.left = blackRect.left ;\r
3156     whiteRect.right = blackRect.right;\r
3157     whiteRect.top = blackRect.bottom;\r
3158     whiteRect.bottom = leftLogoRect.bottom;\r
3159   } else {\r
3160     whiteRect.left = OUTER_MARGIN;\r
3161     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3162     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3163     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3164 \r
3165     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3166     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3167     blackRect.top = whiteRect.top;\r
3168     blackRect.bottom = whiteRect.bottom;\r
3169   }\r
3170 \r
3171   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3172   if (appData.showButtonBar) {\r
3173     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3174       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3175   } else {\r
3176     messageRect.right = OUTER_MARGIN + boardWidth;\r
3177   }\r
3178   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3179   messageRect.bottom = messageRect.top + messageSize.cy;\r
3180 \r
3181   boardRect.left = OUTER_MARGIN;\r
3182   boardRect.right = boardRect.left + boardWidth;\r
3183   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3184   boardRect.bottom = boardRect.top + boardHeight;\r
3185 \r
3186   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3187   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3188   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3189   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3190   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3191     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3192   GetWindowRect(hwndMain, &wrect);\r
3193   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3194                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3195   /* compensate if menu bar wrapped */\r
3196   GetClientRect(hwndMain, &crect);\r
3197   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3198   winHeight += offby;\r
3199   switch (flags) {\r
3200   case WMSZ_TOPLEFT:\r
3201     SetWindowPos(hwndMain, NULL, \r
3202                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3203                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3204     break;\r
3205 \r
3206   case WMSZ_TOPRIGHT:\r
3207   case WMSZ_TOP:\r
3208     SetWindowPos(hwndMain, NULL, \r
3209                  wrect.left, wrect.bottom - winHeight, \r
3210                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3211     break;\r
3212 \r
3213   case WMSZ_BOTTOMLEFT:\r
3214   case WMSZ_LEFT:\r
3215     SetWindowPos(hwndMain, NULL, \r
3216                  wrect.right - winWidth, wrect.top, \r
3217                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3218     break;\r
3219 \r
3220   case WMSZ_BOTTOMRIGHT:\r
3221   case WMSZ_BOTTOM:\r
3222   case WMSZ_RIGHT:\r
3223   default:\r
3224     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3225                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3226     break;\r
3227   }\r
3228 \r
3229   hwndPause = NULL;\r
3230   for (i = 0; i < N_BUTTONS; i++) {\r
3231     if (buttonDesc[i].hwnd != NULL) {\r
3232       DestroyWindow(buttonDesc[i].hwnd);\r
3233       buttonDesc[i].hwnd = NULL;\r
3234     }\r
3235     if (appData.showButtonBar) {\r
3236       buttonDesc[i].hwnd =\r
3237         CreateWindow("BUTTON", buttonDesc[i].label,\r
3238                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3239                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3240                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3241                      (HMENU) buttonDesc[i].id,\r
3242                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3243       if (tinyLayout) {\r
3244         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3245                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3246                     MAKELPARAM(FALSE, 0));\r
3247       }\r
3248       if (buttonDesc[i].id == IDM_Pause)\r
3249         hwndPause = buttonDesc[i].hwnd;\r
3250       buttonDesc[i].wndproc = (WNDPROC)\r
3251         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3252     }\r
3253   }\r
3254   if (gridPen != NULL) DeleteObject(gridPen);\r
3255   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3256   if (premovePen != NULL) DeleteObject(premovePen);\r
3257   if (lineGap != 0) {\r
3258     logbrush.lbStyle = BS_SOLID;\r
3259     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3260     gridPen =\r
3261       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3262                    lineGap, &logbrush, 0, NULL);\r
3263     logbrush.lbColor = highlightSquareColor;\r
3264     highlightPen =\r
3265       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3266                    lineGap, &logbrush, 0, NULL);\r
3267 \r
3268     logbrush.lbColor = premoveHighlightColor; \r
3269     premovePen =\r
3270       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3271                    lineGap, &logbrush, 0, NULL);\r
3272 \r
3273     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3274     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3275       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3276       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3277         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3278       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3279         BOARD_WIDTH * (squareSize + lineGap);\r
3280       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3281     }\r
3282     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3283       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3284       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3285         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3286         lineGap / 2 + (i * (squareSize + lineGap));\r
3287       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3288         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3289       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3290     }\r
3291   }\r
3292 \r
3293   /* [HGM] Licensing requirement */\r
3294 #ifdef GOTHIC\r
3295   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3296 #endif\r
3297 #ifdef FALCON\r
3298   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3299 #endif\r
3300   GothicPopUp( "", VariantNormal);\r
3301 \r
3302 \r
3303 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3304   oldBoardSize = boardSize;\r
3305   oldTinyLayout = tinyLayout;\r
3306 \r
3307   /* Load piece bitmaps for this board size */\r
3308   for (i=0; i<=2; i++) {\r
3309     for (piece = WhitePawn;\r
3310          (int) piece < (int) BlackPawn;\r
3311          piece = (ChessSquare) ((int) piece + 1)) {\r
3312       if (pieceBitmap[i][piece] != NULL)\r
3313         DeleteObject(pieceBitmap[i][piece]);\r
3314     }\r
3315   }\r
3316 \r
3317   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3318   // Orthodox Chess pieces\r
3319   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3320   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3321   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3322   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3323   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3324   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3325   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3326   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3327   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3328   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3329   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3330   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3331   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3332   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3333   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3334   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3335     // in Shogi, Hijack the unused Queen for Lance\r
3336     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3337     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3338     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3339   } else {\r
3340     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3341     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3342     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3343   }\r
3344 \r
3345   if(squareSize <= 72 && squareSize >= 33) { \r
3346     /* A & C are available in most sizes now */\r
3347     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3348       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3349       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3350       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3351       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3352       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3353       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3354       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3355       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3356       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3357       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3358       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3359       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3360     } else { // Smirf-like\r
3361       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3362       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3363       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3364     }\r
3365     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3366       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3367       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3368       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3369     } else { // WinBoard standard\r
3370       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3371       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3372       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3373     }\r
3374   }\r
3375 \r
3376 \r
3377   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3378     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3379     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3380     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3381     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3382     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3383     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3384     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3385     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3386     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3387     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3388     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3389     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3390     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3391     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3392     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3393     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3394     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3395     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3396     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3397     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3398     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3399     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3400     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3401     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3402     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3403     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3404     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3405     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3406     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3407     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3408 \r
3409     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3410       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3411       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3412       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3413       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3414       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3415       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3416       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3417       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3418       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3419       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3420       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3421       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3422     } else {\r
3423       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3424       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3425       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3426       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3427       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3428       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3429       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3430       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3431       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3432       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3433       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3434       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3435     }\r
3436 \r
3437   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3438     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3439     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3440     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3441     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3442     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3443     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3444     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3445     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3446     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3447     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3448     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3449     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3450     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3451     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3452   }\r
3453 \r
3454 \r
3455   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3456   /* special Shogi support in this size */\r
3457   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3458       for (piece = WhitePawn;\r
3459            (int) piece < (int) BlackPawn;\r
3460            piece = (ChessSquare) ((int) piece + 1)) {\r
3461         if (pieceBitmap[i][piece] != NULL)\r
3462           DeleteObject(pieceBitmap[i][piece]);\r
3463       }\r
3464     }\r
3465   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3466   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3467   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3468   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3469   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3470   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3471   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3472   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3473   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3474   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3475   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3476   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3477   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3478   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3479   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3480   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3481   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3482   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3483   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3484   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3485   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3486   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3487   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3488   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3489   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3490   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3491   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3492   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3493   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3494   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3495   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3496   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3497   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3498   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3499   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3500   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3501   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3502   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3503   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3504   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3505   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3506   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3507   minorSize = 0;\r
3508   }\r
3509 }\r
3510 \r
3511 HBITMAP\r
3512 PieceBitmap(ChessSquare p, int kind)\r
3513 {\r
3514   if ((int) p >= (int) BlackPawn)\r
3515     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3516 \r
3517   return pieceBitmap[kind][(int) p];\r
3518 }\r
3519 \r
3520 /***************************************************************/\r
3521 \r
3522 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3523 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3524 /*\r
3525 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3526 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3527 */\r
3528 \r
3529 VOID\r
3530 SquareToPos(int row, int column, int * x, int * y)\r
3531 {\r
3532   if (flipView) {\r
3533     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3534     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3535   } else {\r
3536     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3537     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3538   }\r
3539 }\r
3540 \r
3541 VOID\r
3542 DrawCoordsOnDC(HDC hdc)\r
3543 {\r
3544   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
3545   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
3546   char str[2] = { NULLCHAR, NULLCHAR };\r
3547   int oldMode, oldAlign, x, y, start, i;\r
3548   HFONT oldFont;\r
3549   HBRUSH oldBrush;\r
3550 \r
3551   if (!appData.showCoords)\r
3552     return;\r
3553 \r
3554   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3555 \r
3556   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3557   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3558   oldAlign = GetTextAlign(hdc);\r
3559   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3560 \r
3561   y = boardRect.top + lineGap;\r
3562   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3563 \r
3564   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3565   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3566     str[0] = files[start + i];\r
3567     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3568     y += squareSize + lineGap;\r
3569   }\r
3570 \r
3571   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3572 \r
3573   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3574   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3575     str[0] = ranks[start + i];\r
3576     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3577     x += squareSize + lineGap;\r
3578   }    \r
3579 \r
3580   SelectObject(hdc, oldBrush);\r
3581   SetBkMode(hdc, oldMode);\r
3582   SetTextAlign(hdc, oldAlign);\r
3583   SelectObject(hdc, oldFont);\r
3584 }\r
3585 \r
3586 VOID\r
3587 DrawGridOnDC(HDC hdc)\r
3588 {\r
3589   HPEN oldPen;\r
3590  \r
3591   if (lineGap != 0) {\r
3592     oldPen = SelectObject(hdc, gridPen);\r
3593     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3594     SelectObject(hdc, oldPen);\r
3595   }\r
3596 }\r
3597 \r
3598 #define HIGHLIGHT_PEN 0\r
3599 #define PREMOVE_PEN   1\r
3600 \r
3601 VOID\r
3602 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3603 {\r
3604   int x1, y1;\r
3605   HPEN oldPen, hPen;\r
3606   if (lineGap == 0) return;\r
3607   if (flipView) {\r
3608     x1 = boardRect.left +\r
3609       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3610     y1 = boardRect.top +\r
3611       lineGap/2 + y * (squareSize + lineGap);\r
3612   } else {\r
3613     x1 = boardRect.left +\r
3614       lineGap/2 + x * (squareSize + lineGap);\r
3615     y1 = boardRect.top +\r
3616       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3617   }\r
3618   hPen = pen ? premovePen : highlightPen;\r
3619   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3620   MoveToEx(hdc, x1, y1, NULL);\r
3621   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3622   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3623   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3624   LineTo(hdc, x1, y1);\r
3625   SelectObject(hdc, oldPen);\r
3626 }\r
3627 \r
3628 VOID\r
3629 DrawHighlightsOnDC(HDC hdc)\r
3630 {\r
3631   int i;\r
3632   for (i=0; i<2; i++) {\r
3633     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3634       DrawHighlightOnDC(hdc, TRUE,\r
3635                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3636                         HIGHLIGHT_PEN);\r
3637   }\r
3638   for (i=0; i<2; i++) {\r
3639     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3640         premoveHighlightInfo.sq[i].y >= 0) {\r
3641         DrawHighlightOnDC(hdc, TRUE,\r
3642                           premoveHighlightInfo.sq[i].x, \r
3643                           premoveHighlightInfo.sq[i].y,\r
3644                           PREMOVE_PEN);\r
3645     }\r
3646   }\r
3647 }\r
3648 \r
3649 /* Note: sqcolor is used only in monoMode */\r
3650 /* Note that this code is largely duplicated in woptions.c,\r
3651    function DrawSampleSquare, so that needs to be updated too */\r
3652 VOID\r
3653 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3654 {\r
3655   HBITMAP oldBitmap;\r
3656   HBRUSH oldBrush;\r
3657   int tmpSize;\r
3658 \r
3659   if (appData.blindfold) return;\r
3660 \r
3661   /* [AS] Use font-based pieces if needed */\r
3662   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3663     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3664     CreatePiecesFromFont();\r
3665 \r
3666     if( fontBitmapSquareSize == squareSize ) {\r
3667         int index = TranslatePieceToFontPiece(piece);\r
3668 \r
3669         SelectObject( tmphdc, hPieceMask[ index ] );\r
3670 \r
3671         BitBlt( hdc,\r
3672             x, y,\r
3673             squareSize, squareSize,\r
3674             tmphdc,\r
3675             0, 0,\r
3676             SRCAND );\r
3677 \r
3678         SelectObject( tmphdc, hPieceFace[ index ] );\r
3679 \r
3680         BitBlt( hdc,\r
3681             x, y,\r
3682             squareSize, squareSize,\r
3683             tmphdc,\r
3684             0, 0,\r
3685             SRCPAINT );\r
3686 \r
3687         return;\r
3688     }\r
3689   }\r
3690 \r
3691   if (appData.monoMode) {\r
3692     SelectObject(tmphdc, PieceBitmap(piece, \r
3693       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3694     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3695            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3696   } else {\r
3697     tmpSize = squareSize;\r
3698     if(minorSize &&\r
3699         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3700          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3701       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3702       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3703       x += (squareSize - minorSize)>>1;\r
3704       y += squareSize - minorSize - 2;\r
3705       tmpSize = minorSize;\r
3706     }\r
3707     if (color || appData.allWhite ) {\r
3708       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3709       if( color )\r
3710               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3711       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3712       if(appData.upsideDown && color==flipView)\r
3713         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3714       else\r
3715         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3716 #if 0\r
3717       /* Use black piece color for outline of white pieces */\r
3718       /* Not sure this looks really good (though xboard does it).\r
3719          Maybe better to have another selectable color, default black */\r
3720       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3721       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3722       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3723 #else\r
3724       /* Use black for outline of white pieces */\r
3725       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3726       if(appData.upsideDown && color==flipView)\r
3727         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3728       else\r
3729         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3730 #endif\r
3731     } else {\r
3732 #if 0\r
3733       /* Use white piece color for details of black pieces */\r
3734       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3735          WHITE_PIECE ones aren't always the right shape. */\r
3736       /* Not sure this looks really good (though xboard does it).\r
3737          Maybe better to have another selectable color, default medium gray? */\r
3738       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3739       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3740       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3741       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3742       SelectObject(hdc, blackPieceBrush);\r
3743       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3744 #else\r
3745       /* Use square color for details of black pieces */\r
3746       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3747       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3748       if(appData.upsideDown && !flipView)\r
3749         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3750       else\r
3751         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3752 #endif\r
3753     }\r
3754     SelectObject(hdc, oldBrush);\r
3755     SelectObject(tmphdc, oldBitmap);\r
3756   }\r
3757 }\r
3758 \r
3759 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3760 int GetBackTextureMode( int algo )\r
3761 {\r
3762     int result = BACK_TEXTURE_MODE_DISABLED;\r
3763 \r
3764     switch( algo ) \r
3765     {\r
3766         case BACK_TEXTURE_MODE_PLAIN:\r
3767             result = 1; /* Always use identity map */\r
3768             break;\r
3769         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3770             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3771             break;\r
3772     }\r
3773 \r
3774     return result;\r
3775 }\r
3776 \r
3777 /* \r
3778     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3779     to handle redraws cleanly (as random numbers would always be different).\r
3780 */\r
3781 VOID RebuildTextureSquareInfo()\r
3782 {\r
3783     BITMAP bi;\r
3784     int lite_w = 0;\r
3785     int lite_h = 0;\r
3786     int dark_w = 0;\r
3787     int dark_h = 0;\r
3788     int row;\r
3789     int col;\r
3790 \r
3791     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3792 \r
3793     if( liteBackTexture != NULL ) {\r
3794         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3795             lite_w = bi.bmWidth;\r
3796             lite_h = bi.bmHeight;\r
3797         }\r
3798     }\r
3799 \r
3800     if( darkBackTexture != NULL ) {\r
3801         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3802             dark_w = bi.bmWidth;\r
3803             dark_h = bi.bmHeight;\r
3804         }\r
3805     }\r
3806 \r
3807     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3808         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3809             if( (col + row) & 1 ) {\r
3810                 /* Lite square */\r
3811                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3812                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3813                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3814                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3815                 }\r
3816             }\r
3817             else {\r
3818                 /* Dark square */\r
3819                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3820                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3821                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3822                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3823                 }\r
3824             }\r
3825         }\r
3826     }\r
3827 }\r
3828 \r
3829 /* [AS] Arrow highlighting support */\r
3830 \r
3831 static int A_WIDTH = 5; /* Width of arrow body */\r
3832 \r
3833 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3834 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3835 \r
3836 static double Sqr( double x )\r
3837 {\r
3838     return x*x;\r
3839 }\r
3840 \r
3841 static int Round( double x )\r
3842 {\r
3843     return (int) (x + 0.5);\r
3844 }\r
3845 \r
3846 /* Draw an arrow between two points using current settings */\r
3847 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3848 {\r
3849     POINT arrow[7];\r
3850     double dx, dy, j, k, x, y;\r
3851 \r
3852     if( d_x == s_x ) {\r
3853         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3854 \r
3855         arrow[0].x = s_x + A_WIDTH;\r
3856         arrow[0].y = s_y;\r
3857 \r
3858         arrow[1].x = s_x + A_WIDTH;\r
3859         arrow[1].y = d_y - h;\r
3860 \r
3861         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3862         arrow[2].y = d_y - h;\r
3863 \r
3864         arrow[3].x = d_x;\r
3865         arrow[3].y = d_y;\r
3866 \r
3867         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3868         arrow[4].y = d_y - h;\r
3869 \r
3870         arrow[5].x = s_x - A_WIDTH;\r
3871         arrow[5].y = d_y - h;\r
3872 \r
3873         arrow[6].x = s_x - A_WIDTH;\r
3874         arrow[6].y = s_y;\r
3875     }\r
3876     else if( d_y == s_y ) {\r
3877         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3878 \r
3879         arrow[0].x = s_x;\r
3880         arrow[0].y = s_y + A_WIDTH;\r
3881 \r
3882         arrow[1].x = d_x - w;\r
3883         arrow[1].y = s_y + A_WIDTH;\r
3884 \r
3885         arrow[2].x = d_x - w;\r
3886         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3887 \r
3888         arrow[3].x = d_x;\r
3889         arrow[3].y = d_y;\r
3890 \r
3891         arrow[4].x = d_x - w;\r
3892         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3893 \r
3894         arrow[5].x = d_x - w;\r
3895         arrow[5].y = s_y - A_WIDTH;\r
3896 \r
3897         arrow[6].x = s_x;\r
3898         arrow[6].y = s_y - A_WIDTH;\r
3899     }\r
3900     else {\r
3901         /* [AS] Needed a lot of paper for this! :-) */\r
3902         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3903         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3904   \r
3905         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3906 \r
3907         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3908 \r
3909         x = s_x;\r
3910         y = s_y;\r
3911 \r
3912         arrow[0].x = Round(x - j);\r
3913         arrow[0].y = Round(y + j*dx);\r
3914 \r
3915         arrow[1].x = Round(x + j);\r
3916         arrow[1].y = Round(y - j*dx);\r
3917 \r
3918         if( d_x > s_x ) {\r
3919             x = (double) d_x - k;\r
3920             y = (double) d_y - k*dy;\r
3921         }\r
3922         else {\r
3923             x = (double) d_x + k;\r
3924             y = (double) d_y + k*dy;\r
3925         }\r
3926 \r
3927         arrow[2].x = Round(x + j);\r
3928         arrow[2].y = Round(y - j*dx);\r
3929 \r
3930         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3931         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3932 \r
3933         arrow[4].x = d_x;\r
3934         arrow[4].y = d_y;\r
3935 \r
3936         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3937         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3938 \r
3939         arrow[6].x = Round(x - j);\r
3940         arrow[6].y = Round(y + j*dx);\r
3941     }\r
3942 \r
3943     Polygon( hdc, arrow, 7 );\r
3944 }\r
3945 \r
3946 /* [AS] Draw an arrow between two squares */\r
3947 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3948 {\r
3949     int s_x, s_y, d_x, d_y;\r
3950     HPEN hpen;\r
3951     HPEN holdpen;\r
3952     HBRUSH hbrush;\r
3953     HBRUSH holdbrush;\r
3954     LOGBRUSH stLB;\r
3955 \r
3956     if( s_col == d_col && s_row == d_row ) {\r
3957         return;\r
3958     }\r
3959 \r
3960     /* Get source and destination points */\r
3961     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3962     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3963 \r
3964     if( d_y > s_y ) {\r
3965         d_y += squareSize / 4;\r
3966     }\r
3967     else if( d_y < s_y ) {\r
3968         d_y += 3 * squareSize / 4;\r
3969     }\r
3970     else {\r
3971         d_y += squareSize / 2;\r
3972     }\r
3973 \r
3974     if( d_x > s_x ) {\r
3975         d_x += squareSize / 4;\r
3976     }\r
3977     else if( d_x < s_x ) {\r
3978         d_x += 3 * squareSize / 4;\r
3979     }\r
3980     else {\r
3981         d_x += squareSize / 2;\r
3982     }\r
3983 \r
3984     s_x += squareSize / 2;\r
3985     s_y += squareSize / 2;\r
3986 \r
3987     /* Adjust width */\r
3988     A_WIDTH = squareSize / 14;\r
3989 \r
3990     /* Draw */\r
3991     stLB.lbStyle = BS_SOLID;\r
3992     stLB.lbColor = appData.highlightArrowColor;\r
3993     stLB.lbHatch = 0;\r
3994 \r
3995     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3996     holdpen = SelectObject( hdc, hpen );\r
3997     hbrush = CreateBrushIndirect( &stLB );\r
3998     holdbrush = SelectObject( hdc, hbrush );\r
3999 \r
4000     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4001 \r
4002     SelectObject( hdc, holdpen );\r
4003     SelectObject( hdc, holdbrush );\r
4004     DeleteObject( hpen );\r
4005     DeleteObject( hbrush );\r
4006 }\r
4007 \r
4008 BOOL HasHighlightInfo()\r
4009 {\r
4010     BOOL result = FALSE;\r
4011 \r
4012     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4013         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4014     {\r
4015         result = TRUE;\r
4016     }\r
4017 \r
4018     return result;\r
4019 }\r
4020 \r
4021 BOOL IsDrawArrowEnabled()\r
4022 {\r
4023     BOOL result = FALSE;\r
4024 \r
4025     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4026         result = TRUE;\r
4027     }\r
4028 \r
4029     return result;\r
4030 }\r
4031 \r
4032 VOID DrawArrowHighlight( HDC hdc )\r
4033 {\r
4034     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4035         DrawArrowBetweenSquares( hdc,\r
4036             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4037             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4038     }\r
4039 }\r
4040 \r
4041 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4042 {\r
4043     HRGN result = NULL;\r
4044 \r
4045     if( HasHighlightInfo() ) {\r
4046         int x1, y1, x2, y2;\r
4047         int sx, sy, dx, dy;\r
4048 \r
4049         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4050         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4051 \r
4052         sx = MIN( x1, x2 );\r
4053         sy = MIN( y1, y2 );\r
4054         dx = MAX( x1, x2 ) + squareSize;\r
4055         dy = MAX( y1, y2 ) + squareSize;\r
4056 \r
4057         result = CreateRectRgn( sx, sy, dx, dy );\r
4058     }\r
4059 \r
4060     return result;\r
4061 }\r
4062 \r
4063 /*\r
4064     Warning: this function modifies the behavior of several other functions. \r
4065     \r
4066     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4067     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4068     repaint is scattered all over the place, which is not good for features such as\r
4069     "arrow highlighting" that require a full repaint of the board.\r
4070 \r
4071     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4072     user interaction, when speed is not so important) but especially to avoid errors\r
4073     in the displayed graphics.\r
4074 \r
4075     In such patched places, I always try refer to this function so there is a single\r
4076     place to maintain knowledge.\r
4077     \r
4078     To restore the original behavior, just return FALSE unconditionally.\r
4079 */\r
4080 BOOL IsFullRepaintPreferrable()\r
4081 {\r
4082     BOOL result = FALSE;\r
4083 \r
4084     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4085         /* Arrow may appear on the board */\r
4086         result = TRUE;\r
4087     }\r
4088 \r
4089     return result;\r
4090 }\r
4091 \r
4092 /* \r
4093     This function is called by DrawPosition to know whether a full repaint must\r
4094     be forced or not.\r
4095 \r
4096     Only DrawPosition may directly call this function, which makes use of \r
4097     some state information. Other function should call DrawPosition specifying \r
4098     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4099 */\r
4100 BOOL DrawPositionNeedsFullRepaint()\r
4101 {\r
4102     BOOL result = FALSE;\r
4103 \r
4104     /* \r
4105         Probably a slightly better policy would be to trigger a full repaint\r
4106         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4107         but animation is fast enough that it's difficult to notice.\r
4108     */\r
4109     if( animInfo.piece == EmptySquare ) {\r
4110         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4111             result = TRUE;\r
4112         }\r
4113     }\r
4114 \r
4115     return result;\r
4116 }\r
4117 \r
4118 VOID\r
4119 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4120 {\r
4121   int row, column, x, y, square_color, piece_color;\r
4122   ChessSquare piece;\r
4123   HBRUSH oldBrush;\r
4124   HDC texture_hdc = NULL;\r
4125 \r
4126   /* [AS] Initialize background textures if needed */\r
4127   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4128       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4129       if( backTextureSquareSize != squareSize \r
4130        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4131           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4132           backTextureSquareSize = squareSize;\r
4133           RebuildTextureSquareInfo();\r
4134       }\r
4135 \r
4136       texture_hdc = CreateCompatibleDC( hdc );\r
4137   }\r
4138 \r
4139   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4140     for (column = 0; column < BOARD_WIDTH; column++) {\r
4141   \r
4142       SquareToPos(row, column, &x, &y);\r
4143 \r
4144       piece = board[row][column];\r
4145 \r
4146       square_color = ((column + row) % 2) == 1;\r
4147       if( gameInfo.variant == VariantXiangqi ) {\r
4148           square_color = !InPalace(row, column);\r
4149           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4150           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4151       }\r
4152       piece_color = (int) piece < (int) BlackPawn;\r
4153 \r
4154 \r
4155       /* [HGM] holdings file: light square or black */\r
4156       if(column == BOARD_LEFT-2) {\r
4157             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4158                 square_color = 1;\r
4159             else {\r
4160                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4161                 continue;\r
4162             }\r
4163       } else\r
4164       if(column == BOARD_RGHT + 1 ) {\r
4165             if( row < gameInfo.holdingsSize )\r
4166                 square_color = 1;\r
4167             else {\r
4168                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4169                 continue;\r
4170             }\r
4171       }\r
4172       if(column == BOARD_LEFT-1 ) /* left align */\r
4173             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4174       else if( column == BOARD_RGHT) /* right align */\r
4175             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4176       else\r
4177       if (appData.monoMode) {\r
4178         if (piece == EmptySquare) {\r
4179           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4180                  square_color ? WHITENESS : BLACKNESS);\r
4181         } else {\r
4182           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4183         }\r
4184       } \r
4185       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4186           /* [AS] Draw the square using a texture bitmap */\r
4187           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4188           int r = row, c = column; // [HGM] do not flip board in flipView\r
4189           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4190 \r
4191           DrawTile( x, y, \r
4192               squareSize, squareSize, \r
4193               hdc, \r
4194               texture_hdc,\r
4195               backTextureSquareInfo[r][c].mode,\r
4196               backTextureSquareInfo[r][c].x,\r
4197               backTextureSquareInfo[r][c].y );\r
4198 \r
4199           SelectObject( texture_hdc, hbm );\r
4200 \r
4201           if (piece != EmptySquare) {\r
4202               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4203           }\r
4204       }\r
4205       else {\r
4206         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4207 \r
4208         oldBrush = SelectObject(hdc, brush );\r
4209         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4210         SelectObject(hdc, oldBrush);\r
4211         if (piece != EmptySquare)\r
4212           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4213       }\r
4214     }\r
4215   }\r
4216 \r
4217   if( texture_hdc != NULL ) {\r
4218     DeleteDC( texture_hdc );\r
4219   }\r
4220 }\r
4221 \r
4222 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4223 void fputDW(FILE *f, int x)\r
4224 {\r
4225         fputc(x     & 255, f);\r
4226         fputc(x>>8  & 255, f);\r
4227         fputc(x>>16 & 255, f);\r
4228         fputc(x>>24 & 255, f);\r
4229 }\r
4230 \r
4231 #define MAX_CLIPS 200   /* more than enough */\r
4232 \r
4233 VOID\r
4234 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4235 {\r
4236 //  HBITMAP bufferBitmap;\r
4237   BITMAP bi;\r
4238 //  RECT Rect;\r
4239   HDC tmphdc;\r
4240   HBITMAP hbm;\r
4241   int w = 100, h = 50;\r
4242 \r
4243   if(cps->programLogo == NULL) return;\r
4244 //  GetClientRect(hwndMain, &Rect);\r
4245 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4246 //                                      Rect.bottom-Rect.top+1);\r
4247   tmphdc = CreateCompatibleDC(hdc);\r
4248   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4249   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4250             w = bi.bmWidth;\r
4251             h = bi.bmHeight;\r
4252   }\r
4253   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4254                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4255   SelectObject(tmphdc, hbm);\r
4256   DeleteDC(tmphdc);\r
4257 }\r
4258 \r
4259 VOID\r
4260 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4261 {\r
4262   static Board lastReq, lastDrawn;\r
4263   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4264   static int lastDrawnFlipView = 0;\r
4265   static int lastReqValid = 0, lastDrawnValid = 0;\r
4266   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4267   HDC tmphdc;\r
4268   HDC hdcmem;\r
4269   HBITMAP bufferBitmap;\r
4270   HBITMAP oldBitmap;\r
4271   RECT Rect;\r
4272   HRGN clips[MAX_CLIPS];\r
4273   ChessSquare dragged_piece = EmptySquare;\r
4274 \r
4275   /* I'm undecided on this - this function figures out whether a full\r
4276    * repaint is necessary on its own, so there's no real reason to have the\r
4277    * caller tell it that.  I think this can safely be set to FALSE - but\r
4278    * if we trust the callers not to request full repaints unnessesarily, then\r
4279    * we could skip some clipping work.  In other words, only request a full\r
4280    * redraw when the majority of pieces have changed positions (ie. flip, \r
4281    * gamestart and similar)  --Hawk\r
4282    */\r
4283   Boolean fullrepaint = repaint;\r
4284 \r
4285   if( DrawPositionNeedsFullRepaint() ) {\r
4286       fullrepaint = TRUE;\r
4287   }\r
4288 \r
4289 #if 0\r
4290   if( fullrepaint ) {\r
4291       static int repaint_count = 0;\r
4292       char buf[128];\r
4293 \r
4294       repaint_count++;\r
4295       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4296       OutputDebugString( buf );\r
4297   }\r
4298 #endif\r
4299 \r
4300   if (board == NULL) {\r
4301     if (!lastReqValid) {\r
4302       return;\r
4303     }\r
4304     board = lastReq;\r
4305   } else {\r
4306     CopyBoard(lastReq, board);\r
4307     lastReqValid = 1;\r
4308   }\r
4309 \r
4310   if (doingSizing) {\r
4311     return;\r
4312   }\r
4313 \r
4314   if (IsIconic(hwndMain)) {\r
4315     return;\r
4316   }\r
4317 \r
4318   if (hdc == NULL) {\r
4319     hdc = GetDC(hwndMain);\r
4320     if (!appData.monoMode) {\r
4321       SelectPalette(hdc, hPal, FALSE);\r
4322       RealizePalette(hdc);\r
4323     }\r
4324     releaseDC = TRUE;\r
4325   } else {\r
4326     releaseDC = FALSE;\r
4327   }\r
4328 \r
4329 #if 0\r
4330   fprintf(debugFP, "*******************************\n"\r
4331                    "repaint = %s\n"\r
4332                    "dragInfo.from (%d,%d)\n"\r
4333                    "dragInfo.start (%d,%d)\n"\r
4334                    "dragInfo.pos (%d,%d)\n"\r
4335                    "dragInfo.lastpos (%d,%d)\n", \r
4336                     repaint ? "TRUE" : "FALSE",\r
4337                     dragInfo.from.x, dragInfo.from.y, \r
4338                     dragInfo.start.x, dragInfo.start.y,\r
4339                     dragInfo.pos.x, dragInfo.pos.y,\r
4340                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4341   fprintf(debugFP, "prev:  ");\r
4342   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4343     for (column = 0; column < BOARD_WIDTH; column++) {\r
4344       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4345     }\r
4346   }\r
4347   fprintf(debugFP, "\n");\r
4348   fprintf(debugFP, "board: ");\r
4349   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4350     for (column = 0; column < BOARD_WIDTH; column++) {\r
4351       fprintf(debugFP, "%d ", board[row][column]);\r
4352     }\r
4353   }\r
4354   fprintf(debugFP, "\n");\r
4355   fflush(debugFP);\r
4356 #endif\r
4357 \r
4358   /* Create some work-DCs */\r
4359   hdcmem = CreateCompatibleDC(hdc);\r
4360   tmphdc = CreateCompatibleDC(hdc);\r
4361 \r
4362   /* If dragging is in progress, we temporarely remove the piece */\r
4363   /* [HGM] or temporarily decrease count if stacked              */\r
4364   /*       !! Moved to before board compare !!                   */\r
4365   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4366     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4367     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4368             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4369         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4370     } else \r
4371     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4372             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4373         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4374     } else \r
4375         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4376   }\r
4377 \r
4378   /* Figure out which squares need updating by comparing the \r
4379    * newest board with the last drawn board and checking if\r
4380    * flipping has changed.\r
4381    */\r
4382   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4383     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4384       for (column = 0; column < BOARD_WIDTH; column++) {\r
4385         if (lastDrawn[row][column] != board[row][column]) {\r
4386           SquareToPos(row, column, &x, &y);\r
4387           clips[num_clips++] =\r
4388             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4389         }\r
4390       }\r
4391     }\r
4392     for (i=0; i<2; i++) {\r
4393       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4394           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4395         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4396             lastDrawnHighlight.sq[i].y >= 0) {\r
4397           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4398                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4399           clips[num_clips++] =\r
4400             CreateRectRgn(x - lineGap, y - lineGap, \r
4401                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4402         }\r
4403         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4404           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4405           clips[num_clips++] =\r
4406             CreateRectRgn(x - lineGap, y - lineGap, \r
4407                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4408         }\r
4409       }\r
4410     }\r
4411     for (i=0; i<2; i++) {\r
4412       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4413           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4414         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4415             lastDrawnPremove.sq[i].y >= 0) {\r
4416           SquareToPos(lastDrawnPremove.sq[i].y,\r
4417                       lastDrawnPremove.sq[i].x, &x, &y);\r
4418           clips[num_clips++] =\r
4419             CreateRectRgn(x - lineGap, y - lineGap, \r
4420                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4421         }\r
4422         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4423             premoveHighlightInfo.sq[i].y >= 0) {\r
4424           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4425                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4426           clips[num_clips++] =\r
4427             CreateRectRgn(x - lineGap, y - lineGap, \r
4428                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4429         }\r
4430       }\r
4431     }\r
4432   } else {\r
4433     fullrepaint = TRUE;\r
4434   }\r
4435 \r
4436   /* Create a buffer bitmap - this is the actual bitmap\r
4437    * being written to.  When all the work is done, we can\r
4438    * copy it to the real DC (the screen).  This avoids\r
4439    * the problems with flickering.\r
4440    */\r
4441   GetClientRect(hwndMain, &Rect);\r
4442   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4443                                         Rect.bottom-Rect.top+1);\r
4444   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4445   if (!appData.monoMode) {\r
4446     SelectPalette(hdcmem, hPal, FALSE);\r
4447   }\r
4448 \r
4449   /* Create clips for dragging */\r
4450   if (!fullrepaint) {\r
4451     if (dragInfo.from.x >= 0) {\r
4452       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4453       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4454     }\r
4455     if (dragInfo.start.x >= 0) {\r
4456       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4457       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4458     }\r
4459     if (dragInfo.pos.x >= 0) {\r
4460       x = dragInfo.pos.x - squareSize / 2;\r
4461       y = dragInfo.pos.y - squareSize / 2;\r
4462       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4463     }\r
4464     if (dragInfo.lastpos.x >= 0) {\r
4465       x = dragInfo.lastpos.x - squareSize / 2;\r
4466       y = dragInfo.lastpos.y - squareSize / 2;\r
4467       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4468     }\r
4469   }\r
4470 \r
4471   /* Are we animating a move?  \r
4472    * If so, \r
4473    *   - remove the piece from the board (temporarely)\r
4474    *   - calculate the clipping region\r
4475    */\r
4476   if (!fullrepaint) {\r
4477     if (animInfo.piece != EmptySquare) {\r
4478       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4479       x = boardRect.left + animInfo.lastpos.x;\r
4480       y = boardRect.top + animInfo.lastpos.y;\r
4481       x2 = boardRect.left + animInfo.pos.x;\r
4482       y2 = boardRect.top + animInfo.pos.y;\r
4483       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4484       /* Slight kludge.  The real problem is that after AnimateMove is\r
4485          done, the position on the screen does not match lastDrawn.\r
4486          This currently causes trouble only on e.p. captures in\r
4487          atomic, where the piece moves to an empty square and then\r
4488          explodes.  The old and new positions both had an empty square\r
4489          at the destination, but animation has drawn a piece there and\r
4490          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4491       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4492     }\r
4493   }\r
4494 \r
4495   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4496   if (num_clips == 0)\r
4497     fullrepaint = TRUE;\r
4498 \r
4499   /* Set clipping on the memory DC */\r
4500   if (!fullrepaint) {\r
4501     SelectClipRgn(hdcmem, clips[0]);\r
4502     for (x = 1; x < num_clips; x++) {\r
4503       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4504         abort();  // this should never ever happen!\r
4505     }\r
4506   }\r
4507 \r
4508   /* Do all the drawing to the memory DC */\r
4509   if(explodeInfo.radius) { // [HGM] atomic\r
4510         HBRUSH oldBrush;\r
4511         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4512         SquareToPos(explodeInfo.y, explodeInfo.x, &x, &y);\r
4513         x += squareSize/2;\r
4514         y += squareSize/2;\r
4515         if(!fullrepaint) {\r
4516           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4517           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4518         }\r
4519         DrawGridOnDC(hdcmem);\r
4520         DrawHighlightsOnDC(hdcmem);\r
4521         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4522         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4523         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4524         SelectObject(hdcmem, oldBrush);\r
4525   } else {\r
4526     DrawGridOnDC(hdcmem);\r
4527     DrawHighlightsOnDC(hdcmem);\r
4528     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4529   }\r
4530   if(logoHeight) {\r
4531         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4532         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4533   }\r
4534 \r
4535   if( appData.highlightMoveWithArrow ) {\r
4536     DrawArrowHighlight(hdcmem);\r
4537   }\r
4538 \r
4539   DrawCoordsOnDC(hdcmem);\r
4540 \r
4541   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4542                  /* to make sure lastDrawn contains what is actually drawn */\r
4543 \r
4544   /* Put the dragged piece back into place and draw it (out of place!) */\r
4545     if (dragged_piece != EmptySquare) {\r
4546     /* [HGM] or restack */\r
4547     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4548                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4549     else\r
4550     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4551                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4552     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4553     x = dragInfo.pos.x - squareSize / 2;\r
4554     y = dragInfo.pos.y - squareSize / 2;\r
4555     DrawPieceOnDC(hdcmem, dragged_piece,\r
4556                   ((int) dragged_piece < (int) BlackPawn), \r
4557                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4558   }   \r
4559   \r
4560   /* Put the animated piece back into place and draw it */\r
4561   if (animInfo.piece != EmptySquare) {\r
4562     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4563     x = boardRect.left + animInfo.pos.x;\r
4564     y = boardRect.top + animInfo.pos.y;\r
4565     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4566                   ((int) animInfo.piece < (int) BlackPawn),\r
4567                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4568   }\r
4569 \r
4570   /* Release the bufferBitmap by selecting in the old bitmap \r
4571    * and delete the memory DC\r
4572    */\r
4573   SelectObject(hdcmem, oldBitmap);\r
4574   DeleteDC(hdcmem);\r
4575 \r
4576   /* Set clipping on the target DC */\r
4577   if (!fullrepaint) {\r
4578     SelectClipRgn(hdc, clips[0]);\r
4579     for (x = 1; x < num_clips; x++) {\r
4580       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4581         abort();   // this should never ever happen!\r
4582     } \r
4583   }\r
4584 \r
4585   /* Copy the new bitmap onto the screen in one go.\r
4586    * This way we avoid any flickering\r
4587    */\r
4588   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4589   BitBlt(hdc, boardRect.left, boardRect.top,\r
4590          boardRect.right - boardRect.left,\r
4591          boardRect.bottom - boardRect.top,\r
4592          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4593   if(saveDiagFlag) { \r
4594     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4595     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4596 \r
4597     GetObject(bufferBitmap, sizeof(b), &b);\r
4598     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4599         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4600         bih.biWidth = b.bmWidth;\r
4601         bih.biHeight = b.bmHeight;\r
4602         bih.biPlanes = 1;\r
4603         bih.biBitCount = b.bmBitsPixel;\r
4604         bih.biCompression = 0;\r
4605         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4606         bih.biXPelsPerMeter = 0;\r
4607         bih.biYPelsPerMeter = 0;\r
4608         bih.biClrUsed = 0;\r
4609         bih.biClrImportant = 0;\r
4610 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4611 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4612         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4613 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4614 \r
4615 #if 1\r
4616         wb = b.bmWidthBytes;\r
4617         // count colors\r
4618         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4619                 int k = ((int*) pData)[i];\r
4620                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4621                 if(j >= 16) break;\r
4622                 color[j] = k;\r
4623                 if(j >= nrColors) nrColors = j+1;\r
4624         }\r
4625         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4626                 INT p = 0;\r
4627                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4628                     for(w=0; w<(wb>>2); w+=2) {\r
4629                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4630                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4631                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4632                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4633                         pData[p++] = m | j<<4;\r
4634                     }\r
4635                     while(p&3) pData[p++] = 0;\r
4636                 }\r
4637                 fac = 3;\r
4638                 wb = ((wb+31)>>5)<<2;\r
4639         }\r
4640         // write BITMAPFILEHEADER\r
4641         fprintf(diagFile, "BM");\r
4642         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4643         fputDW(diagFile, 0);\r
4644         fputDW(diagFile, 0x36 + (fac?64:0));\r
4645         // write BITMAPINFOHEADER\r
4646         fputDW(diagFile, 40);\r
4647         fputDW(diagFile, b.bmWidth);\r
4648         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4649         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4650         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4651         fputDW(diagFile, 0);\r
4652         fputDW(diagFile, 0);\r
4653         fputDW(diagFile, 0);\r
4654         fputDW(diagFile, 0);\r
4655         fputDW(diagFile, 0);\r
4656         fputDW(diagFile, 0);\r
4657         // write color table\r
4658         if(fac)\r
4659         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4660         // write bitmap data\r
4661         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4662                 fputc(pData[i], diagFile);\r
4663 #endif\r
4664      }\r
4665   }\r
4666 \r
4667   SelectObject(tmphdc, oldBitmap);\r
4668 \r
4669   /* Massive cleanup */\r
4670   for (x = 0; x < num_clips; x++)\r
4671     DeleteObject(clips[x]);\r
4672 \r
4673   DeleteDC(tmphdc);\r
4674   DeleteObject(bufferBitmap);\r
4675 \r
4676   if (releaseDC) \r
4677     ReleaseDC(hwndMain, hdc);\r
4678   \r
4679   if (lastDrawnFlipView != flipView) {\r
4680     if (flipView)\r
4681       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4682     else\r
4683       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4684   }\r
4685 \r
4686 /*  CopyBoard(lastDrawn, board);*/\r
4687   lastDrawnHighlight = highlightInfo;\r
4688   lastDrawnPremove   = premoveHighlightInfo;\r
4689   lastDrawnFlipView = flipView;\r
4690   lastDrawnValid = 1;\r
4691 }\r
4692 \r
4693 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4694 int\r
4695 SaveDiagram(f)\r
4696      FILE *f;\r
4697 {\r
4698     saveDiagFlag = 1; diagFile = f;\r
4699     HDCDrawPosition(NULL, TRUE, NULL);\r
4700 \r
4701     saveDiagFlag = 0;\r
4702 \r
4703 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4704     \r
4705     fclose(f);\r
4706     return TRUE;\r
4707 }\r
4708 \r
4709 \r
4710 /*---------------------------------------------------------------------------*\\r
4711 | CLIENT PAINT PROCEDURE\r
4712 |   This is the main event-handler for the WM_PAINT message.\r
4713 |\r
4714 \*---------------------------------------------------------------------------*/\r
4715 VOID\r
4716 PaintProc(HWND hwnd)\r
4717 {\r
4718   HDC         hdc;\r
4719   PAINTSTRUCT ps;\r
4720   HFONT       oldFont;\r
4721 \r
4722   if((hdc = BeginPaint(hwnd, &ps))) {\r
4723     if (IsIconic(hwnd)) {\r
4724       DrawIcon(hdc, 2, 2, iconCurrent);\r
4725     } else {\r
4726       if (!appData.monoMode) {\r
4727         SelectPalette(hdc, hPal, FALSE);\r
4728         RealizePalette(hdc);\r
4729       }\r
4730       HDCDrawPosition(hdc, 1, NULL);\r
4731       oldFont =\r
4732         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4733       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4734                  ETO_CLIPPED|ETO_OPAQUE,\r
4735                  &messageRect, messageText, strlen(messageText), NULL);\r
4736       SelectObject(hdc, oldFont);\r
4737       DisplayBothClocks();\r
4738     }\r
4739     EndPaint(hwnd,&ps);\r
4740   }\r
4741 \r
4742   return;\r
4743 }\r
4744 \r
4745 \r
4746 /*\r
4747  * If the user selects on a border boundary, return -1; if off the board,\r
4748  *   return -2.  Otherwise map the event coordinate to the square.\r
4749  * The offset boardRect.left or boardRect.top must already have been\r
4750  *   subtracted from x.\r
4751  */\r
4752 int\r
4753 EventToSquare(int x)\r
4754 {\r
4755   if (x <= 0)\r
4756     return -2;\r
4757   if (x < lineGap)\r
4758     return -1;\r
4759   x -= lineGap;\r
4760   if ((x % (squareSize + lineGap)) >= squareSize)\r
4761     return -1;\r
4762   x /= (squareSize + lineGap);\r
4763   if (x >= BOARD_SIZE)\r
4764     return -2;\r
4765   return x;\r
4766 }\r
4767 \r
4768 typedef struct {\r
4769   char piece;\r
4770   int command;\r
4771   char* name;\r
4772 } DropEnable;\r
4773 \r
4774 DropEnable dropEnables[] = {\r
4775   { 'P', DP_Pawn, "Pawn" },\r
4776   { 'N', DP_Knight, "Knight" },\r
4777   { 'B', DP_Bishop, "Bishop" },\r
4778   { 'R', DP_Rook, "Rook" },\r
4779   { 'Q', DP_Queen, "Queen" },\r
4780 };\r
4781 \r
4782 VOID\r
4783 SetupDropMenu(HMENU hmenu)\r
4784 {\r
4785   int i, count, enable;\r
4786   char *p;\r
4787   extern char white_holding[], black_holding[];\r
4788   char item[MSG_SIZ];\r
4789 \r
4790   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4791     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4792                dropEnables[i].piece);\r
4793     count = 0;\r
4794     while (p && *p++ == dropEnables[i].piece) count++;\r
4795     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4796     enable = count > 0 || !appData.testLegality\r
4797       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4798                       && !appData.icsActive);\r
4799     ModifyMenu(hmenu, dropEnables[i].command,\r
4800                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4801                dropEnables[i].command, item);\r
4802   }\r
4803 }\r
4804 \r
4805 static int fromX = -1, fromY = -1, toX, toY;\r
4806 \r
4807 /* Event handler for mouse messages */\r
4808 VOID\r
4809 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4810 {\r
4811   int x, y;\r
4812   POINT pt;\r
4813   static int recursive = 0;\r
4814   HMENU hmenu;\r
4815 //  BOOLEAN needsRedraw = FALSE;\r
4816   BOOLEAN saveAnimate;\r
4817   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4818   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4819   ChessMove moveType;\r
4820 \r
4821   if (recursive) {\r
4822     if (message == WM_MBUTTONUP) {\r
4823       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4824          to the middle button: we simulate pressing the left button too!\r
4825          */\r
4826       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4827       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4828     }\r
4829     return;\r
4830   }\r
4831   recursive++;\r
4832   \r
4833   pt.x = LOWORD(lParam);\r
4834   pt.y = HIWORD(lParam);\r
4835   x = EventToSquare(pt.x - boardRect.left);\r
4836   y = EventToSquare(pt.y - boardRect.top);\r
4837   if (!flipView && y >= 0) {\r
4838     y = BOARD_HEIGHT - 1 - y;\r
4839   }\r
4840   if (flipView && x >= 0) {\r
4841     x = BOARD_WIDTH - 1 - x;\r
4842   }\r
4843 \r
4844   switch (message) {\r
4845   case WM_LBUTTONDOWN:\r
4846     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4847         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4848         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4849         if(gameInfo.holdingsWidth && \r
4850                 (WhiteOnMove(currentMove) \r
4851                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4852                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4853             // click in right holdings, for determining promotion piece\r
4854             ChessSquare p = boards[currentMove][y][x];\r
4855             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4856             if(p != EmptySquare) {\r
4857                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4858                 fromX = fromY = -1;\r
4859                 break;\r
4860             }\r
4861         }\r
4862         DrawPosition(FALSE, boards[currentMove]);\r
4863         break;\r
4864     }\r
4865     ErrorPopDown();\r
4866     sameAgain = FALSE;\r
4867     if (y == -2) {\r
4868       /* Downclick vertically off board; check if on clock */\r
4869       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4870         if (gameMode == EditPosition) {\r
4871           SetWhiteToPlayEvent();\r
4872         } else if (gameMode == IcsPlayingBlack ||\r
4873                    gameMode == MachinePlaysWhite) {\r
4874           CallFlagEvent();\r
4875         } else if (gameMode == EditGame) {\r
4876           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4877         }\r
4878       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4879         if (gameMode == EditPosition) {\r
4880           SetBlackToPlayEvent();\r
4881         } else if (gameMode == IcsPlayingWhite ||\r
4882                    gameMode == MachinePlaysBlack) {\r
4883           CallFlagEvent();\r
4884         } else if (gameMode == EditGame) {\r
4885           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4886         }\r
4887       }\r
4888       if (!appData.highlightLastMove) {\r
4889         ClearHighlights();\r
4890         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4891       }\r
4892       fromX = fromY = -1;\r
4893       dragInfo.start.x = dragInfo.start.y = -1;\r
4894       dragInfo.from = dragInfo.start;\r
4895       break;\r
4896     } else if (x < 0 || y < 0\r
4897       /* [HGM] block clicks between board and holdings */\r
4898               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4899               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4900               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4901         /* EditPosition, empty square, or different color piece;\r
4902            click-click move is possible */\r
4903                                ) {\r
4904       break;\r
4905     } else if (fromX == x && fromY == y) {\r
4906       /* Downclick on same square again */\r
4907       ClearHighlights();\r
4908       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4909       sameAgain = TRUE;  \r
4910     } else if (fromX != -1 &&\r
4911                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4912                                                                         ) {\r
4913       /* Downclick on different square. */\r
4914       /* [HGM] if on holdings file, should count as new first click ! */\r
4915       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4916         toX = x;\r
4917         toY = y;\r
4918         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4919            to make sure move is legal before showing promotion popup */\r
4920         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4921         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4922                 fromX = fromY = -1; \r
4923                 ClearHighlights();\r
4924                 DrawPosition(FALSE, boards[currentMove]);\r
4925                 break; \r
4926         } else \r
4927         if(moveType != ImpossibleMove) {\r
4928           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4929           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4930             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4931               appData.alwaysPromoteToQueen)) {\r
4932                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4933                   if (!appData.highlightLastMove) {\r
4934                       ClearHighlights();\r
4935                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4936                   }\r
4937           } else\r
4938           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4939                   SetHighlights(fromX, fromY, toX, toY);\r
4940                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4941                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4942                      If promotion to Q is legal, all are legal! */\r
4943                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4944                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4945                     // kludge to temporarily execute move on display, wthout promotng yet\r
4946                     promotionChoice = TRUE;\r
4947                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4948                     boards[currentMove][toY][toX] = p;\r
4949                     DrawPosition(FALSE, boards[currentMove]);\r
4950                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4951                     boards[currentMove][toY][toX] = q;\r
4952                   } else\r
4953                   PromotionPopup(hwnd);\r
4954           } else {       /* not a promotion */\r
4955              if (appData.animate || appData.highlightLastMove) {\r
4956                  SetHighlights(fromX, fromY, toX, toY);\r
4957              } else {\r
4958                  ClearHighlights();\r
4959              }\r
4960              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4961              fromX = fromY = -1;\r
4962              if (appData.animate && !appData.highlightLastMove) {\r
4963                   ClearHighlights();\r
4964                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4965              }\r
4966           }\r
4967           break;\r
4968         }\r
4969         if (gotPremove) {\r
4970             /* [HGM] it seemed that braces were missing here */\r
4971             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4972             fromX = fromY = -1;\r
4973             break;\r
4974         }\r
4975       }\r
4976       ClearHighlights();\r
4977       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4978     }\r
4979     /* First downclick, or restart on a square with same color piece */\r
4980     if (!frozen && OKToStartUserMove(x, y)) {\r
4981       fromX = x;\r
4982       fromY = y;\r
4983       dragInfo.lastpos = pt;\r
4984       dragInfo.from.x = fromX;\r
4985       dragInfo.from.y = fromY;\r
4986       dragInfo.start = dragInfo.from;\r
4987       SetCapture(hwndMain);\r
4988     } else {\r
4989       fromX = fromY = -1;\r
4990       dragInfo.start.x = dragInfo.start.y = -1;\r
4991       dragInfo.from = dragInfo.start;\r
4992       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4993     }\r
4994     break;\r
4995 \r
4996   case WM_LBUTTONUP:\r
4997     ReleaseCapture();\r
4998     if (fromX == -1) break;\r
4999     if (x == fromX && y == fromY) {\r
5000       dragInfo.from.x = dragInfo.from.y = -1;\r
5001       /* Upclick on same square */\r
5002       if (sameAgain) {\r
5003         /* Clicked same square twice: abort click-click move */\r
5004         fromX = fromY = -1;\r
5005         gotPremove = 0;\r
5006         ClearPremoveHighlights();\r
5007       } else {\r
5008         /* First square clicked: start click-click move */\r
5009         SetHighlights(fromX, fromY, -1, -1);\r
5010       }\r
5011       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5012     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5013       /* Errant click; ignore */\r
5014       break;\r
5015     } else {\r
5016       /* Finish drag move. */\r
5017     if (appData.debugMode) {\r
5018         fprintf(debugFP, "release\n");\r
5019     }\r
5020       dragInfo.from.x = dragInfo.from.y = -1;\r
5021       toX = x;\r
5022       toY = y;\r
5023       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5024       appData.animate = appData.animate && !appData.animateDragging;\r
5025       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5026       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5027                 fromX = fromY = -1; \r
5028                 ClearHighlights();\r
5029                 DrawPosition(FALSE, boards[currentMove]);\r
5030                 break; \r
5031       } else \r
5032       if(moveType != ImpossibleMove) {\r
5033           /* [HGM] use move type to determine if move is promotion.\r
5034              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5035           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5036             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5037               appData.alwaysPromoteToQueen)) \r
5038                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5039           else \r
5040           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5041                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5042                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5043                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5044                     // kludge to temporarily execute move on display, wthout promotng yet\r
5045                     promotionChoice = TRUE;\r
5046                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5047                     boards[currentMove][toY][toX] = p;\r
5048                     DrawPosition(FALSE, boards[currentMove]);\r
5049                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5050                     boards[currentMove][toY][toX] = q;\r
5051                     break;\r
5052                   } else\r
5053                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5054           } else {\r
5055             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5056                         && boards[currentMove][toY][toX] != EmptySquare) AnimateAtomicCapture(toX, toY, 20);\r
5057             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5058           }\r
5059       }\r
5060       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5061       appData.animate = saveAnimate;\r
5062       fromX = fromY = -1;\r
5063       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5064         ClearHighlights();\r
5065       }\r
5066       if (appData.animate || appData.animateDragging ||\r
5067           appData.highlightDragging || gotPremove) {\r
5068         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5069       }\r
5070     }\r
5071     dragInfo.start.x = dragInfo.start.y = -1; \r
5072     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5073     break;\r
5074 \r
5075   case WM_MOUSEMOVE:\r
5076     if ((appData.animateDragging || appData.highlightDragging)\r
5077         && (wParam & MK_LBUTTON)\r
5078         && dragInfo.from.x >= 0) \r
5079     {\r
5080       BOOL full_repaint = FALSE;\r
5081 \r
5082       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5083       if (appData.animateDragging) {\r
5084         dragInfo.pos = pt;\r
5085       }\r
5086       if (appData.highlightDragging) {\r
5087         SetHighlights(fromX, fromY, x, y);\r
5088         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5089             full_repaint = TRUE;\r
5090         }\r
5091       }\r
5092       \r
5093       DrawPosition( full_repaint, NULL);\r
5094       \r
5095       dragInfo.lastpos = dragInfo.pos;\r
5096     }\r
5097     break;\r
5098 \r
5099   case WM_MOUSEWHEEL: // [DM]\r
5100     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5101        /* Mouse Wheel is being rolled forward\r
5102         * Play moves forward\r
5103         */\r
5104        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5105                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5106        /* Mouse Wheel is being rolled backward\r
5107         * Play moves backward\r
5108         */\r
5109        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5110                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5111     }\r
5112     break;\r
5113 \r
5114   case WM_MBUTTONDOWN:\r
5115   case WM_RBUTTONDOWN:\r
5116     ErrorPopDown();\r
5117     ReleaseCapture();\r
5118     fromX = fromY = -1;\r
5119     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5120     dragInfo.start.x = dragInfo.start.y = -1;\r
5121     dragInfo.from = dragInfo.start;\r
5122     dragInfo.lastpos = dragInfo.pos;\r
5123     if (appData.highlightDragging) {\r
5124       ClearHighlights();\r
5125     }\r
5126     if(y == -2) {\r
5127       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5128       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5129           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5130       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5131           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5132       }\r
5133     }\r
5134     DrawPosition(TRUE, NULL);\r
5135 \r
5136     switch (gameMode) {\r
5137     case EditPosition:\r
5138     case IcsExamining:\r
5139       if (x < 0 || y < 0) break;\r
5140       fromX = x;\r
5141       fromY = y;\r
5142       if (message == WM_MBUTTONDOWN) {\r
5143         buttonCount = 3;  /* even if system didn't think so */\r
5144         if (wParam & MK_SHIFT) \r
5145           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5146         else\r
5147           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5148       } else { /* message == WM_RBUTTONDOWN */\r
5149 #if 0\r
5150         if (buttonCount == 3) {\r
5151           if (wParam & MK_SHIFT) \r
5152             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5153           else\r
5154             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5155         } else {\r
5156           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5157         }\r
5158 #else\r
5159         /* Just have one menu, on the right button.  Windows users don't\r
5160            think to try the middle one, and sometimes other software steals\r
5161            it, or it doesn't really exist. */\r
5162         if(gameInfo.variant != VariantShogi)\r
5163             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5164         else\r
5165             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5166 #endif\r
5167       }\r
5168       break;\r
5169     case IcsPlayingWhite:\r
5170     case IcsPlayingBlack:\r
5171     case EditGame:\r
5172     case MachinePlaysWhite:\r
5173     case MachinePlaysBlack:\r
5174       if (appData.testLegality &&\r
5175           gameInfo.variant != VariantBughouse &&\r
5176           gameInfo.variant != VariantCrazyhouse) break;\r
5177       if (x < 0 || y < 0) break;\r
5178       fromX = x;\r
5179       fromY = y;\r
5180       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5181       SetupDropMenu(hmenu);\r
5182       MenuPopup(hwnd, pt, hmenu, -1);\r
5183       break;\r
5184     default:\r
5185       break;\r
5186     }\r
5187     break;\r
5188   }\r
5189 \r
5190   recursive--;\r
5191 }\r
5192 \r
5193 /* Preprocess messages for buttons in main window */\r
5194 LRESULT CALLBACK\r
5195 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5196 {\r
5197   int id = GetWindowLong(hwnd, GWL_ID);\r
5198   int i, dir;\r
5199 \r
5200   for (i=0; i<N_BUTTONS; i++) {\r
5201     if (buttonDesc[i].id == id) break;\r
5202   }\r
5203   if (i == N_BUTTONS) return 0;\r
5204   switch (message) {\r
5205   case WM_KEYDOWN:\r
5206     switch (wParam) {\r
5207     case VK_LEFT:\r
5208     case VK_RIGHT:\r
5209       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5210       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5211       return TRUE;\r
5212     }\r
5213     break;\r
5214   case WM_CHAR:\r
5215     switch (wParam) {\r
5216     case '\r':\r
5217       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5218       return TRUE;\r
5219     case '\t':\r
5220       if (appData.icsActive) {\r
5221         if (GetKeyState(VK_SHIFT) < 0) {\r
5222           /* shifted */\r
5223           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5224           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5225           SetFocus(h);\r
5226         } else {\r
5227           /* unshifted */\r
5228           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5229           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5230           SetFocus(h);\r
5231         }\r
5232         return TRUE;\r
5233       }\r
5234       break;\r
5235     default:\r
5236       if (appData.icsActive) {\r
5237         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5238         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5239         SetFocus(h);\r
5240         SendMessage(h, WM_CHAR, wParam, lParam);\r
5241         return TRUE;\r
5242       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5243         PopUpMoveDialog((char)wParam);\r
5244       }\r
5245       break;\r
5246     }\r
5247     break;\r
5248   }\r
5249   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5250 }\r
5251 \r
5252 /* Process messages for Promotion dialog box */\r
5253 LRESULT CALLBACK\r
5254 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5255 {\r
5256   char promoChar;\r
5257 \r
5258   switch (message) {\r
5259   case WM_INITDIALOG: /* message: initialize dialog box */\r
5260     /* Center the dialog over the application window */\r
5261     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5262     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5263       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5264        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5265                SW_SHOW : SW_HIDE);\r
5266     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5267     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5268        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5269          PieceToChar(WhiteAngel) != '~') ||\r
5270         (PieceToChar(BlackAngel) >= 'A' &&\r
5271          PieceToChar(BlackAngel) != '~')   ) ?\r
5272                SW_SHOW : SW_HIDE);\r
5273     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5274        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5275          PieceToChar(WhiteMarshall) != '~') ||\r
5276         (PieceToChar(BlackMarshall) >= 'A' &&\r
5277          PieceToChar(BlackMarshall) != '~')   ) ?\r
5278                SW_SHOW : SW_HIDE);\r
5279     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5280     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5281        gameInfo.variant != VariantShogi ?\r
5282                SW_SHOW : SW_HIDE);\r
5283     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5284        gameInfo.variant != VariantShogi ?\r
5285                SW_SHOW : SW_HIDE);\r
5286     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5287        gameInfo.variant == VariantShogi ?\r
5288                SW_SHOW : SW_HIDE);\r
5289     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5290        gameInfo.variant == VariantShogi ?\r
5291                SW_SHOW : SW_HIDE);\r
5292     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5293        gameInfo.variant == VariantSuper ?\r
5294                SW_SHOW : SW_HIDE);\r
5295     return TRUE;\r
5296 \r
5297   case WM_COMMAND: /* message: received a command */\r
5298     switch (LOWORD(wParam)) {\r
5299     case IDCANCEL:\r
5300       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5301       ClearHighlights();\r
5302       DrawPosition(FALSE, NULL);\r
5303       return TRUE;\r
5304     case PB_King:\r
5305       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5306       break;\r
5307     case PB_Queen:\r
5308       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5309       break;\r
5310     case PB_Rook:\r
5311       promoChar = PieceToChar(BlackRook);\r
5312       break;\r
5313     case PB_Bishop:\r
5314       promoChar = PieceToChar(BlackBishop);\r
5315       break;\r
5316     case PB_Chancellor:\r
5317       promoChar = PieceToChar(BlackMarshall);\r
5318       break;\r
5319     case PB_Archbishop:\r
5320       promoChar = PieceToChar(BlackAngel);\r
5321       break;\r
5322     case PB_Knight:\r
5323       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5324       break;\r
5325     default:\r
5326       return FALSE;\r
5327     }\r
5328     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5329     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5330        only show the popup when we are already sure the move is valid or\r
5331        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5332        will figure out it is a promotion from the promoChar. */\r
5333     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5334     if (!appData.highlightLastMove) {\r
5335       ClearHighlights();\r
5336       DrawPosition(FALSE, NULL);\r
5337     }\r
5338     return TRUE;\r
5339   }\r
5340   return FALSE;\r
5341 }\r
5342 \r
5343 /* Pop up promotion dialog */\r
5344 VOID\r
5345 PromotionPopup(HWND hwnd)\r
5346 {\r
5347   FARPROC lpProc;\r
5348 \r
5349   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5350   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5351     hwnd, (DLGPROC)lpProc);\r
5352   FreeProcInstance(lpProc);\r
5353 }\r
5354 \r
5355 /* Toggle ShowThinking */\r
5356 VOID\r
5357 ToggleShowThinking()\r
5358 {\r
5359   appData.showThinking = !appData.showThinking;\r
5360   ShowThinkingEvent();\r
5361 }\r
5362 \r
5363 VOID\r
5364 LoadGameDialog(HWND hwnd, char* title)\r
5365 {\r
5366   UINT number = 0;\r
5367   FILE *f;\r
5368   char fileTitle[MSG_SIZ];\r
5369   f = OpenFileDialog(hwnd, "rb", "",\r
5370                      appData.oldSaveStyle ? "gam" : "pgn",\r
5371                      GAME_FILT,\r
5372                      title, &number, fileTitle, NULL);\r
5373   if (f != NULL) {\r
5374     cmailMsgLoaded = FALSE;\r
5375     if (number == 0) {\r
5376       int error = GameListBuild(f);\r
5377       if (error) {\r
5378         DisplayError("Cannot build game list", error);\r
5379       } else if (!ListEmpty(&gameList) &&\r
5380                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5381         GameListPopUp(f, fileTitle);\r
5382         return;\r
5383       }\r
5384       GameListDestroy();\r
5385       number = 1;\r
5386     }\r
5387     LoadGame(f, number, fileTitle, FALSE);\r
5388   }\r
5389 }\r
5390 \r
5391 VOID\r
5392 ChangedConsoleFont()\r
5393 {\r
5394   CHARFORMAT cfmt;\r
5395   CHARRANGE tmpsel, sel;\r
5396   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5397   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5398   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5399   PARAFORMAT paraf;\r
5400 \r
5401   cfmt.cbSize = sizeof(CHARFORMAT);\r
5402   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5403   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5404   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5405    * size.  This was undocumented in the version of MSVC++ that I had\r
5406    * when I wrote the code, but is apparently documented now.\r
5407    */\r
5408   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5409   cfmt.bCharSet = f->lf.lfCharSet;\r
5410   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5411   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5412   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5413   /* Why are the following seemingly needed too? */\r
5414   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5415   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5416   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5417   tmpsel.cpMin = 0;\r
5418   tmpsel.cpMax = -1; /*999999?*/\r
5419   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5420   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5421   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5422    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5423    */\r
5424   paraf.cbSize = sizeof(paraf);\r
5425   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5426   paraf.dxStartIndent = 0;\r
5427   paraf.dxOffset = WRAP_INDENT;\r
5428   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5429   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5430 }\r
5431 \r
5432 /*---------------------------------------------------------------------------*\\r
5433  *\r
5434  * Window Proc for main window\r
5435  *\r
5436 \*---------------------------------------------------------------------------*/\r
5437 \r
5438 /* Process messages for main window, etc. */\r
5439 LRESULT CALLBACK\r
5440 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5441 {\r
5442   FARPROC lpProc;\r
5443   int wmId, wmEvent;\r
5444   char *defName;\r
5445   FILE *f;\r
5446   UINT number;\r
5447   char fileTitle[MSG_SIZ];\r
5448   char buf[MSG_SIZ];\r
5449   static SnapData sd;\r
5450 \r
5451   switch (message) {\r
5452 \r
5453   case WM_PAINT: /* message: repaint portion of window */\r
5454     PaintProc(hwnd);\r
5455     break;\r
5456 \r
5457   case WM_ERASEBKGND:\r
5458     if (IsIconic(hwnd)) {\r
5459       /* Cheat; change the message */\r
5460       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5461     } else {\r
5462       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5463     }\r
5464     break;\r
5465 \r
5466   case WM_LBUTTONDOWN:\r
5467   case WM_MBUTTONDOWN:\r
5468   case WM_RBUTTONDOWN:\r
5469   case WM_LBUTTONUP:\r
5470   case WM_MBUTTONUP:\r
5471   case WM_RBUTTONUP:\r
5472   case WM_MOUSEMOVE:\r
5473   case WM_MOUSEWHEEL:\r
5474     MouseEvent(hwnd, message, wParam, lParam);\r
5475     break;\r
5476 \r
5477   case WM_CHAR:\r
5478     \r
5479     if (appData.icsActive) {\r
5480       if (wParam == '\t') {\r
5481         if (GetKeyState(VK_SHIFT) < 0) {\r
5482           /* shifted */\r
5483           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5484           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5485           SetFocus(h);\r
5486         } else {\r
5487           /* unshifted */\r
5488           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5489           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5490           SetFocus(h);\r
5491         }\r
5492       } else {\r
5493         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5494         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5495         SetFocus(h);\r
5496         SendMessage(h, message, wParam, lParam);\r
5497       }\r
5498     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5499       PopUpMoveDialog((char)wParam);\r
5500     }\r
5501     break;\r
5502 \r
5503   case WM_PALETTECHANGED:\r
5504     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5505       int nnew;\r
5506       HDC hdc = GetDC(hwndMain);\r
5507       SelectPalette(hdc, hPal, TRUE);\r
5508       nnew = RealizePalette(hdc);\r
5509       if (nnew > 0) {\r
5510         paletteChanged = TRUE;\r
5511 #if 0\r
5512         UpdateColors(hdc);\r
5513 #else\r
5514         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5515 #endif\r
5516       }\r
5517       ReleaseDC(hwnd, hdc);\r
5518     }\r
5519     break;\r
5520 \r
5521   case WM_QUERYNEWPALETTE:\r
5522     if (!appData.monoMode /*&& paletteChanged*/) {\r
5523       int nnew;\r
5524       HDC hdc = GetDC(hwndMain);\r
5525       paletteChanged = FALSE;\r
5526       SelectPalette(hdc, hPal, FALSE);\r
5527       nnew = RealizePalette(hdc);\r
5528       if (nnew > 0) {\r
5529         InvalidateRect(hwnd, &boardRect, FALSE);\r
5530       }\r
5531       ReleaseDC(hwnd, hdc);\r
5532       return TRUE;\r
5533     }\r
5534     return FALSE;\r
5535 \r
5536   case WM_COMMAND: /* message: command from application menu */\r
5537     wmId    = LOWORD(wParam);\r
5538     wmEvent = HIWORD(wParam);\r
5539 \r
5540     switch (wmId) {\r
5541     case IDM_NewGame:\r
5542       ResetGameEvent();\r
5543       AnalysisPopDown();\r
5544       break;\r
5545 \r
5546     case IDM_NewGameFRC:\r
5547       if( NewGameFRC() == 0 ) {\r
5548         ResetGameEvent();\r
5549         AnalysisPopDown();\r
5550       }\r
5551       break;\r
5552 \r
5553     case IDM_NewVariant:\r
5554       NewVariantPopup(hwnd);\r
5555       break;\r
5556 \r
5557     case IDM_LoadGame:\r
5558       LoadGameDialog(hwnd, "Load Game from File");\r
5559       break;\r
5560 \r
5561     case IDM_LoadNextGame:\r
5562       ReloadGame(1);\r
5563       break;\r
5564 \r
5565     case IDM_LoadPrevGame:\r
5566       ReloadGame(-1);\r
5567       break;\r
5568 \r
5569     case IDM_ReloadGame:\r
5570       ReloadGame(0);\r
5571       break;\r
5572 \r
5573     case IDM_LoadPosition:\r
5574       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5575         Reset(FALSE, TRUE);\r
5576       }\r
5577       number = 1;\r
5578       f = OpenFileDialog(hwnd, "rb", "",\r
5579                          appData.oldSaveStyle ? "pos" : "fen",\r
5580                          POSITION_FILT,\r
5581                          "Load Position from File", &number, fileTitle, NULL);\r
5582       if (f != NULL) {\r
5583         LoadPosition(f, number, fileTitle);\r
5584       }\r
5585       break;\r
5586 \r
5587     case IDM_LoadNextPosition:\r
5588       ReloadPosition(1);\r
5589       break;\r
5590 \r
5591     case IDM_LoadPrevPosition:\r
5592       ReloadPosition(-1);\r
5593       break;\r
5594 \r
5595     case IDM_ReloadPosition:\r
5596       ReloadPosition(0);\r
5597       break;\r
5598 \r
5599     case IDM_SaveGame:\r
5600       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5601       f = OpenFileDialog(hwnd, "a", defName,\r
5602                          appData.oldSaveStyle ? "gam" : "pgn",\r
5603                          GAME_FILT,\r
5604                          "Save Game to File", NULL, fileTitle, NULL);\r
5605       if (f != NULL) {\r
5606         SaveGame(f, 0, "");\r
5607       }\r
5608       break;\r
5609 \r
5610     case IDM_SavePosition:\r
5611       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5612       f = OpenFileDialog(hwnd, "a", defName,\r
5613                          appData.oldSaveStyle ? "pos" : "fen",\r
5614                          POSITION_FILT,\r
5615                          "Save Position to File", NULL, fileTitle, NULL);\r
5616       if (f != NULL) {\r
5617         SavePosition(f, 0, "");\r
5618       }\r
5619       break;\r
5620 \r
5621     case IDM_SaveDiagram:\r
5622       defName = "diagram";\r
5623       f = OpenFileDialog(hwnd, "wb", defName,\r
5624                          "bmp",\r
5625                          DIAGRAM_FILT,\r
5626                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5627       if (f != NULL) {\r
5628         SaveDiagram(f);\r
5629       }\r
5630       break;\r
5631 \r
5632     case IDM_CopyGame:\r
5633       CopyGameToClipboard();\r
5634       break;\r
5635 \r
5636     case IDM_PasteGame:\r
5637       PasteGameFromClipboard();\r
5638       break;\r
5639 \r
5640     case IDM_CopyGameListToClipboard:\r
5641       CopyGameListToClipboard();\r
5642       break;\r
5643 \r
5644     /* [AS] Autodetect FEN or PGN data */\r
5645     case IDM_PasteAny:\r
5646       PasteGameOrFENFromClipboard();\r
5647       break;\r
5648 \r
5649     /* [AS] Move history */\r
5650     case IDM_ShowMoveHistory:\r
5651         if( MoveHistoryIsUp() ) {\r
5652             MoveHistoryPopDown();\r
5653         }\r
5654         else {\r
5655             MoveHistoryPopUp();\r
5656         }\r
5657         break;\r
5658 \r
5659     /* [AS] Eval graph */\r
5660     case IDM_ShowEvalGraph:\r
5661         if( EvalGraphIsUp() ) {\r
5662             EvalGraphPopDown();\r
5663         }\r
5664         else {\r
5665             EvalGraphPopUp();\r
5666         }\r
5667         break;\r
5668 \r
5669     /* [AS] Engine output */\r
5670     case IDM_ShowEngineOutput:\r
5671         if( EngineOutputIsUp() ) {\r
5672             EngineOutputPopDown();\r
5673         }\r
5674         else {\r
5675             EngineOutputPopUp();\r
5676         }\r
5677         break;\r
5678 \r
5679     /* [AS] User adjudication */\r
5680     case IDM_UserAdjudication_White:\r
5681         UserAdjudicationEvent( +1 );\r
5682         break;\r
5683 \r
5684     case IDM_UserAdjudication_Black:\r
5685         UserAdjudicationEvent( -1 );\r
5686         break;\r
5687 \r
5688     case IDM_UserAdjudication_Draw:\r
5689         UserAdjudicationEvent( 0 );\r
5690         break;\r
5691 \r
5692     /* [AS] Game list options dialog */\r
5693     case IDM_GameListOptions:\r
5694       GameListOptions();\r
5695       break;\r
5696 \r
5697     case IDM_CopyPosition:\r
5698       CopyFENToClipboard();\r
5699       break;\r
5700 \r
5701     case IDM_PastePosition:\r
5702       PasteFENFromClipboard();\r
5703       break;\r
5704 \r
5705     case IDM_MailMove:\r
5706       MailMoveEvent();\r
5707       break;\r
5708 \r
5709     case IDM_ReloadCMailMsg:\r
5710       Reset(TRUE, TRUE);\r
5711       ReloadCmailMsgEvent(FALSE);\r
5712       break;\r
5713 \r
5714     case IDM_Minimize:\r
5715       ShowWindow(hwnd, SW_MINIMIZE);\r
5716       break;\r
5717 \r
5718     case IDM_Exit:\r
5719       ExitEvent(0);\r
5720       break;\r
5721 \r
5722     case IDM_MachineWhite:\r
5723       MachineWhiteEvent();\r
5724       /*\r
5725        * refresh the tags dialog only if it's visible\r
5726        */\r
5727       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5728           char *tags;\r
5729           tags = PGNTags(&gameInfo);\r
5730           TagsPopUp(tags, CmailMsg());\r
5731           free(tags);\r
5732       }\r
5733       break;\r
5734 \r
5735     case IDM_MachineBlack:\r
5736       MachineBlackEvent();\r
5737       /*\r
5738        * refresh the tags dialog only if it's visible\r
5739        */\r
5740       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5741           char *tags;\r
5742           tags = PGNTags(&gameInfo);\r
5743           TagsPopUp(tags, CmailMsg());\r
5744           free(tags);\r
5745       }\r
5746       break;\r
5747 \r
5748     case IDM_TwoMachines:\r
5749       TwoMachinesEvent();\r
5750       /*\r
5751        * refresh the tags dialog only if it's visible\r
5752        */\r
5753       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5754           char *tags;\r
5755           tags = PGNTags(&gameInfo);\r
5756           TagsPopUp(tags, CmailMsg());\r
5757           free(tags);\r
5758       }\r
5759       break;\r
5760 \r
5761     case IDM_AnalysisMode:\r
5762       if (!first.analysisSupport) {\r
5763         sprintf(buf, "%s does not support analysis", first.tidy);\r
5764         DisplayError(buf, 0);\r
5765       } else {\r
5766         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5767         if (appData.icsActive) {\r
5768                if (gameMode != IcsObserving) {\r
5769                        sprintf(buf, "You are not observing a game");\r
5770                        DisplayError(buf, 0);\r
5771                        /* secure check */\r
5772                        if (appData.icsEngineAnalyze) {\r
5773                                if (appData.debugMode) \r
5774                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5775                                ExitAnalyzeMode();\r
5776                                ModeHighlight();\r
5777                                break;\r
5778                        }\r
5779                        break;\r
5780                } else {\r
5781                        /* if enable, user want disable icsEngineAnalyze */\r
5782                        if (appData.icsEngineAnalyze) {\r
5783                                ExitAnalyzeMode();\r
5784                                ModeHighlight();\r
5785                                break;\r
5786                        }\r
5787                        appData.icsEngineAnalyze = TRUE;\r
5788                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5789                }\r
5790         } \r
5791         if (!appData.showThinking) ToggleShowThinking();\r
5792         AnalyzeModeEvent();\r
5793       }\r
5794       break;\r
5795 \r
5796     case IDM_AnalyzeFile:\r
5797       if (!first.analysisSupport) {\r
5798         char buf[MSG_SIZ];\r
5799         sprintf(buf, "%s does not support analysis", first.tidy);\r
5800         DisplayError(buf, 0);\r
5801       } else {\r
5802         if (!appData.showThinking) ToggleShowThinking();\r
5803         AnalyzeFileEvent();\r
5804         LoadGameDialog(hwnd, "Analyze Game from File");\r
5805         AnalysisPeriodicEvent(1);\r
5806       }\r
5807       break;\r
5808 \r
5809     case IDM_IcsClient:\r
5810       IcsClientEvent();\r
5811       break;\r
5812 \r
5813     case IDM_EditGame:\r
5814       EditGameEvent();\r
5815       break;\r
5816 \r
5817     case IDM_EditPosition:\r
5818       EditPositionEvent();\r
5819       break;\r
5820 \r
5821     case IDM_Training:\r
5822       TrainingEvent();\r
5823       break;\r
5824 \r
5825     case IDM_ShowGameList:\r
5826       ShowGameListProc();\r
5827       break;\r
5828 \r
5829     case IDM_EditTags:\r
5830       EditTagsProc();\r
5831       break;\r
5832 \r
5833     case IDM_EditComment:\r
5834       if (commentDialogUp && editComment) {\r
5835         CommentPopDown();\r
5836       } else {\r
5837         EditCommentEvent();\r
5838       }\r
5839       break;\r
5840 \r
5841     case IDM_Pause:\r
5842       PauseEvent();\r
5843       break;\r
5844 \r
5845     case IDM_Accept:\r
5846       AcceptEvent();\r
5847       break;\r
5848 \r
5849     case IDM_Decline:\r
5850       DeclineEvent();\r
5851       break;\r
5852 \r
5853     case IDM_Rematch:\r
5854       RematchEvent();\r
5855       break;\r
5856 \r
5857     case IDM_CallFlag:\r
5858       CallFlagEvent();\r
5859       break;\r
5860 \r
5861     case IDM_Draw:\r
5862       DrawEvent();\r
5863       break;\r
5864 \r
5865     case IDM_Adjourn:\r
5866       AdjournEvent();\r
5867       break;\r
5868 \r
5869     case IDM_Abort:\r
5870       AbortEvent();\r
5871       break;\r
5872 \r
5873     case IDM_Resign:\r
5874       ResignEvent();\r
5875       break;\r
5876 \r
5877     case IDM_StopObserving:\r
5878       StopObservingEvent();\r
5879       break;\r
5880 \r
5881     case IDM_StopExamining:\r
5882       StopExaminingEvent();\r
5883       break;\r
5884 \r
5885     case IDM_TypeInMove:\r
5886       PopUpMoveDialog('\000');\r
5887       break;\r
5888 \r
5889     case IDM_TypeInName:\r
5890       PopUpNameDialog('\000');\r
5891       break;\r
5892 \r
5893     case IDM_Backward:\r
5894       BackwardEvent();\r
5895       SetFocus(hwndMain);\r
5896       break;\r
5897 \r
5898     case IDM_Forward:\r
5899       ForwardEvent();\r
5900       SetFocus(hwndMain);\r
5901       break;\r
5902 \r
5903     case IDM_ToStart:\r
5904       ToStartEvent();\r
5905       SetFocus(hwndMain);\r
5906       break;\r
5907 \r
5908     case IDM_ToEnd:\r
5909       ToEndEvent();\r
5910       SetFocus(hwndMain);\r
5911       break;\r
5912 \r
5913     case IDM_Revert:\r
5914       RevertEvent();\r
5915       break;\r
5916 \r
5917     case IDM_TruncateGame:\r
5918       TruncateGameEvent();\r
5919       break;\r
5920 \r
5921     case IDM_MoveNow:\r
5922       MoveNowEvent();\r
5923       break;\r
5924 \r
5925     case IDM_RetractMove:\r
5926       RetractMoveEvent();\r
5927       break;\r
5928 \r
5929     case IDM_FlipView:\r
5930       flipView = !flipView;\r
5931       DrawPosition(FALSE, NULL);\r
5932       break;\r
5933 \r
5934     case IDM_FlipClock:\r
5935       flipClock = !flipClock;\r
5936       DisplayBothClocks();\r
5937       break;\r
5938 \r
5939     case IDM_GeneralOptions:\r
5940       GeneralOptionsPopup(hwnd);\r
5941       DrawPosition(TRUE, NULL);\r
5942       break;\r
5943 \r
5944     case IDM_BoardOptions:\r
5945       BoardOptionsPopup(hwnd);\r
5946       break;\r
5947 \r
5948     case IDM_EnginePlayOptions:\r
5949       EnginePlayOptionsPopup(hwnd);\r
5950       break;\r
5951 \r
5952     case IDM_OptionsUCI:\r
5953       UciOptionsPopup(hwnd);\r
5954       break;\r
5955 \r
5956     case IDM_IcsOptions:\r
5957       IcsOptionsPopup(hwnd);\r
5958       break;\r
5959 \r
5960     case IDM_Fonts:\r
5961       FontsOptionsPopup(hwnd);\r
5962       break;\r
5963 \r
5964     case IDM_Sounds:\r
5965       SoundOptionsPopup(hwnd);\r
5966       break;\r
5967 \r
5968     case IDM_CommPort:\r
5969       CommPortOptionsPopup(hwnd);\r
5970       break;\r
5971 \r
5972     case IDM_LoadOptions:\r
5973       LoadOptionsPopup(hwnd);\r
5974       break;\r
5975 \r
5976     case IDM_SaveOptions:\r
5977       SaveOptionsPopup(hwnd);\r
5978       break;\r
5979 \r
5980     case IDM_TimeControl:\r
5981       TimeControlOptionsPopup(hwnd);\r
5982       break;\r
5983 \r
5984     case IDM_SaveSettings:\r
5985       SaveSettings(settingsFileName);\r
5986       break;\r
5987 \r
5988     case IDM_SaveSettingsOnExit:\r
5989       saveSettingsOnExit = !saveSettingsOnExit;\r
5990       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5991                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5992                                          MF_CHECKED : MF_UNCHECKED));\r
5993       break;\r
5994 \r
5995     case IDM_Hint:\r
5996       HintEvent();\r
5997       break;\r
5998 \r
5999     case IDM_Book:\r
6000       BookEvent();\r
6001       break;\r
6002 \r
6003     case IDM_AboutGame:\r
6004       AboutGameEvent();\r
6005       break;\r
6006 \r
6007     case IDM_Debug:\r
6008       appData.debugMode = !appData.debugMode;\r
6009       if (appData.debugMode) {\r
6010         char dir[MSG_SIZ];\r
6011         GetCurrentDirectory(MSG_SIZ, dir);\r
6012         SetCurrentDirectory(installDir);\r
6013         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6014         SetCurrentDirectory(dir);\r
6015         setbuf(debugFP, NULL);\r
6016       } else {\r
6017         fclose(debugFP);\r
6018         debugFP = NULL;\r
6019       }\r
6020       break;\r
6021 \r
6022     case IDM_HELPCONTENTS:\r
6023       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6024         MessageBox (GetFocus(),\r
6025                     "Unable to activate help",\r
6026                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6027       }\r
6028       break;\r
6029 \r
6030     case IDM_HELPSEARCH:\r
6031       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6032         MessageBox (GetFocus(),\r
6033                     "Unable to activate help",\r
6034                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6035       }\r
6036       break;\r
6037 \r
6038     case IDM_HELPHELP:\r
6039       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6040         MessageBox (GetFocus(),\r
6041                     "Unable to activate help",\r
6042                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6043       }\r
6044       break;\r
6045 \r
6046     case IDM_ABOUT:\r
6047       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6048       DialogBox(hInst, \r
6049         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6050         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6051       FreeProcInstance(lpProc);\r
6052       break;\r
6053 \r
6054     case IDM_DirectCommand1:\r
6055       AskQuestionEvent("Direct Command",\r
6056                        "Send to chess program:", "", "1");\r
6057       break;\r
6058     case IDM_DirectCommand2:\r
6059       AskQuestionEvent("Direct Command",\r
6060                        "Send to second chess program:", "", "2");\r
6061       break;\r
6062 \r
6063     case EP_WhitePawn:\r
6064       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6065       fromX = fromY = -1;\r
6066       break;\r
6067 \r
6068     case EP_WhiteKnight:\r
6069       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6070       fromX = fromY = -1;\r
6071       break;\r
6072 \r
6073     case EP_WhiteBishop:\r
6074       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6075       fromX = fromY = -1;\r
6076       break;\r
6077 \r
6078     case EP_WhiteRook:\r
6079       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6080       fromX = fromY = -1;\r
6081       break;\r
6082 \r
6083     case EP_WhiteQueen:\r
6084       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6085       fromX = fromY = -1;\r
6086       break;\r
6087 \r
6088     case EP_WhiteFerz:\r
6089       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6090       fromX = fromY = -1;\r
6091       break;\r
6092 \r
6093     case EP_WhiteWazir:\r
6094       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6095       fromX = fromY = -1;\r
6096       break;\r
6097 \r
6098     case EP_WhiteAlfil:\r
6099       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6100       fromX = fromY = -1;\r
6101       break;\r
6102 \r
6103     case EP_WhiteCannon:\r
6104       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6105       fromX = fromY = -1;\r
6106       break;\r
6107 \r
6108     case EP_WhiteCardinal:\r
6109       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6110       fromX = fromY = -1;\r
6111       break;\r
6112 \r
6113     case EP_WhiteMarshall:\r
6114       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6115       fromX = fromY = -1;\r
6116       break;\r
6117 \r
6118     case EP_WhiteKing:\r
6119       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6120       fromX = fromY = -1;\r
6121       break;\r
6122 \r
6123     case EP_BlackPawn:\r
6124       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6125       fromX = fromY = -1;\r
6126       break;\r
6127 \r
6128     case EP_BlackKnight:\r
6129       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6130       fromX = fromY = -1;\r
6131       break;\r
6132 \r
6133     case EP_BlackBishop:\r
6134       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6135       fromX = fromY = -1;\r
6136       break;\r
6137 \r
6138     case EP_BlackRook:\r
6139       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6140       fromX = fromY = -1;\r
6141       break;\r
6142 \r
6143     case EP_BlackQueen:\r
6144       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6145       fromX = fromY = -1;\r
6146       break;\r
6147 \r
6148     case EP_BlackFerz:\r
6149       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6150       fromX = fromY = -1;\r
6151       break;\r
6152 \r
6153     case EP_BlackWazir:\r
6154       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6155       fromX = fromY = -1;\r
6156       break;\r
6157 \r
6158     case EP_BlackAlfil:\r
6159       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6160       fromX = fromY = -1;\r
6161       break;\r
6162 \r
6163     case EP_BlackCannon:\r
6164       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6165       fromX = fromY = -1;\r
6166       break;\r
6167 \r
6168     case EP_BlackCardinal:\r
6169       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6170       fromX = fromY = -1;\r
6171       break;\r
6172 \r
6173     case EP_BlackMarshall:\r
6174       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6175       fromX = fromY = -1;\r
6176       break;\r
6177 \r
6178     case EP_BlackKing:\r
6179       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6180       fromX = fromY = -1;\r
6181       break;\r
6182 \r
6183     case EP_EmptySquare:\r
6184       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6185       fromX = fromY = -1;\r
6186       break;\r
6187 \r
6188     case EP_ClearBoard:\r
6189       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6190       fromX = fromY = -1;\r
6191       break;\r
6192 \r
6193     case EP_White:\r
6194       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6195       fromX = fromY = -1;\r
6196       break;\r
6197 \r
6198     case EP_Black:\r
6199       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6200       fromX = fromY = -1;\r
6201       break;\r
6202 \r
6203     case EP_Promote:\r
6204       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6205       fromX = fromY = -1;\r
6206       break;\r
6207 \r
6208     case EP_Demote:\r
6209       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6210       fromX = fromY = -1;\r
6211       break;\r
6212 \r
6213     case DP_Pawn:\r
6214       DropMenuEvent(WhitePawn, fromX, fromY);\r
6215       fromX = fromY = -1;\r
6216       break;\r
6217 \r
6218     case DP_Knight:\r
6219       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6220       fromX = fromY = -1;\r
6221       break;\r
6222 \r
6223     case DP_Bishop:\r
6224       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6225       fromX = fromY = -1;\r
6226       break;\r
6227 \r
6228     case DP_Rook:\r
6229       DropMenuEvent(WhiteRook, fromX, fromY);\r
6230       fromX = fromY = -1;\r
6231       break;\r
6232 \r
6233     case DP_Queen:\r
6234       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6235       fromX = fromY = -1;\r
6236       break;\r
6237 \r
6238     default:\r
6239       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6240     }\r
6241     break;\r
6242 \r
6243   case WM_TIMER:\r
6244     switch (wParam) {\r
6245     case CLOCK_TIMER_ID:\r
6246       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6247       clockTimerEvent = 0;\r
6248       DecrementClocks(); /* call into back end */\r
6249       break;\r
6250     case LOAD_GAME_TIMER_ID:\r
6251       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6252       loadGameTimerEvent = 0;\r
6253       AutoPlayGameLoop(); /* call into back end */\r
6254       break;\r
6255     case ANALYSIS_TIMER_ID:\r
6256       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6257                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6258         AnalysisPeriodicEvent(0);\r
6259       } else {\r
6260         KillTimer(hwnd, analysisTimerEvent);\r
6261         analysisTimerEvent = 0;\r
6262       }\r
6263       break;\r
6264     case DELAYED_TIMER_ID:\r
6265       KillTimer(hwnd, delayedTimerEvent);\r
6266       delayedTimerEvent = 0;\r
6267       delayedTimerCallback();\r
6268       break;\r
6269     }\r
6270     break;\r
6271 \r
6272   case WM_USER_Input:\r
6273     InputEvent(hwnd, message, wParam, lParam);\r
6274     break;\r
6275 \r
6276   /* [AS] Also move "attached" child windows */\r
6277   case WM_WINDOWPOSCHANGING:\r
6278     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6279         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6280 \r
6281         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6282             /* Window is moving */\r
6283             RECT rcMain;\r
6284 \r
6285             GetWindowRect( hwnd, &rcMain );\r
6286             \r
6287             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6288             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6289             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6290         }\r
6291     }\r
6292     break;\r
6293 \r
6294   /* [AS] Snapping */\r
6295   case WM_ENTERSIZEMOVE:\r
6296     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6297     if (hwnd == hwndMain) {\r
6298       doingSizing = TRUE;\r
6299       lastSizing = 0;\r
6300     }\r
6301     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6302     break;\r
6303 \r
6304   case WM_SIZING:\r
6305     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6306     if (hwnd == hwndMain) {\r
6307       lastSizing = wParam;\r
6308     }\r
6309     break;\r
6310 \r
6311   case WM_MOVING:\r
6312     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6313       return OnMoving( &sd, hwnd, wParam, lParam );\r
6314 \r
6315   case WM_EXITSIZEMOVE:\r
6316     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6317     if (hwnd == hwndMain) {\r
6318       RECT client;\r
6319       doingSizing = FALSE;\r
6320       InvalidateRect(hwnd, &boardRect, FALSE);\r
6321       GetClientRect(hwnd, &client);\r
6322       ResizeBoard(client.right, client.bottom, lastSizing);\r
6323       lastSizing = 0;\r
6324       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6325     }\r
6326     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6327     break;\r
6328 \r
6329   case WM_DESTROY: /* message: window being destroyed */\r
6330     PostQuitMessage(0);\r
6331     break;\r
6332 \r
6333   case WM_CLOSE:\r
6334     if (hwnd == hwndMain) {\r
6335       ExitEvent(0);\r
6336     }\r
6337     break;\r
6338 \r
6339   default:      /* Passes it on if unprocessed */\r
6340     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6341   }\r
6342   return 0;\r
6343 }\r
6344 \r
6345 /*---------------------------------------------------------------------------*\\r
6346  *\r
6347  * Misc utility routines\r
6348  *\r
6349 \*---------------------------------------------------------------------------*/\r
6350 \r
6351 /*\r
6352  * Decent random number generator, at least not as bad as Windows\r
6353  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6354  */\r
6355 unsigned int randstate;\r
6356 \r
6357 int\r
6358 myrandom(void)\r
6359 {\r
6360   randstate = randstate * 1664525 + 1013904223;\r
6361   return (int) randstate & 0x7fffffff;\r
6362 }\r
6363 \r
6364 void\r
6365 mysrandom(unsigned int seed)\r
6366 {\r
6367   randstate = seed;\r
6368 }\r
6369 \r
6370 \r
6371 /* \r
6372  * returns TRUE if user selects a different color, FALSE otherwise \r
6373  */\r
6374 \r
6375 BOOL\r
6376 ChangeColor(HWND hwnd, COLORREF *which)\r
6377 {\r
6378   static BOOL firstTime = TRUE;\r
6379   static DWORD customColors[16];\r
6380   CHOOSECOLOR cc;\r
6381   COLORREF newcolor;\r
6382   int i;\r
6383   ColorClass ccl;\r
6384 \r
6385   if (firstTime) {\r
6386     /* Make initial colors in use available as custom colors */\r
6387     /* Should we put the compiled-in defaults here instead? */\r
6388     i = 0;\r
6389     customColors[i++] = lightSquareColor & 0xffffff;\r
6390     customColors[i++] = darkSquareColor & 0xffffff;\r
6391     customColors[i++] = whitePieceColor & 0xffffff;\r
6392     customColors[i++] = blackPieceColor & 0xffffff;\r
6393     customColors[i++] = highlightSquareColor & 0xffffff;\r
6394     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6395 \r
6396     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6397       customColors[i++] = textAttribs[ccl].color;\r
6398     }\r
6399     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6400     firstTime = FALSE;\r
6401   }\r
6402 \r
6403   cc.lStructSize = sizeof(cc);\r
6404   cc.hwndOwner = hwnd;\r
6405   cc.hInstance = NULL;\r
6406   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6407   cc.lpCustColors = (LPDWORD) customColors;\r
6408   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6409 \r
6410   if (!ChooseColor(&cc)) return FALSE;\r
6411 \r
6412   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6413   if (newcolor == *which) return FALSE;\r
6414   *which = newcolor;\r
6415   return TRUE;\r
6416 \r
6417   /*\r
6418   InitDrawingColors();\r
6419   InvalidateRect(hwnd, &boardRect, FALSE);\r
6420   */\r
6421 }\r
6422 \r
6423 BOOLEAN\r
6424 MyLoadSound(MySound *ms)\r
6425 {\r
6426   BOOL ok = FALSE;\r
6427   struct stat st;\r
6428   FILE *f;\r
6429 \r
6430   if (ms->data) free(ms->data);\r
6431   ms->data = NULL;\r
6432 \r
6433   switch (ms->name[0]) {\r
6434   case NULLCHAR:\r
6435     /* Silence */\r
6436     ok = TRUE;\r
6437     break;\r
6438   case '$':\r
6439     /* System sound from Control Panel.  Don't preload here. */\r
6440     ok = TRUE;\r
6441     break;\r
6442   case '!':\r
6443     if (ms->name[1] == NULLCHAR) {\r
6444       /* "!" alone = silence */\r
6445       ok = TRUE;\r
6446     } else {\r
6447       /* Builtin wave resource.  Error if not found. */\r
6448       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6449       if (h == NULL) break;\r
6450       ms->data = (void *)LoadResource(hInst, h);\r
6451       if (h == NULL) break;\r
6452       ok = TRUE;\r
6453     }\r
6454     break;\r
6455   default:\r
6456     /* .wav file.  Error if not found. */\r
6457     f = fopen(ms->name, "rb");\r
6458     if (f == NULL) break;\r
6459     if (fstat(fileno(f), &st) < 0) break;\r
6460     ms->data = malloc(st.st_size);\r
6461     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6462     fclose(f);\r
6463     ok = TRUE;\r
6464     break;\r
6465   }\r
6466   if (!ok) {\r
6467     char buf[MSG_SIZ];\r
6468     sprintf(buf, "Error loading sound %s", ms->name);\r
6469     DisplayError(buf, GetLastError());\r
6470   }\r
6471   return ok;\r
6472 }\r
6473 \r
6474 BOOLEAN\r
6475 MyPlaySound(MySound *ms)\r
6476 {\r
6477   BOOLEAN ok = FALSE;\r
6478   switch (ms->name[0]) {\r
6479   case NULLCHAR:\r
6480     /* Silence */\r
6481     ok = TRUE;\r
6482     break;\r
6483   case '$':\r
6484     /* System sound from Control Panel (deprecated feature).\r
6485        "$" alone or an unset sound name gets default beep (still in use). */\r
6486     if (ms->name[1]) {\r
6487       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6488     }\r
6489     if (!ok) ok = MessageBeep(MB_OK);\r
6490     break; \r
6491   case '!':\r
6492     /* Builtin wave resource, or "!" alone for silence */\r
6493     if (ms->name[1]) {\r
6494       if (ms->data == NULL) return FALSE;\r
6495       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6496     } else {\r
6497       ok = TRUE;\r
6498     }\r
6499     break;\r
6500   default:\r
6501     /* .wav file.  Error if not found. */\r
6502     if (ms->data == NULL) return FALSE;\r
6503     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6504     break;\r
6505   }\r
6506   /* Don't print an error: this can happen innocently if the sound driver\r
6507      is busy; for instance, if another instance of WinBoard is playing\r
6508      a sound at about the same time. */\r
6509 #if 0\r
6510   if (!ok) {\r
6511     char buf[MSG_SIZ];\r
6512     sprintf(buf, "Error playing sound %s", ms->name);\r
6513     DisplayError(buf, GetLastError());\r
6514   }\r
6515 #endif\r
6516   return ok;\r
6517 }\r
6518 \r
6519 \r
6520 LRESULT CALLBACK\r
6521 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6522 {\r
6523   BOOL ok;\r
6524   OPENFILENAME *ofn;\r
6525   static UINT *number; /* gross that this is static */\r
6526 \r
6527   switch (message) {\r
6528   case WM_INITDIALOG: /* message: initialize dialog box */\r
6529     /* Center the dialog over the application window */\r
6530     ofn = (OPENFILENAME *) lParam;\r
6531     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6532       number = (UINT *) ofn->lCustData;\r
6533       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6534     } else {\r
6535       number = NULL;\r
6536     }\r
6537     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6538     return FALSE;  /* Allow for further processing */\r
6539 \r
6540   case WM_COMMAND:\r
6541     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6542       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6543     }\r
6544     return FALSE;  /* Allow for further processing */\r
6545   }\r
6546   return FALSE;\r
6547 }\r
6548 \r
6549 UINT APIENTRY\r
6550 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6551 {\r
6552   static UINT *number;\r
6553   OPENFILENAME *ofname;\r
6554   OFNOTIFY *ofnot;\r
6555   switch (uiMsg) {\r
6556   case WM_INITDIALOG:\r
6557     ofname = (OPENFILENAME *)lParam;\r
6558     number = (UINT *)(ofname->lCustData);\r
6559     break;\r
6560   case WM_NOTIFY:\r
6561     ofnot = (OFNOTIFY *)lParam;\r
6562     if (ofnot->hdr.code == CDN_FILEOK) {\r
6563       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6564     }\r
6565     break;\r
6566   }\r
6567   return 0;\r
6568 }\r
6569 \r
6570 \r
6571 FILE *\r
6572 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6573                char *nameFilt, char *dlgTitle, UINT *number,\r
6574                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6575 {\r
6576   OPENFILENAME openFileName;\r
6577   char buf1[MSG_SIZ];\r
6578   FILE *f;\r
6579 \r
6580   if (fileName == NULL) fileName = buf1;\r
6581   if (defName == NULL) {\r
6582     strcpy(fileName, "*.");\r
6583     strcat(fileName, defExt);\r
6584   } else {\r
6585     strcpy(fileName, defName);\r
6586   }\r
6587   if (fileTitle) strcpy(fileTitle, "");\r
6588   if (number) *number = 0;\r
6589 \r
6590   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6591   openFileName.hwndOwner         = hwnd;\r
6592   openFileName.hInstance         = (HANDLE) hInst;\r
6593   openFileName.lpstrFilter       = nameFilt;\r
6594   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6595   openFileName.nMaxCustFilter    = 0L;\r
6596   openFileName.nFilterIndex      = 1L;\r
6597   openFileName.lpstrFile         = fileName;\r
6598   openFileName.nMaxFile          = MSG_SIZ;\r
6599   openFileName.lpstrFileTitle    = fileTitle;\r
6600   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6601   openFileName.lpstrInitialDir   = NULL;\r
6602   openFileName.lpstrTitle        = dlgTitle;\r
6603   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6604     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6605     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6606     | (oldDialog ? 0 : OFN_EXPLORER);\r
6607   openFileName.nFileOffset       = 0;\r
6608   openFileName.nFileExtension    = 0;\r
6609   openFileName.lpstrDefExt       = defExt;\r
6610   openFileName.lCustData         = (LONG) number;\r
6611   openFileName.lpfnHook          = oldDialog ?\r
6612     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6613   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6614 \r
6615   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6616                         GetOpenFileName(&openFileName)) {\r
6617     /* open the file */\r
6618     f = fopen(openFileName.lpstrFile, write);\r
6619     if (f == NULL) {\r
6620       MessageBox(hwnd, "File open failed", NULL,\r
6621                  MB_OK|MB_ICONEXCLAMATION);\r
6622       return NULL;\r
6623     }\r
6624   } else {\r
6625     int err = CommDlgExtendedError();\r
6626     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6627     return FALSE;\r
6628   }\r
6629   return f;\r
6630 }\r
6631 \r
6632 \r
6633 \r
6634 VOID APIENTRY\r
6635 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6636 {\r
6637   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6638 \r
6639   /*\r
6640    * Get the first pop-up menu in the menu template. This is the\r
6641    * menu that TrackPopupMenu displays.\r
6642    */\r
6643   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6644 \r
6645   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6646 \r
6647   /*\r
6648    * TrackPopup uses screen coordinates, so convert the\r
6649    * coordinates of the mouse click to screen coordinates.\r
6650    */\r
6651   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6652 \r
6653   /* Draw and track the floating pop-up menu. */\r
6654   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6655                  pt.x, pt.y, 0, hwnd, NULL);\r
6656 \r
6657   /* Destroy the menu.*/\r
6658   DestroyMenu(hmenu);\r
6659 }\r
6660    \r
6661 typedef struct {\r
6662   HWND hDlg, hText;\r
6663   int sizeX, sizeY, newSizeX, newSizeY;\r
6664   HDWP hdwp;\r
6665 } ResizeEditPlusButtonsClosure;\r
6666 \r
6667 BOOL CALLBACK\r
6668 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6669 {\r
6670   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6671   RECT rect;\r
6672   POINT pt;\r
6673 \r
6674   if (hChild == cl->hText) return TRUE;\r
6675   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6676   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6677   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6678   ScreenToClient(cl->hDlg, &pt);\r
6679   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6680     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6681   return TRUE;\r
6682 }\r
6683 \r
6684 /* Resize a dialog that has a (rich) edit field filling most of\r
6685    the top, with a row of buttons below */\r
6686 VOID\r
6687 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6688 {\r
6689   RECT rectText;\r
6690   int newTextHeight, newTextWidth;\r
6691   ResizeEditPlusButtonsClosure cl;\r
6692   \r
6693   /*if (IsIconic(hDlg)) return;*/\r
6694   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6695   \r
6696   cl.hdwp = BeginDeferWindowPos(8);\r
6697 \r
6698   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6699   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6700   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6701   if (newTextHeight < 0) {\r
6702     newSizeY += -newTextHeight;\r
6703     newTextHeight = 0;\r
6704   }\r
6705   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6706     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6707 \r
6708   cl.hDlg = hDlg;\r
6709   cl.hText = hText;\r
6710   cl.sizeX = sizeX;\r
6711   cl.sizeY = sizeY;\r
6712   cl.newSizeX = newSizeX;\r
6713   cl.newSizeY = newSizeY;\r
6714   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6715 \r
6716   EndDeferWindowPos(cl.hdwp);\r
6717 }\r
6718 \r
6719 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6720 {\r
6721     RECT    rChild, rParent;\r
6722     int     wChild, hChild, wParent, hParent;\r
6723     int     wScreen, hScreen, xNew, yNew;\r
6724     HDC     hdc;\r
6725 \r
6726     /* Get the Height and Width of the child window */\r
6727     GetWindowRect (hwndChild, &rChild);\r
6728     wChild = rChild.right - rChild.left;\r
6729     hChild = rChild.bottom - rChild.top;\r
6730 \r
6731     /* Get the Height and Width of the parent window */\r
6732     GetWindowRect (hwndParent, &rParent);\r
6733     wParent = rParent.right - rParent.left;\r
6734     hParent = rParent.bottom - rParent.top;\r
6735 \r
6736     /* Get the display limits */\r
6737     hdc = GetDC (hwndChild);\r
6738     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6739     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6740     ReleaseDC(hwndChild, hdc);\r
6741 \r
6742     /* Calculate new X position, then adjust for screen */\r
6743     xNew = rParent.left + ((wParent - wChild) /2);\r
6744     if (xNew < 0) {\r
6745         xNew = 0;\r
6746     } else if ((xNew+wChild) > wScreen) {\r
6747         xNew = wScreen - wChild;\r
6748     }\r
6749 \r
6750     /* Calculate new Y position, then adjust for screen */\r
6751     if( mode == 0 ) {\r
6752         yNew = rParent.top  + ((hParent - hChild) /2);\r
6753     }\r
6754     else {\r
6755         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6756     }\r
6757 \r
6758     if (yNew < 0) {\r
6759         yNew = 0;\r
6760     } else if ((yNew+hChild) > hScreen) {\r
6761         yNew = hScreen - hChild;\r
6762     }\r
6763 \r
6764     /* Set it, and return */\r
6765     return SetWindowPos (hwndChild, NULL,\r
6766                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6767 }\r
6768 \r
6769 /* Center one window over another */\r
6770 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6771 {\r
6772     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6773 }\r
6774 \r
6775 /*---------------------------------------------------------------------------*\\r
6776  *\r
6777  * Startup Dialog functions\r
6778  *\r
6779 \*---------------------------------------------------------------------------*/\r
6780 void\r
6781 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6782 {\r
6783   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6784 \r
6785   while (*cd != NULL) {\r
6786     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6787     cd++;\r
6788   }\r
6789 }\r
6790 \r
6791 void\r
6792 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6793 {\r
6794   char buf1[ARG_MAX];\r
6795   int len;\r
6796 \r
6797   if (str[0] == '@') {\r
6798     FILE* f = fopen(str + 1, "r");\r
6799     if (f == NULL) {\r
6800       DisplayFatalError(str + 1, errno, 2);\r
6801       return;\r
6802     }\r
6803     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6804     fclose(f);\r
6805     buf1[len] = NULLCHAR;\r
6806     str = buf1;\r
6807   }\r
6808 \r
6809   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6810 \r
6811   for (;;) {\r
6812     char buf[MSG_SIZ];\r
6813     char *end = strchr(str, '\n');\r
6814     if (end == NULL) return;\r
6815     memcpy(buf, str, end - str);\r
6816     buf[end - str] = NULLCHAR;\r
6817     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6818     str = end + 1;\r
6819   }\r
6820 }\r
6821 \r
6822 void\r
6823 SetStartupDialogEnables(HWND hDlg)\r
6824 {\r
6825   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6826     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6827     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6828   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6829     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6830   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6831     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6832   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6833     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6834   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6835     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6836     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6837     IsDlgButtonChecked(hDlg, OPT_View));\r
6838 }\r
6839 \r
6840 char *\r
6841 QuoteForFilename(char *filename)\r
6842 {\r
6843   int dquote, space;\r
6844   dquote = strchr(filename, '"') != NULL;\r
6845   space = strchr(filename, ' ') != NULL;\r
6846   if (dquote || space) {\r
6847     if (dquote) {\r
6848       return "'";\r
6849     } else {\r
6850       return "\"";\r
6851     }\r
6852   } else {\r
6853     return "";\r
6854   }\r
6855 }\r
6856 \r
6857 VOID\r
6858 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6859 {\r
6860   char buf[MSG_SIZ];\r
6861   char *q;\r
6862 \r
6863   InitComboStringsFromOption(hwndCombo, nthnames);\r
6864   q = QuoteForFilename(nthcp);\r
6865   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6866   if (*nthdir != NULLCHAR) {\r
6867     q = QuoteForFilename(nthdir);\r
6868     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6869   }\r
6870   if (*nthcp == NULLCHAR) {\r
6871     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6872   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6873     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6874     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6875   }\r
6876 }\r
6877 \r
6878 LRESULT CALLBACK\r
6879 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6880 {\r
6881   char buf[MSG_SIZ];\r
6882   HANDLE hwndCombo;\r
6883   char *p;\r
6884 \r
6885   switch (message) {\r
6886   case WM_INITDIALOG:\r
6887     /* Center the dialog */\r
6888     CenterWindow (hDlg, GetDesktopWindow());\r
6889     /* Initialize the dialog items */\r
6890     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6891                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6892                   firstChessProgramNames);\r
6893     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6894                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6895                   secondChessProgramNames);\r
6896     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6897     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6898     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6899     if (*appData.icsHelper != NULLCHAR) {\r
6900       char *q = QuoteForFilename(appData.icsHelper);\r
6901       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6902     }\r
6903     if (*appData.icsHost == NULLCHAR) {\r
6904       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6905       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6906     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6907       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6908       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6909     }\r
6910 \r
6911     if (appData.icsActive) {\r
6912       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6913     }\r
6914     else if (appData.noChessProgram) {\r
6915       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6916     }\r
6917     else {\r
6918       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6919     }\r
6920 \r
6921     SetStartupDialogEnables(hDlg);\r
6922     return TRUE;\r
6923 \r
6924   case WM_COMMAND:\r
6925     switch (LOWORD(wParam)) {\r
6926     case IDOK:\r
6927       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6928         strcpy(buf, "/fcp=");\r
6929         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6930         p = buf;\r
6931         ParseArgs(StringGet, &p);\r
6932         strcpy(buf, "/scp=");\r
6933         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6934         p = buf;\r
6935         ParseArgs(StringGet, &p);\r
6936         appData.noChessProgram = FALSE;\r
6937         appData.icsActive = FALSE;\r
6938       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6939         strcpy(buf, "/ics /icshost=");\r
6940         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6941         p = buf;\r
6942         ParseArgs(StringGet, &p);\r
6943         if (appData.zippyPlay) {\r
6944           strcpy(buf, "/fcp=");\r
6945           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6946           p = buf;\r
6947           ParseArgs(StringGet, &p);\r
6948         }\r
6949       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6950         appData.noChessProgram = TRUE;\r
6951         appData.icsActive = FALSE;\r
6952       } else {\r
6953         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6954                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6955         return TRUE;\r
6956       }\r
6957       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6958         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6959         p = buf;\r
6960         ParseArgs(StringGet, &p);\r
6961       }\r
6962       EndDialog(hDlg, TRUE);\r
6963       return TRUE;\r
6964 \r
6965     case IDCANCEL:\r
6966       ExitEvent(0);\r
6967       return TRUE;\r
6968 \r
6969     case IDM_HELPCONTENTS:\r
6970       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6971         MessageBox (GetFocus(),\r
6972                     "Unable to activate help",\r
6973                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6974       }\r
6975       break;\r
6976 \r
6977     default:\r
6978       SetStartupDialogEnables(hDlg);\r
6979       break;\r
6980     }\r
6981     break;\r
6982   }\r
6983   return FALSE;\r
6984 }\r
6985 \r
6986 /*---------------------------------------------------------------------------*\\r
6987  *\r
6988  * About box dialog functions\r
6989  *\r
6990 \*---------------------------------------------------------------------------*/\r
6991 \r
6992 /* Process messages for "About" dialog box */\r
6993 LRESULT CALLBACK\r
6994 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6995 {\r
6996   switch (message) {\r
6997   case WM_INITDIALOG: /* message: initialize dialog box */\r
6998     /* Center the dialog over the application window */\r
6999     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7000     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7001     return (TRUE);\r
7002 \r
7003   case WM_COMMAND: /* message: received a command */\r
7004     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7005         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7006       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7007       return (TRUE);\r
7008     }\r
7009     break;\r
7010   }\r
7011   return (FALSE);\r
7012 }\r
7013 \r
7014 /*---------------------------------------------------------------------------*\\r
7015  *\r
7016  * Comment Dialog functions\r
7017  *\r
7018 \*---------------------------------------------------------------------------*/\r
7019 \r
7020 LRESULT CALLBACK\r
7021 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7022 {\r
7023   static HANDLE hwndText = NULL;\r
7024   int len, newSizeX, newSizeY, flags;\r
7025   static int sizeX, sizeY;\r
7026   char *str;\r
7027   RECT rect;\r
7028   MINMAXINFO *mmi;\r
7029 \r
7030   switch (message) {\r
7031   case WM_INITDIALOG: /* message: initialize dialog box */\r
7032     /* Initialize the dialog items */\r
7033     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7034     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7035     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7036     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7037     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7038     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7039     SetWindowText(hDlg, commentTitle);\r
7040     if (editComment) {\r
7041       SetFocus(hwndText);\r
7042     } else {\r
7043       SetFocus(GetDlgItem(hDlg, IDOK));\r
7044     }\r
7045     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7046                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7047                 MAKELPARAM(FALSE, 0));\r
7048     /* Size and position the dialog */\r
7049     if (!commentDialog) {\r
7050       commentDialog = hDlg;\r
7051       flags = SWP_NOZORDER;\r
7052       GetClientRect(hDlg, &rect);\r
7053       sizeX = rect.right;\r
7054       sizeY = rect.bottom;\r
7055       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7056           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7057         WINDOWPLACEMENT wp;\r
7058         EnsureOnScreen(&commentX, &commentY);\r
7059         wp.length = sizeof(WINDOWPLACEMENT);\r
7060         wp.flags = 0;\r
7061         wp.showCmd = SW_SHOW;\r
7062         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7063         wp.rcNormalPosition.left = commentX;\r
7064         wp.rcNormalPosition.right = commentX + commentW;\r
7065         wp.rcNormalPosition.top = commentY;\r
7066         wp.rcNormalPosition.bottom = commentY + commentH;\r
7067         SetWindowPlacement(hDlg, &wp);\r
7068 \r
7069         GetClientRect(hDlg, &rect);\r
7070         newSizeX = rect.right;\r
7071         newSizeY = rect.bottom;\r
7072         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7073                               newSizeX, newSizeY);\r
7074         sizeX = newSizeX;\r
7075         sizeY = newSizeY;\r
7076       }\r
7077     }\r
7078     return FALSE;\r
7079 \r
7080   case WM_COMMAND: /* message: received a command */\r
7081     switch (LOWORD(wParam)) {\r
7082     case IDOK:\r
7083       if (editComment) {\r
7084         char *p, *q;\r
7085         /* Read changed options from the dialog box */\r
7086         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7087         len = GetWindowTextLength(hwndText);\r
7088         str = (char *) malloc(len + 1);\r
7089         GetWindowText(hwndText, str, len + 1);\r
7090         p = q = str;\r
7091         while (*q) {\r
7092           if (*q == '\r')\r
7093             q++;\r
7094           else\r
7095             *p++ = *q++;\r
7096         }\r
7097         *p = NULLCHAR;\r
7098         ReplaceComment(commentIndex, str);\r
7099         free(str);\r
7100       }\r
7101       CommentPopDown();\r
7102       return TRUE;\r
7103 \r
7104     case IDCANCEL:\r
7105     case OPT_CancelComment:\r
7106       CommentPopDown();\r
7107       return TRUE;\r
7108 \r
7109     case OPT_ClearComment:\r
7110       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7111       break;\r
7112 \r
7113     case OPT_EditComment:\r
7114       EditCommentEvent();\r
7115       return TRUE;\r
7116 \r
7117     default:\r
7118       break;\r
7119     }\r
7120     break;\r
7121 \r
7122   case WM_SIZE:\r
7123     newSizeX = LOWORD(lParam);\r
7124     newSizeY = HIWORD(lParam);\r
7125     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7126     sizeX = newSizeX;\r
7127     sizeY = newSizeY;\r
7128     break;\r
7129 \r
7130   case WM_GETMINMAXINFO:\r
7131     /* Prevent resizing window too small */\r
7132     mmi = (MINMAXINFO *) lParam;\r
7133     mmi->ptMinTrackSize.x = 100;\r
7134     mmi->ptMinTrackSize.y = 100;\r
7135     break;\r
7136   }\r
7137   return FALSE;\r
7138 }\r
7139 \r
7140 VOID\r
7141 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7142 {\r
7143   FARPROC lpProc;\r
7144   char *p, *q;\r
7145 \r
7146   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7147 \r
7148   if (str == NULL) str = "";\r
7149   p = (char *) malloc(2 * strlen(str) + 2);\r
7150   q = p;\r
7151   while (*str) {\r
7152     if (*str == '\n') *q++ = '\r';\r
7153     *q++ = *str++;\r
7154   }\r
7155   *q = NULLCHAR;\r
7156   if (commentText != NULL) free(commentText);\r
7157 \r
7158   commentIndex = index;\r
7159   commentTitle = title;\r
7160   commentText = p;\r
7161   editComment = edit;\r
7162 \r
7163   if (commentDialog) {\r
7164     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7165     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7166   } else {\r
7167     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7168     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7169                  hwndMain, (DLGPROC)lpProc);\r
7170     FreeProcInstance(lpProc);\r
7171   }\r
7172   commentDialogUp = TRUE;\r
7173 }\r
7174 \r
7175 \r
7176 /*---------------------------------------------------------------------------*\\r
7177  *\r
7178  * Type-in move dialog functions\r
7179  * \r
7180 \*---------------------------------------------------------------------------*/\r
7181 \r
7182 LRESULT CALLBACK\r
7183 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7184 {\r
7185   char move[MSG_SIZ];\r
7186   HWND hInput;\r
7187   ChessMove moveType;\r
7188   int fromX, fromY, toX, toY;\r
7189   char promoChar;\r
7190 \r
7191   switch (message) {\r
7192   case WM_INITDIALOG:\r
7193     move[0] = (char) lParam;\r
7194     move[1] = NULLCHAR;\r
7195     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7196     hInput = GetDlgItem(hDlg, OPT_Move);\r
7197     SetWindowText(hInput, move);\r
7198     SetFocus(hInput);\r
7199     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7200     return FALSE;\r
7201 \r
7202   case WM_COMMAND:\r
7203     switch (LOWORD(wParam)) {\r
7204     case IDOK:\r
7205       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7206         gameMode != Training) {\r
7207         DisplayMoveError("Displayed move is not current");\r
7208       } else {\r
7209         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7210         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7211           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7212           if (gameMode != Training)\r
7213               forwardMostMove = currentMove;\r
7214           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7215         } else {\r
7216           DisplayMoveError("Could not parse move");\r
7217         }\r
7218       }\r
7219       EndDialog(hDlg, TRUE);\r
7220       return TRUE;\r
7221     case IDCANCEL:\r
7222       EndDialog(hDlg, FALSE);\r
7223       return TRUE;\r
7224     default:\r
7225       break;\r
7226     }\r
7227     break;\r
7228   }\r
7229   return FALSE;\r
7230 }\r
7231 \r
7232 VOID\r
7233 PopUpMoveDialog(char firstchar)\r
7234 {\r
7235     FARPROC lpProc;\r
7236     \r
7237     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7238         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7239         gameMode == AnalyzeMode || gameMode == EditGame || \r
7240         gameMode == EditPosition || gameMode == IcsExamining ||\r
7241         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7242         gameMode == Training) {\r
7243       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7244       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7245         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7246       FreeProcInstance(lpProc);\r
7247     }\r
7248 }\r
7249 \r
7250 /*---------------------------------------------------------------------------*\\r
7251  *\r
7252  * Type-in name dialog functions\r
7253  * \r
7254 \*---------------------------------------------------------------------------*/\r
7255 \r
7256 LRESULT CALLBACK\r
7257 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7258 {\r
7259   char move[MSG_SIZ];\r
7260   HWND hInput;\r
7261 \r
7262   switch (message) {\r
7263   case WM_INITDIALOG:\r
7264     move[0] = (char) lParam;\r
7265     move[1] = NULLCHAR;\r
7266     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7267     hInput = GetDlgItem(hDlg, OPT_Name);\r
7268     SetWindowText(hInput, move);\r
7269     SetFocus(hInput);\r
7270     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7271     return FALSE;\r
7272 \r
7273   case WM_COMMAND:\r
7274     switch (LOWORD(wParam)) {\r
7275     case IDOK:\r
7276       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7277       appData.userName = strdup(move);\r
7278 \r
7279       EndDialog(hDlg, TRUE);\r
7280       return TRUE;\r
7281     case IDCANCEL:\r
7282       EndDialog(hDlg, FALSE);\r
7283       return TRUE;\r
7284     default:\r
7285       break;\r
7286     }\r
7287     break;\r
7288   }\r
7289   return FALSE;\r
7290 }\r
7291 \r
7292 VOID\r
7293 PopUpNameDialog(char firstchar)\r
7294 {\r
7295     FARPROC lpProc;\r
7296     \r
7297       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7298       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7299         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7300       FreeProcInstance(lpProc);\r
7301 }\r
7302 \r
7303 /*---------------------------------------------------------------------------*\\r
7304  *\r
7305  *  Error dialogs\r
7306  * \r
7307 \*---------------------------------------------------------------------------*/\r
7308 \r
7309 /* Nonmodal error box */\r
7310 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7311                              WPARAM wParam, LPARAM lParam);\r
7312 \r
7313 VOID\r
7314 ErrorPopUp(char *title, char *content)\r
7315 {\r
7316   FARPROC lpProc;\r
7317   char *p, *q;\r
7318   BOOLEAN modal = hwndMain == NULL;\r
7319 \r
7320   p = content;\r
7321   q = errorMessage;\r
7322   while (*p) {\r
7323     if (*p == '\n') {\r
7324       if (modal) {\r
7325         *q++ = ' ';\r
7326         p++;\r
7327       } else {\r
7328         *q++ = '\r';\r
7329         *q++ = *p++;\r
7330       }\r
7331     } else {\r
7332       *q++ = *p++;\r
7333     }\r
7334   }\r
7335   *q = NULLCHAR;\r
7336   strncpy(errorTitle, title, sizeof(errorTitle));\r
7337   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7338   \r
7339   if (modal) {\r
7340     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7341   } else {\r
7342     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7343     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7344                  hwndMain, (DLGPROC)lpProc);\r
7345     FreeProcInstance(lpProc);\r
7346   }\r
7347 }\r
7348 \r
7349 VOID\r
7350 ErrorPopDown()\r
7351 {\r
7352   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7353   if (errorDialog == NULL) return;\r
7354   DestroyWindow(errorDialog);\r
7355   errorDialog = NULL;\r
7356 }\r
7357 \r
7358 LRESULT CALLBACK\r
7359 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7360 {\r
7361   HANDLE hwndText;\r
7362   RECT rChild;\r
7363 \r
7364   switch (message) {\r
7365   case WM_INITDIALOG:\r
7366     GetWindowRect(hDlg, &rChild);\r
7367 \r
7368     /*\r
7369     SetWindowPos(hDlg, NULL, rChild.left,\r
7370       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7371       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7372     */\r
7373 \r
7374     /* \r
7375         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7376         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7377         and it doesn't work when you resize the dialog.\r
7378         For now, just give it a default position.\r
7379     */\r
7380     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7381 \r
7382     errorDialog = hDlg;\r
7383     SetWindowText(hDlg, errorTitle);\r
7384     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7385     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7386     return FALSE;\r
7387 \r
7388   case WM_COMMAND:\r
7389     switch (LOWORD(wParam)) {\r
7390     case IDOK:\r
7391     case IDCANCEL:\r
7392       if (errorDialog == hDlg) errorDialog = NULL;\r
7393       DestroyWindow(hDlg);\r
7394       return TRUE;\r
7395 \r
7396     default:\r
7397       break;\r
7398     }\r
7399     break;\r
7400   }\r
7401   return FALSE;\r
7402 }\r
7403 \r
7404 #ifdef GOTHIC\r
7405 HWND gothicDialog = NULL;\r
7406 \r
7407 LRESULT CALLBACK\r
7408 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7409 {\r
7410   HANDLE hwndText;\r
7411   RECT rChild;\r
7412   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7413 \r
7414   switch (message) {\r
7415   case WM_INITDIALOG:\r
7416     GetWindowRect(hDlg, &rChild);\r
7417 \r
7418     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7419                                                              SWP_NOZORDER);\r
7420 \r
7421     /* \r
7422         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7423         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7424         and it doesn't work when you resize the dialog.\r
7425         For now, just give it a default position.\r
7426     */\r
7427     gothicDialog = hDlg;\r
7428     SetWindowText(hDlg, errorTitle);\r
7429     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7430     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7431     return FALSE;\r
7432 \r
7433   case WM_COMMAND:\r
7434     switch (LOWORD(wParam)) {\r
7435     case IDOK:\r
7436     case IDCANCEL:\r
7437       if (errorDialog == hDlg) errorDialog = NULL;\r
7438       DestroyWindow(hDlg);\r
7439       return TRUE;\r
7440 \r
7441     default:\r
7442       break;\r
7443     }\r
7444     break;\r
7445   }\r
7446   return FALSE;\r
7447 }\r
7448 \r
7449 VOID\r
7450 GothicPopUp(char *title, VariantClass variant)\r
7451 {\r
7452   FARPROC lpProc;\r
7453   static char *lastTitle;\r
7454 \r
7455   strncpy(errorTitle, title, sizeof(errorTitle));\r
7456   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7457 \r
7458   if(lastTitle != title && gothicDialog != NULL) {\r
7459     DestroyWindow(gothicDialog);\r
7460     gothicDialog = NULL;\r
7461   }\r
7462   if(variant != VariantNormal && gothicDialog == NULL) {\r
7463     title = lastTitle;\r
7464     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7465     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7466                  hwndMain, (DLGPROC)lpProc);\r
7467     FreeProcInstance(lpProc);\r
7468   }\r
7469 }\r
7470 #endif\r
7471 \r
7472 /*---------------------------------------------------------------------------*\\r
7473  *\r
7474  *  Ics Interaction console functions\r
7475  *\r
7476 \*---------------------------------------------------------------------------*/\r
7477 \r
7478 #define HISTORY_SIZE 64\r
7479 static char *history[HISTORY_SIZE];\r
7480 int histIn = 0, histP = 0;\r
7481 \r
7482 VOID\r
7483 SaveInHistory(char *cmd)\r
7484 {\r
7485   if (history[histIn] != NULL) {\r
7486     free(history[histIn]);\r
7487     history[histIn] = NULL;\r
7488   }\r
7489   if (*cmd == NULLCHAR) return;\r
7490   history[histIn] = StrSave(cmd);\r
7491   histIn = (histIn + 1) % HISTORY_SIZE;\r
7492   if (history[histIn] != NULL) {\r
7493     free(history[histIn]);\r
7494     history[histIn] = NULL;\r
7495   }\r
7496   histP = histIn;\r
7497 }\r
7498 \r
7499 char *\r
7500 PrevInHistory(char *cmd)\r
7501 {\r
7502   int newhp;\r
7503   if (histP == histIn) {\r
7504     if (history[histIn] != NULL) free(history[histIn]);\r
7505     history[histIn] = StrSave(cmd);\r
7506   }\r
7507   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7508   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7509   histP = newhp;\r
7510   return history[histP];\r
7511 }\r
7512 \r
7513 char *\r
7514 NextInHistory()\r
7515 {\r
7516   if (histP == histIn) return NULL;\r
7517   histP = (histP + 1) % HISTORY_SIZE;\r
7518   return history[histP];\r
7519 }\r
7520 \r
7521 typedef struct {\r
7522   char *item;\r
7523   char *command;\r
7524   BOOLEAN getname;\r
7525   BOOLEAN immediate;\r
7526 } IcsTextMenuEntry;\r
7527 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7528 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7529 \r
7530 void\r
7531 ParseIcsTextMenu(char *icsTextMenuString)\r
7532 {\r
7533 //  int flags = 0;\r
7534   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7535   char *p = icsTextMenuString;\r
7536   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7537     free(e->item);\r
7538     e->item = NULL;\r
7539     if (e->command != NULL) {\r
7540       free(e->command);\r
7541       e->command = NULL;\r
7542     }\r
7543     e++;\r
7544   }\r
7545   e = icsTextMenuEntry;\r
7546   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7547     if (*p == ';' || *p == '\n') {\r
7548       e->item = strdup("-");\r
7549       e->command = NULL;\r
7550       p++;\r
7551     } else if (*p == '-') {\r
7552       e->item = strdup("-");\r
7553       e->command = NULL;\r
7554       p++;\r
7555       if (*p) p++;\r
7556     } else {\r
7557       char *q, *r, *s, *t;\r
7558       char c;\r
7559       q = strchr(p, ',');\r
7560       if (q == NULL) break;\r
7561       *q = NULLCHAR;\r
7562       r = strchr(q + 1, ',');\r
7563       if (r == NULL) break;\r
7564       *r = NULLCHAR;\r
7565       s = strchr(r + 1, ',');\r
7566       if (s == NULL) break;\r
7567       *s = NULLCHAR;\r
7568       c = ';';\r
7569       t = strchr(s + 1, c);\r
7570       if (t == NULL) {\r
7571         c = '\n';\r
7572         t = strchr(s + 1, c);\r
7573       }\r
7574       if (t != NULL) *t = NULLCHAR;\r
7575       e->item = strdup(p);\r
7576       e->command = strdup(q + 1);\r
7577       e->getname = *(r + 1) != '0';\r
7578       e->immediate = *(s + 1) != '0';\r
7579       *q = ',';\r
7580       *r = ',';\r
7581       *s = ',';\r
7582       if (t == NULL) break;\r
7583       *t = c;\r
7584       p = t + 1;\r
7585     }\r
7586     e++;\r
7587   } \r
7588 }\r
7589 \r
7590 HMENU\r
7591 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7592 {\r
7593   HMENU hmenu, h;\r
7594   int i = 0;\r
7595   hmenu = LoadMenu(hInst, "TextMenu");\r
7596   h = GetSubMenu(hmenu, 0);\r
7597   while (e->item) {\r
7598     if (strcmp(e->item, "-") == 0) {\r
7599       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7600     } else {\r
7601       if (e->item[0] == '|') {\r
7602         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7603                    IDM_CommandX + i, &e->item[1]);\r
7604       } else {\r
7605         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7606       }\r
7607     }\r
7608     e++;\r
7609     i++;\r
7610   } \r
7611   return hmenu;\r
7612 }\r
7613 \r
7614 WNDPROC consoleTextWindowProc;\r
7615 \r
7616 void\r
7617 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7618 {\r
7619   char buf[MSG_SIZ], name[MSG_SIZ];\r
7620   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7621   CHARRANGE sel;\r
7622 \r
7623   if (!getname) {\r
7624     SetWindowText(hInput, command);\r
7625     if (immediate) {\r
7626       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7627     } else {\r
7628       sel.cpMin = 999999;\r
7629       sel.cpMax = 999999;\r
7630       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7631       SetFocus(hInput);\r
7632     }\r
7633     return;\r
7634   }    \r
7635   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7636   if (sel.cpMin == sel.cpMax) {\r
7637     /* Expand to surrounding word */\r
7638     TEXTRANGE tr;\r
7639     do {\r
7640       tr.chrg.cpMax = sel.cpMin;\r
7641       tr.chrg.cpMin = --sel.cpMin;\r
7642       if (sel.cpMin < 0) break;\r
7643       tr.lpstrText = name;\r
7644       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7645     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7646     sel.cpMin++;\r
7647 \r
7648     do {\r
7649       tr.chrg.cpMin = sel.cpMax;\r
7650       tr.chrg.cpMax = ++sel.cpMax;\r
7651       tr.lpstrText = name;\r
7652       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7653     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7654     sel.cpMax--;\r
7655 \r
7656     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7657       MessageBeep(MB_ICONEXCLAMATION);\r
7658       return;\r
7659     }\r
7660     tr.chrg = sel;\r
7661     tr.lpstrText = name;\r
7662     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7663   } else {\r
7664     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7665       MessageBeep(MB_ICONEXCLAMATION);\r
7666       return;\r
7667     }\r
7668     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7669   }\r
7670   if (immediate) {\r
7671     sprintf(buf, "%s %s", command, name);\r
7672     SetWindowText(hInput, buf);\r
7673     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7674   } else {\r
7675     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7676     SetWindowText(hInput, buf);\r
7677     sel.cpMin = 999999;\r
7678     sel.cpMax = 999999;\r
7679     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7680     SetFocus(hInput);\r
7681   }\r
7682 }\r
7683 \r
7684 LRESULT CALLBACK \r
7685 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7686 {\r
7687   HWND hInput;\r
7688   CHARRANGE sel;\r
7689 \r
7690   switch (message) {\r
7691   case WM_KEYDOWN:\r
7692     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7693     switch (wParam) {\r
7694     case VK_PRIOR:\r
7695       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7696       return 0;\r
7697     case VK_NEXT:\r
7698       sel.cpMin = 999999;\r
7699       sel.cpMax = 999999;\r
7700       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7701       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7702       return 0;\r
7703     }\r
7704     break;\r
7705   case WM_CHAR:\r
7706     if (wParam == '\t') {\r
7707       if (GetKeyState(VK_SHIFT) < 0) {\r
7708         /* shifted */\r
7709         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7710         if (buttonDesc[0].hwnd) {\r
7711           SetFocus(buttonDesc[0].hwnd);\r
7712         } else {\r
7713           SetFocus(hwndMain);\r
7714         }\r
7715       } else {\r
7716         /* unshifted */\r
7717         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7718       }\r
7719     } else {\r
7720       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7721       SetFocus(hInput);\r
7722       SendMessage(hInput, message, wParam, lParam);\r
7723     }\r
7724     return 0;\r
7725   case WM_PASTE:\r
7726     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7727     SetFocus(hInput);\r
7728     return SendMessage(hInput, message, wParam, lParam);\r
7729   case WM_MBUTTONDOWN:\r
7730     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7731   case WM_RBUTTONDOWN:\r
7732     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7733       /* Move selection here if it was empty */\r
7734       POINT pt;\r
7735       pt.x = LOWORD(lParam);\r
7736       pt.y = HIWORD(lParam);\r
7737       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7738       if (sel.cpMin == sel.cpMax) {\r
7739         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7740         sel.cpMax = sel.cpMin;\r
7741         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7742       }\r
7743       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7744     }\r
7745     return 0;\r
7746   case WM_RBUTTONUP:\r
7747     if (GetKeyState(VK_SHIFT) & ~1) {\r
7748       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7749         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7750     } else {\r
7751       POINT pt;\r
7752       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7753       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7754       if (sel.cpMin == sel.cpMax) {\r
7755         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7756         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7757       }\r
7758       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7759         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7760       }\r
7761       pt.x = LOWORD(lParam);\r
7762       pt.y = HIWORD(lParam);\r
7763       MenuPopup(hwnd, pt, hmenu, -1);\r
7764     }\r
7765     return 0;\r
7766   case WM_COMMAND:\r
7767     switch (LOWORD(wParam)) {\r
7768     case IDM_QuickPaste:\r
7769       {\r
7770         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7771         if (sel.cpMin == sel.cpMax) {\r
7772           MessageBeep(MB_ICONEXCLAMATION);\r
7773           return 0;\r
7774         }\r
7775         SendMessage(hwnd, WM_COPY, 0, 0);\r
7776         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7777         SendMessage(hInput, WM_PASTE, 0, 0);\r
7778         SetFocus(hInput);\r
7779         return 0;\r
7780       }\r
7781     case IDM_Cut:\r
7782       SendMessage(hwnd, WM_CUT, 0, 0);\r
7783       return 0;\r
7784     case IDM_Paste:\r
7785       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7786       return 0;\r
7787     case IDM_Copy:\r
7788       SendMessage(hwnd, WM_COPY, 0, 0);\r
7789       return 0;\r
7790     default:\r
7791       {\r
7792         int i = LOWORD(wParam) - IDM_CommandX;\r
7793         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7794             icsTextMenuEntry[i].command != NULL) {\r
7795           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7796                    icsTextMenuEntry[i].getname,\r
7797                    icsTextMenuEntry[i].immediate);\r
7798           return 0;\r
7799         }\r
7800       }\r
7801       break;\r
7802     }\r
7803     break;\r
7804   }\r
7805   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7806 }\r
7807 \r
7808 WNDPROC consoleInputWindowProc;\r
7809 \r
7810 LRESULT CALLBACK\r
7811 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7812 {\r
7813   char buf[MSG_SIZ];\r
7814   char *p;\r
7815   static BOOL sendNextChar = FALSE;\r
7816   static BOOL quoteNextChar = FALSE;\r
7817   InputSource *is = consoleInputSource;\r
7818   CHARFORMAT cf;\r
7819   CHARRANGE sel;\r
7820 \r
7821   switch (message) {\r
7822   case WM_CHAR:\r
7823     if (!appData.localLineEditing || sendNextChar) {\r
7824       is->buf[0] = (CHAR) wParam;\r
7825       is->count = 1;\r
7826       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7827       sendNextChar = FALSE;\r
7828       return 0;\r
7829     }\r
7830     if (quoteNextChar) {\r
7831       buf[0] = (char) wParam;\r
7832       buf[1] = NULLCHAR;\r
7833       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7834       quoteNextChar = FALSE;\r
7835       return 0;\r
7836     }\r
7837     switch (wParam) {\r
7838     case '\r':   /* Enter key */\r
7839       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7840       if (consoleEcho) SaveInHistory(is->buf);\r
7841       is->buf[is->count++] = '\n';\r
7842       is->buf[is->count] = NULLCHAR;\r
7843       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7844       if (consoleEcho) {\r
7845         ConsoleOutput(is->buf, is->count, TRUE);\r
7846       } else if (appData.localLineEditing) {\r
7847         ConsoleOutput("\n", 1, TRUE);\r
7848       }\r
7849       /* fall thru */\r
7850     case '\033': /* Escape key */\r
7851       SetWindowText(hwnd, "");\r
7852       cf.cbSize = sizeof(CHARFORMAT);\r
7853       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7854       if (consoleEcho) {\r
7855         cf.crTextColor = textAttribs[ColorNormal].color;\r
7856       } else {\r
7857         cf.crTextColor = COLOR_ECHOOFF;\r
7858       }\r
7859       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7860       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7861       return 0;\r
7862     case '\t':   /* Tab key */\r
7863       if (GetKeyState(VK_SHIFT) < 0) {\r
7864         /* shifted */\r
7865         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7866       } else {\r
7867         /* unshifted */\r
7868         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7869         if (buttonDesc[0].hwnd) {\r
7870           SetFocus(buttonDesc[0].hwnd);\r
7871         } else {\r
7872           SetFocus(hwndMain);\r
7873         }\r
7874       }\r
7875       return 0;\r
7876     case '\023': /* Ctrl+S */\r
7877       sendNextChar = TRUE;\r
7878       return 0;\r
7879     case '\021': /* Ctrl+Q */\r
7880       quoteNextChar = TRUE;\r
7881       return 0;\r
7882     default:\r
7883       break;\r
7884     }\r
7885     break;\r
7886   case WM_KEYDOWN:\r
7887     switch (wParam) {\r
7888     case VK_UP:\r
7889       GetWindowText(hwnd, buf, MSG_SIZ);\r
7890       p = PrevInHistory(buf);\r
7891       if (p != NULL) {\r
7892         SetWindowText(hwnd, p);\r
7893         sel.cpMin = 999999;\r
7894         sel.cpMax = 999999;\r
7895         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7896         return 0;\r
7897       }\r
7898       break;\r
7899     case VK_DOWN:\r
7900       p = NextInHistory();\r
7901       if (p != NULL) {\r
7902         SetWindowText(hwnd, p);\r
7903         sel.cpMin = 999999;\r
7904         sel.cpMax = 999999;\r
7905         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7906         return 0;\r
7907       }\r
7908       break;\r
7909     case VK_HOME:\r
7910     case VK_END:\r
7911       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7912       /* fall thru */\r
7913     case VK_PRIOR:\r
7914     case VK_NEXT:\r
7915       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7916       return 0;\r
7917     }\r
7918     break;\r
7919   case WM_MBUTTONDOWN:\r
7920     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7921       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7922     break;\r
7923   case WM_RBUTTONUP:\r
7924     if (GetKeyState(VK_SHIFT) & ~1) {\r
7925       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7926         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7927     } else {\r
7928       POINT pt;\r
7929       HMENU hmenu;\r
7930       hmenu = LoadMenu(hInst, "InputMenu");\r
7931       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7932       if (sel.cpMin == sel.cpMax) {\r
7933         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7934         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7935       }\r
7936       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7937         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7938       }\r
7939       pt.x = LOWORD(lParam);\r
7940       pt.y = HIWORD(lParam);\r
7941       MenuPopup(hwnd, pt, hmenu, -1);\r
7942     }\r
7943     return 0;\r
7944   case WM_COMMAND:\r
7945     switch (LOWORD(wParam)) { \r
7946     case IDM_Undo:\r
7947       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7948       return 0;\r
7949     case IDM_SelectAll:\r
7950       sel.cpMin = 0;\r
7951       sel.cpMax = -1; /*999999?*/\r
7952       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7953       return 0;\r
7954     case IDM_Cut:\r
7955       SendMessage(hwnd, WM_CUT, 0, 0);\r
7956       return 0;\r
7957     case IDM_Paste:\r
7958       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7959       return 0;\r
7960     case IDM_Copy:\r
7961       SendMessage(hwnd, WM_COPY, 0, 0);\r
7962       return 0;\r
7963     }\r
7964     break;\r
7965   }\r
7966   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7967 }\r
7968 \r
7969 #define CO_MAX  100000\r
7970 #define CO_TRIM   1000\r
7971 \r
7972 LRESULT CALLBACK\r
7973 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7974 {\r
7975   static SnapData sd;\r
7976   static HWND hText, hInput /*, hFocus*/;\r
7977 //  InputSource *is = consoleInputSource;\r
7978   RECT rect;\r
7979   static int sizeX, sizeY;\r
7980   int newSizeX, newSizeY;\r
7981   MINMAXINFO *mmi;\r
7982 \r
7983   switch (message) {\r
7984   case WM_INITDIALOG: /* message: initialize dialog box */\r
7985     hwndConsole = hDlg;\r
7986     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7987     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7988     SetFocus(hInput);\r
7989     consoleTextWindowProc = (WNDPROC)\r
7990       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7991     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7992     consoleInputWindowProc = (WNDPROC)\r
7993       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7994     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7995     Colorize(ColorNormal, TRUE);\r
7996     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7997     ChangedConsoleFont();\r
7998     GetClientRect(hDlg, &rect);\r
7999     sizeX = rect.right;\r
8000     sizeY = rect.bottom;\r
8001     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8002         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8003       WINDOWPLACEMENT wp;\r
8004       EnsureOnScreen(&consoleX, &consoleY);\r
8005       wp.length = sizeof(WINDOWPLACEMENT);\r
8006       wp.flags = 0;\r
8007       wp.showCmd = SW_SHOW;\r
8008       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8009       wp.rcNormalPosition.left = consoleX;\r
8010       wp.rcNormalPosition.right = consoleX + consoleW;\r
8011       wp.rcNormalPosition.top = consoleY;\r
8012       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8013       SetWindowPlacement(hDlg, &wp);\r
8014     }\r
8015 #if 0 \r
8016    // [HGM] Chessknight's change 2004-07-13\r
8017    else { /* Determine Defaults */\r
8018        WINDOWPLACEMENT wp;\r
8019        consoleX = winWidth + 1;\r
8020        consoleY = boardY;\r
8021        consoleW = screenWidth -  winWidth;\r
8022        consoleH = winHeight;\r
8023        EnsureOnScreen(&consoleX, &consoleY);\r
8024        wp.length = sizeof(WINDOWPLACEMENT);\r
8025        wp.flags = 0;\r
8026        wp.showCmd = SW_SHOW;\r
8027        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8028        wp.rcNormalPosition.left = consoleX;\r
8029        wp.rcNormalPosition.right = consoleX + consoleW;\r
8030        wp.rcNormalPosition.top = consoleY;\r
8031        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8032        SetWindowPlacement(hDlg, &wp);\r
8033     }\r
8034 #endif\r
8035     return FALSE;\r
8036 \r
8037   case WM_SETFOCUS:\r
8038     SetFocus(hInput);\r
8039     return 0;\r
8040 \r
8041   case WM_CLOSE:\r
8042     ExitEvent(0);\r
8043     /* not reached */\r
8044     break;\r
8045 \r
8046   case WM_SIZE:\r
8047     if (IsIconic(hDlg)) break;\r
8048     newSizeX = LOWORD(lParam);\r
8049     newSizeY = HIWORD(lParam);\r
8050     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8051       RECT rectText, rectInput;\r
8052       POINT pt;\r
8053       int newTextHeight, newTextWidth;\r
8054       GetWindowRect(hText, &rectText);\r
8055       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8056       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8057       if (newTextHeight < 0) {\r
8058         newSizeY += -newTextHeight;\r
8059         newTextHeight = 0;\r
8060       }\r
8061       SetWindowPos(hText, NULL, 0, 0,\r
8062         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8063       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8064       pt.x = rectInput.left;\r
8065       pt.y = rectInput.top + newSizeY - sizeY;\r
8066       ScreenToClient(hDlg, &pt);\r
8067       SetWindowPos(hInput, NULL, \r
8068         pt.x, pt.y, /* needs client coords */   \r
8069         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8070         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8071     }\r
8072     sizeX = newSizeX;\r
8073     sizeY = newSizeY;\r
8074     break;\r
8075 \r
8076   case WM_GETMINMAXINFO:\r
8077     /* Prevent resizing window too small */\r
8078     mmi = (MINMAXINFO *) lParam;\r
8079     mmi->ptMinTrackSize.x = 100;\r
8080     mmi->ptMinTrackSize.y = 100;\r
8081     break;\r
8082 \r
8083   /* [AS] Snapping */\r
8084   case WM_ENTERSIZEMOVE:\r
8085     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8086 \r
8087   case WM_SIZING:\r
8088     return OnSizing( &sd, hDlg, wParam, lParam );\r
8089 \r
8090   case WM_MOVING:\r
8091     return OnMoving( &sd, hDlg, wParam, lParam );\r
8092 \r
8093   case WM_EXITSIZEMOVE:\r
8094     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8095   }\r
8096 \r
8097   return DefWindowProc(hDlg, message, wParam, lParam);\r
8098 }\r
8099 \r
8100 \r
8101 VOID\r
8102 ConsoleCreate()\r
8103 {\r
8104   HWND hCons;\r
8105   if (hwndConsole) return;\r
8106   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8107   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8108 }\r
8109 \r
8110 \r
8111 VOID\r
8112 ConsoleOutput(char* data, int length, int forceVisible)\r
8113 {\r
8114   HWND hText;\r
8115   int trim, exlen;\r
8116   char *p, *q;\r
8117   char buf[CO_MAX+1];\r
8118   POINT pEnd;\r
8119   RECT rect;\r
8120   static int delayLF = 0;\r
8121   CHARRANGE savesel, sel;\r
8122 \r
8123   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8124   p = data;\r
8125   q = buf;\r
8126   if (delayLF) {\r
8127     *q++ = '\r';\r
8128     *q++ = '\n';\r
8129     delayLF = 0;\r
8130   }\r
8131   while (length--) {\r
8132     if (*p == '\n') {\r
8133       if (*++p) {\r
8134         *q++ = '\r';\r
8135         *q++ = '\n';\r
8136       } else {\r
8137         delayLF = 1;\r
8138       }\r
8139     } else if (*p == '\007') {\r
8140        MyPlaySound(&sounds[(int)SoundBell]);\r
8141        p++;\r
8142     } else {\r
8143       *q++ = *p++;\r
8144     }\r
8145   }\r
8146   *q = NULLCHAR;\r
8147   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8148   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8149   /* Save current selection */\r
8150   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8151   exlen = GetWindowTextLength(hText);\r
8152   /* Find out whether current end of text is visible */\r
8153   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8154   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8155   /* Trim existing text if it's too long */\r
8156   if (exlen + (q - buf) > CO_MAX) {\r
8157     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8158     sel.cpMin = 0;\r
8159     sel.cpMax = trim;\r
8160     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8161     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8162     exlen -= trim;\r
8163     savesel.cpMin -= trim;\r
8164     savesel.cpMax -= trim;\r
8165     if (exlen < 0) exlen = 0;\r
8166     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8167     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8168   }\r
8169   /* Append the new text */\r
8170   sel.cpMin = exlen;\r
8171   sel.cpMax = exlen;\r
8172   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8173   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8174   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8175   if (forceVisible || exlen == 0 ||\r
8176       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8177        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8178     /* Scroll to make new end of text visible if old end of text\r
8179        was visible or new text is an echo of user typein */\r
8180     sel.cpMin = 9999999;\r
8181     sel.cpMax = 9999999;\r
8182     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8183     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8184     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8185     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8186   }\r
8187   if (savesel.cpMax == exlen || forceVisible) {\r
8188     /* Move insert point to new end of text if it was at the old\r
8189        end of text or if the new text is an echo of user typein */\r
8190     sel.cpMin = 9999999;\r
8191     sel.cpMax = 9999999;\r
8192     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8193   } else {\r
8194     /* Restore previous selection */\r
8195     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8196   }\r
8197   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8198 }\r
8199 \r
8200 /*---------*/\r
8201 \r
8202 \r
8203 void\r
8204 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8205 {\r
8206   char buf[100];\r
8207   char *str;\r
8208   COLORREF oldFg, oldBg;\r
8209   HFONT oldFont;\r
8210   RECT rect;\r
8211 \r
8212   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8213 \r
8214   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8215   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8216   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8217 \r
8218   rect.left = x;\r
8219   rect.right = x + squareSize;\r
8220   rect.top  = y;\r
8221   rect.bottom = y + squareSize;\r
8222   str = buf;\r
8223 \r
8224   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8225                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8226              y, ETO_CLIPPED|ETO_OPAQUE,\r
8227              &rect, str, strlen(str), NULL);\r
8228 \r
8229   (void) SetTextColor(hdc, oldFg);\r
8230   (void) SetBkColor(hdc, oldBg);\r
8231   (void) SelectObject(hdc, oldFont);\r
8232 }\r
8233 \r
8234 void\r
8235 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8236               RECT *rect, char *color, char *flagFell)\r
8237 {\r
8238   char buf[100];\r
8239   char *str;\r
8240   COLORREF oldFg, oldBg;\r
8241   HFONT oldFont;\r
8242 \r
8243   if (appData.clockMode) {\r
8244     if (tinyLayout)\r
8245       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8246     else\r
8247       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8248     str = buf;\r
8249   } else {\r
8250     str = color;\r
8251   }\r
8252 \r
8253   if (highlight) {\r
8254     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8255     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8256   } else {\r
8257     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8258     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8259   }\r
8260   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8261 \r
8262   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8263              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8264              rect, str, strlen(str), NULL);\r
8265 \r
8266   (void) SetTextColor(hdc, oldFg);\r
8267   (void) SetBkColor(hdc, oldBg);\r
8268   (void) SelectObject(hdc, oldFont);\r
8269 }\r
8270 \r
8271 \r
8272 int\r
8273 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8274            OVERLAPPED *ovl)\r
8275 {\r
8276   int ok, err;\r
8277 \r
8278   /* [AS]  */\r
8279   if( count <= 0 ) {\r
8280     if (appData.debugMode) {\r
8281       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8282     }\r
8283 \r
8284     return ERROR_INVALID_USER_BUFFER;\r
8285   }\r
8286 \r
8287   ResetEvent(ovl->hEvent);\r
8288   ovl->Offset = ovl->OffsetHigh = 0;\r
8289   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8290   if (ok) {\r
8291     err = NO_ERROR;\r
8292   } else {\r
8293     err = GetLastError();\r
8294     if (err == ERROR_IO_PENDING) {\r
8295       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8296       if (ok)\r
8297         err = NO_ERROR;\r
8298       else\r
8299         err = GetLastError();\r
8300     }\r
8301   }\r
8302   return err;\r
8303 }\r
8304 \r
8305 int\r
8306 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8307             OVERLAPPED *ovl)\r
8308 {\r
8309   int ok, err;\r
8310 \r
8311   ResetEvent(ovl->hEvent);\r
8312   ovl->Offset = ovl->OffsetHigh = 0;\r
8313   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8314   if (ok) {\r
8315     err = NO_ERROR;\r
8316   } else {\r
8317     err = GetLastError();\r
8318     if (err == ERROR_IO_PENDING) {\r
8319       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8320       if (ok)\r
8321         err = NO_ERROR;\r
8322       else\r
8323         err = GetLastError();\r
8324     }\r
8325   }\r
8326   return err;\r
8327 }\r
8328 \r
8329 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8330 void CheckForInputBufferFull( InputSource * is )\r
8331 {\r
8332     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8333         /* Look for end of line */\r
8334         char * p = is->buf;\r
8335         \r
8336         while( p < is->next && *p != '\n' ) {\r
8337             p++;\r
8338         }\r
8339 \r
8340         if( p >= is->next ) {\r
8341             if (appData.debugMode) {\r
8342                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8343             }\r
8344 \r
8345             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8346             is->count = (DWORD) -1;\r
8347             is->next = is->buf;\r
8348         }\r
8349     }\r
8350 }\r
8351 \r
8352 DWORD\r
8353 InputThread(LPVOID arg)\r
8354 {\r
8355   InputSource *is;\r
8356   OVERLAPPED ovl;\r
8357 \r
8358   is = (InputSource *) arg;\r
8359   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8360   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8361   while (is->hThread != NULL) {\r
8362     is->error = DoReadFile(is->hFile, is->next,\r
8363                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8364                            &is->count, &ovl);\r
8365     if (is->error == NO_ERROR) {\r
8366       is->next += is->count;\r
8367     } else {\r
8368       if (is->error == ERROR_BROKEN_PIPE) {\r
8369         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8370         is->count = 0;\r
8371       } else {\r
8372         is->count = (DWORD) -1;\r
8373         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8374         break; \r
8375       }\r
8376     }\r
8377 \r
8378     CheckForInputBufferFull( is );\r
8379 \r
8380     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8381 \r
8382     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8383 \r
8384     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8385   }\r
8386 \r
8387   CloseHandle(ovl.hEvent);\r
8388   CloseHandle(is->hFile);\r
8389 \r
8390   if (appData.debugMode) {\r
8391     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8392   }\r
8393 \r
8394   return 0;\r
8395 }\r
8396 \r
8397 \r
8398 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8399 DWORD\r
8400 NonOvlInputThread(LPVOID arg)\r
8401 {\r
8402   InputSource *is;\r
8403   char *p, *q;\r
8404   int i;\r
8405   char prev;\r
8406 \r
8407   is = (InputSource *) arg;\r
8408   while (is->hThread != NULL) {\r
8409     is->error = ReadFile(is->hFile, is->next,\r
8410                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8411                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8412     if (is->error == NO_ERROR) {\r
8413       /* Change CRLF to LF */\r
8414       if (is->next > is->buf) {\r
8415         p = is->next - 1;\r
8416         i = is->count + 1;\r
8417       } else {\r
8418         p = is->next;\r
8419         i = is->count;\r
8420       }\r
8421       q = p;\r
8422       prev = NULLCHAR;\r
8423       while (i > 0) {\r
8424         if (prev == '\r' && *p == '\n') {\r
8425           *(q-1) = '\n';\r
8426           is->count--;\r
8427         } else { \r
8428           *q++ = *p;\r
8429         }\r
8430         prev = *p++;\r
8431         i--;\r
8432       }\r
8433       *q = NULLCHAR;\r
8434       is->next = q;\r
8435     } else {\r
8436       if (is->error == ERROR_BROKEN_PIPE) {\r
8437         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8438         is->count = 0; \r
8439       } else {\r
8440         is->count = (DWORD) -1;\r
8441       }\r
8442     }\r
8443 \r
8444     CheckForInputBufferFull( is );\r
8445 \r
8446     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8447 \r
8448     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8449 \r
8450     if (is->count < 0) break;  /* Quit on error */\r
8451   }\r
8452   CloseHandle(is->hFile);\r
8453   return 0;\r
8454 }\r
8455 \r
8456 DWORD\r
8457 SocketInputThread(LPVOID arg)\r
8458 {\r
8459   InputSource *is;\r
8460 \r
8461   is = (InputSource *) arg;\r
8462   while (is->hThread != NULL) {\r
8463     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8464     if ((int)is->count == SOCKET_ERROR) {\r
8465       is->count = (DWORD) -1;\r
8466       is->error = WSAGetLastError();\r
8467     } else {\r
8468       is->error = NO_ERROR;\r
8469       is->next += is->count;\r
8470       if (is->count == 0 && is->second == is) {\r
8471         /* End of file on stderr; quit with no message */\r
8472         break;\r
8473       }\r
8474     }\r
8475     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8476 \r
8477     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8478 \r
8479     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8480   }\r
8481   return 0;\r
8482 }\r
8483 \r
8484 VOID\r
8485 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8486 {\r
8487   InputSource *is;\r
8488 \r
8489   is = (InputSource *) lParam;\r
8490   if (is->lineByLine) {\r
8491     /* Feed in lines one by one */\r
8492     char *p = is->buf;\r
8493     char *q = p;\r
8494     while (q < is->next) {\r
8495       if (*q++ == '\n') {\r
8496         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8497         p = q;\r
8498       }\r
8499     }\r
8500     \r
8501     /* Move any partial line to the start of the buffer */\r
8502     q = is->buf;\r
8503     while (p < is->next) {\r
8504       *q++ = *p++;\r
8505     }\r
8506     is->next = q;\r
8507 \r
8508     if (is->error != NO_ERROR || is->count == 0) {\r
8509       /* Notify backend of the error.  Note: If there was a partial\r
8510          line at the end, it is not flushed through. */\r
8511       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8512     }\r
8513   } else {\r
8514     /* Feed in the whole chunk of input at once */\r
8515     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8516     is->next = is->buf;\r
8517   }\r
8518 }\r
8519 \r
8520 /*---------------------------------------------------------------------------*\\r
8521  *\r
8522  *  Menu enables. Used when setting various modes.\r
8523  *\r
8524 \*---------------------------------------------------------------------------*/\r
8525 \r
8526 typedef struct {\r
8527   int item;\r
8528   int flags;\r
8529 } Enables;\r
8530 \r
8531 VOID\r
8532 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8533 {\r
8534   while (enab->item > 0) {\r
8535     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8536     enab++;\r
8537   }\r
8538 }\r
8539 \r
8540 Enables gnuEnables[] = {\r
8541   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8542   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8543   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8544   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8545   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8546   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8547   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8548   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8550   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8551   { -1, -1 }\r
8552 };\r
8553 \r
8554 Enables icsEnables[] = {\r
8555   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8560   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8561   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8567   { -1, -1 }\r
8568 };\r
8569 \r
8570 #ifdef ZIPPY\r
8571 Enables zippyEnables[] = {\r
8572   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8573   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8574   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8575   { -1, -1 }\r
8576 };\r
8577 #endif\r
8578 \r
8579 Enables ncpEnables[] = {\r
8580   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8581   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8583   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8584   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8586   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8587   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8588   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8589   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8593   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8595   { -1, -1 }\r
8596 };\r
8597 \r
8598 Enables trainingOnEnables[] = {\r
8599   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8607   { -1, -1 }\r
8608 };\r
8609 \r
8610 Enables trainingOffEnables[] = {\r
8611   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8612   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8613   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8614   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8615   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8616   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8617   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8618   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8619   { -1, -1 }\r
8620 };\r
8621 \r
8622 /* These modify either ncpEnables or gnuEnables */\r
8623 Enables cmailEnables[] = {\r
8624   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8625   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8626   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8627   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8628   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8630   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8631   { -1, -1 }\r
8632 };\r
8633 \r
8634 Enables machineThinkingEnables[] = {\r
8635   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8636   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8638   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8639   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8640   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8641   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8642   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8643   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8644   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8645   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8646   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8647   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8648   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8650   { -1, -1 }\r
8651 };\r
8652 \r
8653 Enables userThinkingEnables[] = {\r
8654   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8655   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8656   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8657   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8658   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8659   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8660   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8661   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8662   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8663   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8664   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8665   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8666   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8667   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8668   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8669   { -1, -1 }\r
8670 };\r
8671 \r
8672 /*---------------------------------------------------------------------------*\\r
8673  *\r
8674  *  Front-end interface functions exported by XBoard.\r
8675  *  Functions appear in same order as prototypes in frontend.h.\r
8676  * \r
8677 \*---------------------------------------------------------------------------*/\r
8678 VOID\r
8679 ModeHighlight()\r
8680 {\r
8681   static UINT prevChecked = 0;\r
8682   static int prevPausing = 0;\r
8683   UINT nowChecked;\r
8684 \r
8685   if (pausing != prevPausing) {\r
8686     prevPausing = pausing;\r
8687     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8688                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8689     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8690   }\r
8691 \r
8692   switch (gameMode) {\r
8693   case BeginningOfGame:\r
8694     if (appData.icsActive)\r
8695       nowChecked = IDM_IcsClient;\r
8696     else if (appData.noChessProgram)\r
8697       nowChecked = IDM_EditGame;\r
8698     else\r
8699       nowChecked = IDM_MachineBlack;\r
8700     break;\r
8701   case MachinePlaysBlack:\r
8702     nowChecked = IDM_MachineBlack;\r
8703     break;\r
8704   case MachinePlaysWhite:\r
8705     nowChecked = IDM_MachineWhite;\r
8706     break;\r
8707   case TwoMachinesPlay:\r
8708     nowChecked = IDM_TwoMachines;\r
8709     break;\r
8710   case AnalyzeMode:\r
8711     nowChecked = IDM_AnalysisMode;\r
8712     break;\r
8713   case AnalyzeFile:\r
8714     nowChecked = IDM_AnalyzeFile;\r
8715     break;\r
8716   case EditGame:\r
8717     nowChecked = IDM_EditGame;\r
8718     break;\r
8719   case PlayFromGameFile:\r
8720     nowChecked = IDM_LoadGame;\r
8721     break;\r
8722   case EditPosition:\r
8723     nowChecked = IDM_EditPosition;\r
8724     break;\r
8725   case Training:\r
8726     nowChecked = IDM_Training;\r
8727     break;\r
8728   case IcsPlayingWhite:\r
8729   case IcsPlayingBlack:\r
8730   case IcsObserving:\r
8731   case IcsIdle:\r
8732     nowChecked = IDM_IcsClient;\r
8733     break;\r
8734   default:\r
8735   case EndOfGame:\r
8736     nowChecked = 0;\r
8737     break;\r
8738   }\r
8739   if (prevChecked != 0)\r
8740     (void) CheckMenuItem(GetMenu(hwndMain),\r
8741                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8742   if (nowChecked != 0)\r
8743     (void) CheckMenuItem(GetMenu(hwndMain),\r
8744                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8745 \r
8746   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8747     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8748                           MF_BYCOMMAND|MF_ENABLED);\r
8749   } else {\r
8750     (void) EnableMenuItem(GetMenu(hwndMain), \r
8751                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8752   }\r
8753 \r
8754   prevChecked = nowChecked;\r
8755 \r
8756   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8757   if (appData.icsActive) {\r
8758        if (appData.icsEngineAnalyze) {\r
8759                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8760                        MF_BYCOMMAND|MF_CHECKED);\r
8761        } else {\r
8762                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8763                        MF_BYCOMMAND|MF_UNCHECKED);\r
8764        }\r
8765   }\r
8766 }\r
8767 \r
8768 VOID\r
8769 SetICSMode()\r
8770 {\r
8771   HMENU hmenu = GetMenu(hwndMain);\r
8772   SetMenuEnables(hmenu, icsEnables);\r
8773   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8774     MF_BYPOSITION|MF_ENABLED);\r
8775 #ifdef ZIPPY\r
8776   if (appData.zippyPlay) {\r
8777     SetMenuEnables(hmenu, zippyEnables);\r
8778     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8779          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8780           MF_BYCOMMAND|MF_ENABLED);\r
8781   }\r
8782 #endif\r
8783 }\r
8784 \r
8785 VOID\r
8786 SetGNUMode()\r
8787 {\r
8788   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8789 }\r
8790 \r
8791 VOID\r
8792 SetNCPMode()\r
8793 {\r
8794   HMENU hmenu = GetMenu(hwndMain);\r
8795   SetMenuEnables(hmenu, ncpEnables);\r
8796   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8797     MF_BYPOSITION|MF_GRAYED);\r
8798     DrawMenuBar(hwndMain);\r
8799 }\r
8800 \r
8801 VOID\r
8802 SetCmailMode()\r
8803 {\r
8804   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8805 }\r
8806 \r
8807 VOID \r
8808 SetTrainingModeOn()\r
8809 {\r
8810   int i;\r
8811   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8812   for (i = 0; i < N_BUTTONS; i++) {\r
8813     if (buttonDesc[i].hwnd != NULL)\r
8814       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8815   }\r
8816   CommentPopDown();\r
8817 }\r
8818 \r
8819 VOID SetTrainingModeOff()\r
8820 {\r
8821   int i;\r
8822   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8823   for (i = 0; i < N_BUTTONS; i++) {\r
8824     if (buttonDesc[i].hwnd != NULL)\r
8825       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8826   }\r
8827 }\r
8828 \r
8829 \r
8830 VOID\r
8831 SetUserThinkingEnables()\r
8832 {\r
8833   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8834 }\r
8835 \r
8836 VOID\r
8837 SetMachineThinkingEnables()\r
8838 {\r
8839   HMENU hMenu = GetMenu(hwndMain);\r
8840   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8841 \r
8842   SetMenuEnables(hMenu, machineThinkingEnables);\r
8843 \r
8844   if (gameMode == MachinePlaysBlack) {\r
8845     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8846   } else if (gameMode == MachinePlaysWhite) {\r
8847     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8848   } else if (gameMode == TwoMachinesPlay) {\r
8849     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8850   }\r
8851 }\r
8852 \r
8853 \r
8854 VOID\r
8855 DisplayTitle(char *str)\r
8856 {\r
8857   char title[MSG_SIZ], *host;\r
8858   if (str[0] != NULLCHAR) {\r
8859     strcpy(title, str);\r
8860   } else if (appData.icsActive) {\r
8861     if (appData.icsCommPort[0] != NULLCHAR)\r
8862       host = "ICS";\r
8863     else \r
8864       host = appData.icsHost;\r
8865     sprintf(title, "%s: %s", szTitle, host);\r
8866   } else if (appData.noChessProgram) {\r
8867     strcpy(title, szTitle);\r
8868   } else {\r
8869     strcpy(title, szTitle);\r
8870     strcat(title, ": ");\r
8871     strcat(title, first.tidy);\r
8872   }\r
8873   SetWindowText(hwndMain, title);\r
8874 }\r
8875 \r
8876 \r
8877 VOID\r
8878 DisplayMessage(char *str1, char *str2)\r
8879 {\r
8880   HDC hdc;\r
8881   HFONT oldFont;\r
8882   int remain = MESSAGE_TEXT_MAX - 1;\r
8883   int len;\r
8884 \r
8885   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8886   messageText[0] = NULLCHAR;\r
8887   if (*str1) {\r
8888     len = strlen(str1);\r
8889     if (len > remain) len = remain;\r
8890     strncpy(messageText, str1, len);\r
8891     messageText[len] = NULLCHAR;\r
8892     remain -= len;\r
8893   }\r
8894   if (*str2 && remain >= 2) {\r
8895     if (*str1) {\r
8896       strcat(messageText, "  ");\r
8897       remain -= 2;\r
8898     }\r
8899     len = strlen(str2);\r
8900     if (len > remain) len = remain;\r
8901     strncat(messageText, str2, len);\r
8902   }\r
8903   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8904 \r
8905   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8906   hdc = GetDC(hwndMain);\r
8907   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8908   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8909              &messageRect, messageText, strlen(messageText), NULL);\r
8910   (void) SelectObject(hdc, oldFont);\r
8911   (void) ReleaseDC(hwndMain, hdc);\r
8912 }\r
8913 \r
8914 VOID\r
8915 DisplayError(char *str, int error)\r
8916 {\r
8917   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8918   int len;\r
8919 \r
8920   if (error == 0) {\r
8921     strcpy(buf, str);\r
8922   } else {\r
8923     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8924                         NULL, error, LANG_NEUTRAL,\r
8925                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8926     if (len > 0) {\r
8927       sprintf(buf, "%s:\n%s", str, buf2);\r
8928     } else {\r
8929       ErrorMap *em = errmap;\r
8930       while (em->err != 0 && em->err != error) em++;\r
8931       if (em->err != 0) {\r
8932         sprintf(buf, "%s:\n%s", str, em->msg);\r
8933       } else {\r
8934         sprintf(buf, "%s:\nError code %d", str, error);\r
8935       }\r
8936     }\r
8937   }\r
8938   \r
8939   ErrorPopUp("Error", buf);\r
8940 }\r
8941 \r
8942 \r
8943 VOID\r
8944 DisplayMoveError(char *str)\r
8945 {\r
8946   fromX = fromY = -1;\r
8947   ClearHighlights();\r
8948   DrawPosition(FALSE, NULL);\r
8949   if (appData.popupMoveErrors) {\r
8950     ErrorPopUp("Error", str);\r
8951   } else {\r
8952     DisplayMessage(str, "");\r
8953     moveErrorMessageUp = TRUE;\r
8954   }\r
8955 }\r
8956 \r
8957 VOID\r
8958 DisplayFatalError(char *str, int error, int exitStatus)\r
8959 {\r
8960   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8961   int len;\r
8962   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8963 \r
8964   if (error != 0) {\r
8965     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8966                         NULL, error, LANG_NEUTRAL,\r
8967                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8968     if (len > 0) {\r
8969       sprintf(buf, "%s:\n%s", str, buf2);\r
8970     } else {\r
8971       ErrorMap *em = errmap;\r
8972       while (em->err != 0 && em->err != error) em++;\r
8973       if (em->err != 0) {\r
8974         sprintf(buf, "%s:\n%s", str, em->msg);\r
8975       } else {\r
8976         sprintf(buf, "%s:\nError code %d", str, error);\r
8977       }\r
8978     }\r
8979     str = buf;\r
8980   }\r
8981   if (appData.debugMode) {\r
8982     fprintf(debugFP, "%s: %s\n", label, str);\r
8983   }\r
8984   if (appData.popupExitMessage) {\r
8985     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8986                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8987   }\r
8988   ExitEvent(exitStatus);\r
8989 }\r
8990 \r
8991 \r
8992 VOID\r
8993 DisplayInformation(char *str)\r
8994 {\r
8995   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8996 }\r
8997 \r
8998 \r
8999 VOID\r
9000 DisplayNote(char *str)\r
9001 {\r
9002   ErrorPopUp("Note", str);\r
9003 }\r
9004 \r
9005 \r
9006 typedef struct {\r
9007   char *title, *question, *replyPrefix;\r
9008   ProcRef pr;\r
9009 } QuestionParams;\r
9010 \r
9011 LRESULT CALLBACK\r
9012 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9013 {\r
9014   static QuestionParams *qp;\r
9015   char reply[MSG_SIZ];\r
9016   int len, err;\r
9017 \r
9018   switch (message) {\r
9019   case WM_INITDIALOG:\r
9020     qp = (QuestionParams *) lParam;\r
9021     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9022     SetWindowText(hDlg, qp->title);\r
9023     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9024     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9025     return FALSE;\r
9026 \r
9027   case WM_COMMAND:\r
9028     switch (LOWORD(wParam)) {\r
9029     case IDOK:\r
9030       strcpy(reply, qp->replyPrefix);\r
9031       if (*reply) strcat(reply, " ");\r
9032       len = strlen(reply);\r
9033       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9034       strcat(reply, "\n");\r
9035       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9036       EndDialog(hDlg, TRUE);\r
9037       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9038       return TRUE;\r
9039     case IDCANCEL:\r
9040       EndDialog(hDlg, FALSE);\r
9041       return TRUE;\r
9042     default:\r
9043       break;\r
9044     }\r
9045     break;\r
9046   }\r
9047   return FALSE;\r
9048 }\r
9049 \r
9050 VOID\r
9051 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9052 {\r
9053     QuestionParams qp;\r
9054     FARPROC lpProc;\r
9055     \r
9056     qp.title = title;\r
9057     qp.question = question;\r
9058     qp.replyPrefix = replyPrefix;\r
9059     qp.pr = pr;\r
9060     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9061     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9062       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9063     FreeProcInstance(lpProc);\r
9064 }\r
9065 \r
9066 /* [AS] Pick FRC position */\r
9067 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9068 {\r
9069     static int * lpIndexFRC;\r
9070     BOOL index_is_ok;\r
9071     char buf[16];\r
9072 \r
9073     switch( message )\r
9074     {\r
9075     case WM_INITDIALOG:\r
9076         lpIndexFRC = (int *) lParam;\r
9077 \r
9078         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9079 \r
9080         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9081         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9082         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9083         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9084 \r
9085         break;\r
9086 \r
9087     case WM_COMMAND:\r
9088         switch( LOWORD(wParam) ) {\r
9089         case IDOK:\r
9090             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9091             EndDialog( hDlg, 0 );\r
9092             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9093             return TRUE;\r
9094         case IDCANCEL:\r
9095             EndDialog( hDlg, 1 );   \r
9096             return TRUE;\r
9097         case IDC_NFG_Edit:\r
9098             if( HIWORD(wParam) == EN_CHANGE ) {\r
9099                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9100 \r
9101                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9102             }\r
9103             return TRUE;\r
9104         case IDC_NFG_Random:\r
9105             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9106             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9107             return TRUE;\r
9108         }\r
9109 \r
9110         break;\r
9111     }\r
9112 \r
9113     return FALSE;\r
9114 }\r
9115 \r
9116 int NewGameFRC()\r
9117 {\r
9118     int result;\r
9119     int index = appData.defaultFrcPosition;\r
9120     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9121 \r
9122     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9123 \r
9124     if( result == 0 ) {\r
9125         appData.defaultFrcPosition = index;\r
9126     }\r
9127 \r
9128     return result;\r
9129 }\r
9130 \r
9131 /* [AS] Game list options */\r
9132 typedef struct {\r
9133     char id;\r
9134     char * name;\r
9135 } GLT_Item;\r
9136 \r
9137 static GLT_Item GLT_ItemInfo[] = {\r
9138     { GLT_EVENT,      "Event" },\r
9139     { GLT_SITE,       "Site" },\r
9140     { GLT_DATE,       "Date" },\r
9141     { GLT_ROUND,      "Round" },\r
9142     { GLT_PLAYERS,    "Players" },\r
9143     { GLT_RESULT,     "Result" },\r
9144     { GLT_WHITE_ELO,  "White Rating" },\r
9145     { GLT_BLACK_ELO,  "Black Rating" },\r
9146     { GLT_TIME_CONTROL,"Time Control" },\r
9147     { GLT_VARIANT,    "Variant" },\r
9148     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9149     { 0, 0 }\r
9150 };\r
9151 \r
9152 const char * GLT_FindItem( char id )\r
9153 {\r
9154     const char * result = 0;\r
9155 \r
9156     GLT_Item * list = GLT_ItemInfo;\r
9157 \r
9158     while( list->id != 0 ) {\r
9159         if( list->id == id ) {\r
9160             result = list->name;\r
9161             break;\r
9162         }\r
9163 \r
9164         list++;\r
9165     }\r
9166 \r
9167     return result;\r
9168 }\r
9169 \r
9170 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9171 {\r
9172     const char * name = GLT_FindItem( id );\r
9173 \r
9174     if( name != 0 ) {\r
9175         if( index >= 0 ) {\r
9176             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9177         }\r
9178         else {\r
9179             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9180         }\r
9181     }\r
9182 }\r
9183 \r
9184 void GLT_TagsToList( HWND hDlg, char * tags )\r
9185 {\r
9186     char * pc = tags;\r
9187 \r
9188     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9189 \r
9190     while( *pc ) {\r
9191         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9192         pc++;\r
9193     }\r
9194 \r
9195     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9196 \r
9197     pc = GLT_ALL_TAGS;\r
9198 \r
9199     while( *pc ) {\r
9200         if( strchr( tags, *pc ) == 0 ) {\r
9201             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9202         }\r
9203         pc++;\r
9204     }\r
9205 \r
9206     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9207 }\r
9208 \r
9209 char GLT_ListItemToTag( HWND hDlg, int index )\r
9210 {\r
9211     char result = '\0';\r
9212     char name[128];\r
9213 \r
9214     GLT_Item * list = GLT_ItemInfo;\r
9215 \r
9216     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9217         while( list->id != 0 ) {\r
9218             if( strcmp( list->name, name ) == 0 ) {\r
9219                 result = list->id;\r
9220                 break;\r
9221             }\r
9222 \r
9223             list++;\r
9224         }\r
9225     }\r
9226 \r
9227     return result;\r
9228 }\r
9229 \r
9230 void GLT_MoveSelection( HWND hDlg, int delta )\r
9231 {\r
9232     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9233     int idx2 = idx1 + delta;\r
9234     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9235 \r
9236     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9237         char buf[128];\r
9238 \r
9239         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9240         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9241         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9242         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9243     }\r
9244 }\r
9245 \r
9246 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9247 {\r
9248     static char glt[64];\r
9249     static char * lpUserGLT;\r
9250 \r
9251     switch( message )\r
9252     {\r
9253     case WM_INITDIALOG:\r
9254         lpUserGLT = (char *) lParam;\r
9255         \r
9256         strcpy( glt, lpUserGLT );\r
9257 \r
9258         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9259 \r
9260         /* Initialize list */\r
9261         GLT_TagsToList( hDlg, glt );\r
9262 \r
9263         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9264 \r
9265         break;\r
9266 \r
9267     case WM_COMMAND:\r
9268         switch( LOWORD(wParam) ) {\r
9269         case IDOK:\r
9270             {\r
9271                 char * pc = lpUserGLT;\r
9272                 int idx = 0;\r
9273 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9274                 char id;\r
9275 \r
9276                 do {\r
9277                     id = GLT_ListItemToTag( hDlg, idx );\r
9278 \r
9279                     *pc++ = id;\r
9280                     idx++;\r
9281                 } while( id != '\0' );\r
9282             }\r
9283             EndDialog( hDlg, 0 );\r
9284             return TRUE;\r
9285         case IDCANCEL:\r
9286             EndDialog( hDlg, 1 );\r
9287             return TRUE;\r
9288 \r
9289         case IDC_GLT_Default:\r
9290             strcpy( glt, GLT_DEFAULT_TAGS );\r
9291             GLT_TagsToList( hDlg, glt );\r
9292             return TRUE;\r
9293 \r
9294         case IDC_GLT_Restore:\r
9295             strcpy( glt, lpUserGLT );\r
9296             GLT_TagsToList( hDlg, glt );\r
9297             return TRUE;\r
9298 \r
9299         case IDC_GLT_Up:\r
9300             GLT_MoveSelection( hDlg, -1 );\r
9301             return TRUE;\r
9302 \r
9303         case IDC_GLT_Down:\r
9304             GLT_MoveSelection( hDlg, +1 );\r
9305             return TRUE;\r
9306         }\r
9307 \r
9308         break;\r
9309     }\r
9310 \r
9311     return FALSE;\r
9312 }\r
9313 \r
9314 int GameListOptions()\r
9315 {\r
9316     char glt[64];\r
9317     int result;\r
9318     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9319 \r
9320     strcpy( glt, appData.gameListTags );\r
9321 \r
9322     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9323 \r
9324     if( result == 0 ) {\r
9325         /* [AS] Memory leak here! */\r
9326         appData.gameListTags = strdup( glt ); \r
9327     }\r
9328 \r
9329     return result;\r
9330 }\r
9331 \r
9332 \r
9333 VOID\r
9334 DisplayIcsInteractionTitle(char *str)\r
9335 {\r
9336   char consoleTitle[MSG_SIZ];\r
9337 \r
9338   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9339   SetWindowText(hwndConsole, consoleTitle);\r
9340 }\r
9341 \r
9342 void\r
9343 DrawPosition(int fullRedraw, Board board)\r
9344 {\r
9345   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9346 }\r
9347 \r
9348 \r
9349 VOID\r
9350 ResetFrontEnd()\r
9351 {\r
9352   fromX = fromY = -1;\r
9353   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9354     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9355     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9356     dragInfo.lastpos = dragInfo.pos;\r
9357     dragInfo.start.x = dragInfo.start.y = -1;\r
9358     dragInfo.from = dragInfo.start;\r
9359     ReleaseCapture();\r
9360     DrawPosition(TRUE, NULL);\r
9361   }\r
9362 }\r
9363 \r
9364 \r
9365 VOID\r
9366 CommentPopUp(char *title, char *str)\r
9367 {\r
9368   HWND hwnd = GetActiveWindow();\r
9369   EitherCommentPopUp(0, title, str, FALSE);\r
9370   SetActiveWindow(hwnd);\r
9371 }\r
9372 \r
9373 VOID\r
9374 CommentPopDown(void)\r
9375 {\r
9376   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9377   if (commentDialog) {\r
9378     ShowWindow(commentDialog, SW_HIDE);\r
9379   }\r
9380   commentDialogUp = FALSE;\r
9381 }\r
9382 \r
9383 VOID\r
9384 EditCommentPopUp(int index, char *title, char *str)\r
9385 {\r
9386   EitherCommentPopUp(index, title, str, TRUE);\r
9387 }\r
9388 \r
9389 \r
9390 VOID\r
9391 RingBell()\r
9392 {\r
9393   MyPlaySound(&sounds[(int)SoundMove]);\r
9394 }\r
9395 \r
9396 VOID PlayIcsWinSound()\r
9397 {\r
9398   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9399 }\r
9400 \r
9401 VOID PlayIcsLossSound()\r
9402 {\r
9403   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9404 }\r
9405 \r
9406 VOID PlayIcsDrawSound()\r
9407 {\r
9408   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9409 }\r
9410 \r
9411 VOID PlayIcsUnfinishedSound()\r
9412 {\r
9413   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9414 }\r
9415 \r
9416 VOID\r
9417 PlayAlarmSound()\r
9418 {\r
9419   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9420 }\r
9421 \r
9422 \r
9423 VOID\r
9424 EchoOn()\r
9425 {\r
9426   HWND hInput;\r
9427   consoleEcho = TRUE;\r
9428   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9429   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9430   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9431 }\r
9432 \r
9433 \r
9434 VOID\r
9435 EchoOff()\r
9436 {\r
9437   CHARFORMAT cf;\r
9438   HWND hInput;\r
9439   consoleEcho = FALSE;\r
9440   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9441   /* This works OK: set text and background both to the same color */\r
9442   cf = consoleCF;\r
9443   cf.crTextColor = COLOR_ECHOOFF;\r
9444   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9445   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9446 }\r
9447 \r
9448 /* No Raw()...? */\r
9449 \r
9450 void Colorize(ColorClass cc, int continuation)\r
9451 {\r
9452   currentColorClass = cc;\r
9453   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9454   consoleCF.crTextColor = textAttribs[cc].color;\r
9455   consoleCF.dwEffects = textAttribs[cc].effects;\r
9456   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9457 }\r
9458 \r
9459 char *\r
9460 UserName()\r
9461 {\r
9462   static char buf[MSG_SIZ];\r
9463   DWORD bufsiz = MSG_SIZ;\r
9464 \r
9465   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9466         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9467   }\r
9468   if (!GetUserName(buf, &bufsiz)) {\r
9469     /*DisplayError("Error getting user name", GetLastError());*/\r
9470     strcpy(buf, "User");\r
9471   }\r
9472   return buf;\r
9473 }\r
9474 \r
9475 char *\r
9476 HostName()\r
9477 {\r
9478   static char buf[MSG_SIZ];\r
9479   DWORD bufsiz = MSG_SIZ;\r
9480 \r
9481   if (!GetComputerName(buf, &bufsiz)) {\r
9482     /*DisplayError("Error getting host name", GetLastError());*/\r
9483     strcpy(buf, "Unknown");\r
9484   }\r
9485   return buf;\r
9486 }\r
9487 \r
9488 \r
9489 int\r
9490 ClockTimerRunning()\r
9491 {\r
9492   return clockTimerEvent != 0;\r
9493 }\r
9494 \r
9495 int\r
9496 StopClockTimer()\r
9497 {\r
9498   if (clockTimerEvent == 0) return FALSE;\r
9499   KillTimer(hwndMain, clockTimerEvent);\r
9500   clockTimerEvent = 0;\r
9501   return TRUE;\r
9502 }\r
9503 \r
9504 void\r
9505 StartClockTimer(long millisec)\r
9506 {\r
9507   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9508                              (UINT) millisec, NULL);\r
9509 }\r
9510 \r
9511 void\r
9512 DisplayWhiteClock(long timeRemaining, int highlight)\r
9513 {\r
9514   HDC hdc;\r
9515   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9516 \r
9517   if(appData.noGUI) return;\r
9518   hdc = GetDC(hwndMain);\r
9519   if (!IsIconic(hwndMain)) {\r
9520     DisplayAClock(hdc, timeRemaining, highlight, \r
9521                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9522   }\r
9523   if (highlight && iconCurrent == iconBlack) {\r
9524     iconCurrent = iconWhite;\r
9525     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9526     if (IsIconic(hwndMain)) {\r
9527       DrawIcon(hdc, 2, 2, iconCurrent);\r
9528     }\r
9529   }\r
9530   (void) ReleaseDC(hwndMain, hdc);\r
9531   if (hwndConsole)\r
9532     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9533 }\r
9534 \r
9535 void\r
9536 DisplayBlackClock(long timeRemaining, int highlight)\r
9537 {\r
9538   HDC hdc;\r
9539   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9540 \r
9541   if(appData.noGUI) return;\r
9542   hdc = GetDC(hwndMain);\r
9543   if (!IsIconic(hwndMain)) {\r
9544     DisplayAClock(hdc, timeRemaining, highlight, \r
9545                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9546   }\r
9547   if (highlight && iconCurrent == iconWhite) {\r
9548     iconCurrent = iconBlack;\r
9549     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9550     if (IsIconic(hwndMain)) {\r
9551       DrawIcon(hdc, 2, 2, iconCurrent);\r
9552     }\r
9553   }\r
9554   (void) ReleaseDC(hwndMain, hdc);\r
9555   if (hwndConsole)\r
9556     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9557 }\r
9558 \r
9559 \r
9560 int\r
9561 LoadGameTimerRunning()\r
9562 {\r
9563   return loadGameTimerEvent != 0;\r
9564 }\r
9565 \r
9566 int\r
9567 StopLoadGameTimer()\r
9568 {\r
9569   if (loadGameTimerEvent == 0) return FALSE;\r
9570   KillTimer(hwndMain, loadGameTimerEvent);\r
9571   loadGameTimerEvent = 0;\r
9572   return TRUE;\r
9573 }\r
9574 \r
9575 void\r
9576 StartLoadGameTimer(long millisec)\r
9577 {\r
9578   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9579                                 (UINT) millisec, NULL);\r
9580 }\r
9581 \r
9582 void\r
9583 AutoSaveGame()\r
9584 {\r
9585   char *defName;\r
9586   FILE *f;\r
9587   char fileTitle[MSG_SIZ];\r
9588 \r
9589   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9590   f = OpenFileDialog(hwndMain, "a", defName,\r
9591                      appData.oldSaveStyle ? "gam" : "pgn",\r
9592                      GAME_FILT, \r
9593                      "Save Game to File", NULL, fileTitle, NULL);\r
9594   if (f != NULL) {\r
9595     SaveGame(f, 0, "");\r
9596     fclose(f);\r
9597   }\r
9598 }\r
9599 \r
9600 \r
9601 void\r
9602 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9603 {\r
9604   if (delayedTimerEvent != 0) {\r
9605     if (appData.debugMode) {\r
9606       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9607     }\r
9608     KillTimer(hwndMain, delayedTimerEvent);\r
9609     delayedTimerEvent = 0;\r
9610     delayedTimerCallback();\r
9611   }\r
9612   delayedTimerCallback = cb;\r
9613   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9614                                 (UINT) millisec, NULL);\r
9615 }\r
9616 \r
9617 DelayedEventCallback\r
9618 GetDelayedEvent()\r
9619 {\r
9620   if (delayedTimerEvent) {\r
9621     return delayedTimerCallback;\r
9622   } else {\r
9623     return NULL;\r
9624   }\r
9625 }\r
9626 \r
9627 void\r
9628 CancelDelayedEvent()\r
9629 {\r
9630   if (delayedTimerEvent) {\r
9631     KillTimer(hwndMain, delayedTimerEvent);\r
9632     delayedTimerEvent = 0;\r
9633   }\r
9634 }\r
9635 \r
9636 DWORD GetWin32Priority(int nice)\r
9637 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9638 /*\r
9639 REALTIME_PRIORITY_CLASS     0x00000100\r
9640 HIGH_PRIORITY_CLASS         0x00000080\r
9641 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9642 NORMAL_PRIORITY_CLASS       0x00000020\r
9643 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9644 IDLE_PRIORITY_CLASS         0x00000040\r
9645 */\r
9646         if (nice < -15) return 0x00000080;\r
9647         if (nice < 0)   return 0x00008000;\r
9648         if (nice == 0)  return 0x00000020;\r
9649         if (nice < 15)  return 0x00004000;\r
9650         return 0x00000040;\r
9651 }\r
9652 \r
9653 /* Start a child process running the given program.\r
9654    The process's standard output can be read from "from", and its\r
9655    standard input can be written to "to".\r
9656    Exit with fatal error if anything goes wrong.\r
9657    Returns an opaque pointer that can be used to destroy the process\r
9658    later.\r
9659 */\r
9660 int\r
9661 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9662 {\r
9663 #define BUFSIZE 4096\r
9664 \r
9665   HANDLE hChildStdinRd, hChildStdinWr,\r
9666     hChildStdoutRd, hChildStdoutWr;\r
9667   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9668   SECURITY_ATTRIBUTES saAttr;\r
9669   BOOL fSuccess;\r
9670   PROCESS_INFORMATION piProcInfo;\r
9671   STARTUPINFO siStartInfo;\r
9672   ChildProc *cp;\r
9673   char buf[MSG_SIZ];\r
9674   DWORD err;\r
9675 \r
9676   if (appData.debugMode) {\r
9677     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9678   }\r
9679 \r
9680   *pr = NoProc;\r
9681 \r
9682   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9683   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9684   saAttr.bInheritHandle = TRUE;\r
9685   saAttr.lpSecurityDescriptor = NULL;\r
9686 \r
9687   /*\r
9688    * The steps for redirecting child's STDOUT:\r
9689    *     1. Create anonymous pipe to be STDOUT for child.\r
9690    *     2. Create a noninheritable duplicate of read handle,\r
9691    *         and close the inheritable read handle.\r
9692    */\r
9693 \r
9694   /* Create a pipe for the child's STDOUT. */\r
9695   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9696     return GetLastError();\r
9697   }\r
9698 \r
9699   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9700   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9701                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9702                              FALSE,     /* not inherited */\r
9703                              DUPLICATE_SAME_ACCESS);\r
9704   if (! fSuccess) {\r
9705     return GetLastError();\r
9706   }\r
9707   CloseHandle(hChildStdoutRd);\r
9708 \r
9709   /*\r
9710    * The steps for redirecting child's STDIN:\r
9711    *     1. Create anonymous pipe to be STDIN for child.\r
9712    *     2. Create a noninheritable duplicate of write handle,\r
9713    *         and close the inheritable write handle.\r
9714    */\r
9715 \r
9716   /* Create a pipe for the child's STDIN. */\r
9717   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9718     return GetLastError();\r
9719   }\r
9720 \r
9721   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9722   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9723                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9724                              FALSE,     /* not inherited */\r
9725                              DUPLICATE_SAME_ACCESS);\r
9726   if (! fSuccess) {\r
9727     return GetLastError();\r
9728   }\r
9729   CloseHandle(hChildStdinWr);\r
9730 \r
9731   /* Arrange to (1) look in dir for the child .exe file, and\r
9732    * (2) have dir be the child's working directory.  Interpret\r
9733    * dir relative to the directory WinBoard loaded from. */\r
9734   GetCurrentDirectory(MSG_SIZ, buf);\r
9735   SetCurrentDirectory(installDir);\r
9736   SetCurrentDirectory(dir);\r
9737 \r
9738   /* Now create the child process. */\r
9739 \r
9740   siStartInfo.cb = sizeof(STARTUPINFO);\r
9741   siStartInfo.lpReserved = NULL;\r
9742   siStartInfo.lpDesktop = NULL;\r
9743   siStartInfo.lpTitle = NULL;\r
9744   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9745   siStartInfo.cbReserved2 = 0;\r
9746   siStartInfo.lpReserved2 = NULL;\r
9747   siStartInfo.hStdInput = hChildStdinRd;\r
9748   siStartInfo.hStdOutput = hChildStdoutWr;\r
9749   siStartInfo.hStdError = hChildStdoutWr;\r
9750 \r
9751   fSuccess = CreateProcess(NULL,\r
9752                            cmdLine,        /* command line */\r
9753                            NULL,           /* process security attributes */\r
9754                            NULL,           /* primary thread security attrs */\r
9755                            TRUE,           /* handles are inherited */\r
9756                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9757                            NULL,           /* use parent's environment */\r
9758                            NULL,\r
9759                            &siStartInfo, /* STARTUPINFO pointer */\r
9760                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9761 \r
9762   err = GetLastError();\r
9763   SetCurrentDirectory(buf); /* return to prev directory */\r
9764   if (! fSuccess) {\r
9765     return err;\r
9766   }\r
9767 \r
9768   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9769     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9770     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9771   }\r
9772 \r
9773   /* Close the handles we don't need in the parent */\r
9774   CloseHandle(piProcInfo.hThread);\r
9775   CloseHandle(hChildStdinRd);\r
9776   CloseHandle(hChildStdoutWr);\r
9777 \r
9778   /* Prepare return value */\r
9779   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9780   cp->kind = CPReal;\r
9781   cp->hProcess = piProcInfo.hProcess;\r
9782   cp->pid = piProcInfo.dwProcessId;\r
9783   cp->hFrom = hChildStdoutRdDup;\r
9784   cp->hTo = hChildStdinWrDup;\r
9785 \r
9786   *pr = (void *) cp;\r
9787 \r
9788   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9789      2000 where engines sometimes don't see the initial command(s)\r
9790      from WinBoard and hang.  I don't understand how that can happen,\r
9791      but the Sleep is harmless, so I've put it in.  Others have also\r
9792      reported what may be the same problem, so hopefully this will fix\r
9793      it for them too.  */\r
9794   Sleep(500);\r
9795 \r
9796   return NO_ERROR;\r
9797 }\r
9798 \r
9799 \r
9800 void\r
9801 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9802 {\r
9803   ChildProc *cp; int result;\r
9804 \r
9805   cp = (ChildProc *) pr;\r
9806   if (cp == NULL) return;\r
9807 \r
9808   switch (cp->kind) {\r
9809   case CPReal:\r
9810     /* TerminateProcess is considered harmful, so... */\r
9811     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9812     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9813     /* The following doesn't work because the chess program\r
9814        doesn't "have the same console" as WinBoard.  Maybe\r
9815        we could arrange for this even though neither WinBoard\r
9816        nor the chess program uses a console for stdio? */\r
9817     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9818 \r
9819     /* [AS] Special termination modes for misbehaving programs... */\r
9820     if( signal == 9 ) { \r
9821         result = TerminateProcess( cp->hProcess, 0 );\r
9822 \r
9823         if ( appData.debugMode) {\r
9824             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9825         }\r
9826     }\r
9827     else if( signal == 10 ) {\r
9828         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9829 \r
9830         if( dw != WAIT_OBJECT_0 ) {\r
9831             result = TerminateProcess( cp->hProcess, 0 );\r
9832 \r
9833             if ( appData.debugMode) {\r
9834                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9835             }\r
9836 \r
9837         }\r
9838     }\r
9839 \r
9840     CloseHandle(cp->hProcess);\r
9841     break;\r
9842 \r
9843   case CPComm:\r
9844     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9845     break;\r
9846 \r
9847   case CPSock:\r
9848     closesocket(cp->sock);\r
9849     WSACleanup();\r
9850     break;\r
9851 \r
9852   case CPRcmd:\r
9853     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9854     closesocket(cp->sock);\r
9855     closesocket(cp->sock2);\r
9856     WSACleanup();\r
9857     break;\r
9858   }\r
9859   free(cp);\r
9860 }\r
9861 \r
9862 void\r
9863 InterruptChildProcess(ProcRef pr)\r
9864 {\r
9865   ChildProc *cp;\r
9866 \r
9867   cp = (ChildProc *) pr;\r
9868   if (cp == NULL) return;\r
9869   switch (cp->kind) {\r
9870   case CPReal:\r
9871     /* The following doesn't work because the chess program\r
9872        doesn't "have the same console" as WinBoard.  Maybe\r
9873        we could arrange for this even though neither WinBoard\r
9874        nor the chess program uses a console for stdio */\r
9875     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9876     break;\r
9877 \r
9878   case CPComm:\r
9879   case CPSock:\r
9880     /* Can't interrupt */\r
9881     break;\r
9882 \r
9883   case CPRcmd:\r
9884     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9885     break;\r
9886   }\r
9887 }\r
9888 \r
9889 \r
9890 int\r
9891 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9892 {\r
9893   char cmdLine[MSG_SIZ];\r
9894 \r
9895   if (port[0] == NULLCHAR) {\r
9896     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9897   } else {\r
9898     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9899   }\r
9900   return StartChildProcess(cmdLine, "", pr);\r
9901 }\r
9902 \r
9903 \r
9904 /* Code to open TCP sockets */\r
9905 \r
9906 int\r
9907 OpenTCP(char *host, char *port, ProcRef *pr)\r
9908 {\r
9909   ChildProc *cp;\r
9910   int err;\r
9911   SOCKET s;\r
9912   struct sockaddr_in sa, mysa;\r
9913   struct hostent FAR *hp;\r
9914   unsigned short uport;\r
9915   WORD wVersionRequested;\r
9916   WSADATA wsaData;\r
9917 \r
9918   /* Initialize socket DLL */\r
9919   wVersionRequested = MAKEWORD(1, 1);\r
9920   err = WSAStartup(wVersionRequested, &wsaData);\r
9921   if (err != 0) return err;\r
9922 \r
9923   /* Make socket */\r
9924   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9925     err = WSAGetLastError();\r
9926     WSACleanup();\r
9927     return err;\r
9928   }\r
9929 \r
9930   /* Bind local address using (mostly) don't-care values.\r
9931    */\r
9932   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9933   mysa.sin_family = AF_INET;\r
9934   mysa.sin_addr.s_addr = INADDR_ANY;\r
9935   uport = (unsigned short) 0;\r
9936   mysa.sin_port = htons(uport);\r
9937   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9938       == SOCKET_ERROR) {\r
9939     err = WSAGetLastError();\r
9940     WSACleanup();\r
9941     return err;\r
9942   }\r
9943 \r
9944   /* Resolve remote host name */\r
9945   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9946   if (!(hp = gethostbyname(host))) {\r
9947     unsigned int b0, b1, b2, b3;\r
9948 \r
9949     err = WSAGetLastError();\r
9950 \r
9951     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9952       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9953       hp->h_addrtype = AF_INET;\r
9954       hp->h_length = 4;\r
9955       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9956       hp->h_addr_list[0] = (char *) malloc(4);\r
9957       hp->h_addr_list[0][0] = (char) b0;\r
9958       hp->h_addr_list[0][1] = (char) b1;\r
9959       hp->h_addr_list[0][2] = (char) b2;\r
9960       hp->h_addr_list[0][3] = (char) b3;\r
9961     } else {\r
9962       WSACleanup();\r
9963       return err;\r
9964     }\r
9965   }\r
9966   sa.sin_family = hp->h_addrtype;\r
9967   uport = (unsigned short) atoi(port);\r
9968   sa.sin_port = htons(uport);\r
9969   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9970 \r
9971   /* Make connection */\r
9972   if (connect(s, (struct sockaddr *) &sa,\r
9973               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9974     err = WSAGetLastError();\r
9975     WSACleanup();\r
9976     return err;\r
9977   }\r
9978 \r
9979   /* Prepare return value */\r
9980   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9981   cp->kind = CPSock;\r
9982   cp->sock = s;\r
9983   *pr = (ProcRef *) cp;\r
9984 \r
9985   return NO_ERROR;\r
9986 }\r
9987 \r
9988 int\r
9989 OpenCommPort(char *name, ProcRef *pr)\r
9990 {\r
9991   HANDLE h;\r
9992   COMMTIMEOUTS ct;\r
9993   ChildProc *cp;\r
9994   char fullname[MSG_SIZ];\r
9995 \r
9996   if (*name != '\\')\r
9997     sprintf(fullname, "\\\\.\\%s", name);\r
9998   else\r
9999     strcpy(fullname, name);\r
10000 \r
10001   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10002                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10003   if (h == (HANDLE) -1) {\r
10004     return GetLastError();\r
10005   }\r
10006   hCommPort = h;\r
10007 \r
10008   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10009 \r
10010   /* Accumulate characters until a 100ms pause, then parse */\r
10011   ct.ReadIntervalTimeout = 100;\r
10012   ct.ReadTotalTimeoutMultiplier = 0;\r
10013   ct.ReadTotalTimeoutConstant = 0;\r
10014   ct.WriteTotalTimeoutMultiplier = 0;\r
10015   ct.WriteTotalTimeoutConstant = 0;\r
10016   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10017 \r
10018   /* Prepare return value */\r
10019   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10020   cp->kind = CPComm;\r
10021   cp->hFrom = h;\r
10022   cp->hTo = h;\r
10023   *pr = (ProcRef *) cp;\r
10024 \r
10025   return NO_ERROR;\r
10026 }\r
10027 \r
10028 int\r
10029 OpenLoopback(ProcRef *pr)\r
10030 {\r
10031   DisplayFatalError("Not implemented", 0, 1);\r
10032   return NO_ERROR;\r
10033 }\r
10034 \r
10035 \r
10036 int\r
10037 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10038 {\r
10039   ChildProc *cp;\r
10040   int err;\r
10041   SOCKET s, s2, s3;\r
10042   struct sockaddr_in sa, mysa;\r
10043   struct hostent FAR *hp;\r
10044   unsigned short uport;\r
10045   WORD wVersionRequested;\r
10046   WSADATA wsaData;\r
10047   int fromPort;\r
10048   char stderrPortStr[MSG_SIZ];\r
10049 \r
10050   /* Initialize socket DLL */\r
10051   wVersionRequested = MAKEWORD(1, 1);\r
10052   err = WSAStartup(wVersionRequested, &wsaData);\r
10053   if (err != 0) return err;\r
10054 \r
10055   /* Resolve remote host name */\r
10056   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10057   if (!(hp = gethostbyname(host))) {\r
10058     unsigned int b0, b1, b2, b3;\r
10059 \r
10060     err = WSAGetLastError();\r
10061 \r
10062     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10063       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10064       hp->h_addrtype = AF_INET;\r
10065       hp->h_length = 4;\r
10066       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10067       hp->h_addr_list[0] = (char *) malloc(4);\r
10068       hp->h_addr_list[0][0] = (char) b0;\r
10069       hp->h_addr_list[0][1] = (char) b1;\r
10070       hp->h_addr_list[0][2] = (char) b2;\r
10071       hp->h_addr_list[0][3] = (char) b3;\r
10072     } else {\r
10073       WSACleanup();\r
10074       return err;\r
10075     }\r
10076   }\r
10077   sa.sin_family = hp->h_addrtype;\r
10078   uport = (unsigned short) 514;\r
10079   sa.sin_port = htons(uport);\r
10080   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10081 \r
10082   /* Bind local socket to unused "privileged" port address\r
10083    */\r
10084   s = INVALID_SOCKET;\r
10085   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10086   mysa.sin_family = AF_INET;\r
10087   mysa.sin_addr.s_addr = INADDR_ANY;\r
10088   for (fromPort = 1023;; fromPort--) {\r
10089     if (fromPort < 0) {\r
10090       WSACleanup();\r
10091       return WSAEADDRINUSE;\r
10092     }\r
10093     if (s == INVALID_SOCKET) {\r
10094       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10095         err = WSAGetLastError();\r
10096         WSACleanup();\r
10097         return err;\r
10098       }\r
10099     }\r
10100     uport = (unsigned short) fromPort;\r
10101     mysa.sin_port = htons(uport);\r
10102     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10103         == SOCKET_ERROR) {\r
10104       err = WSAGetLastError();\r
10105       if (err == WSAEADDRINUSE) continue;\r
10106       WSACleanup();\r
10107       return err;\r
10108     }\r
10109     if (connect(s, (struct sockaddr *) &sa,\r
10110       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10111       err = WSAGetLastError();\r
10112       if (err == WSAEADDRINUSE) {\r
10113         closesocket(s);\r
10114         s = -1;\r
10115         continue;\r
10116       }\r
10117       WSACleanup();\r
10118       return err;\r
10119     }\r
10120     break;\r
10121   }\r
10122 \r
10123   /* Bind stderr local socket to unused "privileged" port address\r
10124    */\r
10125   s2 = INVALID_SOCKET;\r
10126   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10127   mysa.sin_family = AF_INET;\r
10128   mysa.sin_addr.s_addr = INADDR_ANY;\r
10129   for (fromPort = 1023;; fromPort--) {\r
10130     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10131     if (fromPort < 0) {\r
10132       (void) closesocket(s);\r
10133       WSACleanup();\r
10134       return WSAEADDRINUSE;\r
10135     }\r
10136     if (s2 == INVALID_SOCKET) {\r
10137       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10138         err = WSAGetLastError();\r
10139         closesocket(s);\r
10140         WSACleanup();\r
10141         return err;\r
10142       }\r
10143     }\r
10144     uport = (unsigned short) fromPort;\r
10145     mysa.sin_port = htons(uport);\r
10146     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10147         == SOCKET_ERROR) {\r
10148       err = WSAGetLastError();\r
10149       if (err == WSAEADDRINUSE) continue;\r
10150       (void) closesocket(s);\r
10151       WSACleanup();\r
10152       return err;\r
10153     }\r
10154     if (listen(s2, 1) == SOCKET_ERROR) {\r
10155       err = WSAGetLastError();\r
10156       if (err == WSAEADDRINUSE) {\r
10157         closesocket(s2);\r
10158         s2 = INVALID_SOCKET;\r
10159         continue;\r
10160       }\r
10161       (void) closesocket(s);\r
10162       (void) closesocket(s2);\r
10163       WSACleanup();\r
10164       return err;\r
10165     }\r
10166     break;\r
10167   }\r
10168   prevStderrPort = fromPort; // remember port used\r
10169   sprintf(stderrPortStr, "%d", fromPort);\r
10170 \r
10171   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10172     err = WSAGetLastError();\r
10173     (void) closesocket(s);\r
10174     (void) closesocket(s2);\r
10175     WSACleanup();\r
10176     return err;\r
10177   }\r
10178 \r
10179   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10180     err = WSAGetLastError();\r
10181     (void) closesocket(s);\r
10182     (void) closesocket(s2);\r
10183     WSACleanup();\r
10184     return err;\r
10185   }\r
10186   if (*user == NULLCHAR) user = UserName();\r
10187   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10188     err = WSAGetLastError();\r
10189     (void) closesocket(s);\r
10190     (void) closesocket(s2);\r
10191     WSACleanup();\r
10192     return err;\r
10193   }\r
10194   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10195     err = WSAGetLastError();\r
10196     (void) closesocket(s);\r
10197     (void) closesocket(s2);\r
10198     WSACleanup();\r
10199     return err;\r
10200   }\r
10201 \r
10202   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10203     err = WSAGetLastError();\r
10204     (void) closesocket(s);\r
10205     (void) closesocket(s2);\r
10206     WSACleanup();\r
10207     return err;\r
10208   }\r
10209   (void) closesocket(s2);  /* Stop listening */\r
10210 \r
10211   /* Prepare return value */\r
10212   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10213   cp->kind = CPRcmd;\r
10214   cp->sock = s;\r
10215   cp->sock2 = s3;\r
10216   *pr = (ProcRef *) cp;\r
10217 \r
10218   return NO_ERROR;\r
10219 }\r
10220 \r
10221 \r
10222 InputSourceRef\r
10223 AddInputSource(ProcRef pr, int lineByLine,\r
10224                InputCallback func, VOIDSTAR closure)\r
10225 {\r
10226   InputSource *is, *is2 = NULL;\r
10227   ChildProc *cp = (ChildProc *) pr;\r
10228 \r
10229   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10230   is->lineByLine = lineByLine;\r
10231   is->func = func;\r
10232   is->closure = closure;\r
10233   is->second = NULL;\r
10234   is->next = is->buf;\r
10235   if (pr == NoProc) {\r
10236     is->kind = CPReal;\r
10237     consoleInputSource = is;\r
10238   } else {\r
10239     is->kind = cp->kind;\r
10240     /* \r
10241         [AS] Try to avoid a race condition if the thread is given control too early:\r
10242         we create all threads suspended so that the is->hThread variable can be\r
10243         safely assigned, then let the threads start with ResumeThread.\r
10244     */\r
10245     switch (cp->kind) {\r
10246     case CPReal:\r
10247       is->hFile = cp->hFrom;\r
10248       cp->hFrom = NULL; /* now owned by InputThread */\r
10249       is->hThread =\r
10250         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10251                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10252       break;\r
10253 \r
10254     case CPComm:\r
10255       is->hFile = cp->hFrom;\r
10256       cp->hFrom = NULL; /* now owned by InputThread */\r
10257       is->hThread =\r
10258         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10259                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10260       break;\r
10261 \r
10262     case CPSock:\r
10263       is->sock = cp->sock;\r
10264       is->hThread =\r
10265         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10266                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10267       break;\r
10268 \r
10269     case CPRcmd:\r
10270       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10271       *is2 = *is;\r
10272       is->sock = cp->sock;\r
10273       is->second = is2;\r
10274       is2->sock = cp->sock2;\r
10275       is2->second = is2;\r
10276       is->hThread =\r
10277         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10278                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10279       is2->hThread =\r
10280         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10281                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10282       break;\r
10283     }\r
10284 \r
10285     if( is->hThread != NULL ) {\r
10286         ResumeThread( is->hThread );\r
10287     }\r
10288 \r
10289     if( is2 != NULL && is2->hThread != NULL ) {\r
10290         ResumeThread( is2->hThread );\r
10291     }\r
10292   }\r
10293 \r
10294   return (InputSourceRef) is;\r
10295 }\r
10296 \r
10297 void\r
10298 RemoveInputSource(InputSourceRef isr)\r
10299 {\r
10300   InputSource *is;\r
10301 \r
10302   is = (InputSource *) isr;\r
10303   is->hThread = NULL;  /* tell thread to stop */\r
10304   CloseHandle(is->hThread);\r
10305   if (is->second != NULL) {\r
10306     is->second->hThread = NULL;\r
10307     CloseHandle(is->second->hThread);\r
10308   }\r
10309 }\r
10310 \r
10311 \r
10312 int\r
10313 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10314 {\r
10315   DWORD dOutCount;\r
10316   int outCount = SOCKET_ERROR;\r
10317   ChildProc *cp = (ChildProc *) pr;\r
10318   static OVERLAPPED ovl;\r
10319 \r
10320   if (pr == NoProc) {\r
10321     ConsoleOutput(message, count, FALSE);\r
10322     return count;\r
10323   } \r
10324 \r
10325   if (ovl.hEvent == NULL) {\r
10326     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10327   }\r
10328   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10329 \r
10330   switch (cp->kind) {\r
10331   case CPSock:\r
10332   case CPRcmd:\r
10333     outCount = send(cp->sock, message, count, 0);\r
10334     if (outCount == SOCKET_ERROR) {\r
10335       *outError = WSAGetLastError();\r
10336     } else {\r
10337       *outError = NO_ERROR;\r
10338     }\r
10339     break;\r
10340 \r
10341   case CPReal:\r
10342     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10343                   &dOutCount, NULL)) {\r
10344       *outError = NO_ERROR;\r
10345       outCount = (int) dOutCount;\r
10346     } else {\r
10347       *outError = GetLastError();\r
10348     }\r
10349     break;\r
10350 \r
10351   case CPComm:\r
10352     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10353                             &dOutCount, &ovl);\r
10354     if (*outError == NO_ERROR) {\r
10355       outCount = (int) dOutCount;\r
10356     }\r
10357     break;\r
10358   }\r
10359   return outCount;\r
10360 }\r
10361 \r
10362 int\r
10363 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10364                        long msdelay)\r
10365 {\r
10366   /* Ignore delay, not implemented for WinBoard */\r
10367   return OutputToProcess(pr, message, count, outError);\r
10368 }\r
10369 \r
10370 \r
10371 void\r
10372 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10373                         char *buf, int count, int error)\r
10374 {\r
10375   DisplayFatalError("Not implemented", 0, 1);\r
10376 }\r
10377 \r
10378 /* see wgamelist.c for Game List functions */\r
10379 /* see wedittags.c for Edit Tags functions */\r
10380 \r
10381 \r
10382 VOID\r
10383 ICSInitScript()\r
10384 {\r
10385   FILE *f;\r
10386   char buf[MSG_SIZ];\r
10387   char *dummy;\r
10388 \r
10389   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10390     f = fopen(buf, "r");\r
10391     if (f != NULL) {\r
10392       ProcessICSInitScript(f);\r
10393       fclose(f);\r
10394     }\r
10395   }\r
10396 }\r
10397 \r
10398 \r
10399 VOID\r
10400 StartAnalysisClock()\r
10401 {\r
10402   if (analysisTimerEvent) return;\r
10403   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10404                                         (UINT) 2000, NULL);\r
10405 }\r
10406 \r
10407 LRESULT CALLBACK\r
10408 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10409 {\r
10410   static HANDLE hwndText;\r
10411   RECT rect;\r
10412   static int sizeX, sizeY;\r
10413   int newSizeX, newSizeY, flags;\r
10414   MINMAXINFO *mmi;\r
10415 \r
10416   switch (message) {\r
10417   case WM_INITDIALOG: /* message: initialize dialog box */\r
10418     /* Initialize the dialog items */\r
10419     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10420     SetWindowText(hDlg, analysisTitle);\r
10421     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10422     /* Size and position the dialog */\r
10423     if (!analysisDialog) {\r
10424       analysisDialog = hDlg;\r
10425       flags = SWP_NOZORDER;\r
10426       GetClientRect(hDlg, &rect);\r
10427       sizeX = rect.right;\r
10428       sizeY = rect.bottom;\r
10429       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10430           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10431         WINDOWPLACEMENT wp;\r
10432         EnsureOnScreen(&analysisX, &analysisY);\r
10433         wp.length = sizeof(WINDOWPLACEMENT);\r
10434         wp.flags = 0;\r
10435         wp.showCmd = SW_SHOW;\r
10436         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10437         wp.rcNormalPosition.left = analysisX;\r
10438         wp.rcNormalPosition.right = analysisX + analysisW;\r
10439         wp.rcNormalPosition.top = analysisY;\r
10440         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10441         SetWindowPlacement(hDlg, &wp);\r
10442 \r
10443         GetClientRect(hDlg, &rect);\r
10444         newSizeX = rect.right;\r
10445         newSizeY = rect.bottom;\r
10446         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10447                               newSizeX, newSizeY);\r
10448         sizeX = newSizeX;\r
10449         sizeY = newSizeY;\r
10450       }\r
10451     }\r
10452     return FALSE;\r
10453 \r
10454   case WM_COMMAND: /* message: received a command */\r
10455     switch (LOWORD(wParam)) {\r
10456     case IDCANCEL:\r
10457       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10458           ExitAnalyzeMode();\r
10459           ModeHighlight();\r
10460           return TRUE;\r
10461       }\r
10462       EditGameEvent();\r
10463       return TRUE;\r
10464     default:\r
10465       break;\r
10466     }\r
10467     break;\r
10468 \r
10469   case WM_SIZE:\r
10470     newSizeX = LOWORD(lParam);\r
10471     newSizeY = HIWORD(lParam);\r
10472     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10473     sizeX = newSizeX;\r
10474     sizeY = newSizeY;\r
10475     break;\r
10476 \r
10477   case WM_GETMINMAXINFO:\r
10478     /* Prevent resizing window too small */\r
10479     mmi = (MINMAXINFO *) lParam;\r
10480     mmi->ptMinTrackSize.x = 100;\r
10481     mmi->ptMinTrackSize.y = 100;\r
10482     break;\r
10483   }\r
10484   return FALSE;\r
10485 }\r
10486 \r
10487 VOID\r
10488 AnalysisPopUp(char* title, char* str)\r
10489 {\r
10490   FARPROC lpProc;\r
10491   char *p, *q;\r
10492 \r
10493   /* [AS] */\r
10494   EngineOutputPopUp();\r
10495   return;\r
10496 \r
10497   if (str == NULL) str = "";\r
10498   p = (char *) malloc(2 * strlen(str) + 2);\r
10499   q = p;\r
10500   while (*str) {\r
10501     if (*str == '\n') *q++ = '\r';\r
10502     *q++ = *str++;\r
10503   }\r
10504   *q = NULLCHAR;\r
10505   if (analysisText != NULL) free(analysisText);\r
10506   analysisText = p;\r
10507 \r
10508   if (analysisDialog) {\r
10509     SetWindowText(analysisDialog, title);\r
10510     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10511     ShowWindow(analysisDialog, SW_SHOW);\r
10512   } else {\r
10513     analysisTitle = title;\r
10514     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10515     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10516                  hwndMain, (DLGPROC)lpProc);\r
10517     FreeProcInstance(lpProc);\r
10518   }\r
10519   analysisDialogUp = TRUE;  \r
10520 }\r
10521 \r
10522 VOID\r
10523 AnalysisPopDown()\r
10524 {\r
10525   if (analysisDialog) {\r
10526     ShowWindow(analysisDialog, SW_HIDE);\r
10527   }\r
10528   analysisDialogUp = FALSE;  \r
10529 }\r
10530 \r
10531 \r
10532 VOID\r
10533 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10534 {\r
10535   highlightInfo.sq[0].x = fromX;\r
10536   highlightInfo.sq[0].y = fromY;\r
10537   highlightInfo.sq[1].x = toX;\r
10538   highlightInfo.sq[1].y = toY;\r
10539 }\r
10540 \r
10541 VOID\r
10542 ClearHighlights()\r
10543 {\r
10544   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10545     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10546 }\r
10547 \r
10548 VOID\r
10549 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10550 {\r
10551   premoveHighlightInfo.sq[0].x = fromX;\r
10552   premoveHighlightInfo.sq[0].y = fromY;\r
10553   premoveHighlightInfo.sq[1].x = toX;\r
10554   premoveHighlightInfo.sq[1].y = toY;\r
10555 }\r
10556 \r
10557 VOID\r
10558 ClearPremoveHighlights()\r
10559 {\r
10560   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10561     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10562 }\r
10563 \r
10564 VOID\r
10565 ShutDownFrontEnd()\r
10566 {\r
10567   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10568   DeleteClipboardTempFiles();\r
10569 }\r
10570 \r
10571 void\r
10572 BoardToTop()\r
10573 {\r
10574     if (IsIconic(hwndMain))\r
10575       ShowWindow(hwndMain, SW_RESTORE);\r
10576 \r
10577     SetActiveWindow(hwndMain);\r
10578 }\r
10579 \r
10580 /*\r
10581  * Prototypes for animation support routines\r
10582  */\r
10583 static void ScreenSquare(int column, int row, POINT * pt);\r
10584 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10585      POINT frames[], int * nFrames);\r
10586 \r
10587 \r
10588 void\r
10589 AnimateAtomicCapture(int toX, int toY, int nFrames)\r
10590 {       // [HGM] atomic: animate blast wave\r
10591         int i;\r
10592 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10593         explodeInfo.x = toX;\r
10594         explodeInfo.y = toY;\r
10595         for(i=0; i<nFrames; i++) {\r
10596             explodeInfo.radius = (i*180)/(nFrames-1);\r
10597             DrawPosition(FALSE, NULL);\r
10598             Sleep(appData.animSpeed);\r
10599         }\r
10600         explodeInfo.radius = 0;\r
10601         DrawPosition(TRUE, NULL);\r
10602 }\r
10603 \r
10604 #define kFactor 4\r
10605 \r
10606 void\r
10607 AnimateMove(board, fromX, fromY, toX, toY)\r
10608      Board board;\r
10609      int fromX;\r
10610      int fromY;\r
10611      int toX;\r
10612      int toY;\r
10613 {\r
10614   ChessSquare piece;\r
10615   POINT start, finish, mid;\r
10616   POINT frames[kFactor * 2 + 1];\r
10617   int nFrames, n;\r
10618 \r
10619   if (!appData.animate) return;\r
10620   if (doingSizing) return;\r
10621   if (fromY < 0 || fromX < 0) return;\r
10622   piece = board[fromY][fromX];\r
10623   if (piece >= EmptySquare) return;\r
10624 \r
10625   ScreenSquare(fromX, fromY, &start);\r
10626   ScreenSquare(toX, toY, &finish);\r
10627 \r
10628   /* All pieces except knights move in straight line */\r
10629   if (piece != WhiteKnight && piece != BlackKnight) {\r
10630     mid.x = start.x + (finish.x - start.x) / 2;\r
10631     mid.y = start.y + (finish.y - start.y) / 2;\r
10632   } else {\r
10633     /* Knight: make diagonal movement then straight */\r
10634     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10635        mid.x = start.x + (finish.x - start.x) / 2;\r
10636        mid.y = finish.y;\r
10637      } else {\r
10638        mid.x = finish.x;\r
10639        mid.y = start.y + (finish.y - start.y) / 2;\r
10640      }\r
10641   }\r
10642   \r
10643   /* Don't use as many frames for very short moves */\r
10644   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10645     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10646   else\r
10647     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10648 \r
10649   animInfo.from.x = fromX;\r
10650   animInfo.from.y = fromY;\r
10651   animInfo.to.x = toX;\r
10652   animInfo.to.y = toY;\r
10653   animInfo.lastpos = start;\r
10654   animInfo.piece = piece;\r
10655   for (n = 0; n < nFrames; n++) {\r
10656     animInfo.pos = frames[n];\r
10657     DrawPosition(FALSE, NULL);\r
10658     animInfo.lastpos = animInfo.pos;\r
10659     Sleep(appData.animSpeed);\r
10660   }\r
10661   animInfo.pos = finish;\r
10662   DrawPosition(FALSE, NULL);\r
10663   animInfo.piece = EmptySquare;\r
10664   if(gameInfo.variant == VariantAtomic && board[toY][toX] != EmptySquare)\r
10665     AnimateAtomicCapture(toX, toY, 2*nFrames);\r
10666 }\r
10667 \r
10668 /*      Convert board position to corner of screen rect and color       */\r
10669 \r
10670 static void\r
10671 ScreenSquare(column, row, pt)\r
10672      int column; int row; POINT * pt;\r
10673 {\r
10674   if (flipView) {\r
10675     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10676     pt->y = lineGap + row * (squareSize + lineGap);\r
10677   } else {\r
10678     pt->x = lineGap + column * (squareSize + lineGap);\r
10679     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10680   }\r
10681 }\r
10682 \r
10683 /*      Generate a series of frame coords from start->mid->finish.\r
10684         The movement rate doubles until the half way point is\r
10685         reached, then halves back down to the final destination,\r
10686         which gives a nice slow in/out effect. The algorithmn\r
10687         may seem to generate too many intermediates for short\r
10688         moves, but remember that the purpose is to attract the\r
10689         viewers attention to the piece about to be moved and\r
10690         then to where it ends up. Too few frames would be less\r
10691         noticeable.                                             */\r
10692 \r
10693 static void\r
10694 Tween(start, mid, finish, factor, frames, nFrames)\r
10695      POINT * start; POINT * mid;\r
10696      POINT * finish; int factor;\r
10697      POINT frames[]; int * nFrames;\r
10698 {\r
10699   int n, fraction = 1, count = 0;\r
10700 \r
10701   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10702   for (n = 0; n < factor; n++)\r
10703     fraction *= 2;\r
10704   for (n = 0; n < factor; n++) {\r
10705     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10706     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10707     count ++;\r
10708     fraction = fraction / 2;\r
10709   }\r
10710   \r
10711   /* Midpoint */\r
10712   frames[count] = *mid;\r
10713   count ++;\r
10714   \r
10715   /* Slow out, stepping 1/2, then 1/4, ... */\r
10716   fraction = 2;\r
10717   for (n = 0; n < factor; n++) {\r
10718     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10719     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10720     count ++;\r
10721     fraction = fraction * 2;\r
10722   }\r
10723   *nFrames = count;\r
10724 }\r
10725 \r
10726 void\r
10727 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10728 {\r
10729 #if 0\r
10730     char buf[256];\r
10731 \r
10732     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10733         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10734 \r
10735     OutputDebugString( buf );\r
10736 #endif\r
10737 \r
10738     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10739 \r
10740     EvalGraphSet( first, last, current, pvInfoList );\r
10741 }\r
10742 \r
10743 void SetProgramStats( FrontEndProgramStats * stats )\r
10744 {\r
10745 #if 0\r
10746     char buf[1024];\r
10747 \r
10748     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10749         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10750 \r
10751     OutputDebugString( buf );\r
10752 #endif\r
10753 \r
10754     EngineOutputUpdate( stats );\r
10755 }\r