fixed some bugs in the animation of atomic captures
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
107 \r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int fromX, fromY, toX, toY, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo;\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "ICS Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
156 static int squareSize, lineGap, minorSize;\r
157 static int winWidth, winHeight;\r
158 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
159 static int logoHeight = 0;\r
160 static char messageText[MESSAGE_TEXT_MAX];\r
161 static int clockTimerEvent = 0;\r
162 static int loadGameTimerEvent = 0;\r
163 static int analysisTimerEvent = 0;\r
164 static DelayedEventCallback delayedTimerCallback;\r
165 static int delayedTimerEvent = 0;\r
166 static int buttonCount = 2;\r
167 char *icsTextMenuString;\r
168 char *icsNames;\r
169 char *firstChessProgramNames;\r
170 char *secondChessProgramNames;\r
171 \r
172 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
173 \r
174 #define PALETTESIZE 256\r
175 \r
176 HINSTANCE hInst;          /* current instance */\r
177 HWND hwndMain = NULL;        /* root window*/\r
178 HWND hwndConsole = NULL;\r
179 BOOLEAN alwaysOnTop = FALSE;\r
180 RECT boardRect;\r
181 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
182   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
183 HPALETTE hPal;\r
184 ColorClass currentColorClass;\r
185 \r
186 HWND hCommPort = NULL;    /* currently open comm port */\r
187 static HWND hwndPause;    /* pause button */\r
188 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
189 static HBRUSH lightSquareBrush, darkSquareBrush,\r
190   blackSquareBrush, /* [HGM] for band between board and holdings */\r
191   explodeBrush,     /* [HGM] atomic */\r
192   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
193 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
194 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
195 static HPEN gridPen = NULL;\r
196 static HPEN highlightPen = NULL;\r
197 static HPEN premovePen = NULL;\r
198 static NPLOGPALETTE pLogPal;\r
199 static BOOL paletteChanged = FALSE;\r
200 static HICON iconWhite, iconBlack, iconCurrent;\r
201 static int doingSizing = FALSE;\r
202 static int lastSizing = 0;\r
203 static int prevStderrPort;\r
204 \r
205 /* [AS] Support for background textures */\r
206 #define BACK_TEXTURE_MODE_DISABLED      0\r
207 #define BACK_TEXTURE_MODE_PLAIN         1\r
208 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
209 \r
210 static HBITMAP liteBackTexture = NULL;\r
211 static HBITMAP darkBackTexture = NULL;\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int backTextureSquareSize = 0;\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
216 \r
217 #if __GNUC__ && !defined(_winmajor)\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
219 #else\r
220 #define oldDialog (_winmajor < 4)\r
221 #endif\r
222 \r
223 char *defaultTextAttribs[] = \r
224 {\r
225   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
226   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
227   COLOR_NONE\r
228 };\r
229 \r
230 typedef struct {\r
231   char *name;\r
232   int squareSize;\r
233   int lineGap;\r
234   int smallLayout;\r
235   int tinyLayout;\r
236   int cliWidth, cliHeight;\r
237 } SizeInfo;\r
238 \r
239 SizeInfo sizeInfo[] = \r
240 {\r
241   { "tiny",     21, 0, 1, 1, 0, 0 },\r
242   { "teeny",    25, 1, 1, 1, 0, 0 },\r
243   { "dinky",    29, 1, 1, 1, 0, 0 },\r
244   { "petite",   33, 1, 1, 1, 0, 0 },\r
245   { "slim",     37, 2, 1, 0, 0, 0 },\r
246   { "small",    40, 2, 1, 0, 0, 0 },\r
247   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
248   { "middling", 49, 2, 0, 0, 0, 0 },\r
249   { "average",  54, 2, 0, 0, 0, 0 },\r
250   { "moderate", 58, 3, 0, 0, 0, 0 },\r
251   { "medium",   64, 3, 0, 0, 0, 0 },\r
252   { "bulky",    72, 3, 0, 0, 0, 0 },\r
253   { "large",    80, 3, 0, 0, 0, 0 },\r
254   { "big",      87, 3, 0, 0, 0, 0 },\r
255   { "huge",     95, 3, 0, 0, 0, 0 },\r
256   { "giant",    108, 3, 0, 0, 0, 0 },\r
257   { "colossal", 116, 4, 0, 0, 0, 0 },\r
258   { "titanic",  129, 4, 0, 0, 0, 0 },\r
259   { NULL, 0, 0, 0, 0, 0, 0 }\r
260 };\r
261 \r
262 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
263 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
264 {\r
265   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
266   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
267   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
268   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
269   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
270   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
283 };\r
284 \r
285 MyFont *font[NUM_SIZES][NUM_FONTS];\r
286 \r
287 typedef struct {\r
288   char *label;\r
289   int id;\r
290   HWND hwnd;\r
291   WNDPROC wndproc;\r
292 } MyButtonDesc;\r
293 \r
294 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
295 #define N_BUTTONS 5\r
296 \r
297 MyButtonDesc buttonDesc[N_BUTTONS] =\r
298 {\r
299   {"<<", IDM_ToStart, NULL, NULL},\r
300   {"<", IDM_Backward, NULL, NULL},\r
301   {"P", IDM_Pause, NULL, NULL},\r
302   {">", IDM_Forward, NULL, NULL},\r
303   {">>", IDM_ToEnd, NULL, NULL},\r
304 };\r
305 \r
306 int tinyLayout = 0, smallLayout = 0;\r
307 #define MENU_BAR_ITEMS 6\r
308 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
309   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
310   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
311 };\r
312 \r
313 \r
314 MySound sounds[(int)NSoundClasses];\r
315 MyTextAttribs textAttribs[(int)NColorClasses];\r
316 \r
317 MyColorizeAttribs colorizeAttribs[] = {\r
318   { (COLORREF)0, 0, "Shout Text" },\r
319   { (COLORREF)0, 0, "SShout/CShout" },\r
320   { (COLORREF)0, 0, "Channel 1 Text" },\r
321   { (COLORREF)0, 0, "Channel Text" },\r
322   { (COLORREF)0, 0, "Kibitz Text" },\r
323   { (COLORREF)0, 0, "Tell Text" },\r
324   { (COLORREF)0, 0, "Challenge Text" },\r
325   { (COLORREF)0, 0, "Request Text" },\r
326   { (COLORREF)0, 0, "Seek Text" },\r
327   { (COLORREF)0, 0, "Normal Text" },\r
328   { (COLORREF)0, 0, "None" }\r
329 };\r
330 \r
331 \r
332 \r
333 static char *commentTitle;\r
334 static char *commentText;\r
335 static int commentIndex;\r
336 static Boolean editComment = FALSE;\r
337 HWND commentDialog = NULL;\r
338 BOOLEAN commentDialogUp = FALSE;\r
339 static int commentX, commentY, commentH, commentW;\r
340 \r
341 static char *analysisTitle;\r
342 static char *analysisText;\r
343 HWND analysisDialog = NULL;\r
344 BOOLEAN analysisDialogUp = FALSE;\r
345 static int analysisX, analysisY, analysisH, analysisW;\r
346 \r
347 char errorTitle[MSG_SIZ];\r
348 char errorMessage[2*MSG_SIZ];\r
349 HWND errorDialog = NULL;\r
350 BOOLEAN moveErrorMessageUp = FALSE;\r
351 BOOLEAN consoleEcho = TRUE;\r
352 CHARFORMAT consoleCF;\r
353 COLORREF consoleBackgroundColor;\r
354 \r
355 char *programVersion;\r
356 \r
357 #define CPReal 1\r
358 #define CPComm 2\r
359 #define CPSock 3\r
360 #define CPRcmd 4\r
361 typedef int CPKind;\r
362 \r
363 typedef struct {\r
364   CPKind kind;\r
365   HANDLE hProcess;\r
366   DWORD pid;\r
367   HANDLE hTo;\r
368   HANDLE hFrom;\r
369   SOCKET sock;\r
370   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
371 } ChildProc;\r
372 \r
373 #define INPUT_SOURCE_BUF_SIZE 4096\r
374 \r
375 typedef struct _InputSource {\r
376   CPKind kind;\r
377   HANDLE hFile;\r
378   SOCKET sock;\r
379   int lineByLine;\r
380   HANDLE hThread;\r
381   DWORD id;\r
382   char buf[INPUT_SOURCE_BUF_SIZE];\r
383   char *next;\r
384   DWORD count;\r
385   int error;\r
386   InputCallback func;\r
387   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
388   VOIDSTAR closure;\r
389 } InputSource;\r
390 \r
391 InputSource *consoleInputSource;\r
392 \r
393 DCB dcb;\r
394 \r
395 /* forward */\r
396 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
397 VOID ConsoleCreate();\r
398 LRESULT CALLBACK\r
399   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
400 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
401 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
402 VOID ParseCommSettings(char *arg, DCB *dcb);\r
403 LRESULT CALLBACK\r
404   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
406 void ParseIcsTextMenu(char *icsTextMenuString);\r
407 VOID PopUpMoveDialog(char firstchar);\r
408 VOID PopUpNameDialog(char firstchar);\r
409 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
410 \r
411 /* [AS] */\r
412 int NewGameFRC();\r
413 int GameListOptions();\r
414 \r
415 HWND moveHistoryDialog = NULL;\r
416 BOOLEAN moveHistoryDialogUp = FALSE;\r
417 \r
418 WindowPlacement wpMoveHistory;\r
419 \r
420 HWND evalGraphDialog = NULL;\r
421 BOOLEAN evalGraphDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpEvalGraph;\r
424 \r
425 HWND engineOutputDialog = NULL;\r
426 BOOLEAN engineOutputDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEngineOutput;\r
429 \r
430 VOID MoveHistoryPopUp();\r
431 VOID MoveHistoryPopDown();\r
432 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
433 BOOL MoveHistoryIsUp();\r
434 \r
435 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
436 VOID EvalGraphPopUp();\r
437 VOID EvalGraphPopDown();\r
438 BOOL EvalGraphIsUp();\r
439 \r
440 VOID EngineOutputPopUp();\r
441 VOID EngineOutputPopDown();\r
442 BOOL EngineOutputIsUp();\r
443 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
444 \r
445 VOID GothicPopUp(char *title, VariantClass variant);\r
446 /*\r
447  * Setting "frozen" should disable all user input other than deleting\r
448  * the window.  We do this while engines are initializing themselves.\r
449  */\r
450 static int frozen = 0;\r
451 static int oldMenuItemState[MENU_BAR_ITEMS];\r
452 void FreezeUI()\r
453 {\r
454   HMENU hmenu;\r
455   int i;\r
456 \r
457   if (frozen) return;\r
458   frozen = 1;\r
459   hmenu = GetMenu(hwndMain);\r
460   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
461     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
462   }\r
463   DrawMenuBar(hwndMain);\r
464 }\r
465 \r
466 /* Undo a FreezeUI */\r
467 void ThawUI()\r
468 {\r
469   HMENU hmenu;\r
470   int i;\r
471 \r
472   if (!frozen) return;\r
473   frozen = 0;\r
474   hmenu = GetMenu(hwndMain);\r
475   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
476     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
477   }\r
478   DrawMenuBar(hwndMain);\r
479 }\r
480 \r
481 /*---------------------------------------------------------------------------*\\r
482  *\r
483  * WinMain\r
484  *\r
485 \*---------------------------------------------------------------------------*/\r
486 \r
487 int APIENTRY\r
488 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
489         LPSTR lpCmdLine, int nCmdShow)\r
490 {\r
491   MSG msg;\r
492   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
493 //  INITCOMMONCONTROLSEX ex;\r
494 \r
495   debugFP = stderr;\r
496 \r
497   LoadLibrary("RICHED32.DLL");\r
498   consoleCF.cbSize = sizeof(CHARFORMAT);\r
499 \r
500   if (!InitApplication(hInstance)) {\r
501     return (FALSE);\r
502   }\r
503   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
504     return (FALSE);\r
505   }\r
506 \r
507 //  InitCommonControlsEx(&ex);\r
508   InitCommonControls();\r
509 \r
510   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
511   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
512   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
513 \r
514   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
515 \r
516   while (GetMessage(&msg, /* message structure */\r
517                     NULL, /* handle of window receiving the message */\r
518                     0,    /* lowest message to examine */\r
519                     0))   /* highest message to examine */\r
520     {\r
521       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
522           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
523           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
524           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
525           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
526           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
527           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
528           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
529           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
530           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
531         TranslateMessage(&msg); /* Translates virtual key codes */\r
532         DispatchMessage(&msg);  /* Dispatches message to window */\r
533       }\r
534     }\r
535 \r
536 \r
537   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
538 }\r
539 \r
540 /*---------------------------------------------------------------------------*\\r
541  *\r
542  * Initialization functions\r
543  *\r
544 \*---------------------------------------------------------------------------*/\r
545 \r
546 BOOL\r
547 InitApplication(HINSTANCE hInstance)\r
548 {\r
549   WNDCLASS wc;\r
550 \r
551   /* Fill in window class structure with parameters that describe the */\r
552   /* main window. */\r
553 \r
554   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
555   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
556   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
557   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
558   wc.hInstance     = hInstance;         /* Owner of this class */\r
559   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
560   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
561   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
562   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
563   wc.lpszClassName = szAppName;                 /* Name to register as */\r
564 \r
565   /* Register the window class and return success/failure code. */\r
566   if (!RegisterClass(&wc)) return FALSE;\r
567 \r
568   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
569   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
570   wc.cbClsExtra    = 0;\r
571   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
572   wc.hInstance     = hInstance;\r
573   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
574   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
575   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
576   wc.lpszMenuName  = NULL;\r
577   wc.lpszClassName = szConsoleName;\r
578 \r
579   if (!RegisterClass(&wc)) return FALSE;\r
580   return TRUE;\r
581 }\r
582 \r
583 \r
584 /* Set by InitInstance, used by EnsureOnScreen */\r
585 int screenHeight, screenWidth;\r
586 \r
587 void\r
588 EnsureOnScreen(int *x, int *y)\r
589 {\r
590 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
591   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
592   if (*x > screenWidth - 32) *x = 0;\r
593   if (*y > screenHeight - 32) *y = 0;\r
594   if (*x < 0) *x = 0;\r
595   if (*y < 0) *y = 0;\r
596 //  if (*x < 10) *x = 10;\r
597 //  if (*y < gap) *y = gap;\r
598 }\r
599 \r
600 BOOL\r
601 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
602 {\r
603   HWND hwnd; /* Main window handle. */\r
604   int ibs;\r
605   WINDOWPLACEMENT wp;\r
606   char *filepart;\r
607 \r
608   hInst = hInstance;    /* Store instance handle in our global variable */\r
609 \r
610   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
611     *filepart = NULLCHAR;\r
612   } else {\r
613     GetCurrentDirectory(MSG_SIZ, installDir);\r
614   }\r
615   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
616   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
617   if (appData.debugMode) {\r
618     debugFP = fopen(appData.nameOfDebugFile, "w");\r
619     setbuf(debugFP, NULL);\r
620   }\r
621 \r
622   InitBackEnd1();\r
623 \r
624 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
625 //  InitEngineUCI( installDir, &second );\r
626 \r
627   /* Create a main window for this application instance. */\r
628   hwnd = CreateWindow(szAppName, szTitle,\r
629                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
630                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
631                       NULL, NULL, hInstance, NULL);\r
632   hwndMain = hwnd;\r
633 \r
634   /* If window could not be created, return "failure" */\r
635   if (!hwnd) {\r
636     return (FALSE);\r
637   }\r
638 \r
639   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
640   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
641       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
642 \r
643       if (first.programLogo == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
645       }\r
646   } else if(appData.autoLogo) {\r
647       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
648         char buf[MSG_SIZ];\r
649         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
650         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
651       }\r
652   }\r
653 \r
654   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
655       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
656 \r
657       if (second.programLogo == NULL && appData.debugMode) {\r
658           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
659       }\r
660   } else if(appData.autoLogo) {\r
661       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
662         char buf[MSG_SIZ];\r
663         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
664         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
665       }\r
666   }\r
667 \r
668   iconWhite = LoadIcon(hInstance, "icon_white");\r
669   iconBlack = LoadIcon(hInstance, "icon_black");\r
670   iconCurrent = iconWhite;\r
671   InitDrawingColors();\r
672   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
673   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
674   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
675     /* Compute window size for each board size, and use the largest\r
676        size that fits on this screen as the default. */\r
677     InitDrawingSizes((BoardSize)ibs, 0);\r
678     if (boardSize == (BoardSize)-1 &&\r
679         winHeight <= screenHeight\r
680            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
681         && winWidth <= screenWidth) {\r
682       boardSize = (BoardSize)ibs;\r
683     }\r
684   }\r
685 \r
686   InitDrawingSizes(boardSize, 0);\r
687   InitMenuChecks();\r
688   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
689 \r
690   /* [AS] Load textures if specified */\r
691   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
692   \r
693   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
694       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
695       liteBackTextureMode = appData.liteBackTextureMode;\r
696 \r
697       if (liteBackTexture == NULL && appData.debugMode) {\r
698           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
699       }\r
700   }\r
701   \r
702   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
703       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
704       darkBackTextureMode = appData.darkBackTextureMode;\r
705 \r
706       if (darkBackTexture == NULL && appData.debugMode) {\r
707           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
708       }\r
709   }\r
710 \r
711   mysrandom( (unsigned) time(NULL) );\r
712 \r
713   /* [AS] Restore layout */\r
714   if( wpMoveHistory.visible ) {\r
715       MoveHistoryPopUp();\r
716   }\r
717 \r
718   if( wpEvalGraph.visible ) {\r
719       EvalGraphPopUp();\r
720   }\r
721 \r
722   if( wpEngineOutput.visible ) {\r
723       EngineOutputPopUp();\r
724   }\r
725 \r
726   InitBackEnd2();\r
727 \r
728   /* Make the window visible; update its client area; and return "success" */\r
729   EnsureOnScreen(&boardX, &boardY);\r
730   wp.length = sizeof(WINDOWPLACEMENT);\r
731   wp.flags = 0;\r
732   wp.showCmd = nCmdShow;\r
733   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
734   wp.rcNormalPosition.left = boardX;\r
735   wp.rcNormalPosition.right = boardX + winWidth;\r
736   wp.rcNormalPosition.top = boardY;\r
737   wp.rcNormalPosition.bottom = boardY + winHeight;\r
738   SetWindowPlacement(hwndMain, &wp);\r
739 \r
740   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
741                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
742 \r
743 #if 0\r
744   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
745   if( gameInfo.variant != VariantFischeRandom ) {\r
746       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
747   }\r
748 #endif\r
749   if (hwndConsole) {\r
750 #if AOT_CONSOLE\r
751     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
752                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
753 #endif\r
754     ShowWindow(hwndConsole, nCmdShow);\r
755   }\r
756   UpdateWindow(hwnd);\r
757 \r
758   return TRUE;\r
759 \r
760 }\r
761 \r
762 \r
763 typedef enum {\r
764   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
765   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
766   ArgSettingsFilename\r
767 } ArgType;\r
768 \r
769 typedef struct {\r
770   char *argName;\r
771   ArgType argType;\r
772   /***\r
773   union {\r
774     String *pString;       // ArgString\r
775     int *pInt;             // ArgInt\r
776     float *pFloat;         // ArgFloat\r
777     Boolean *pBoolean;     // ArgBoolean\r
778     COLORREF *pColor;      // ArgColor\r
779     ColorClass cc;         // ArgAttribs\r
780     String *pFilename;     // ArgFilename\r
781     BoardSize *pBoardSize; // ArgBoardSize\r
782     int whichFont;         // ArgFont\r
783     DCB *pDCB;             // ArgCommSettings\r
784     String *pFilename;     // ArgSettingsFilename\r
785   } argLoc;\r
786   ***/\r
787   LPVOID argLoc;\r
788   BOOL save;\r
789 } ArgDescriptor;\r
790 \r
791 int junk;\r
792 ArgDescriptor argDescriptors[] = {\r
793   /* positional arguments */\r
794   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
795   { "", ArgNone, NULL },\r
796   /* keyword arguments */\r
797   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
798   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
799   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
800   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
801   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
802   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
803   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
804   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
805   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
806   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
807   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
808   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
809   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
810   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
811   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
812   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
813   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
814   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
815     FALSE },\r
816   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
817     FALSE },\r
818   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
819     FALSE },\r
820   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
821   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
822     FALSE },\r
823   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
824   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
825   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
826   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
827   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
828   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
829   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
830   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
831   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
832   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
833   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
834   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
835   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
836   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
837   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
838   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
839   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
840   /*!!bitmapDirectory?*/\r
841   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
842   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
843   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
844   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
845   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
846   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
847   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
848   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
849   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
850   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
851   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
852   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
853   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
854   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
855   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
856   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
857   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
858   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
859   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
860   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
861   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
862   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
863   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
864   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
865   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
866   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
867   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
868   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
869   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
870   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
871   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
872   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
873   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
874   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
875   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
876   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
877   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
878   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
879   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
880   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
881   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
882   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
883   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
884   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
885   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
886   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
887   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
888   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
889   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
890   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
891   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
892   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
893   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
894   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
895   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
896   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
897   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
898   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
899   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
900   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
901   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
902   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
903   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
904   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
905   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
906   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
907   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
908   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
909   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
910   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
911   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
912   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
913   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
914   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
915   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
916   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
917   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
918   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
919   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
920   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
921   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
922   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
923   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
924   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
925   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
926   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
927   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
928   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
929   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
930   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
931   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
932   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
933   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
934   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
935     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
936   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
937   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
938   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
939   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
940   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
941   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
942   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
943   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
944     TRUE }, /* must come after all fonts */\r
945   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
946   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
947     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
948   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
949   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
950   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
951   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
952   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
953   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
954   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
955   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
956   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
957   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
958   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
959   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
960   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
961   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
962   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
963   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
964   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
965   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
966   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
967   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
968   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
969   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
970   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
971   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
972   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
973   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
974   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
975   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
976 #if 0\r
977   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
978   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
979 #endif\r
980   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
981   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
982   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
983   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
984   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
985   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
986   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
987   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
988   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
989   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
990   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
991   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
992   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
993   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
994   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
995   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
996   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
997   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
998   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
999   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1000   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1001   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1002   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1003   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1004   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1005   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1006   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1007   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1008   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1009   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1010   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1011   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1012   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1013   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1014   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1015   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1016   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1017   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1018   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1019   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1020   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1021   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1022   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1023   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1024   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1025   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1026   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1027   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1028   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1029   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1030   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1031   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1032   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1033   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1034   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1035   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1036   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1037   { "highlightLastMove", ArgBoolean,\r
1038     (LPVOID) &appData.highlightLastMove, TRUE },\r
1039   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1040   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1041   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1042   { "highlightDragging", ArgBoolean,\r
1043     (LPVOID) &appData.highlightDragging, TRUE },\r
1044   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1045   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1046   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1047   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1048   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1049   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1050   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1051   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1052   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1053   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1054   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1055   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1056   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1057   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1058   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1059   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1060   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1061   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1062   { "soundShout", ArgFilename,\r
1063     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1064   { "soundSShout", ArgFilename,\r
1065     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1066   { "soundChannel1", ArgFilename,\r
1067     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1068   { "soundChannel", ArgFilename,\r
1069     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1070   { "soundKibitz", ArgFilename,\r
1071     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1072   { "soundTell", ArgFilename,\r
1073     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1074   { "soundChallenge", ArgFilename,\r
1075     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1076   { "soundRequest", ArgFilename,\r
1077     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1078   { "soundSeek", ArgFilename,\r
1079     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1080   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1081   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1082   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1083   { "soundIcsLoss", ArgFilename, \r
1084     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1085   { "soundIcsDraw", ArgFilename, \r
1086     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1087   { "soundIcsUnfinished", ArgFilename, \r
1088     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1089   { "soundIcsAlarm", ArgFilename, \r
1090     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1091   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1092   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1093   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1094   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1095   { "reuseChessPrograms", ArgBoolean,\r
1096     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1097   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1098   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1099   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1100   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1101   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1102   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1103   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1104   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1105   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1106   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1107   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1108   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1109   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1110   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1111   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1112   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1113   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1114   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1115   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1116   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1117   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1118   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1119   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1120   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1121   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1122   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1123   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1124   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1125   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1126   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1127   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1128   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1129   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1130   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1131   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1132   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1133   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1134     TRUE },\r
1135   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1136     TRUE },\r
1137   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1138   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1139   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1140   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1141   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1142   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1143   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1144   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1145   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1146   /* [AS] New features */\r
1147   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1148   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1149   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1150   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1151   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1152   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1153   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1154   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1155   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1156   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1157   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1158   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1159   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1160   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1161   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1162   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1163   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1164   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1165   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1166   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1167   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1168   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1169   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1170   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1171   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1172   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1173   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1174   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1175   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1176   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1177   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1178   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1179   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1180   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1181   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1182   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1183   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1184   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1185   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1186   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1187   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1188   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1189   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1190   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1191   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1192   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1193   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1194   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1195   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1196   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1197 \r
1198   /* [AS] Layout stuff */\r
1199   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1200   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1201   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1202   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1203   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1204 \r
1205   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1206   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1207   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1208   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1209   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1210 \r
1211   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1212   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1213   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1214   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1215   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1216 \r
1217   /* [HGM] board-size, adjudication and misc. options */\r
1218   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1219   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1220   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1221   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1222   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1223   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1224   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1225   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1226   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1227   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1228   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1229   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1230   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1231   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1232   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1233   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1234   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1235   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1236   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1237   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1238   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1239   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1240   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1241   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1242   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1243   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1244   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1245   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1246   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1247   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1248   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1249 \r
1250 #ifdef ZIPPY\r
1251   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1252   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1253   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1254   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1255   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1256   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1257   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1258   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1259   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1260   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1261   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1262   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1263   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1264     FALSE },\r
1265   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1266   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1267   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1268   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1269   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1270   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1271   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1272     FALSE },\r
1273   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1274   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1275   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1276   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1277   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1278   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1279   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1280   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1281   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1282   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1283   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1284   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1285   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1286   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1287   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1288   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1289   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1290   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1291 #endif\r
1292   /* [HGM] options for broadcasting and time odds */\r
1293   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1294   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1295   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1296   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1297   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1298   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1299   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1300   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1301   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1302   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1303   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1304   { NULL, ArgNone, NULL, FALSE }\r
1305 };\r
1306 \r
1307 \r
1308 /* Kludge for indirection files on command line */\r
1309 char* lastIndirectionFilename;\r
1310 ArgDescriptor argDescriptorIndirection =\r
1311 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1312 \r
1313 \r
1314 VOID\r
1315 ExitArgError(char *msg, char *badArg)\r
1316 {\r
1317   char buf[MSG_SIZ];\r
1318 \r
1319   sprintf(buf, "%s %s", msg, badArg);\r
1320   DisplayFatalError(buf, 0, 2);\r
1321   exit(2);\r
1322 }\r
1323 \r
1324 /* Command line font name parser.  NULL name means do nothing.\r
1325    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1326    For backward compatibility, syntax without the colon is also\r
1327    accepted, but font names with digits in them won't work in that case.\r
1328 */\r
1329 VOID\r
1330 ParseFontName(char *name, MyFontParams *mfp)\r
1331 {\r
1332   char *p, *q;\r
1333   if (name == NULL) return;\r
1334   p = name;\r
1335   q = strchr(p, ':');\r
1336   if (q) {\r
1337     if (q - p >= sizeof(mfp->faceName))\r
1338       ExitArgError("Font name too long:", name);\r
1339     memcpy(mfp->faceName, p, q - p);\r
1340     mfp->faceName[q - p] = NULLCHAR;\r
1341     p = q + 1;\r
1342   } else {\r
1343     q = mfp->faceName;\r
1344     while (*p && !isdigit(*p)) {\r
1345       *q++ = *p++;\r
1346       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1347         ExitArgError("Font name too long:", name);\r
1348     }\r
1349     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1350     *q = NULLCHAR;\r
1351   }\r
1352   if (!*p) ExitArgError("Font point size missing:", name);\r
1353   mfp->pointSize = (float) atof(p);\r
1354   mfp->bold = (strchr(p, 'b') != NULL);\r
1355   mfp->italic = (strchr(p, 'i') != NULL);\r
1356   mfp->underline = (strchr(p, 'u') != NULL);\r
1357   mfp->strikeout = (strchr(p, 's') != NULL);\r
1358 }\r
1359 \r
1360 /* Color name parser.\r
1361    X version accepts X color names, but this one\r
1362    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1363 COLORREF\r
1364 ParseColorName(char *name)\r
1365 {\r
1366   int red, green, blue, count;\r
1367   char buf[MSG_SIZ];\r
1368 \r
1369   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1370   if (count != 3) {\r
1371     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1372       &red, &green, &blue);\r
1373   }\r
1374   if (count != 3) {\r
1375     sprintf(buf, "Can't parse color name %s", name);\r
1376     DisplayError(buf, 0);\r
1377     return RGB(0, 0, 0);\r
1378   }\r
1379   return PALETTERGB(red, green, blue);\r
1380 }\r
1381 \r
1382 \r
1383 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1384 {\r
1385   char *e = argValue;\r
1386   int eff = 0;\r
1387 \r
1388   while (*e) {\r
1389     if (*e == 'b')      eff |= CFE_BOLD;\r
1390     else if (*e == 'i') eff |= CFE_ITALIC;\r
1391     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1392     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1393     else if (*e == '#' || isdigit(*e)) break;\r
1394     e++;\r
1395   }\r
1396   *effects = eff;\r
1397   *color   = ParseColorName(e);\r
1398 }\r
1399 \r
1400 \r
1401 BoardSize\r
1402 ParseBoardSize(char *name)\r
1403 {\r
1404   BoardSize bs = SizeTiny;\r
1405   while (sizeInfo[bs].name != NULL) {\r
1406     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1407     bs++;\r
1408   }\r
1409   ExitArgError("Unrecognized board size value", name);\r
1410   return bs; /* not reached */\r
1411 }\r
1412 \r
1413 \r
1414 char\r
1415 StringGet(void *getClosure)\r
1416 {\r
1417   char **p = (char **) getClosure;\r
1418   return *((*p)++);\r
1419 }\r
1420 \r
1421 char\r
1422 FileGet(void *getClosure)\r
1423 {\r
1424   int c;\r
1425   FILE* f = (FILE*) getClosure;\r
1426 \r
1427   c = getc(f);\r
1428   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1429   if (c == EOF)\r
1430     return NULLCHAR;\r
1431   else\r
1432     return (char) c;\r
1433 }\r
1434 \r
1435 /* Parse settings file named "name". If file found, return the\r
1436    full name in fullname and return TRUE; else return FALSE */\r
1437 BOOLEAN\r
1438 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1439 {\r
1440   char *dummy;\r
1441   FILE *f;\r
1442   int ok; char buf[MSG_SIZ];\r
1443 \r
1444   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1445   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1446     sprintf(buf, "%s.ini", name);\r
1447     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1448   }\r
1449   if (ok) {\r
1450     f = fopen(fullname, "r");\r
1451     if (f != NULL) {\r
1452       ParseArgs(FileGet, f);\r
1453       fclose(f);\r
1454       return TRUE;\r
1455     }\r
1456   }\r
1457   return FALSE;\r
1458 }\r
1459 \r
1460 VOID\r
1461 ParseArgs(GetFunc get, void *cl)\r
1462 {\r
1463   char argName[ARG_MAX];\r
1464   char argValue[ARG_MAX];\r
1465   ArgDescriptor *ad;\r
1466   char start;\r
1467   char *q;\r
1468   int i, octval;\r
1469   char ch;\r
1470   int posarg = 0;\r
1471 \r
1472   ch = get(cl);\r
1473   for (;;) {\r
1474     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1475     if (ch == NULLCHAR) break;\r
1476     if (ch == ';') {\r
1477       /* Comment to end of line */\r
1478       ch = get(cl);\r
1479       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1480       continue;\r
1481     } else if (ch == '/' || ch == '-') {\r
1482       /* Switch */\r
1483       q = argName;\r
1484       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1485              ch != '\n' && ch != '\t') {\r
1486         *q++ = ch;\r
1487         ch = get(cl);\r
1488       }\r
1489       *q = NULLCHAR;\r
1490 \r
1491       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1492         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1493 \r
1494       if (ad->argName == NULL)\r
1495         ExitArgError("Unrecognized argument", argName);\r
1496 \r
1497     } else if (ch == '@') {\r
1498       /* Indirection file */\r
1499       ad = &argDescriptorIndirection;\r
1500       ch = get(cl);\r
1501     } else {\r
1502       /* Positional argument */\r
1503       ad = &argDescriptors[posarg++];\r
1504       strcpy(argName, ad->argName);\r
1505     }\r
1506 \r
1507     if (ad->argType == ArgTrue) {\r
1508       *(Boolean *) ad->argLoc = TRUE;\r
1509       continue;\r
1510     }\r
1511     if (ad->argType == ArgFalse) {\r
1512       *(Boolean *) ad->argLoc = FALSE;\r
1513       continue;\r
1514     }\r
1515 \r
1516     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1517     if (ch == NULLCHAR || ch == '\n') {\r
1518       ExitArgError("No value provided for argument", argName);\r
1519     }\r
1520     q = argValue;\r
1521     if (ch == '{') {\r
1522       // Quoting with { }.  No characters have to (or can) be escaped.\r
1523       // Thus the string cannot contain a '}' character.\r
1524       start = ch;\r
1525       ch = get(cl);\r
1526       while (start) {\r
1527         switch (ch) {\r
1528         case NULLCHAR:\r
1529           start = NULLCHAR;\r
1530           break;\r
1531           \r
1532         case '}':\r
1533           ch = get(cl);\r
1534           start = NULLCHAR;\r
1535           break;\r
1536 \r
1537         default:\r
1538           *q++ = ch;\r
1539           ch = get(cl);\r
1540           break;\r
1541         }\r
1542       }   \r
1543     } else if (ch == '\'' || ch == '"') {\r
1544       // Quoting with ' ' or " ", with \ as escape character.\r
1545       // Inconvenient for long strings that may contain Windows filenames.\r
1546       start = ch;\r
1547       ch = get(cl);\r
1548       while (start) {\r
1549         switch (ch) {\r
1550         case NULLCHAR:\r
1551           start = NULLCHAR;\r
1552           break;\r
1553 \r
1554         default:\r
1555         not_special:\r
1556           *q++ = ch;\r
1557           ch = get(cl);\r
1558           break;\r
1559 \r
1560         case '\'':\r
1561         case '\"':\r
1562           if (ch == start) {\r
1563             ch = get(cl);\r
1564             start = NULLCHAR;\r
1565             break;\r
1566           } else {\r
1567             goto not_special;\r
1568           }\r
1569 \r
1570         case '\\':\r
1571           if (ad->argType == ArgFilename\r
1572               || ad->argType == ArgSettingsFilename) {\r
1573               goto not_special;\r
1574           }\r
1575           ch = get(cl);\r
1576           switch (ch) {\r
1577           case NULLCHAR:\r
1578             ExitArgError("Incomplete \\ escape in value for", argName);\r
1579             break;\r
1580           case 'n':\r
1581             *q++ = '\n';\r
1582             ch = get(cl);\r
1583             break;\r
1584           case 'r':\r
1585             *q++ = '\r';\r
1586             ch = get(cl);\r
1587             break;\r
1588           case 't':\r
1589             *q++ = '\t';\r
1590             ch = get(cl);\r
1591             break;\r
1592           case 'b':\r
1593             *q++ = '\b';\r
1594             ch = get(cl);\r
1595             break;\r
1596           case 'f':\r
1597             *q++ = '\f';\r
1598             ch = get(cl);\r
1599             break;\r
1600           default:\r
1601             octval = 0;\r
1602             for (i = 0; i < 3; i++) {\r
1603               if (ch >= '0' && ch <= '7') {\r
1604                 octval = octval*8 + (ch - '0');\r
1605                 ch = get(cl);\r
1606               } else {\r
1607                 break;\r
1608               }\r
1609             }\r
1610             if (i > 0) {\r
1611               *q++ = (char) octval;\r
1612             } else {\r
1613               *q++ = ch;\r
1614               ch = get(cl);\r
1615             }\r
1616             break;\r
1617           }\r
1618           break;\r
1619         }\r
1620       }\r
1621     } else {\r
1622       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1623         *q++ = ch;\r
1624         ch = get(cl);\r
1625       }\r
1626     }\r
1627     *q = NULLCHAR;\r
1628 \r
1629     switch (ad->argType) {\r
1630     case ArgInt:\r
1631       *(int *) ad->argLoc = atoi(argValue);\r
1632       break;\r
1633 \r
1634     case ArgFloat:\r
1635       *(float *) ad->argLoc = (float) atof(argValue);\r
1636       break;\r
1637 \r
1638     case ArgString:\r
1639     case ArgFilename:\r
1640       *(char **) ad->argLoc = strdup(argValue);\r
1641       break;\r
1642 \r
1643     case ArgSettingsFilename:\r
1644       {\r
1645         char fullname[MSG_SIZ];\r
1646         if (ParseSettingsFile(argValue, fullname)) {\r
1647           if (ad->argLoc != NULL) {\r
1648             *(char **) ad->argLoc = strdup(fullname);\r
1649           }\r
1650         } else {\r
1651           if (ad->argLoc != NULL) {\r
1652           } else {\r
1653             ExitArgError("Failed to open indirection file", argValue);\r
1654           }\r
1655         }\r
1656       }\r
1657       break;\r
1658 \r
1659     case ArgBoolean:\r
1660       switch (argValue[0]) {\r
1661       case 't':\r
1662       case 'T':\r
1663         *(Boolean *) ad->argLoc = TRUE;\r
1664         break;\r
1665       case 'f':\r
1666       case 'F':\r
1667         *(Boolean *) ad->argLoc = FALSE;\r
1668         break;\r
1669       default:\r
1670         ExitArgError("Unrecognized boolean argument value", argValue);\r
1671         break;\r
1672       }\r
1673       break;\r
1674 \r
1675     case ArgColor:\r
1676       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1677       break;\r
1678 \r
1679     case ArgAttribs: {\r
1680       ColorClass cc = (ColorClass)ad->argLoc;\r
1681       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1682       }\r
1683       break;\r
1684       \r
1685     case ArgBoardSize:\r
1686       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1687       break;\r
1688 \r
1689     case ArgFont:\r
1690       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1691       break;\r
1692 \r
1693     case ArgCommSettings:\r
1694       ParseCommSettings(argValue, &dcb);\r
1695       break;\r
1696 \r
1697     case ArgNone:\r
1698       ExitArgError("Unrecognized argument", argValue);\r
1699       break;\r
1700     case ArgTrue:\r
1701     case ArgFalse: ;\r
1702     }\r
1703   }\r
1704 }\r
1705 \r
1706 VOID\r
1707 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1708 {\r
1709   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1710   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1711   DeleteDC(hdc);\r
1712   lf->lfWidth = 0;\r
1713   lf->lfEscapement = 0;\r
1714   lf->lfOrientation = 0;\r
1715   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1716   lf->lfItalic = mfp->italic;\r
1717   lf->lfUnderline = mfp->underline;\r
1718   lf->lfStrikeOut = mfp->strikeout;\r
1719   lf->lfCharSet = DEFAULT_CHARSET;\r
1720   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1721   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1722   lf->lfQuality = DEFAULT_QUALITY;\r
1723   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1724   strcpy(lf->lfFaceName, mfp->faceName);\r
1725 }\r
1726 \r
1727 VOID\r
1728 CreateFontInMF(MyFont *mf)\r
1729 {\r
1730   LFfromMFP(&mf->lf, &mf->mfp);\r
1731   if (mf->hf) DeleteObject(mf->hf);\r
1732   mf->hf = CreateFontIndirect(&mf->lf);\r
1733 }\r
1734 \r
1735 VOID\r
1736 SetDefaultTextAttribs()\r
1737 {\r
1738   ColorClass cc;\r
1739   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1740     ParseAttribs(&textAttribs[cc].color, \r
1741                  &textAttribs[cc].effects, \r
1742                  defaultTextAttribs[cc]);\r
1743   }\r
1744 }\r
1745 \r
1746 VOID\r
1747 SetDefaultSounds()\r
1748 {\r
1749   ColorClass cc;\r
1750   SoundClass sc;\r
1751   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1752     textAttribs[cc].sound.name = strdup("");\r
1753     textAttribs[cc].sound.data = NULL;\r
1754   }\r
1755   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1756     sounds[sc].name = strdup("");\r
1757     sounds[sc].data = NULL;\r
1758   }\r
1759   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1760 }\r
1761 \r
1762 VOID\r
1763 LoadAllSounds()\r
1764 {\r
1765   ColorClass cc;\r
1766   SoundClass sc;\r
1767   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1768     MyLoadSound(&textAttribs[cc].sound);\r
1769   }\r
1770   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1771     MyLoadSound(&sounds[sc]);\r
1772   }\r
1773 }\r
1774 \r
1775 VOID\r
1776 InitAppData(LPSTR lpCmdLine)\r
1777 {\r
1778   int i, j;\r
1779   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1780   char *dummy, *p;\r
1781 \r
1782   programName = szAppName;\r
1783 \r
1784   /* Initialize to defaults */\r
1785   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1786   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1787   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1788   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1789   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1790   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1791   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1792   SetDefaultTextAttribs();\r
1793   SetDefaultSounds();\r
1794   appData.movesPerSession = MOVES_PER_SESSION;\r
1795   appData.initString = INIT_STRING;\r
1796   appData.secondInitString = INIT_STRING;\r
1797   appData.firstComputerString = COMPUTER_STRING;\r
1798   appData.secondComputerString = COMPUTER_STRING;\r
1799   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1800   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1801   appData.firstPlaysBlack = FALSE;\r
1802   appData.noChessProgram = FALSE;\r
1803   chessProgram = FALSE;\r
1804   appData.firstHost = FIRST_HOST;\r
1805   appData.secondHost = SECOND_HOST;\r
1806   appData.firstDirectory = FIRST_DIRECTORY;\r
1807   appData.secondDirectory = SECOND_DIRECTORY;\r
1808   appData.bitmapDirectory = "";\r
1809   appData.remoteShell = REMOTE_SHELL;\r
1810   appData.remoteUser = "";\r
1811   appData.timeDelay = TIME_DELAY;\r
1812   appData.timeControl = TIME_CONTROL;\r
1813   appData.timeIncrement = TIME_INCREMENT;\r
1814   appData.icsActive = FALSE;\r
1815   appData.icsHost = "";\r
1816   appData.icsPort = ICS_PORT;\r
1817   appData.icsCommPort = ICS_COMM_PORT;\r
1818   appData.icsLogon = ICS_LOGON;\r
1819   appData.icsHelper = "";\r
1820   appData.useTelnet = FALSE;\r
1821   appData.telnetProgram = TELNET_PROGRAM;\r
1822   appData.gateway = "";\r
1823   appData.loadGameFile = "";\r
1824   appData.loadGameIndex = 0;\r
1825   appData.saveGameFile = "";\r
1826   appData.autoSaveGames = FALSE;\r
1827   appData.loadPositionFile = "";\r
1828   appData.loadPositionIndex = 1;\r
1829   appData.savePositionFile = "";\r
1830   appData.matchMode = FALSE;\r
1831   appData.matchGames = 0;\r
1832   appData.monoMode = FALSE;\r
1833   appData.debugMode = FALSE;\r
1834   appData.clockMode = TRUE;\r
1835   boardSize = (BoardSize) -1; /* determine by screen size */\r
1836   appData.Iconic = FALSE; /*unused*/\r
1837   appData.searchTime = "";\r
1838   appData.searchDepth = 0;\r
1839   appData.showCoords = FALSE;\r
1840   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1841   appData.autoCallFlag = FALSE;\r
1842   appData.flipView = FALSE;\r
1843   appData.autoFlipView = TRUE;\r
1844   appData.cmailGameName = "";\r
1845   appData.alwaysPromoteToQueen = FALSE;\r
1846   appData.oldSaveStyle = FALSE;\r
1847   appData.quietPlay = FALSE;\r
1848   appData.showThinking = FALSE;\r
1849   appData.ponderNextMove = TRUE;\r
1850   appData.periodicUpdates = TRUE;\r
1851   appData.popupExitMessage = TRUE;\r
1852   appData.popupMoveErrors = FALSE;\r
1853   appData.autoObserve = FALSE;\r
1854   appData.autoComment = FALSE;\r
1855   appData.animate = TRUE;\r
1856   appData.animSpeed = 10;\r
1857   appData.animateDragging = TRUE;\r
1858   appData.highlightLastMove = TRUE;\r
1859   appData.getMoveList = TRUE;\r
1860   appData.testLegality = TRUE;\r
1861   appData.premove = TRUE;\r
1862   appData.premoveWhite = FALSE;\r
1863   appData.premoveWhiteText = "";\r
1864   appData.premoveBlack = FALSE;\r
1865   appData.premoveBlackText = "";\r
1866   appData.icsAlarm = TRUE;\r
1867   appData.icsAlarmTime = 5000;\r
1868   appData.autoRaiseBoard = TRUE;\r
1869   appData.localLineEditing = TRUE;\r
1870   appData.colorize = TRUE;\r
1871   appData.reuseFirst = TRUE;\r
1872   appData.reuseSecond = TRUE;\r
1873   appData.blindfold = FALSE;\r
1874   appData.icsEngineAnalyze = FALSE;\r
1875   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1876   dcb.DCBlength = sizeof(DCB);\r
1877   dcb.BaudRate = 9600;\r
1878   dcb.fBinary = TRUE;\r
1879   dcb.fParity = FALSE;\r
1880   dcb.fOutxCtsFlow = FALSE;\r
1881   dcb.fOutxDsrFlow = FALSE;\r
1882   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1883   dcb.fDsrSensitivity = FALSE;\r
1884   dcb.fTXContinueOnXoff = TRUE;\r
1885   dcb.fOutX = FALSE;\r
1886   dcb.fInX = FALSE;\r
1887   dcb.fNull = FALSE;\r
1888   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1889   dcb.fAbortOnError = FALSE;\r
1890   dcb.ByteSize = 7;\r
1891   dcb.Parity = SPACEPARITY;\r
1892   dcb.StopBits = ONESTOPBIT;\r
1893   settingsFileName = SETTINGS_FILE;\r
1894   saveSettingsOnExit = TRUE;\r
1895   boardX = CW_USEDEFAULT;\r
1896   boardY = CW_USEDEFAULT;\r
1897   consoleX = CW_USEDEFAULT; \r
1898   consoleY = CW_USEDEFAULT; \r
1899   consoleW = CW_USEDEFAULT;\r
1900   consoleH = CW_USEDEFAULT;\r
1901   analysisX = CW_USEDEFAULT; \r
1902   analysisY = CW_USEDEFAULT; \r
1903   analysisW = CW_USEDEFAULT;\r
1904   analysisH = CW_USEDEFAULT;\r
1905   commentX = CW_USEDEFAULT; \r
1906   commentY = CW_USEDEFAULT; \r
1907   commentW = CW_USEDEFAULT;\r
1908   commentH = CW_USEDEFAULT;\r
1909   editTagsX = CW_USEDEFAULT; \r
1910   editTagsY = CW_USEDEFAULT; \r
1911   editTagsW = CW_USEDEFAULT;\r
1912   editTagsH = CW_USEDEFAULT;\r
1913   gameListX = CW_USEDEFAULT; \r
1914   gameListY = CW_USEDEFAULT; \r
1915   gameListW = CW_USEDEFAULT;\r
1916   gameListH = CW_USEDEFAULT;\r
1917   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1918   icsNames = ICS_NAMES;\r
1919   firstChessProgramNames = FCP_NAMES;\r
1920   secondChessProgramNames = SCP_NAMES;\r
1921   appData.initialMode = "";\r
1922   appData.variant = "normal";\r
1923   appData.firstProtocolVersion = PROTOVER;\r
1924   appData.secondProtocolVersion = PROTOVER;\r
1925   appData.showButtonBar = TRUE;\r
1926 \r
1927    /* [AS] New properties (see comments in header file) */\r
1928   appData.firstScoreIsAbsolute = FALSE;\r
1929   appData.secondScoreIsAbsolute = FALSE;\r
1930   appData.saveExtendedInfoInPGN = FALSE;\r
1931   appData.hideThinkingFromHuman = FALSE;\r
1932   appData.liteBackTextureFile = "";\r
1933   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1934   appData.darkBackTextureFile = "";\r
1935   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1936   appData.renderPiecesWithFont = "";\r
1937   appData.fontToPieceTable = "";\r
1938   appData.fontBackColorWhite = 0;\r
1939   appData.fontForeColorWhite = 0;\r
1940   appData.fontBackColorBlack = 0;\r
1941   appData.fontForeColorBlack = 0;\r
1942   appData.fontPieceSize = 80;\r
1943   appData.overrideLineGap = 1;\r
1944   appData.adjudicateLossThreshold = 0;\r
1945   appData.delayBeforeQuit = 0;\r
1946   appData.delayAfterQuit = 0;\r
1947   appData.nameOfDebugFile = "winboard.debug";\r
1948   appData.pgnEventHeader = "Computer Chess Game";\r
1949   appData.defaultFrcPosition = -1;\r
1950   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1951   appData.saveOutOfBookInfo = TRUE;\r
1952   appData.showEvalInMoveHistory = TRUE;\r
1953   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1954   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1955   appData.highlightMoveWithArrow = FALSE;\r
1956   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1957   appData.useStickyWindows = TRUE;\r
1958   appData.adjudicateDrawMoves = 0;\r
1959   appData.autoDisplayComment = TRUE;\r
1960   appData.autoDisplayTags = TRUE;\r
1961   appData.firstIsUCI = FALSE;\r
1962   appData.secondIsUCI = FALSE;\r
1963   appData.firstHasOwnBookUCI = TRUE;\r
1964   appData.secondHasOwnBookUCI = TRUE;\r
1965   appData.polyglotDir = "";\r
1966   appData.usePolyglotBook = FALSE;\r
1967   appData.polyglotBook = "";\r
1968   appData.defaultHashSize = 64;\r
1969   appData.defaultCacheSizeEGTB = 4;\r
1970   appData.defaultPathEGTB = "c:\\egtb";\r
1971   appData.firstOptions = "";\r
1972   appData.secondOptions = "";\r
1973 \r
1974   InitWindowPlacement( &wpMoveHistory );\r
1975   InitWindowPlacement( &wpEvalGraph );\r
1976   InitWindowPlacement( &wpEngineOutput );\r
1977 \r
1978   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1979   appData.NrFiles      = -1;\r
1980   appData.NrRanks      = -1;\r
1981   appData.holdingsSize = -1;\r
1982   appData.testClaims   = FALSE;\r
1983   appData.checkMates   = FALSE;\r
1984   appData.materialDraws= FALSE;\r
1985   appData.trivialDraws = FALSE;\r
1986   appData.ruleMoves    = 51;\r
1987   appData.drawRepeats  = 6;\r
1988   appData.matchPause   = 10000;\r
1989   appData.alphaRank    = FALSE;\r
1990   appData.allWhite     = FALSE;\r
1991   appData.upsideDown   = FALSE;\r
1992   appData.serverPause  = 15;\r
1993   appData.serverMovesName   = NULL;\r
1994   appData.suppressLoadMoves = FALSE;\r
1995   appData.firstTimeOdds  = 1;\r
1996   appData.secondTimeOdds = 1;\r
1997   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1998   appData.secondAccumulateTC = 1;\r
1999   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2000   appData.secondNPS = -1;\r
2001   appData.engineComments = 1;\r
2002   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2003   appData.egtFormats = "";\r
2004 \r
2005 #ifdef ZIPPY\r
2006   appData.zippyTalk = ZIPPY_TALK;\r
2007   appData.zippyPlay = ZIPPY_PLAY;\r
2008   appData.zippyLines = ZIPPY_LINES;\r
2009   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2010   appData.zippyPassword = ZIPPY_PASSWORD;\r
2011   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2012   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2013   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2014   appData.zippyUseI = ZIPPY_USE_I;\r
2015   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2016   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2017   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2018   appData.zippyGameStart = ZIPPY_GAME_START;\r
2019   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2020   appData.zippyAbort = ZIPPY_ABORT;\r
2021   appData.zippyVariants = ZIPPY_VARIANTS;\r
2022   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2023   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2024 #endif\r
2025 \r
2026   /* Point font array elements to structures and\r
2027      parse default font names */\r
2028   for (i=0; i<NUM_FONTS; i++) {\r
2029     for (j=0; j<NUM_SIZES; j++) {\r
2030       font[j][i] = &fontRec[j][i];\r
2031       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2032     }\r
2033   }\r
2034   \r
2035   /* Parse default settings file if any */\r
2036   if (ParseSettingsFile(settingsFileName, buf)) {\r
2037     settingsFileName = strdup(buf);\r
2038   }\r
2039 \r
2040   /* Parse command line */\r
2041   ParseArgs(StringGet, &lpCmdLine);\r
2042 \r
2043   /* [HGM] make sure board size is acceptable */\r
2044   if(appData.NrFiles > BOARD_SIZE ||\r
2045      appData.NrRanks > BOARD_SIZE   )\r
2046       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2047 \r
2048   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2049    * with options from the command line, we now make an even higher priority\r
2050    * overrule by WB options attached to the engine command line. This so that\r
2051    * tournament managers can use WB options (such as /timeOdds) that follow\r
2052    * the engines.\r
2053    */\r
2054   if(appData.firstChessProgram != NULL) {\r
2055       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2056       static char *f = "first";\r
2057       char buf[MSG_SIZ], *q = buf;\r
2058       if(p != NULL) { // engine command line contains WinBoard options\r
2059           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2060           ParseArgs(StringGet, &q);\r
2061           p[-1] = 0; // cut them offengine command line\r
2062       }\r
2063   }\r
2064   // now do same for second chess program\r
2065   if(appData.secondChessProgram != NULL) {\r
2066       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2067       static char *s = "second";\r
2068       char buf[MSG_SIZ], *q = buf;\r
2069       if(p != NULL) { // engine command line contains WinBoard options\r
2070           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2071           ParseArgs(StringGet, &q);\r
2072           p[-1] = 0; // cut them offengine command line\r
2073       }\r
2074   }\r
2075 \r
2076 \r
2077   /* Propagate options that affect others */\r
2078   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2079   if (appData.icsActive || appData.noChessProgram) {\r
2080      chessProgram = FALSE;  /* not local chess program mode */\r
2081   }\r
2082 \r
2083   /* Open startup dialog if needed */\r
2084   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2085       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2086       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2087                         *appData.secondChessProgram == NULLCHAR))) {\r
2088     FARPROC lpProc;\r
2089     \r
2090     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2091     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2092     FreeProcInstance(lpProc);\r
2093   }\r
2094 \r
2095   /* Make sure save files land in the right (?) directory */\r
2096   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2097     appData.saveGameFile = strdup(buf);\r
2098   }\r
2099   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2100     appData.savePositionFile = strdup(buf);\r
2101   }\r
2102 \r
2103   /* Finish initialization for fonts and sounds */\r
2104   for (i=0; i<NUM_FONTS; i++) {\r
2105     for (j=0; j<NUM_SIZES; j++) {\r
2106       CreateFontInMF(font[j][i]);\r
2107     }\r
2108   }\r
2109   /* xboard, and older WinBoards, controlled the move sound with the\r
2110      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2111      always turn the option on (so that the backend will call us),\r
2112      then let the user turn the sound off by setting it to silence if\r
2113      desired.  To accommodate old winboard.ini files saved by old\r
2114      versions of WinBoard, we also turn off the sound if the option\r
2115      was initially set to false. */\r
2116   if (!appData.ringBellAfterMoves) {\r
2117     sounds[(int)SoundMove].name = strdup("");\r
2118     appData.ringBellAfterMoves = TRUE;\r
2119   }\r
2120   GetCurrentDirectory(MSG_SIZ, currDir);\r
2121   SetCurrentDirectory(installDir);\r
2122   LoadAllSounds();\r
2123   SetCurrentDirectory(currDir);\r
2124 \r
2125   p = icsTextMenuString;\r
2126   if (p[0] == '@') {\r
2127     FILE* f = fopen(p + 1, "r");\r
2128     if (f == NULL) {\r
2129       DisplayFatalError(p + 1, errno, 2);\r
2130       return;\r
2131     }\r
2132     i = fread(buf, 1, sizeof(buf)-1, f);\r
2133     fclose(f);\r
2134     buf[i] = NULLCHAR;\r
2135     p = buf;\r
2136   }\r
2137   ParseIcsTextMenu(strdup(p));\r
2138 }\r
2139 \r
2140 \r
2141 VOID\r
2142 InitMenuChecks()\r
2143 {\r
2144   HMENU hmenu = GetMenu(hwndMain);\r
2145 \r
2146   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2147                         MF_BYCOMMAND|((appData.icsActive &&\r
2148                                        *appData.icsCommPort != NULLCHAR) ?\r
2149                                       MF_ENABLED : MF_GRAYED));\r
2150   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2151                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2152                                      MF_CHECKED : MF_UNCHECKED));\r
2153 }\r
2154 \r
2155 \r
2156 VOID\r
2157 SaveSettings(char* name)\r
2158 {\r
2159   FILE *f;\r
2160   ArgDescriptor *ad;\r
2161   WINDOWPLACEMENT wp;\r
2162   char dir[MSG_SIZ];\r
2163 \r
2164   if (!hwndMain) return;\r
2165 \r
2166   GetCurrentDirectory(MSG_SIZ, dir);\r
2167   SetCurrentDirectory(installDir);\r
2168   f = fopen(name, "w");\r
2169   SetCurrentDirectory(dir);\r
2170   if (f == NULL) {\r
2171     DisplayError(name, errno);\r
2172     return;\r
2173   }\r
2174   fprintf(f, ";\n");\r
2175   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2176   fprintf(f, ";\n");\r
2177   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2178   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2179   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2180   fprintf(f, ";\n");\r
2181 \r
2182   wp.length = sizeof(WINDOWPLACEMENT);\r
2183   GetWindowPlacement(hwndMain, &wp);\r
2184   boardX = wp.rcNormalPosition.left;\r
2185   boardY = wp.rcNormalPosition.top;\r
2186 \r
2187   if (hwndConsole) {\r
2188     GetWindowPlacement(hwndConsole, &wp);\r
2189     consoleX = wp.rcNormalPosition.left;\r
2190     consoleY = wp.rcNormalPosition.top;\r
2191     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2192     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2193   }\r
2194 \r
2195   if (analysisDialog) {\r
2196     GetWindowPlacement(analysisDialog, &wp);\r
2197     analysisX = wp.rcNormalPosition.left;\r
2198     analysisY = wp.rcNormalPosition.top;\r
2199     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2200     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2201   }\r
2202 \r
2203   if (commentDialog) {\r
2204     GetWindowPlacement(commentDialog, &wp);\r
2205     commentX = wp.rcNormalPosition.left;\r
2206     commentY = wp.rcNormalPosition.top;\r
2207     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2208     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2209   }\r
2210 \r
2211   if (editTagsDialog) {\r
2212     GetWindowPlacement(editTagsDialog, &wp);\r
2213     editTagsX = wp.rcNormalPosition.left;\r
2214     editTagsY = wp.rcNormalPosition.top;\r
2215     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2216     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2217   }\r
2218 \r
2219   if (gameListDialog) {\r
2220     GetWindowPlacement(gameListDialog, &wp);\r
2221     gameListX = wp.rcNormalPosition.left;\r
2222     gameListY = wp.rcNormalPosition.top;\r
2223     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2224     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2225   }\r
2226 \r
2227   /* [AS] Move history */\r
2228   wpMoveHistory.visible = MoveHistoryIsUp();\r
2229   \r
2230   if( moveHistoryDialog ) {\r
2231     GetWindowPlacement(moveHistoryDialog, &wp);\r
2232     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2233     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2234     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2235     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2236   }\r
2237 \r
2238   /* [AS] Eval graph */\r
2239   wpEvalGraph.visible = EvalGraphIsUp();\r
2240 \r
2241   if( evalGraphDialog ) {\r
2242     GetWindowPlacement(evalGraphDialog, &wp);\r
2243     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2244     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2245     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2246     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2247   }\r
2248 \r
2249   /* [AS] Engine output */\r
2250   wpEngineOutput.visible = EngineOutputIsUp();\r
2251 \r
2252   if( engineOutputDialog ) {\r
2253     GetWindowPlacement(engineOutputDialog, &wp);\r
2254     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2255     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2256     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2257     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2258   }\r
2259 \r
2260   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2261     if (!ad->save) continue;\r
2262     switch (ad->argType) {\r
2263     case ArgString:\r
2264       {\r
2265         char *p = *(char **)ad->argLoc;\r
2266         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2267           /* Quote multiline values or \-containing values\r
2268              with { } if possible */\r
2269           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2270         } else {\r
2271           /* Else quote with " " */\r
2272           fprintf(f, "/%s=\"", ad->argName);\r
2273           while (*p) {\r
2274             if (*p == '\n') fprintf(f, "\n");\r
2275             else if (*p == '\r') fprintf(f, "\\r");\r
2276             else if (*p == '\t') fprintf(f, "\\t");\r
2277             else if (*p == '\b') fprintf(f, "\\b");\r
2278             else if (*p == '\f') fprintf(f, "\\f");\r
2279             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2280             else if (*p == '\"') fprintf(f, "\\\"");\r
2281             else if (*p == '\\') fprintf(f, "\\\\");\r
2282             else putc(*p, f);\r
2283             p++;\r
2284           }\r
2285           fprintf(f, "\"\n");\r
2286         }\r
2287       }\r
2288       break;\r
2289     case ArgInt:\r
2290       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2291       break;\r
2292     case ArgFloat:\r
2293       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2294       break;\r
2295     case ArgBoolean:\r
2296       fprintf(f, "/%s=%s\n", ad->argName, \r
2297         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2298       break;\r
2299     case ArgTrue:\r
2300       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2301       break;\r
2302     case ArgFalse:\r
2303       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2304       break;\r
2305     case ArgColor:\r
2306       {\r
2307         COLORREF color = *(COLORREF *)ad->argLoc;\r
2308         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2309           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2310       }\r
2311       break;\r
2312     case ArgAttribs:\r
2313       {\r
2314         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2315         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2316           (ta->effects & CFE_BOLD) ? "b" : "",\r
2317           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2318           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2319           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2320           (ta->effects) ? " " : "",\r
2321           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2322       }\r
2323       break;\r
2324     case ArgFilename:\r
2325       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2326         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2327       } else {\r
2328         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2329       }\r
2330       break;\r
2331     case ArgBoardSize:\r
2332       fprintf(f, "/%s=%s\n", ad->argName,\r
2333               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2334       break;\r
2335     case ArgFont:\r
2336       {\r
2337         int bs;\r
2338         for (bs=0; bs<NUM_SIZES; bs++) {\r
2339           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2340           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2341           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2342             ad->argName, mfp->faceName, mfp->pointSize,\r
2343             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2344             mfp->bold ? "b" : "",\r
2345             mfp->italic ? "i" : "",\r
2346             mfp->underline ? "u" : "",\r
2347             mfp->strikeout ? "s" : "");\r
2348         }\r
2349       }\r
2350       break;\r
2351     case ArgCommSettings:\r
2352       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2353     case ArgNone:\r
2354     case ArgSettingsFilename: ;\r
2355     }\r
2356   }\r
2357   fclose(f);\r
2358 }\r
2359 \r
2360 \r
2361 \r
2362 /*---------------------------------------------------------------------------*\\r
2363  *\r
2364  * GDI board drawing routines\r
2365  *\r
2366 \*---------------------------------------------------------------------------*/\r
2367 \r
2368 /* [AS] Draw square using background texture */\r
2369 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2370 {\r
2371     XFORM   x;\r
2372 \r
2373     if( mode == 0 ) {\r
2374         return; /* Should never happen! */\r
2375     }\r
2376 \r
2377     SetGraphicsMode( dst, GM_ADVANCED );\r
2378 \r
2379     switch( mode ) {\r
2380     case 1:\r
2381         /* Identity */\r
2382         break;\r
2383     case 2:\r
2384         /* X reflection */\r
2385         x.eM11 = -1.0;\r
2386         x.eM12 = 0;\r
2387         x.eM21 = 0;\r
2388         x.eM22 = 1.0;\r
2389         x.eDx = (FLOAT) dw + dx - 1;\r
2390         x.eDy = 0;\r
2391         dx = 0;\r
2392         SetWorldTransform( dst, &x );\r
2393         break;\r
2394     case 3:\r
2395         /* Y reflection */\r
2396         x.eM11 = 1.0;\r
2397         x.eM12 = 0;\r
2398         x.eM21 = 0;\r
2399         x.eM22 = -1.0;\r
2400         x.eDx = 0;\r
2401         x.eDy = (FLOAT) dh + dy - 1;\r
2402         dy = 0;\r
2403         SetWorldTransform( dst, &x );\r
2404         break;\r
2405     case 4:\r
2406         /* X/Y flip */\r
2407         x.eM11 = 0;\r
2408         x.eM12 = 1.0;\r
2409         x.eM21 = 1.0;\r
2410         x.eM22 = 0;\r
2411         x.eDx = (FLOAT) dx;\r
2412         x.eDy = (FLOAT) dy;\r
2413         dx = 0;\r
2414         dy = 0;\r
2415         SetWorldTransform( dst, &x );\r
2416         break;\r
2417     }\r
2418 \r
2419     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2420 \r
2421     x.eM11 = 1.0;\r
2422     x.eM12 = 0;\r
2423     x.eM21 = 0;\r
2424     x.eM22 = 1.0;\r
2425     x.eDx = 0;\r
2426     x.eDy = 0;\r
2427     SetWorldTransform( dst, &x );\r
2428 \r
2429     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2430 }\r
2431 \r
2432 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2433 enum {\r
2434     PM_WP = (int) WhitePawn, \r
2435     PM_WN = (int) WhiteKnight, \r
2436     PM_WB = (int) WhiteBishop, \r
2437     PM_WR = (int) WhiteRook, \r
2438     PM_WQ = (int) WhiteQueen, \r
2439     PM_WF = (int) WhiteFerz, \r
2440     PM_WW = (int) WhiteWazir, \r
2441     PM_WE = (int) WhiteAlfil, \r
2442     PM_WM = (int) WhiteMan, \r
2443     PM_WO = (int) WhiteCannon, \r
2444     PM_WU = (int) WhiteUnicorn, \r
2445     PM_WH = (int) WhiteNightrider, \r
2446     PM_WA = (int) WhiteAngel, \r
2447     PM_WC = (int) WhiteMarshall, \r
2448     PM_WAB = (int) WhiteCardinal, \r
2449     PM_WD = (int) WhiteDragon, \r
2450     PM_WL = (int) WhiteLance, \r
2451     PM_WS = (int) WhiteCobra, \r
2452     PM_WV = (int) WhiteFalcon, \r
2453     PM_WSG = (int) WhiteSilver, \r
2454     PM_WG = (int) WhiteGrasshopper, \r
2455     PM_WK = (int) WhiteKing,\r
2456     PM_BP = (int) BlackPawn, \r
2457     PM_BN = (int) BlackKnight, \r
2458     PM_BB = (int) BlackBishop, \r
2459     PM_BR = (int) BlackRook, \r
2460     PM_BQ = (int) BlackQueen, \r
2461     PM_BF = (int) BlackFerz, \r
2462     PM_BW = (int) BlackWazir, \r
2463     PM_BE = (int) BlackAlfil, \r
2464     PM_BM = (int) BlackMan,\r
2465     PM_BO = (int) BlackCannon, \r
2466     PM_BU = (int) BlackUnicorn, \r
2467     PM_BH = (int) BlackNightrider, \r
2468     PM_BA = (int) BlackAngel, \r
2469     PM_BC = (int) BlackMarshall, \r
2470     PM_BG = (int) BlackGrasshopper, \r
2471     PM_BAB = (int) BlackCardinal,\r
2472     PM_BD = (int) BlackDragon,\r
2473     PM_BL = (int) BlackLance,\r
2474     PM_BS = (int) BlackCobra,\r
2475     PM_BV = (int) BlackFalcon,\r
2476     PM_BSG = (int) BlackSilver,\r
2477     PM_BK = (int) BlackKing\r
2478 };\r
2479 \r
2480 static HFONT hPieceFont = NULL;\r
2481 static HBITMAP hPieceMask[(int) EmptySquare];\r
2482 static HBITMAP hPieceFace[(int) EmptySquare];\r
2483 static int fontBitmapSquareSize = 0;\r
2484 static char pieceToFontChar[(int) EmptySquare] =\r
2485                               { 'p', 'n', 'b', 'r', 'q', \r
2486                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2487                       'k', 'o', 'm', 'v', 't', 'w', \r
2488                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2489                                                               'l' };\r
2490 \r
2491 extern BOOL SetCharTable( char *table, const char * map );\r
2492 /* [HGM] moved to backend.c */\r
2493 \r
2494 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2495 {\r
2496     HBRUSH hbrush;\r
2497     BYTE r1 = GetRValue( color );\r
2498     BYTE g1 = GetGValue( color );\r
2499     BYTE b1 = GetBValue( color );\r
2500     BYTE r2 = r1 / 2;\r
2501     BYTE g2 = g1 / 2;\r
2502     BYTE b2 = b1 / 2;\r
2503     RECT rc;\r
2504 \r
2505     /* Create a uniform background first */\r
2506     hbrush = CreateSolidBrush( color );\r
2507     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2508     FillRect( hdc, &rc, hbrush );\r
2509     DeleteObject( hbrush );\r
2510     \r
2511     if( mode == 1 ) {\r
2512         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2513         int steps = squareSize / 2;\r
2514         int i;\r
2515 \r
2516         for( i=0; i<steps; i++ ) {\r
2517             BYTE r = r1 - (r1-r2) * i / steps;\r
2518             BYTE g = g1 - (g1-g2) * i / steps;\r
2519             BYTE b = b1 - (b1-b2) * i / steps;\r
2520 \r
2521             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2522             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2523             FillRect( hdc, &rc, hbrush );\r
2524             DeleteObject(hbrush);\r
2525         }\r
2526     }\r
2527     else if( mode == 2 ) {\r
2528         /* Diagonal gradient, good more or less for every piece */\r
2529         POINT triangle[3];\r
2530         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2531         HBRUSH hbrush_old;\r
2532         int steps = squareSize;\r
2533         int i;\r
2534 \r
2535         triangle[0].x = squareSize - steps;\r
2536         triangle[0].y = squareSize;\r
2537         triangle[1].x = squareSize;\r
2538         triangle[1].y = squareSize;\r
2539         triangle[2].x = squareSize;\r
2540         triangle[2].y = squareSize - steps;\r
2541 \r
2542         for( i=0; i<steps; i++ ) {\r
2543             BYTE r = r1 - (r1-r2) * i / steps;\r
2544             BYTE g = g1 - (g1-g2) * i / steps;\r
2545             BYTE b = b1 - (b1-b2) * i / steps;\r
2546 \r
2547             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2548             hbrush_old = SelectObject( hdc, hbrush );\r
2549             Polygon( hdc, triangle, 3 );\r
2550             SelectObject( hdc, hbrush_old );\r
2551             DeleteObject(hbrush);\r
2552             triangle[0].x++;\r
2553             triangle[2].y++;\r
2554         }\r
2555 \r
2556         SelectObject( hdc, hpen );\r
2557     }\r
2558 }\r
2559 \r
2560 /*\r
2561     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2562     seems to work ok. The main problem here is to find the "inside" of a chess\r
2563     piece: follow the steps as explained below.\r
2564 */\r
2565 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2566 {\r
2567     HBITMAP hbm;\r
2568     HBITMAP hbm_old;\r
2569     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2570     RECT rc;\r
2571     SIZE sz;\r
2572     POINT pt;\r
2573     int backColor = whitePieceColor; \r
2574     int foreColor = blackPieceColor;\r
2575     \r
2576     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2577         backColor = appData.fontBackColorWhite;\r
2578         foreColor = appData.fontForeColorWhite;\r
2579     }\r
2580     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2581         backColor = appData.fontBackColorBlack;\r
2582         foreColor = appData.fontForeColorBlack;\r
2583     }\r
2584 \r
2585     /* Mask */\r
2586     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2587 \r
2588     hbm_old = SelectObject( hdc, hbm );\r
2589 \r
2590     rc.left = 0;\r
2591     rc.top = 0;\r
2592     rc.right = squareSize;\r
2593     rc.bottom = squareSize;\r
2594 \r
2595     /* Step 1: background is now black */\r
2596     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2597 \r
2598     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2599 \r
2600     pt.x = (squareSize - sz.cx) / 2;\r
2601     pt.y = (squareSize - sz.cy) / 2;\r
2602 \r
2603     SetBkMode( hdc, TRANSPARENT );\r
2604     SetTextColor( hdc, chroma );\r
2605     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2606     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2607 \r
2608     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2609     /* Step 3: the area outside the piece is filled with white */\r
2610 //    FloodFill( hdc, 0, 0, chroma );\r
2611     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2612     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2613     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2614     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2615     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2616     /* \r
2617         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2618         but if the start point is not inside the piece we're lost!\r
2619         There should be a better way to do this... if we could create a region or path\r
2620         from the fill operation we would be fine for example.\r
2621     */\r
2622 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2623     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2624 \r
2625     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2626         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2627         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2628 \r
2629         SelectObject( dc2, bm2 );\r
2630         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2631         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2632         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2633         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2634         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2635 \r
2636         DeleteDC( dc2 );\r
2637         DeleteObject( bm2 );\r
2638     }\r
2639 \r
2640     SetTextColor( hdc, 0 );\r
2641     /* \r
2642         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2643         draw the piece again in black for safety.\r
2644     */\r
2645     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2646 \r
2647     SelectObject( hdc, hbm_old );\r
2648 \r
2649     if( hPieceMask[index] != NULL ) {\r
2650         DeleteObject( hPieceMask[index] );\r
2651     }\r
2652 \r
2653     hPieceMask[index] = hbm;\r
2654 \r
2655     /* Face */\r
2656     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2657 \r
2658     SelectObject( hdc, hbm );\r
2659 \r
2660     {\r
2661         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2662         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2663         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2664 \r
2665         SelectObject( dc1, hPieceMask[index] );\r
2666         SelectObject( dc2, bm2 );\r
2667         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2668         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2669         \r
2670         /* \r
2671             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2672             the piece background and deletes (makes transparent) the rest.\r
2673             Thanks to that mask, we are free to paint the background with the greates\r
2674             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2675             We use this, to make gradients and give the pieces a "roundish" look.\r
2676         */\r
2677         SetPieceBackground( hdc, backColor, 2 );\r
2678         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2679 \r
2680         DeleteDC( dc2 );\r
2681         DeleteDC( dc1 );\r
2682         DeleteObject( bm2 );\r
2683     }\r
2684 \r
2685     SetTextColor( hdc, foreColor );\r
2686     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2687 \r
2688     SelectObject( hdc, hbm_old );\r
2689 \r
2690     if( hPieceFace[index] != NULL ) {\r
2691         DeleteObject( hPieceFace[index] );\r
2692     }\r
2693 \r
2694     hPieceFace[index] = hbm;\r
2695 }\r
2696 \r
2697 static int TranslatePieceToFontPiece( int piece )\r
2698 {\r
2699     switch( piece ) {\r
2700     case BlackPawn:\r
2701         return PM_BP;\r
2702     case BlackKnight:\r
2703         return PM_BN;\r
2704     case BlackBishop:\r
2705         return PM_BB;\r
2706     case BlackRook:\r
2707         return PM_BR;\r
2708     case BlackQueen:\r
2709         return PM_BQ;\r
2710     case BlackKing:\r
2711         return PM_BK;\r
2712     case WhitePawn:\r
2713         return PM_WP;\r
2714     case WhiteKnight:\r
2715         return PM_WN;\r
2716     case WhiteBishop:\r
2717         return PM_WB;\r
2718     case WhiteRook:\r
2719         return PM_WR;\r
2720     case WhiteQueen:\r
2721         return PM_WQ;\r
2722     case WhiteKing:\r
2723         return PM_WK;\r
2724 \r
2725     case BlackAngel:\r
2726         return PM_BA;\r
2727     case BlackMarshall:\r
2728         return PM_BC;\r
2729     case BlackFerz:\r
2730         return PM_BF;\r
2731     case BlackNightrider:\r
2732         return PM_BH;\r
2733     case BlackAlfil:\r
2734         return PM_BE;\r
2735     case BlackWazir:\r
2736         return PM_BW;\r
2737     case BlackUnicorn:\r
2738         return PM_BU;\r
2739     case BlackCannon:\r
2740         return PM_BO;\r
2741     case BlackGrasshopper:\r
2742         return PM_BG;\r
2743     case BlackMan:\r
2744         return PM_BM;\r
2745     case BlackSilver:\r
2746         return PM_BSG;\r
2747     case BlackLance:\r
2748         return PM_BL;\r
2749     case BlackFalcon:\r
2750         return PM_BV;\r
2751     case BlackCobra:\r
2752         return PM_BS;\r
2753     case BlackCardinal:\r
2754         return PM_BAB;\r
2755     case BlackDragon:\r
2756         return PM_BD;\r
2757 \r
2758     case WhiteAngel:\r
2759         return PM_WA;\r
2760     case WhiteMarshall:\r
2761         return PM_WC;\r
2762     case WhiteFerz:\r
2763         return PM_WF;\r
2764     case WhiteNightrider:\r
2765         return PM_WH;\r
2766     case WhiteAlfil:\r
2767         return PM_WE;\r
2768     case WhiteWazir:\r
2769         return PM_WW;\r
2770     case WhiteUnicorn:\r
2771         return PM_WU;\r
2772     case WhiteCannon:\r
2773         return PM_WO;\r
2774     case WhiteGrasshopper:\r
2775         return PM_WG;\r
2776     case WhiteMan:\r
2777         return PM_WM;\r
2778     case WhiteSilver:\r
2779         return PM_WSG;\r
2780     case WhiteLance:\r
2781         return PM_WL;\r
2782     case WhiteFalcon:\r
2783         return PM_WV;\r
2784     case WhiteCobra:\r
2785         return PM_WS;\r
2786     case WhiteCardinal:\r
2787         return PM_WAB;\r
2788     case WhiteDragon:\r
2789         return PM_WD;\r
2790     }\r
2791 \r
2792     return 0;\r
2793 }\r
2794 \r
2795 void CreatePiecesFromFont()\r
2796 {\r
2797     LOGFONT lf;\r
2798     HDC hdc_window = NULL;\r
2799     HDC hdc = NULL;\r
2800     HFONT hfont_old;\r
2801     int fontHeight;\r
2802     int i;\r
2803 \r
2804     if( fontBitmapSquareSize < 0 ) {\r
2805         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2806         return;\r
2807     }\r
2808 \r
2809     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2810         fontBitmapSquareSize = -1;\r
2811         return;\r
2812     }\r
2813 \r
2814     if( fontBitmapSquareSize != squareSize ) {\r
2815         hdc_window = GetDC( hwndMain );\r
2816         hdc = CreateCompatibleDC( hdc_window );\r
2817 \r
2818         if( hPieceFont != NULL ) {\r
2819             DeleteObject( hPieceFont );\r
2820         }\r
2821         else {\r
2822             for( i=0; i<=(int)BlackKing; i++ ) {\r
2823                 hPieceMask[i] = NULL;\r
2824                 hPieceFace[i] = NULL;\r
2825             }\r
2826         }\r
2827 \r
2828         fontHeight = 75;\r
2829 \r
2830         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2831             fontHeight = appData.fontPieceSize;\r
2832         }\r
2833 \r
2834         fontHeight = (fontHeight * squareSize) / 100;\r
2835 \r
2836         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2837         lf.lfWidth = 0;\r
2838         lf.lfEscapement = 0;\r
2839         lf.lfOrientation = 0;\r
2840         lf.lfWeight = FW_NORMAL;\r
2841         lf.lfItalic = 0;\r
2842         lf.lfUnderline = 0;\r
2843         lf.lfStrikeOut = 0;\r
2844         lf.lfCharSet = DEFAULT_CHARSET;\r
2845         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2846         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2847         lf.lfQuality = PROOF_QUALITY;\r
2848         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2849         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2850         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2851 \r
2852         hPieceFont = CreateFontIndirect( &lf );\r
2853 \r
2854         if( hPieceFont == NULL ) {\r
2855             fontBitmapSquareSize = -2;\r
2856         }\r
2857         else {\r
2858             /* Setup font-to-piece character table */\r
2859             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2860                 /* No (or wrong) global settings, try to detect the font */\r
2861                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2862                     /* Alpha */\r
2863                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2864                 }\r
2865                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2866                     /* DiagramTT* family */\r
2867                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2868                 }\r
2869                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2870                     /* Fairy symbols */\r
2871                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2872                 }\r
2873                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2874                     /* Good Companion (Some characters get warped as literal :-( */\r
2875                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2876                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2877                     SetCharTable(pieceToFontChar, s);\r
2878                 }\r
2879                 else {\r
2880                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2881                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2882                 }\r
2883             }\r
2884 \r
2885             /* Create bitmaps */\r
2886             hfont_old = SelectObject( hdc, hPieceFont );\r
2887 #if 0\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2900 \r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2923             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2931             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2932             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2933 #else\r
2934             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2935                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2936                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2937 #endif\r
2938             SelectObject( hdc, hfont_old );\r
2939 \r
2940             fontBitmapSquareSize = squareSize;\r
2941         }\r
2942     }\r
2943 \r
2944     if( hdc != NULL ) {\r
2945         DeleteDC( hdc );\r
2946     }\r
2947 \r
2948     if( hdc_window != NULL ) {\r
2949         ReleaseDC( hwndMain, hdc_window );\r
2950     }\r
2951 }\r
2952 \r
2953 HBITMAP\r
2954 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2955 {\r
2956   char name[128];\r
2957 \r
2958   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2959   if (gameInfo.event &&\r
2960       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2961       strcmp(name, "k80s") == 0) {\r
2962     strcpy(name, "tim");\r
2963   }\r
2964   return LoadBitmap(hinst, name);\r
2965 }\r
2966 \r
2967 \r
2968 /* Insert a color into the program's logical palette\r
2969    structure.  This code assumes the given color is\r
2970    the result of the RGB or PALETTERGB macro, and it\r
2971    knows how those macros work (which is documented).\r
2972 */\r
2973 VOID\r
2974 InsertInPalette(COLORREF color)\r
2975 {\r
2976   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2977 \r
2978   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2979     DisplayFatalError("Too many colors", 0, 1);\r
2980     pLogPal->palNumEntries--;\r
2981     return;\r
2982   }\r
2983 \r
2984   pe->peFlags = (char) 0;\r
2985   pe->peRed = (char) (0xFF & color);\r
2986   pe->peGreen = (char) (0xFF & (color >> 8));\r
2987   pe->peBlue = (char) (0xFF & (color >> 16));\r
2988   return;\r
2989 }\r
2990 \r
2991 \r
2992 VOID\r
2993 InitDrawingColors()\r
2994 {\r
2995   if (pLogPal == NULL) {\r
2996     /* Allocate enough memory for a logical palette with\r
2997      * PALETTESIZE entries and set the size and version fields\r
2998      * of the logical palette structure.\r
2999      */\r
3000     pLogPal = (NPLOGPALETTE)\r
3001       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3002                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3003     pLogPal->palVersion    = 0x300;\r
3004   }\r
3005   pLogPal->palNumEntries = 0;\r
3006 \r
3007   InsertInPalette(lightSquareColor);\r
3008   InsertInPalette(darkSquareColor);\r
3009   InsertInPalette(whitePieceColor);\r
3010   InsertInPalette(blackPieceColor);\r
3011   InsertInPalette(highlightSquareColor);\r
3012   InsertInPalette(premoveHighlightColor);\r
3013 \r
3014   /*  create a logical color palette according the information\r
3015    *  in the LOGPALETTE structure.\r
3016    */\r
3017   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3018 \r
3019   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3020   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3021   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3022   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3023   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3024   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3025   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3026   /* [AS] Force rendering of the font-based pieces */\r
3027   if( fontBitmapSquareSize > 0 ) {\r
3028     fontBitmapSquareSize = 0;\r
3029   }\r
3030 }\r
3031 \r
3032 \r
3033 int\r
3034 BoardWidth(int boardSize, int n)\r
3035 { /* [HGM] argument n added to allow different width and height */\r
3036   int lineGap = sizeInfo[boardSize].lineGap;\r
3037 \r
3038   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3039       lineGap = appData.overrideLineGap;\r
3040   }\r
3041 \r
3042   return (n + 1) * lineGap +\r
3043           n * sizeInfo[boardSize].squareSize;\r
3044 }\r
3045 \r
3046 /* Respond to board resize by dragging edge */\r
3047 VOID\r
3048 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3049 {\r
3050   BoardSize newSize = NUM_SIZES - 1;\r
3051   static int recurse = 0;\r
3052   if (IsIconic(hwndMain)) return;\r
3053   if (recurse > 0) return;\r
3054   recurse++;\r
3055   while (newSize > 0) {\r
3056         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3057         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3058            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3059     newSize--;\r
3060   } \r
3061   boardSize = newSize;\r
3062   InitDrawingSizes(boardSize, flags);\r
3063   recurse--;\r
3064 }\r
3065 \r
3066 \r
3067 \r
3068 VOID\r
3069 InitDrawingSizes(BoardSize boardSize, int flags)\r
3070 {\r
3071   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3072   ChessSquare piece;\r
3073   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3074   HDC hdc;\r
3075   SIZE clockSize, messageSize;\r
3076   HFONT oldFont;\r
3077   char buf[MSG_SIZ];\r
3078   char *str;\r
3079   HMENU hmenu = GetMenu(hwndMain);\r
3080   RECT crect, wrect;\r
3081   int offby;\r
3082   LOGBRUSH logbrush;\r
3083 \r
3084   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3085   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3086 \r
3087   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3088   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3089 \r
3090   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3091   smallLayout = sizeInfo[boardSize].smallLayout;\r
3092   squareSize = sizeInfo[boardSize].squareSize;\r
3093   lineGap = sizeInfo[boardSize].lineGap;\r
3094   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3095 \r
3096   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3097       lineGap = appData.overrideLineGap;\r
3098   }\r
3099 \r
3100   if (tinyLayout != oldTinyLayout) {\r
3101     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3102     if (tinyLayout) {\r
3103       style &= ~WS_SYSMENU;\r
3104       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3105                  "&Minimize\tCtrl+F4");\r
3106     } else {\r
3107       style |= WS_SYSMENU;\r
3108       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3109     }\r
3110     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3111 \r
3112     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3113       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3114         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3115     }\r
3116     DrawMenuBar(hwndMain);\r
3117   }\r
3118 \r
3119   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3120   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3121 \r
3122   /* Get text area sizes */\r
3123   hdc = GetDC(hwndMain);\r
3124   if (appData.clockMode) {\r
3125     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3126   } else {\r
3127     sprintf(buf, "White");\r
3128   }\r
3129   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3130   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3131   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3132   str = "We only care about the height here";\r
3133   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3134   SelectObject(hdc, oldFont);\r
3135   ReleaseDC(hwndMain, hdc);\r
3136 \r
3137   /* Compute where everything goes */\r
3138   if(first.programLogo || second.programLogo) {\r
3139         /* [HGM] logo: if either logo is on, reserve space for it */\r
3140         logoHeight =  2*clockSize.cy;\r
3141         leftLogoRect.left   = OUTER_MARGIN;\r
3142         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3143         leftLogoRect.top    = OUTER_MARGIN;\r
3144         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3145 \r
3146         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3147         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3148         rightLogoRect.top    = OUTER_MARGIN;\r
3149         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3150 \r
3151 \r
3152     blackRect.left = leftLogoRect.right;\r
3153     blackRect.right = rightLogoRect.left;\r
3154     blackRect.top = OUTER_MARGIN;\r
3155     blackRect.bottom = blackRect.top + clockSize.cy;\r
3156 \r
3157     whiteRect.left = blackRect.left ;\r
3158     whiteRect.right = blackRect.right;\r
3159     whiteRect.top = blackRect.bottom;\r
3160     whiteRect.bottom = leftLogoRect.bottom;\r
3161   } else {\r
3162     whiteRect.left = OUTER_MARGIN;\r
3163     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3164     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3165     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3166 \r
3167     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3168     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3169     blackRect.top = whiteRect.top;\r
3170     blackRect.bottom = whiteRect.bottom;\r
3171   }\r
3172 \r
3173   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3174   if (appData.showButtonBar) {\r
3175     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3176       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3177   } else {\r
3178     messageRect.right = OUTER_MARGIN + boardWidth;\r
3179   }\r
3180   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3181   messageRect.bottom = messageRect.top + messageSize.cy;\r
3182 \r
3183   boardRect.left = OUTER_MARGIN;\r
3184   boardRect.right = boardRect.left + boardWidth;\r
3185   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3186   boardRect.bottom = boardRect.top + boardHeight;\r
3187 \r
3188   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3189   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3190   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3191   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3192   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3193     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3194   GetWindowRect(hwndMain, &wrect);\r
3195   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3196                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3197   /* compensate if menu bar wrapped */\r
3198   GetClientRect(hwndMain, &crect);\r
3199   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3200   winHeight += offby;\r
3201   switch (flags) {\r
3202   case WMSZ_TOPLEFT:\r
3203     SetWindowPos(hwndMain, NULL, \r
3204                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3205                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3206     break;\r
3207 \r
3208   case WMSZ_TOPRIGHT:\r
3209   case WMSZ_TOP:\r
3210     SetWindowPos(hwndMain, NULL, \r
3211                  wrect.left, wrect.bottom - winHeight, \r
3212                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3213     break;\r
3214 \r
3215   case WMSZ_BOTTOMLEFT:\r
3216   case WMSZ_LEFT:\r
3217     SetWindowPos(hwndMain, NULL, \r
3218                  wrect.right - winWidth, wrect.top, \r
3219                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3220     break;\r
3221 \r
3222   case WMSZ_BOTTOMRIGHT:\r
3223   case WMSZ_BOTTOM:\r
3224   case WMSZ_RIGHT:\r
3225   default:\r
3226     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3227                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3228     break;\r
3229   }\r
3230 \r
3231   hwndPause = NULL;\r
3232   for (i = 0; i < N_BUTTONS; i++) {\r
3233     if (buttonDesc[i].hwnd != NULL) {\r
3234       DestroyWindow(buttonDesc[i].hwnd);\r
3235       buttonDesc[i].hwnd = NULL;\r
3236     }\r
3237     if (appData.showButtonBar) {\r
3238       buttonDesc[i].hwnd =\r
3239         CreateWindow("BUTTON", buttonDesc[i].label,\r
3240                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3241                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3242                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3243                      (HMENU) buttonDesc[i].id,\r
3244                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3245       if (tinyLayout) {\r
3246         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3247                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3248                     MAKELPARAM(FALSE, 0));\r
3249       }\r
3250       if (buttonDesc[i].id == IDM_Pause)\r
3251         hwndPause = buttonDesc[i].hwnd;\r
3252       buttonDesc[i].wndproc = (WNDPROC)\r
3253         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3254     }\r
3255   }\r
3256   if (gridPen != NULL) DeleteObject(gridPen);\r
3257   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3258   if (premovePen != NULL) DeleteObject(premovePen);\r
3259   if (lineGap != 0) {\r
3260     logbrush.lbStyle = BS_SOLID;\r
3261     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3262     gridPen =\r
3263       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3264                    lineGap, &logbrush, 0, NULL);\r
3265     logbrush.lbColor = highlightSquareColor;\r
3266     highlightPen =\r
3267       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3268                    lineGap, &logbrush, 0, NULL);\r
3269 \r
3270     logbrush.lbColor = premoveHighlightColor; \r
3271     premovePen =\r
3272       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3273                    lineGap, &logbrush, 0, NULL);\r
3274 \r
3275     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3276     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3277       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3278       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3279         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3280       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3281         BOARD_WIDTH * (squareSize + lineGap);\r
3282       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3283     }\r
3284     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3285       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3286       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3287         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3288         lineGap / 2 + (i * (squareSize + lineGap));\r
3289       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3290         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3291       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3292     }\r
3293   }\r
3294 \r
3295   /* [HGM] Licensing requirement */\r
3296 #ifdef GOTHIC\r
3297   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3298 #endif\r
3299 #ifdef FALCON\r
3300   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3301 #endif\r
3302   GothicPopUp( "", VariantNormal);\r
3303 \r
3304 \r
3305 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3306   oldBoardSize = boardSize;\r
3307   oldTinyLayout = tinyLayout;\r
3308 \r
3309   /* Load piece bitmaps for this board size */\r
3310   for (i=0; i<=2; i++) {\r
3311     for (piece = WhitePawn;\r
3312          (int) piece < (int) BlackPawn;\r
3313          piece = (ChessSquare) ((int) piece + 1)) {\r
3314       if (pieceBitmap[i][piece] != NULL)\r
3315         DeleteObject(pieceBitmap[i][piece]);\r
3316     }\r
3317   }\r
3318 \r
3319   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3320   // Orthodox Chess pieces\r
3321   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3322   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3323   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3324   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3325   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3326   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3327   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3328   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3329   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3330   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3331   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3332   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3333   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3334   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3335   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3336   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3337     // in Shogi, Hijack the unused Queen for Lance\r
3338     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3339     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3340     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3341   } else {\r
3342     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3343     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3344     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3345   }\r
3346 \r
3347   if(squareSize <= 72 && squareSize >= 33) { \r
3348     /* A & C are available in most sizes now */\r
3349     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3350       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3351       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3352       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3353       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3354       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3355       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3356       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3357       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3358       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3359       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3360       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3361       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3362     } else { // Smirf-like\r
3363       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3364       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3365       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3366     }\r
3367     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3368       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3369       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3370       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3371     } else { // WinBoard standard\r
3372       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3373       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3374       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3375     }\r
3376   }\r
3377 \r
3378 \r
3379   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3380     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3381     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3382     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3383     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3384     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3385     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3386     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3387     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3388     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3389     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3390     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3391     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3392     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3393     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3394     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3395     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3396     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3397     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3398     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3399     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3400     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3401     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3402     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3403     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3404     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3405     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3406     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3407     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3408     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3409     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3410 \r
3411     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3412       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3413       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3414       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3415       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3416       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3417       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3418       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3419       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3420       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3421       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3422       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3423       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3424     } else {\r
3425       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3426       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3427       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3428       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3429       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3430       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3431       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3432       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3433       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3434       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3435       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3436       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3437     }\r
3438 \r
3439   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3440     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3441     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3442     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3443     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3444     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3445     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3446     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3447     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3448     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3449     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3450     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3451     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3452     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3453     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3454   }\r
3455 \r
3456 \r
3457   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3458   /* special Shogi support in this size */\r
3459   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3460       for (piece = WhitePawn;\r
3461            (int) piece < (int) BlackPawn;\r
3462            piece = (ChessSquare) ((int) piece + 1)) {\r
3463         if (pieceBitmap[i][piece] != NULL)\r
3464           DeleteObject(pieceBitmap[i][piece]);\r
3465       }\r
3466     }\r
3467   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3468   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3469   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3470   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3471   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3472   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3473   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3474   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3475   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3476   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3477   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3478   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3479   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3480   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3481   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3482   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3483   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3484   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3485   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3486   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3487   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3488   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3489   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3490   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3491   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3492   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3493   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3494   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3495   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3496   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3497   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3498   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3499   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3500   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3501   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3502   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3503   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3504   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3505   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3506   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3507   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3508   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3509   minorSize = 0;\r
3510   }\r
3511 }\r
3512 \r
3513 HBITMAP\r
3514 PieceBitmap(ChessSquare p, int kind)\r
3515 {\r
3516   if ((int) p >= (int) BlackPawn)\r
3517     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3518 \r
3519   return pieceBitmap[kind][(int) p];\r
3520 }\r
3521 \r
3522 /***************************************************************/\r
3523 \r
3524 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3525 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3526 /*\r
3527 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3528 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3529 */\r
3530 \r
3531 VOID\r
3532 SquareToPos(int row, int column, int * x, int * y)\r
3533 {\r
3534   if (flipView) {\r
3535     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3536     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3537   } else {\r
3538     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3539     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3540   }\r
3541 }\r
3542 \r
3543 VOID\r
3544 DrawCoordsOnDC(HDC hdc)\r
3545 {\r
3546   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
3547   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
3548   char str[2] = { NULLCHAR, NULLCHAR };\r
3549   int oldMode, oldAlign, x, y, start, i;\r
3550   HFONT oldFont;\r
3551   HBRUSH oldBrush;\r
3552 \r
3553   if (!appData.showCoords)\r
3554     return;\r
3555 \r
3556   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3557 \r
3558   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3559   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3560   oldAlign = GetTextAlign(hdc);\r
3561   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3562 \r
3563   y = boardRect.top + lineGap;\r
3564   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3565 \r
3566   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3567   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3568     str[0] = files[start + i];\r
3569     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3570     y += squareSize + lineGap;\r
3571   }\r
3572 \r
3573   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3574 \r
3575   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3576   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3577     str[0] = ranks[start + i];\r
3578     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3579     x += squareSize + lineGap;\r
3580   }    \r
3581 \r
3582   SelectObject(hdc, oldBrush);\r
3583   SetBkMode(hdc, oldMode);\r
3584   SetTextAlign(hdc, oldAlign);\r
3585   SelectObject(hdc, oldFont);\r
3586 }\r
3587 \r
3588 VOID\r
3589 DrawGridOnDC(HDC hdc)\r
3590 {\r
3591   HPEN oldPen;\r
3592  \r
3593   if (lineGap != 0) {\r
3594     oldPen = SelectObject(hdc, gridPen);\r
3595     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3596     SelectObject(hdc, oldPen);\r
3597   }\r
3598 }\r
3599 \r
3600 #define HIGHLIGHT_PEN 0\r
3601 #define PREMOVE_PEN   1\r
3602 \r
3603 VOID\r
3604 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3605 {\r
3606   int x1, y1;\r
3607   HPEN oldPen, hPen;\r
3608   if (lineGap == 0) return;\r
3609   if (flipView) {\r
3610     x1 = boardRect.left +\r
3611       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3612     y1 = boardRect.top +\r
3613       lineGap/2 + y * (squareSize + lineGap);\r
3614   } else {\r
3615     x1 = boardRect.left +\r
3616       lineGap/2 + x * (squareSize + lineGap);\r
3617     y1 = boardRect.top +\r
3618       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3619   }\r
3620   hPen = pen ? premovePen : highlightPen;\r
3621   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3622   MoveToEx(hdc, x1, y1, NULL);\r
3623   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3624   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3625   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3626   LineTo(hdc, x1, y1);\r
3627   SelectObject(hdc, oldPen);\r
3628 }\r
3629 \r
3630 VOID\r
3631 DrawHighlightsOnDC(HDC hdc)\r
3632 {\r
3633   int i;\r
3634   for (i=0; i<2; i++) {\r
3635     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3636       DrawHighlightOnDC(hdc, TRUE,\r
3637                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3638                         HIGHLIGHT_PEN);\r
3639   }\r
3640   for (i=0; i<2; i++) {\r
3641     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3642         premoveHighlightInfo.sq[i].y >= 0) {\r
3643         DrawHighlightOnDC(hdc, TRUE,\r
3644                           premoveHighlightInfo.sq[i].x, \r
3645                           premoveHighlightInfo.sq[i].y,\r
3646                           PREMOVE_PEN);\r
3647     }\r
3648   }\r
3649 }\r
3650 \r
3651 /* Note: sqcolor is used only in monoMode */\r
3652 /* Note that this code is largely duplicated in woptions.c,\r
3653    function DrawSampleSquare, so that needs to be updated too */\r
3654 VOID\r
3655 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3656 {\r
3657   HBITMAP oldBitmap;\r
3658   HBRUSH oldBrush;\r
3659   int tmpSize;\r
3660 \r
3661   if (appData.blindfold) return;\r
3662 \r
3663   /* [AS] Use font-based pieces if needed */\r
3664   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3665     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3666     CreatePiecesFromFont();\r
3667 \r
3668     if( fontBitmapSquareSize == squareSize ) {\r
3669         int index = TranslatePieceToFontPiece(piece);\r
3670 \r
3671         SelectObject( tmphdc, hPieceMask[ index ] );\r
3672 \r
3673         BitBlt( hdc,\r
3674             x, y,\r
3675             squareSize, squareSize,\r
3676             tmphdc,\r
3677             0, 0,\r
3678             SRCAND );\r
3679 \r
3680         SelectObject( tmphdc, hPieceFace[ index ] );\r
3681 \r
3682         BitBlt( hdc,\r
3683             x, y,\r
3684             squareSize, squareSize,\r
3685             tmphdc,\r
3686             0, 0,\r
3687             SRCPAINT );\r
3688 \r
3689         return;\r
3690     }\r
3691   }\r
3692 \r
3693   if (appData.monoMode) {\r
3694     SelectObject(tmphdc, PieceBitmap(piece, \r
3695       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3696     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3697            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3698   } else {\r
3699     tmpSize = squareSize;\r
3700     if(minorSize &&\r
3701         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3702          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3703       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3704       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3705       x += (squareSize - minorSize)>>1;\r
3706       y += squareSize - minorSize - 2;\r
3707       tmpSize = minorSize;\r
3708     }\r
3709     if (color || appData.allWhite ) {\r
3710       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3711       if( color )\r
3712               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3713       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3714       if(appData.upsideDown && color==flipView)\r
3715         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3716       else\r
3717         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3718 #if 0\r
3719       /* Use black piece color for outline of white pieces */\r
3720       /* Not sure this looks really good (though xboard does it).\r
3721          Maybe better to have another selectable color, default black */\r
3722       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3723       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3724       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3725 #else\r
3726       /* Use black for outline of white pieces */\r
3727       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3728       if(appData.upsideDown && color==flipView)\r
3729         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3730       else\r
3731         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3732 #endif\r
3733     } else {\r
3734 #if 0\r
3735       /* Use white piece color for details of black pieces */\r
3736       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3737          WHITE_PIECE ones aren't always the right shape. */\r
3738       /* Not sure this looks really good (though xboard does it).\r
3739          Maybe better to have another selectable color, default medium gray? */\r
3740       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3741       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3742       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3743       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3744       SelectObject(hdc, blackPieceBrush);\r
3745       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3746 #else\r
3747       /* Use square color for details of black pieces */\r
3748       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3749       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3750       if(appData.upsideDown && !flipView)\r
3751         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3752       else\r
3753         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3754 #endif\r
3755     }\r
3756     SelectObject(hdc, oldBrush);\r
3757     SelectObject(tmphdc, oldBitmap);\r
3758   }\r
3759 }\r
3760 \r
3761 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3762 int GetBackTextureMode( int algo )\r
3763 {\r
3764     int result = BACK_TEXTURE_MODE_DISABLED;\r
3765 \r
3766     switch( algo ) \r
3767     {\r
3768         case BACK_TEXTURE_MODE_PLAIN:\r
3769             result = 1; /* Always use identity map */\r
3770             break;\r
3771         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3772             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3773             break;\r
3774     }\r
3775 \r
3776     return result;\r
3777 }\r
3778 \r
3779 /* \r
3780     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3781     to handle redraws cleanly (as random numbers would always be different).\r
3782 */\r
3783 VOID RebuildTextureSquareInfo()\r
3784 {\r
3785     BITMAP bi;\r
3786     int lite_w = 0;\r
3787     int lite_h = 0;\r
3788     int dark_w = 0;\r
3789     int dark_h = 0;\r
3790     int row;\r
3791     int col;\r
3792 \r
3793     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3794 \r
3795     if( liteBackTexture != NULL ) {\r
3796         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3797             lite_w = bi.bmWidth;\r
3798             lite_h = bi.bmHeight;\r
3799         }\r
3800     }\r
3801 \r
3802     if( darkBackTexture != NULL ) {\r
3803         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3804             dark_w = bi.bmWidth;\r
3805             dark_h = bi.bmHeight;\r
3806         }\r
3807     }\r
3808 \r
3809     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3810         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3811             if( (col + row) & 1 ) {\r
3812                 /* Lite square */\r
3813                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3814                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3815                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3816                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3817                 }\r
3818             }\r
3819             else {\r
3820                 /* Dark square */\r
3821                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3822                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3823                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3824                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3825                 }\r
3826             }\r
3827         }\r
3828     }\r
3829 }\r
3830 \r
3831 /* [AS] Arrow highlighting support */\r
3832 \r
3833 static int A_WIDTH = 5; /* Width of arrow body */\r
3834 \r
3835 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3836 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3837 \r
3838 static double Sqr( double x )\r
3839 {\r
3840     return x*x;\r
3841 }\r
3842 \r
3843 static int Round( double x )\r
3844 {\r
3845     return (int) (x + 0.5);\r
3846 }\r
3847 \r
3848 /* Draw an arrow between two points using current settings */\r
3849 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3850 {\r
3851     POINT arrow[7];\r
3852     double dx, dy, j, k, x, y;\r
3853 \r
3854     if( d_x == s_x ) {\r
3855         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3856 \r
3857         arrow[0].x = s_x + A_WIDTH;\r
3858         arrow[0].y = s_y;\r
3859 \r
3860         arrow[1].x = s_x + A_WIDTH;\r
3861         arrow[1].y = d_y - h;\r
3862 \r
3863         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3864         arrow[2].y = d_y - h;\r
3865 \r
3866         arrow[3].x = d_x;\r
3867         arrow[3].y = d_y;\r
3868 \r
3869         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3870         arrow[4].y = d_y - h;\r
3871 \r
3872         arrow[5].x = s_x - A_WIDTH;\r
3873         arrow[5].y = d_y - h;\r
3874 \r
3875         arrow[6].x = s_x - A_WIDTH;\r
3876         arrow[6].y = s_y;\r
3877     }\r
3878     else if( d_y == s_y ) {\r
3879         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3880 \r
3881         arrow[0].x = s_x;\r
3882         arrow[0].y = s_y + A_WIDTH;\r
3883 \r
3884         arrow[1].x = d_x - w;\r
3885         arrow[1].y = s_y + A_WIDTH;\r
3886 \r
3887         arrow[2].x = d_x - w;\r
3888         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3889 \r
3890         arrow[3].x = d_x;\r
3891         arrow[3].y = d_y;\r
3892 \r
3893         arrow[4].x = d_x - w;\r
3894         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3895 \r
3896         arrow[5].x = d_x - w;\r
3897         arrow[5].y = s_y - A_WIDTH;\r
3898 \r
3899         arrow[6].x = s_x;\r
3900         arrow[6].y = s_y - A_WIDTH;\r
3901     }\r
3902     else {\r
3903         /* [AS] Needed a lot of paper for this! :-) */\r
3904         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3905         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3906   \r
3907         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3908 \r
3909         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3910 \r
3911         x = s_x;\r
3912         y = s_y;\r
3913 \r
3914         arrow[0].x = Round(x - j);\r
3915         arrow[0].y = Round(y + j*dx);\r
3916 \r
3917         arrow[1].x = Round(x + j);\r
3918         arrow[1].y = Round(y - j*dx);\r
3919 \r
3920         if( d_x > s_x ) {\r
3921             x = (double) d_x - k;\r
3922             y = (double) d_y - k*dy;\r
3923         }\r
3924         else {\r
3925             x = (double) d_x + k;\r
3926             y = (double) d_y + k*dy;\r
3927         }\r
3928 \r
3929         arrow[2].x = Round(x + j);\r
3930         arrow[2].y = Round(y - j*dx);\r
3931 \r
3932         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3933         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3934 \r
3935         arrow[4].x = d_x;\r
3936         arrow[4].y = d_y;\r
3937 \r
3938         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3939         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3940 \r
3941         arrow[6].x = Round(x - j);\r
3942         arrow[6].y = Round(y + j*dx);\r
3943     }\r
3944 \r
3945     Polygon( hdc, arrow, 7 );\r
3946 }\r
3947 \r
3948 /* [AS] Draw an arrow between two squares */\r
3949 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3950 {\r
3951     int s_x, s_y, d_x, d_y;\r
3952     HPEN hpen;\r
3953     HPEN holdpen;\r
3954     HBRUSH hbrush;\r
3955     HBRUSH holdbrush;\r
3956     LOGBRUSH stLB;\r
3957 \r
3958     if( s_col == d_col && s_row == d_row ) {\r
3959         return;\r
3960     }\r
3961 \r
3962     /* Get source and destination points */\r
3963     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3964     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3965 \r
3966     if( d_y > s_y ) {\r
3967         d_y += squareSize / 4;\r
3968     }\r
3969     else if( d_y < s_y ) {\r
3970         d_y += 3 * squareSize / 4;\r
3971     }\r
3972     else {\r
3973         d_y += squareSize / 2;\r
3974     }\r
3975 \r
3976     if( d_x > s_x ) {\r
3977         d_x += squareSize / 4;\r
3978     }\r
3979     else if( d_x < s_x ) {\r
3980         d_x += 3 * squareSize / 4;\r
3981     }\r
3982     else {\r
3983         d_x += squareSize / 2;\r
3984     }\r
3985 \r
3986     s_x += squareSize / 2;\r
3987     s_y += squareSize / 2;\r
3988 \r
3989     /* Adjust width */\r
3990     A_WIDTH = squareSize / 14;\r
3991 \r
3992     /* Draw */\r
3993     stLB.lbStyle = BS_SOLID;\r
3994     stLB.lbColor = appData.highlightArrowColor;\r
3995     stLB.lbHatch = 0;\r
3996 \r
3997     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3998     holdpen = SelectObject( hdc, hpen );\r
3999     hbrush = CreateBrushIndirect( &stLB );\r
4000     holdbrush = SelectObject( hdc, hbrush );\r
4001 \r
4002     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4003 \r
4004     SelectObject( hdc, holdpen );\r
4005     SelectObject( hdc, holdbrush );\r
4006     DeleteObject( hpen );\r
4007     DeleteObject( hbrush );\r
4008 }\r
4009 \r
4010 BOOL HasHighlightInfo()\r
4011 {\r
4012     BOOL result = FALSE;\r
4013 \r
4014     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4015         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4016     {\r
4017         result = TRUE;\r
4018     }\r
4019 \r
4020     return result;\r
4021 }\r
4022 \r
4023 BOOL IsDrawArrowEnabled()\r
4024 {\r
4025     BOOL result = FALSE;\r
4026 \r
4027     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4028         result = TRUE;\r
4029     }\r
4030 \r
4031     return result;\r
4032 }\r
4033 \r
4034 VOID DrawArrowHighlight( HDC hdc )\r
4035 {\r
4036     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4037         DrawArrowBetweenSquares( hdc,\r
4038             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4039             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4040     }\r
4041 }\r
4042 \r
4043 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4044 {\r
4045     HRGN result = NULL;\r
4046 \r
4047     if( HasHighlightInfo() ) {\r
4048         int x1, y1, x2, y2;\r
4049         int sx, sy, dx, dy;\r
4050 \r
4051         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4052         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4053 \r
4054         sx = MIN( x1, x2 );\r
4055         sy = MIN( y1, y2 );\r
4056         dx = MAX( x1, x2 ) + squareSize;\r
4057         dy = MAX( y1, y2 ) + squareSize;\r
4058 \r
4059         result = CreateRectRgn( sx, sy, dx, dy );\r
4060     }\r
4061 \r
4062     return result;\r
4063 }\r
4064 \r
4065 /*\r
4066     Warning: this function modifies the behavior of several other functions. \r
4067     \r
4068     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4069     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4070     repaint is scattered all over the place, which is not good for features such as\r
4071     "arrow highlighting" that require a full repaint of the board.\r
4072 \r
4073     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4074     user interaction, when speed is not so important) but especially to avoid errors\r
4075     in the displayed graphics.\r
4076 \r
4077     In such patched places, I always try refer to this function so there is a single\r
4078     place to maintain knowledge.\r
4079     \r
4080     To restore the original behavior, just return FALSE unconditionally.\r
4081 */\r
4082 BOOL IsFullRepaintPreferrable()\r
4083 {\r
4084     BOOL result = FALSE;\r
4085 \r
4086     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4087         /* Arrow may appear on the board */\r
4088         result = TRUE;\r
4089     }\r
4090 \r
4091     return result;\r
4092 }\r
4093 \r
4094 /* \r
4095     This function is called by DrawPosition to know whether a full repaint must\r
4096     be forced or not.\r
4097 \r
4098     Only DrawPosition may directly call this function, which makes use of \r
4099     some state information. Other function should call DrawPosition specifying \r
4100     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4101 */\r
4102 BOOL DrawPositionNeedsFullRepaint()\r
4103 {\r
4104     BOOL result = FALSE;\r
4105 \r
4106     /* \r
4107         Probably a slightly better policy would be to trigger a full repaint\r
4108         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4109         but animation is fast enough that it's difficult to notice.\r
4110     */\r
4111     if( animInfo.piece == EmptySquare ) {\r
4112         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4113             result = TRUE;\r
4114         }\r
4115     }\r
4116 \r
4117     return result;\r
4118 }\r
4119 \r
4120 VOID\r
4121 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4122 {\r
4123   int row, column, x, y, square_color, piece_color;\r
4124   ChessSquare piece;\r
4125   HBRUSH oldBrush;\r
4126   HDC texture_hdc = NULL;\r
4127 \r
4128   /* [AS] Initialize background textures if needed */\r
4129   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4130       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4131       if( backTextureSquareSize != squareSize \r
4132        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4133           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4134           backTextureSquareSize = squareSize;\r
4135           RebuildTextureSquareInfo();\r
4136       }\r
4137 \r
4138       texture_hdc = CreateCompatibleDC( hdc );\r
4139   }\r
4140 \r
4141   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4142     for (column = 0; column < BOARD_WIDTH; column++) {\r
4143   \r
4144       SquareToPos(row, column, &x, &y);\r
4145 \r
4146       piece = board[row][column];\r
4147 \r
4148       square_color = ((column + row) % 2) == 1;\r
4149       if( gameInfo.variant == VariantXiangqi ) {\r
4150           square_color = !InPalace(row, column);\r
4151           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4152           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4153       }\r
4154       piece_color = (int) piece < (int) BlackPawn;\r
4155 \r
4156 \r
4157       /* [HGM] holdings file: light square or black */\r
4158       if(column == BOARD_LEFT-2) {\r
4159             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4160                 square_color = 1;\r
4161             else {\r
4162                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4163                 continue;\r
4164             }\r
4165       } else\r
4166       if(column == BOARD_RGHT + 1 ) {\r
4167             if( row < gameInfo.holdingsSize )\r
4168                 square_color = 1;\r
4169             else {\r
4170                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4171                 continue;\r
4172             }\r
4173       }\r
4174       if(column == BOARD_LEFT-1 ) /* left align */\r
4175             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4176       else if( column == BOARD_RGHT) /* right align */\r
4177             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4178       else\r
4179       if (appData.monoMode) {\r
4180         if (piece == EmptySquare) {\r
4181           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4182                  square_color ? WHITENESS : BLACKNESS);\r
4183         } else {\r
4184           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4185         }\r
4186       } \r
4187       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4188           /* [AS] Draw the square using a texture bitmap */\r
4189           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4190           int r = row, c = column; // [HGM] do not flip board in flipView\r
4191           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4192 \r
4193           DrawTile( x, y, \r
4194               squareSize, squareSize, \r
4195               hdc, \r
4196               texture_hdc,\r
4197               backTextureSquareInfo[r][c].mode,\r
4198               backTextureSquareInfo[r][c].x,\r
4199               backTextureSquareInfo[r][c].y );\r
4200 \r
4201           SelectObject( texture_hdc, hbm );\r
4202 \r
4203           if (piece != EmptySquare) {\r
4204               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4205           }\r
4206       }\r
4207       else {\r
4208         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4209 \r
4210         oldBrush = SelectObject(hdc, brush );\r
4211         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4212         SelectObject(hdc, oldBrush);\r
4213         if (piece != EmptySquare)\r
4214           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4215       }\r
4216     }\r
4217   }\r
4218 \r
4219   if( texture_hdc != NULL ) {\r
4220     DeleteDC( texture_hdc );\r
4221   }\r
4222 }\r
4223 \r
4224 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4225 void fputDW(FILE *f, int x)\r
4226 {\r
4227         fputc(x     & 255, f);\r
4228         fputc(x>>8  & 255, f);\r
4229         fputc(x>>16 & 255, f);\r
4230         fputc(x>>24 & 255, f);\r
4231 }\r
4232 \r
4233 #define MAX_CLIPS 200   /* more than enough */\r
4234 \r
4235 VOID\r
4236 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4237 {\r
4238 //  HBITMAP bufferBitmap;\r
4239   BITMAP bi;\r
4240 //  RECT Rect;\r
4241   HDC tmphdc;\r
4242   HBITMAP hbm;\r
4243   int w = 100, h = 50;\r
4244 \r
4245   if(cps->programLogo == NULL) return;\r
4246 //  GetClientRect(hwndMain, &Rect);\r
4247 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4248 //                                      Rect.bottom-Rect.top+1);\r
4249   tmphdc = CreateCompatibleDC(hdc);\r
4250   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4251   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4252             w = bi.bmWidth;\r
4253             h = bi.bmHeight;\r
4254   }\r
4255   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4256                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4257   SelectObject(tmphdc, hbm);\r
4258   DeleteDC(tmphdc);\r
4259 }\r
4260 \r
4261 VOID\r
4262 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4263 {\r
4264   static Board lastReq, lastDrawn;\r
4265   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4266   static int lastDrawnFlipView = 0;\r
4267   static int lastReqValid = 0, lastDrawnValid = 0;\r
4268   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4269   HDC tmphdc;\r
4270   HDC hdcmem;\r
4271   HBITMAP bufferBitmap;\r
4272   HBITMAP oldBitmap;\r
4273   RECT Rect;\r
4274   HRGN clips[MAX_CLIPS];\r
4275   ChessSquare dragged_piece = EmptySquare;\r
4276 \r
4277   /* I'm undecided on this - this function figures out whether a full\r
4278    * repaint is necessary on its own, so there's no real reason to have the\r
4279    * caller tell it that.  I think this can safely be set to FALSE - but\r
4280    * if we trust the callers not to request full repaints unnessesarily, then\r
4281    * we could skip some clipping work.  In other words, only request a full\r
4282    * redraw when the majority of pieces have changed positions (ie. flip, \r
4283    * gamestart and similar)  --Hawk\r
4284    */\r
4285   Boolean fullrepaint = repaint;\r
4286 \r
4287   if( DrawPositionNeedsFullRepaint() ) {\r
4288       fullrepaint = TRUE;\r
4289   }\r
4290 \r
4291 #if 0\r
4292   if( fullrepaint ) {\r
4293       static int repaint_count = 0;\r
4294       char buf[128];\r
4295 \r
4296       repaint_count++;\r
4297       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4298       OutputDebugString( buf );\r
4299   }\r
4300 #endif\r
4301 \r
4302   if (board == NULL) {\r
4303     if (!lastReqValid) {\r
4304       return;\r
4305     }\r
4306     board = lastReq;\r
4307   } else {\r
4308     CopyBoard(lastReq, board);\r
4309     lastReqValid = 1;\r
4310   }\r
4311 \r
4312   if (doingSizing) {\r
4313     return;\r
4314   }\r
4315 \r
4316   if (IsIconic(hwndMain)) {\r
4317     return;\r
4318   }\r
4319 \r
4320   if (hdc == NULL) {\r
4321     hdc = GetDC(hwndMain);\r
4322     if (!appData.monoMode) {\r
4323       SelectPalette(hdc, hPal, FALSE);\r
4324       RealizePalette(hdc);\r
4325     }\r
4326     releaseDC = TRUE;\r
4327   } else {\r
4328     releaseDC = FALSE;\r
4329   }\r
4330 \r
4331 #if 0\r
4332   fprintf(debugFP, "*******************************\n"\r
4333                    "repaint = %s\n"\r
4334                    "dragInfo.from (%d,%d)\n"\r
4335                    "dragInfo.start (%d,%d)\n"\r
4336                    "dragInfo.pos (%d,%d)\n"\r
4337                    "dragInfo.lastpos (%d,%d)\n", \r
4338                     repaint ? "TRUE" : "FALSE",\r
4339                     dragInfo.from.x, dragInfo.from.y, \r
4340                     dragInfo.start.x, dragInfo.start.y,\r
4341                     dragInfo.pos.x, dragInfo.pos.y,\r
4342                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4343   fprintf(debugFP, "prev:  ");\r
4344   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4345     for (column = 0; column < BOARD_WIDTH; column++) {\r
4346       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4347     }\r
4348   }\r
4349   fprintf(debugFP, "\n");\r
4350   fprintf(debugFP, "board: ");\r
4351   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4352     for (column = 0; column < BOARD_WIDTH; column++) {\r
4353       fprintf(debugFP, "%d ", board[row][column]);\r
4354     }\r
4355   }\r
4356   fprintf(debugFP, "\n");\r
4357   fflush(debugFP);\r
4358 #endif\r
4359 \r
4360   /* Create some work-DCs */\r
4361   hdcmem = CreateCompatibleDC(hdc);\r
4362   tmphdc = CreateCompatibleDC(hdc);\r
4363 \r
4364   /* If dragging is in progress, we temporarely remove the piece */\r
4365   /* [HGM] or temporarily decrease count if stacked              */\r
4366   /*       !! Moved to before board compare !!                   */\r
4367   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4368     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4369     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4370             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4371         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4372     } else \r
4373     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4374             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4375         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4376     } else \r
4377         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4378   }\r
4379 \r
4380   /* Figure out which squares need updating by comparing the \r
4381    * newest board with the last drawn board and checking if\r
4382    * flipping has changed.\r
4383    */\r
4384   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4385     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4386       for (column = 0; column < BOARD_WIDTH; column++) {\r
4387         if (lastDrawn[row][column] != board[row][column]) {\r
4388           SquareToPos(row, column, &x, &y);\r
4389           clips[num_clips++] =\r
4390             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4391         }\r
4392       }\r
4393     }\r
4394     for (i=0; i<2; i++) {\r
4395       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4396           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4397         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4398             lastDrawnHighlight.sq[i].y >= 0) {\r
4399           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4400                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4401           clips[num_clips++] =\r
4402             CreateRectRgn(x - lineGap, y - lineGap, \r
4403                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4404         }\r
4405         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4406           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4407           clips[num_clips++] =\r
4408             CreateRectRgn(x - lineGap, y - lineGap, \r
4409                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4410         }\r
4411       }\r
4412     }\r
4413     for (i=0; i<2; i++) {\r
4414       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4415           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4416         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4417             lastDrawnPremove.sq[i].y >= 0) {\r
4418           SquareToPos(lastDrawnPremove.sq[i].y,\r
4419                       lastDrawnPremove.sq[i].x, &x, &y);\r
4420           clips[num_clips++] =\r
4421             CreateRectRgn(x - lineGap, y - lineGap, \r
4422                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4423         }\r
4424         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4425             premoveHighlightInfo.sq[i].y >= 0) {\r
4426           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4427                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4428           clips[num_clips++] =\r
4429             CreateRectRgn(x - lineGap, y - lineGap, \r
4430                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4431         }\r
4432       }\r
4433     }\r
4434   } else {\r
4435     fullrepaint = TRUE;\r
4436   }\r
4437 \r
4438   /* Create a buffer bitmap - this is the actual bitmap\r
4439    * being written to.  When all the work is done, we can\r
4440    * copy it to the real DC (the screen).  This avoids\r
4441    * the problems with flickering.\r
4442    */\r
4443   GetClientRect(hwndMain, &Rect);\r
4444   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4445                                         Rect.bottom-Rect.top+1);\r
4446   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4447   if (!appData.monoMode) {\r
4448     SelectPalette(hdcmem, hPal, FALSE);\r
4449   }\r
4450 \r
4451   /* Create clips for dragging */\r
4452   if (!fullrepaint) {\r
4453     if (dragInfo.from.x >= 0) {\r
4454       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4455       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4456     }\r
4457     if (dragInfo.start.x >= 0) {\r
4458       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4459       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4460     }\r
4461     if (dragInfo.pos.x >= 0) {\r
4462       x = dragInfo.pos.x - squareSize / 2;\r
4463       y = dragInfo.pos.y - squareSize / 2;\r
4464       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4465     }\r
4466     if (dragInfo.lastpos.x >= 0) {\r
4467       x = dragInfo.lastpos.x - squareSize / 2;\r
4468       y = dragInfo.lastpos.y - squareSize / 2;\r
4469       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4470     }\r
4471   }\r
4472 \r
4473   /* Are we animating a move?  \r
4474    * If so, \r
4475    *   - remove the piece from the board (temporarely)\r
4476    *   - calculate the clipping region\r
4477    */\r
4478   if (!fullrepaint) {\r
4479     if (animInfo.piece != EmptySquare) {\r
4480       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4481       x = boardRect.left + animInfo.lastpos.x;\r
4482       y = boardRect.top + animInfo.lastpos.y;\r
4483       x2 = boardRect.left + animInfo.pos.x;\r
4484       y2 = boardRect.top + animInfo.pos.y;\r
4485       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4486       /* Slight kludge.  The real problem is that after AnimateMove is\r
4487          done, the position on the screen does not match lastDrawn.\r
4488          This currently causes trouble only on e.p. captures in\r
4489          atomic, where the piece moves to an empty square and then\r
4490          explodes.  The old and new positions both had an empty square\r
4491          at the destination, but animation has drawn a piece there and\r
4492          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4493       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4494     }\r
4495   }\r
4496 \r
4497   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4498   if (num_clips == 0)\r
4499     fullrepaint = TRUE;\r
4500 \r
4501   /* Set clipping on the memory DC */\r
4502   if (!fullrepaint) {\r
4503     SelectClipRgn(hdcmem, clips[0]);\r
4504     for (x = 1; x < num_clips; x++) {\r
4505       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4506         abort();  // this should never ever happen!\r
4507     }\r
4508   }\r
4509 \r
4510   /* Do all the drawing to the memory DC */\r
4511   if(explodeInfo.radius) { // [HGM] atomic\r
4512         HBRUSH oldBrush;\r
4513         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4514         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4515         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4516         x += squareSize/2;\r
4517         y += squareSize/2;\r
4518         if(!fullrepaint) {\r
4519           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4520           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4521         }\r
4522         DrawGridOnDC(hdcmem);\r
4523         DrawHighlightsOnDC(hdcmem);\r
4524         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4525         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4526         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4527         SelectObject(hdcmem, oldBrush);\r
4528   } else {\r
4529     DrawGridOnDC(hdcmem);\r
4530     DrawHighlightsOnDC(hdcmem);\r
4531     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4532   }\r
4533   if(logoHeight) {\r
4534         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4535         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4536   }\r
4537 \r
4538   if( appData.highlightMoveWithArrow ) {\r
4539     DrawArrowHighlight(hdcmem);\r
4540   }\r
4541 \r
4542   DrawCoordsOnDC(hdcmem);\r
4543 \r
4544   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4545                  /* to make sure lastDrawn contains what is actually drawn */\r
4546 \r
4547   /* Put the dragged piece back into place and draw it (out of place!) */\r
4548     if (dragged_piece != EmptySquare) {\r
4549     /* [HGM] or restack */\r
4550     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4551                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4552     else\r
4553     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4554                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4555     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4556     x = dragInfo.pos.x - squareSize / 2;\r
4557     y = dragInfo.pos.y - squareSize / 2;\r
4558     DrawPieceOnDC(hdcmem, dragged_piece,\r
4559                   ((int) dragged_piece < (int) BlackPawn), \r
4560                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4561   }   \r
4562   \r
4563   /* Put the animated piece back into place and draw it */\r
4564   if (animInfo.piece != EmptySquare) {\r
4565     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4566     x = boardRect.left + animInfo.pos.x;\r
4567     y = boardRect.top + animInfo.pos.y;\r
4568     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4569                   ((int) animInfo.piece < (int) BlackPawn),\r
4570                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4571   }\r
4572 \r
4573   /* Release the bufferBitmap by selecting in the old bitmap \r
4574    * and delete the memory DC\r
4575    */\r
4576   SelectObject(hdcmem, oldBitmap);\r
4577   DeleteDC(hdcmem);\r
4578 \r
4579   /* Set clipping on the target DC */\r
4580   if (!fullrepaint) {\r
4581     SelectClipRgn(hdc, clips[0]);\r
4582     for (x = 1; x < num_clips; x++) {\r
4583       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4584         abort();   // this should never ever happen!\r
4585     } \r
4586   }\r
4587 \r
4588   /* Copy the new bitmap onto the screen in one go.\r
4589    * This way we avoid any flickering\r
4590    */\r
4591   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4592   BitBlt(hdc, boardRect.left, boardRect.top,\r
4593          boardRect.right - boardRect.left,\r
4594          boardRect.bottom - boardRect.top,\r
4595          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4596   if(saveDiagFlag) { \r
4597     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4598     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4599 \r
4600     GetObject(bufferBitmap, sizeof(b), &b);\r
4601     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4602         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4603         bih.biWidth = b.bmWidth;\r
4604         bih.biHeight = b.bmHeight;\r
4605         bih.biPlanes = 1;\r
4606         bih.biBitCount = b.bmBitsPixel;\r
4607         bih.biCompression = 0;\r
4608         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4609         bih.biXPelsPerMeter = 0;\r
4610         bih.biYPelsPerMeter = 0;\r
4611         bih.biClrUsed = 0;\r
4612         bih.biClrImportant = 0;\r
4613 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4614 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4615         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4616 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4617 \r
4618 #if 1\r
4619         wb = b.bmWidthBytes;\r
4620         // count colors\r
4621         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4622                 int k = ((int*) pData)[i];\r
4623                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4624                 if(j >= 16) break;\r
4625                 color[j] = k;\r
4626                 if(j >= nrColors) nrColors = j+1;\r
4627         }\r
4628         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4629                 INT p = 0;\r
4630                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4631                     for(w=0; w<(wb>>2); w+=2) {\r
4632                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4633                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4634                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4635                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4636                         pData[p++] = m | j<<4;\r
4637                     }\r
4638                     while(p&3) pData[p++] = 0;\r
4639                 }\r
4640                 fac = 3;\r
4641                 wb = ((wb+31)>>5)<<2;\r
4642         }\r
4643         // write BITMAPFILEHEADER\r
4644         fprintf(diagFile, "BM");\r
4645         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4646         fputDW(diagFile, 0);\r
4647         fputDW(diagFile, 0x36 + (fac?64:0));\r
4648         // write BITMAPINFOHEADER\r
4649         fputDW(diagFile, 40);\r
4650         fputDW(diagFile, b.bmWidth);\r
4651         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4652         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4653         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4654         fputDW(diagFile, 0);\r
4655         fputDW(diagFile, 0);\r
4656         fputDW(diagFile, 0);\r
4657         fputDW(diagFile, 0);\r
4658         fputDW(diagFile, 0);\r
4659         fputDW(diagFile, 0);\r
4660         // write color table\r
4661         if(fac)\r
4662         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4663         // write bitmap data\r
4664         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4665                 fputc(pData[i], diagFile);\r
4666 #endif\r
4667      }\r
4668   }\r
4669 \r
4670   SelectObject(tmphdc, oldBitmap);\r
4671 \r
4672   /* Massive cleanup */\r
4673   for (x = 0; x < num_clips; x++)\r
4674     DeleteObject(clips[x]);\r
4675 \r
4676   DeleteDC(tmphdc);\r
4677   DeleteObject(bufferBitmap);\r
4678 \r
4679   if (releaseDC) \r
4680     ReleaseDC(hwndMain, hdc);\r
4681   \r
4682   if (lastDrawnFlipView != flipView) {\r
4683     if (flipView)\r
4684       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4685     else\r
4686       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4687   }\r
4688 \r
4689 /*  CopyBoard(lastDrawn, board);*/\r
4690   lastDrawnHighlight = highlightInfo;\r
4691   lastDrawnPremove   = premoveHighlightInfo;\r
4692   lastDrawnFlipView = flipView;\r
4693   lastDrawnValid = 1;\r
4694 }\r
4695 \r
4696 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4697 int\r
4698 SaveDiagram(f)\r
4699      FILE *f;\r
4700 {\r
4701     saveDiagFlag = 1; diagFile = f;\r
4702     HDCDrawPosition(NULL, TRUE, NULL);\r
4703 \r
4704     saveDiagFlag = 0;\r
4705 \r
4706 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4707     \r
4708     fclose(f);\r
4709     return TRUE;\r
4710 }\r
4711 \r
4712 \r
4713 /*---------------------------------------------------------------------------*\\r
4714 | CLIENT PAINT PROCEDURE\r
4715 |   This is the main event-handler for the WM_PAINT message.\r
4716 |\r
4717 \*---------------------------------------------------------------------------*/\r
4718 VOID\r
4719 PaintProc(HWND hwnd)\r
4720 {\r
4721   HDC         hdc;\r
4722   PAINTSTRUCT ps;\r
4723   HFONT       oldFont;\r
4724 \r
4725   if((hdc = BeginPaint(hwnd, &ps))) {\r
4726     if (IsIconic(hwnd)) {\r
4727       DrawIcon(hdc, 2, 2, iconCurrent);\r
4728     } else {\r
4729       if (!appData.monoMode) {\r
4730         SelectPalette(hdc, hPal, FALSE);\r
4731         RealizePalette(hdc);\r
4732       }\r
4733       HDCDrawPosition(hdc, 1, NULL);\r
4734       oldFont =\r
4735         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4736       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4737                  ETO_CLIPPED|ETO_OPAQUE,\r
4738                  &messageRect, messageText, strlen(messageText), NULL);\r
4739       SelectObject(hdc, oldFont);\r
4740       DisplayBothClocks();\r
4741     }\r
4742     EndPaint(hwnd,&ps);\r
4743   }\r
4744 \r
4745   return;\r
4746 }\r
4747 \r
4748 \r
4749 /*\r
4750  * If the user selects on a border boundary, return -1; if off the board,\r
4751  *   return -2.  Otherwise map the event coordinate to the square.\r
4752  * The offset boardRect.left or boardRect.top must already have been\r
4753  *   subtracted from x.\r
4754  */\r
4755 int\r
4756 EventToSquare(int x)\r
4757 {\r
4758   if (x <= 0)\r
4759     return -2;\r
4760   if (x < lineGap)\r
4761     return -1;\r
4762   x -= lineGap;\r
4763   if ((x % (squareSize + lineGap)) >= squareSize)\r
4764     return -1;\r
4765   x /= (squareSize + lineGap);\r
4766   if (x >= BOARD_SIZE)\r
4767     return -2;\r
4768   return x;\r
4769 }\r
4770 \r
4771 typedef struct {\r
4772   char piece;\r
4773   int command;\r
4774   char* name;\r
4775 } DropEnable;\r
4776 \r
4777 DropEnable dropEnables[] = {\r
4778   { 'P', DP_Pawn, "Pawn" },\r
4779   { 'N', DP_Knight, "Knight" },\r
4780   { 'B', DP_Bishop, "Bishop" },\r
4781   { 'R', DP_Rook, "Rook" },\r
4782   { 'Q', DP_Queen, "Queen" },\r
4783 };\r
4784 \r
4785 VOID\r
4786 SetupDropMenu(HMENU hmenu)\r
4787 {\r
4788   int i, count, enable;\r
4789   char *p;\r
4790   extern char white_holding[], black_holding[];\r
4791   char item[MSG_SIZ];\r
4792 \r
4793   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4794     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4795                dropEnables[i].piece);\r
4796     count = 0;\r
4797     while (p && *p++ == dropEnables[i].piece) count++;\r
4798     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4799     enable = count > 0 || !appData.testLegality\r
4800       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4801                       && !appData.icsActive);\r
4802     ModifyMenu(hmenu, dropEnables[i].command,\r
4803                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4804                dropEnables[i].command, item);\r
4805   }\r
4806 }\r
4807 \r
4808 static int fromX = -1, fromY = -1, toX, toY;\r
4809 \r
4810 /* Event handler for mouse messages */\r
4811 VOID\r
4812 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4813 {\r
4814   int x, y;\r
4815   POINT pt;\r
4816   static int recursive = 0;\r
4817   HMENU hmenu;\r
4818 //  BOOLEAN needsRedraw = FALSE;\r
4819   BOOLEAN saveAnimate;\r
4820   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4821   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4822   ChessMove moveType;\r
4823 \r
4824   if (recursive) {\r
4825     if (message == WM_MBUTTONUP) {\r
4826       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4827          to the middle button: we simulate pressing the left button too!\r
4828          */\r
4829       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4830       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4831     }\r
4832     return;\r
4833   }\r
4834   recursive++;\r
4835   \r
4836   pt.x = LOWORD(lParam);\r
4837   pt.y = HIWORD(lParam);\r
4838   x = EventToSquare(pt.x - boardRect.left);\r
4839   y = EventToSquare(pt.y - boardRect.top);\r
4840   if (!flipView && y >= 0) {\r
4841     y = BOARD_HEIGHT - 1 - y;\r
4842   }\r
4843   if (flipView && x >= 0) {\r
4844     x = BOARD_WIDTH - 1 - x;\r
4845   }\r
4846 \r
4847   switch (message) {\r
4848   case WM_LBUTTONDOWN:\r
4849     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4850         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4851         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4852         if(gameInfo.holdingsWidth && \r
4853                 (WhiteOnMove(currentMove) \r
4854                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4855                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4856             // click in right holdings, for determining promotion piece\r
4857             ChessSquare p = boards[currentMove][y][x];\r
4858             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4859             if(p != EmptySquare) {\r
4860                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4861                 fromX = fromY = -1;\r
4862                 break;\r
4863             }\r
4864         }\r
4865         DrawPosition(FALSE, boards[currentMove]);\r
4866         break;\r
4867     }\r
4868     ErrorPopDown();\r
4869     sameAgain = FALSE;\r
4870     if (y == -2) {\r
4871       /* Downclick vertically off board; check if on clock */\r
4872       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4873         if (gameMode == EditPosition) {\r
4874           SetWhiteToPlayEvent();\r
4875         } else if (gameMode == IcsPlayingBlack ||\r
4876                    gameMode == MachinePlaysWhite) {\r
4877           CallFlagEvent();\r
4878         } else if (gameMode == EditGame) {\r
4879           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4880         }\r
4881       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4882         if (gameMode == EditPosition) {\r
4883           SetBlackToPlayEvent();\r
4884         } else if (gameMode == IcsPlayingWhite ||\r
4885                    gameMode == MachinePlaysBlack) {\r
4886           CallFlagEvent();\r
4887         } else if (gameMode == EditGame) {\r
4888           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4889         }\r
4890       }\r
4891       if (!appData.highlightLastMove) {\r
4892         ClearHighlights();\r
4893         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4894       }\r
4895       fromX = fromY = -1;\r
4896       dragInfo.start.x = dragInfo.start.y = -1;\r
4897       dragInfo.from = dragInfo.start;\r
4898       break;\r
4899     } else if (x < 0 || y < 0\r
4900       /* [HGM] block clicks between board and holdings */\r
4901               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4902               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4903               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4904         /* EditPosition, empty square, or different color piece;\r
4905            click-click move is possible */\r
4906                                ) {\r
4907       break;\r
4908     } else if (fromX == x && fromY == y) {\r
4909       /* Downclick on same square again */\r
4910       ClearHighlights();\r
4911       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4912       sameAgain = TRUE;  \r
4913     } else if (fromX != -1 &&\r
4914                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4915                                                                         ) {\r
4916       /* Downclick on different square. */\r
4917       /* [HGM] if on holdings file, should count as new first click ! */\r
4918       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4919         toX = x;\r
4920         toY = y;\r
4921         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4922            to make sure move is legal before showing promotion popup */\r
4923         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4924         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4925                 fromX = fromY = -1; \r
4926                 ClearHighlights();\r
4927                 DrawPosition(FALSE, boards[currentMove]);\r
4928                 break; \r
4929         } else \r
4930         if(moveType != ImpossibleMove) {\r
4931           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4932           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4933             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4934               appData.alwaysPromoteToQueen)) {\r
4935                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4936                   if (!appData.highlightLastMove) {\r
4937                       ClearHighlights();\r
4938                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4939                   }\r
4940           } else\r
4941           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4942                   SetHighlights(fromX, fromY, toX, toY);\r
4943                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4944                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4945                      If promotion to Q is legal, all are legal! */\r
4946                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4947                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4948                     // kludge to temporarily execute move on display, wthout promotng yet\r
4949                     promotionChoice = TRUE;\r
4950                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4951                     boards[currentMove][toY][toX] = p;\r
4952                     DrawPosition(FALSE, boards[currentMove]);\r
4953                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4954                     boards[currentMove][toY][toX] = q;\r
4955                   } else\r
4956                   PromotionPopup(hwnd);\r
4957           } else {       /* not a promotion */\r
4958              if (appData.animate || appData.highlightLastMove) {\r
4959                  SetHighlights(fromX, fromY, toX, toY);\r
4960              } else {\r
4961                  ClearHighlights();\r
4962              }\r
4963              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4964              fromX = fromY = -1;\r
4965              if (appData.animate && !appData.highlightLastMove) {\r
4966                   ClearHighlights();\r
4967                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4968              }\r
4969           }\r
4970           break;\r
4971         }\r
4972         if (gotPremove) {\r
4973             /* [HGM] it seemed that braces were missing here */\r
4974             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4975             fromX = fromY = -1;\r
4976             break;\r
4977         }\r
4978       }\r
4979       ClearHighlights();\r
4980       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4981     }\r
4982     /* First downclick, or restart on a square with same color piece */\r
4983     if (!frozen && OKToStartUserMove(x, y)) {\r
4984       fromX = x;\r
4985       fromY = y;\r
4986       dragInfo.lastpos = pt;\r
4987       dragInfo.from.x = fromX;\r
4988       dragInfo.from.y = fromY;\r
4989       dragInfo.start = dragInfo.from;\r
4990       SetCapture(hwndMain);\r
4991     } else {\r
4992       fromX = fromY = -1;\r
4993       dragInfo.start.x = dragInfo.start.y = -1;\r
4994       dragInfo.from = dragInfo.start;\r
4995       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4996     }\r
4997     break;\r
4998 \r
4999   case WM_LBUTTONUP:\r
5000     ReleaseCapture();\r
5001     if (fromX == -1) break;\r
5002     if (x == fromX && y == fromY) {\r
5003       dragInfo.from.x = dragInfo.from.y = -1;\r
5004       /* Upclick on same square */\r
5005       if (sameAgain) {\r
5006         /* Clicked same square twice: abort click-click move */\r
5007         fromX = fromY = -1;\r
5008         gotPremove = 0;\r
5009         ClearPremoveHighlights();\r
5010       } else {\r
5011         /* First square clicked: start click-click move */\r
5012         SetHighlights(fromX, fromY, -1, -1);\r
5013       }\r
5014       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5015     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5016       /* Errant click; ignore */\r
5017       break;\r
5018     } else {\r
5019       /* Finish drag move. */\r
5020     if (appData.debugMode) {\r
5021         fprintf(debugFP, "release\n");\r
5022     }\r
5023       dragInfo.from.x = dragInfo.from.y = -1;\r
5024       toX = x;\r
5025       toY = y;\r
5026       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5027       appData.animate = appData.animate && !appData.animateDragging;\r
5028       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5029       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5030                 fromX = fromY = -1; \r
5031                 ClearHighlights();\r
5032                 DrawPosition(FALSE, boards[currentMove]);\r
5033                 break; \r
5034       } else \r
5035       if(moveType != ImpossibleMove) {\r
5036           /* [HGM] use move type to determine if move is promotion.\r
5037              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5038           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5039             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5040               appData.alwaysPromoteToQueen)) \r
5041                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5042           else \r
5043           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5044                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5045                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5046                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5047                     // kludge to temporarily execute move on display, wthout promotng yet\r
5048                     promotionChoice = TRUE;\r
5049                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5050                     boards[currentMove][toY][toX] = p;\r
5051                     DrawPosition(FALSE, boards[currentMove]);\r
5052                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5053                     boards[currentMove][toY][toX] = q;\r
5054                     break;\r
5055                   } else\r
5056                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5057           } else {\r
5058             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5059                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5060                                         moveType == WhiteCapturesEnPassant || \r
5061                                         moveType == BlackCapturesEnPassant   ) )\r
5062                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5063             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5064           }\r
5065       }\r
5066       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5067       appData.animate = saveAnimate;\r
5068       fromX = fromY = -1;\r
5069       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5070         ClearHighlights();\r
5071       }\r
5072       if (appData.animate || appData.animateDragging ||\r
5073           appData.highlightDragging || gotPremove) {\r
5074         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5075       }\r
5076     }\r
5077     dragInfo.start.x = dragInfo.start.y = -1; \r
5078     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5079     break;\r
5080 \r
5081   case WM_MOUSEMOVE:\r
5082     if ((appData.animateDragging || appData.highlightDragging)\r
5083         && (wParam & MK_LBUTTON)\r
5084         && dragInfo.from.x >= 0) \r
5085     {\r
5086       BOOL full_repaint = FALSE;\r
5087 \r
5088       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5089       if (appData.animateDragging) {\r
5090         dragInfo.pos = pt;\r
5091       }\r
5092       if (appData.highlightDragging) {\r
5093         SetHighlights(fromX, fromY, x, y);\r
5094         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5095             full_repaint = TRUE;\r
5096         }\r
5097       }\r
5098       \r
5099       DrawPosition( full_repaint, NULL);\r
5100       \r
5101       dragInfo.lastpos = dragInfo.pos;\r
5102     }\r
5103     break;\r
5104 \r
5105   case WM_MOUSEWHEEL: // [DM]\r
5106     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5107        /* Mouse Wheel is being rolled forward\r
5108         * Play moves forward\r
5109         */\r
5110        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5111                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5112        /* Mouse Wheel is being rolled backward\r
5113         * Play moves backward\r
5114         */\r
5115        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5116                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5117     }\r
5118     break;\r
5119 \r
5120   case WM_MBUTTONDOWN:\r
5121   case WM_RBUTTONDOWN:\r
5122     ErrorPopDown();\r
5123     ReleaseCapture();\r
5124     fromX = fromY = -1;\r
5125     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5126     dragInfo.start.x = dragInfo.start.y = -1;\r
5127     dragInfo.from = dragInfo.start;\r
5128     dragInfo.lastpos = dragInfo.pos;\r
5129     if (appData.highlightDragging) {\r
5130       ClearHighlights();\r
5131     }\r
5132     if(y == -2) {\r
5133       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5134       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5135           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5136       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5137           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5138       }\r
5139     }\r
5140     DrawPosition(TRUE, NULL);\r
5141 \r
5142     switch (gameMode) {\r
5143     case EditPosition:\r
5144     case IcsExamining:\r
5145       if (x < 0 || y < 0) break;\r
5146       fromX = x;\r
5147       fromY = y;\r
5148       if (message == WM_MBUTTONDOWN) {\r
5149         buttonCount = 3;  /* even if system didn't think so */\r
5150         if (wParam & MK_SHIFT) \r
5151           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5152         else\r
5153           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5154       } else { /* message == WM_RBUTTONDOWN */\r
5155 #if 0\r
5156         if (buttonCount == 3) {\r
5157           if (wParam & MK_SHIFT) \r
5158             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5159           else\r
5160             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5161         } else {\r
5162           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5163         }\r
5164 #else\r
5165         /* Just have one menu, on the right button.  Windows users don't\r
5166            think to try the middle one, and sometimes other software steals\r
5167            it, or it doesn't really exist. */\r
5168         if(gameInfo.variant != VariantShogi)\r
5169             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5170         else\r
5171             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5172 #endif\r
5173       }\r
5174       break;\r
5175     case IcsPlayingWhite:\r
5176     case IcsPlayingBlack:\r
5177     case EditGame:\r
5178     case MachinePlaysWhite:\r
5179     case MachinePlaysBlack:\r
5180       if (appData.testLegality &&\r
5181           gameInfo.variant != VariantBughouse &&\r
5182           gameInfo.variant != VariantCrazyhouse) break;\r
5183       if (x < 0 || y < 0) break;\r
5184       fromX = x;\r
5185       fromY = y;\r
5186       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5187       SetupDropMenu(hmenu);\r
5188       MenuPopup(hwnd, pt, hmenu, -1);\r
5189       break;\r
5190     default:\r
5191       break;\r
5192     }\r
5193     break;\r
5194   }\r
5195 \r
5196   recursive--;\r
5197 }\r
5198 \r
5199 /* Preprocess messages for buttons in main window */\r
5200 LRESULT CALLBACK\r
5201 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5202 {\r
5203   int id = GetWindowLong(hwnd, GWL_ID);\r
5204   int i, dir;\r
5205 \r
5206   for (i=0; i<N_BUTTONS; i++) {\r
5207     if (buttonDesc[i].id == id) break;\r
5208   }\r
5209   if (i == N_BUTTONS) return 0;\r
5210   switch (message) {\r
5211   case WM_KEYDOWN:\r
5212     switch (wParam) {\r
5213     case VK_LEFT:\r
5214     case VK_RIGHT:\r
5215       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5216       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5217       return TRUE;\r
5218     }\r
5219     break;\r
5220   case WM_CHAR:\r
5221     switch (wParam) {\r
5222     case '\r':\r
5223       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5224       return TRUE;\r
5225     case '\t':\r
5226       if (appData.icsActive) {\r
5227         if (GetKeyState(VK_SHIFT) < 0) {\r
5228           /* shifted */\r
5229           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5230           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5231           SetFocus(h);\r
5232         } else {\r
5233           /* unshifted */\r
5234           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5235           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5236           SetFocus(h);\r
5237         }\r
5238         return TRUE;\r
5239       }\r
5240       break;\r
5241     default:\r
5242       if (appData.icsActive) {\r
5243         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5244         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5245         SetFocus(h);\r
5246         SendMessage(h, WM_CHAR, wParam, lParam);\r
5247         return TRUE;\r
5248       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5249         PopUpMoveDialog((char)wParam);\r
5250       }\r
5251       break;\r
5252     }\r
5253     break;\r
5254   }\r
5255   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5256 }\r
5257 \r
5258 /* Process messages for Promotion dialog box */\r
5259 LRESULT CALLBACK\r
5260 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5261 {\r
5262   char promoChar;\r
5263 \r
5264   switch (message) {\r
5265   case WM_INITDIALOG: /* message: initialize dialog box */\r
5266     /* Center the dialog over the application window */\r
5267     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5268     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5269       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5270        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5271                SW_SHOW : SW_HIDE);\r
5272     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5273     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5274        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5275          PieceToChar(WhiteAngel) != '~') ||\r
5276         (PieceToChar(BlackAngel) >= 'A' &&\r
5277          PieceToChar(BlackAngel) != '~')   ) ?\r
5278                SW_SHOW : SW_HIDE);\r
5279     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5280        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5281          PieceToChar(WhiteMarshall) != '~') ||\r
5282         (PieceToChar(BlackMarshall) >= 'A' &&\r
5283          PieceToChar(BlackMarshall) != '~')   ) ?\r
5284                SW_SHOW : SW_HIDE);\r
5285     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5286     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5287        gameInfo.variant != VariantShogi ?\r
5288                SW_SHOW : SW_HIDE);\r
5289     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5290        gameInfo.variant != VariantShogi ?\r
5291                SW_SHOW : SW_HIDE);\r
5292     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5293        gameInfo.variant == VariantShogi ?\r
5294                SW_SHOW : SW_HIDE);\r
5295     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5296        gameInfo.variant == VariantShogi ?\r
5297                SW_SHOW : SW_HIDE);\r
5298     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5299        gameInfo.variant == VariantSuper ?\r
5300                SW_SHOW : SW_HIDE);\r
5301     return TRUE;\r
5302 \r
5303   case WM_COMMAND: /* message: received a command */\r
5304     switch (LOWORD(wParam)) {\r
5305     case IDCANCEL:\r
5306       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5307       ClearHighlights();\r
5308       DrawPosition(FALSE, NULL);\r
5309       return TRUE;\r
5310     case PB_King:\r
5311       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5312       break;\r
5313     case PB_Queen:\r
5314       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5315       break;\r
5316     case PB_Rook:\r
5317       promoChar = PieceToChar(BlackRook);\r
5318       break;\r
5319     case PB_Bishop:\r
5320       promoChar = PieceToChar(BlackBishop);\r
5321       break;\r
5322     case PB_Chancellor:\r
5323       promoChar = PieceToChar(BlackMarshall);\r
5324       break;\r
5325     case PB_Archbishop:\r
5326       promoChar = PieceToChar(BlackAngel);\r
5327       break;\r
5328     case PB_Knight:\r
5329       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5330       break;\r
5331     default:\r
5332       return FALSE;\r
5333     }\r
5334     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5335     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5336        only show the popup when we are already sure the move is valid or\r
5337        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5338        will figure out it is a promotion from the promoChar. */\r
5339     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5340     if (!appData.highlightLastMove) {\r
5341       ClearHighlights();\r
5342       DrawPosition(FALSE, NULL);\r
5343     }\r
5344     return TRUE;\r
5345   }\r
5346   return FALSE;\r
5347 }\r
5348 \r
5349 /* Pop up promotion dialog */\r
5350 VOID\r
5351 PromotionPopup(HWND hwnd)\r
5352 {\r
5353   FARPROC lpProc;\r
5354 \r
5355   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5356   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5357     hwnd, (DLGPROC)lpProc);\r
5358   FreeProcInstance(lpProc);\r
5359 }\r
5360 \r
5361 /* Toggle ShowThinking */\r
5362 VOID\r
5363 ToggleShowThinking()\r
5364 {\r
5365   appData.showThinking = !appData.showThinking;\r
5366   ShowThinkingEvent();\r
5367 }\r
5368 \r
5369 VOID\r
5370 LoadGameDialog(HWND hwnd, char* title)\r
5371 {\r
5372   UINT number = 0;\r
5373   FILE *f;\r
5374   char fileTitle[MSG_SIZ];\r
5375   f = OpenFileDialog(hwnd, "rb", "",\r
5376                      appData.oldSaveStyle ? "gam" : "pgn",\r
5377                      GAME_FILT,\r
5378                      title, &number, fileTitle, NULL);\r
5379   if (f != NULL) {\r
5380     cmailMsgLoaded = FALSE;\r
5381     if (number == 0) {\r
5382       int error = GameListBuild(f);\r
5383       if (error) {\r
5384         DisplayError("Cannot build game list", error);\r
5385       } else if (!ListEmpty(&gameList) &&\r
5386                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5387         GameListPopUp(f, fileTitle);\r
5388         return;\r
5389       }\r
5390       GameListDestroy();\r
5391       number = 1;\r
5392     }\r
5393     LoadGame(f, number, fileTitle, FALSE);\r
5394   }\r
5395 }\r
5396 \r
5397 VOID\r
5398 ChangedConsoleFont()\r
5399 {\r
5400   CHARFORMAT cfmt;\r
5401   CHARRANGE tmpsel, sel;\r
5402   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5403   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5404   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5405   PARAFORMAT paraf;\r
5406 \r
5407   cfmt.cbSize = sizeof(CHARFORMAT);\r
5408   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5409   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5410   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5411    * size.  This was undocumented in the version of MSVC++ that I had\r
5412    * when I wrote the code, but is apparently documented now.\r
5413    */\r
5414   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5415   cfmt.bCharSet = f->lf.lfCharSet;\r
5416   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5417   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5418   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5419   /* Why are the following seemingly needed too? */\r
5420   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5421   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5422   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5423   tmpsel.cpMin = 0;\r
5424   tmpsel.cpMax = -1; /*999999?*/\r
5425   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5426   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5427   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5428    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5429    */\r
5430   paraf.cbSize = sizeof(paraf);\r
5431   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5432   paraf.dxStartIndent = 0;\r
5433   paraf.dxOffset = WRAP_INDENT;\r
5434   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5435   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5436 }\r
5437 \r
5438 /*---------------------------------------------------------------------------*\\r
5439  *\r
5440  * Window Proc for main window\r
5441  *\r
5442 \*---------------------------------------------------------------------------*/\r
5443 \r
5444 /* Process messages for main window, etc. */\r
5445 LRESULT CALLBACK\r
5446 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5447 {\r
5448   FARPROC lpProc;\r
5449   int wmId, wmEvent;\r
5450   char *defName;\r
5451   FILE *f;\r
5452   UINT number;\r
5453   char fileTitle[MSG_SIZ];\r
5454   char buf[MSG_SIZ];\r
5455   static SnapData sd;\r
5456 \r
5457   switch (message) {\r
5458 \r
5459   case WM_PAINT: /* message: repaint portion of window */\r
5460     PaintProc(hwnd);\r
5461     break;\r
5462 \r
5463   case WM_ERASEBKGND:\r
5464     if (IsIconic(hwnd)) {\r
5465       /* Cheat; change the message */\r
5466       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5467     } else {\r
5468       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5469     }\r
5470     break;\r
5471 \r
5472   case WM_LBUTTONDOWN:\r
5473   case WM_MBUTTONDOWN:\r
5474   case WM_RBUTTONDOWN:\r
5475   case WM_LBUTTONUP:\r
5476   case WM_MBUTTONUP:\r
5477   case WM_RBUTTONUP:\r
5478   case WM_MOUSEMOVE:\r
5479   case WM_MOUSEWHEEL:\r
5480     MouseEvent(hwnd, message, wParam, lParam);\r
5481     break;\r
5482 \r
5483   case WM_CHAR:\r
5484     \r
5485     if (appData.icsActive) {\r
5486       if (wParam == '\t') {\r
5487         if (GetKeyState(VK_SHIFT) < 0) {\r
5488           /* shifted */\r
5489           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5490           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5491           SetFocus(h);\r
5492         } else {\r
5493           /* unshifted */\r
5494           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5495           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5496           SetFocus(h);\r
5497         }\r
5498       } else {\r
5499         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5500         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5501         SetFocus(h);\r
5502         SendMessage(h, message, wParam, lParam);\r
5503       }\r
5504     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5505       PopUpMoveDialog((char)wParam);\r
5506     }\r
5507     break;\r
5508 \r
5509   case WM_PALETTECHANGED:\r
5510     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5511       int nnew;\r
5512       HDC hdc = GetDC(hwndMain);\r
5513       SelectPalette(hdc, hPal, TRUE);\r
5514       nnew = RealizePalette(hdc);\r
5515       if (nnew > 0) {\r
5516         paletteChanged = TRUE;\r
5517 #if 0\r
5518         UpdateColors(hdc);\r
5519 #else\r
5520         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5521 #endif\r
5522       }\r
5523       ReleaseDC(hwnd, hdc);\r
5524     }\r
5525     break;\r
5526 \r
5527   case WM_QUERYNEWPALETTE:\r
5528     if (!appData.monoMode /*&& paletteChanged*/) {\r
5529       int nnew;\r
5530       HDC hdc = GetDC(hwndMain);\r
5531       paletteChanged = FALSE;\r
5532       SelectPalette(hdc, hPal, FALSE);\r
5533       nnew = RealizePalette(hdc);\r
5534       if (nnew > 0) {\r
5535         InvalidateRect(hwnd, &boardRect, FALSE);\r
5536       }\r
5537       ReleaseDC(hwnd, hdc);\r
5538       return TRUE;\r
5539     }\r
5540     return FALSE;\r
5541 \r
5542   case WM_COMMAND: /* message: command from application menu */\r
5543     wmId    = LOWORD(wParam);\r
5544     wmEvent = HIWORD(wParam);\r
5545 \r
5546     switch (wmId) {\r
5547     case IDM_NewGame:\r
5548       ResetGameEvent();\r
5549       AnalysisPopDown();\r
5550       break;\r
5551 \r
5552     case IDM_NewGameFRC:\r
5553       if( NewGameFRC() == 0 ) {\r
5554         ResetGameEvent();\r
5555         AnalysisPopDown();\r
5556       }\r
5557       break;\r
5558 \r
5559     case IDM_NewVariant:\r
5560       NewVariantPopup(hwnd);\r
5561       break;\r
5562 \r
5563     case IDM_LoadGame:\r
5564       LoadGameDialog(hwnd, "Load Game from File");\r
5565       break;\r
5566 \r
5567     case IDM_LoadNextGame:\r
5568       ReloadGame(1);\r
5569       break;\r
5570 \r
5571     case IDM_LoadPrevGame:\r
5572       ReloadGame(-1);\r
5573       break;\r
5574 \r
5575     case IDM_ReloadGame:\r
5576       ReloadGame(0);\r
5577       break;\r
5578 \r
5579     case IDM_LoadPosition:\r
5580       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5581         Reset(FALSE, TRUE);\r
5582       }\r
5583       number = 1;\r
5584       f = OpenFileDialog(hwnd, "rb", "",\r
5585                          appData.oldSaveStyle ? "pos" : "fen",\r
5586                          POSITION_FILT,\r
5587                          "Load Position from File", &number, fileTitle, NULL);\r
5588       if (f != NULL) {\r
5589         LoadPosition(f, number, fileTitle);\r
5590       }\r
5591       break;\r
5592 \r
5593     case IDM_LoadNextPosition:\r
5594       ReloadPosition(1);\r
5595       break;\r
5596 \r
5597     case IDM_LoadPrevPosition:\r
5598       ReloadPosition(-1);\r
5599       break;\r
5600 \r
5601     case IDM_ReloadPosition:\r
5602       ReloadPosition(0);\r
5603       break;\r
5604 \r
5605     case IDM_SaveGame:\r
5606       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5607       f = OpenFileDialog(hwnd, "a", defName,\r
5608                          appData.oldSaveStyle ? "gam" : "pgn",\r
5609                          GAME_FILT,\r
5610                          "Save Game to File", NULL, fileTitle, NULL);\r
5611       if (f != NULL) {\r
5612         SaveGame(f, 0, "");\r
5613       }\r
5614       break;\r
5615 \r
5616     case IDM_SavePosition:\r
5617       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5618       f = OpenFileDialog(hwnd, "a", defName,\r
5619                          appData.oldSaveStyle ? "pos" : "fen",\r
5620                          POSITION_FILT,\r
5621                          "Save Position to File", NULL, fileTitle, NULL);\r
5622       if (f != NULL) {\r
5623         SavePosition(f, 0, "");\r
5624       }\r
5625       break;\r
5626 \r
5627     case IDM_SaveDiagram:\r
5628       defName = "diagram";\r
5629       f = OpenFileDialog(hwnd, "wb", defName,\r
5630                          "bmp",\r
5631                          DIAGRAM_FILT,\r
5632                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5633       if (f != NULL) {\r
5634         SaveDiagram(f);\r
5635       }\r
5636       break;\r
5637 \r
5638     case IDM_CopyGame:\r
5639       CopyGameToClipboard();\r
5640       break;\r
5641 \r
5642     case IDM_PasteGame:\r
5643       PasteGameFromClipboard();\r
5644       break;\r
5645 \r
5646     case IDM_CopyGameListToClipboard:\r
5647       CopyGameListToClipboard();\r
5648       break;\r
5649 \r
5650     /* [AS] Autodetect FEN or PGN data */\r
5651     case IDM_PasteAny:\r
5652       PasteGameOrFENFromClipboard();\r
5653       break;\r
5654 \r
5655     /* [AS] Move history */\r
5656     case IDM_ShowMoveHistory:\r
5657         if( MoveHistoryIsUp() ) {\r
5658             MoveHistoryPopDown();\r
5659         }\r
5660         else {\r
5661             MoveHistoryPopUp();\r
5662         }\r
5663         break;\r
5664 \r
5665     /* [AS] Eval graph */\r
5666     case IDM_ShowEvalGraph:\r
5667         if( EvalGraphIsUp() ) {\r
5668             EvalGraphPopDown();\r
5669         }\r
5670         else {\r
5671             EvalGraphPopUp();\r
5672         }\r
5673         break;\r
5674 \r
5675     /* [AS] Engine output */\r
5676     case IDM_ShowEngineOutput:\r
5677         if( EngineOutputIsUp() ) {\r
5678             EngineOutputPopDown();\r
5679         }\r
5680         else {\r
5681             EngineOutputPopUp();\r
5682         }\r
5683         break;\r
5684 \r
5685     /* [AS] User adjudication */\r
5686     case IDM_UserAdjudication_White:\r
5687         UserAdjudicationEvent( +1 );\r
5688         break;\r
5689 \r
5690     case IDM_UserAdjudication_Black:\r
5691         UserAdjudicationEvent( -1 );\r
5692         break;\r
5693 \r
5694     case IDM_UserAdjudication_Draw:\r
5695         UserAdjudicationEvent( 0 );\r
5696         break;\r
5697 \r
5698     /* [AS] Game list options dialog */\r
5699     case IDM_GameListOptions:\r
5700       GameListOptions();\r
5701       break;\r
5702 \r
5703     case IDM_CopyPosition:\r
5704       CopyFENToClipboard();\r
5705       break;\r
5706 \r
5707     case IDM_PastePosition:\r
5708       PasteFENFromClipboard();\r
5709       break;\r
5710 \r
5711     case IDM_MailMove:\r
5712       MailMoveEvent();\r
5713       break;\r
5714 \r
5715     case IDM_ReloadCMailMsg:\r
5716       Reset(TRUE, TRUE);\r
5717       ReloadCmailMsgEvent(FALSE);\r
5718       break;\r
5719 \r
5720     case IDM_Minimize:\r
5721       ShowWindow(hwnd, SW_MINIMIZE);\r
5722       break;\r
5723 \r
5724     case IDM_Exit:\r
5725       ExitEvent(0);\r
5726       break;\r
5727 \r
5728     case IDM_MachineWhite:\r
5729       MachineWhiteEvent();\r
5730       /*\r
5731        * refresh the tags dialog only if it's visible\r
5732        */\r
5733       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5734           char *tags;\r
5735           tags = PGNTags(&gameInfo);\r
5736           TagsPopUp(tags, CmailMsg());\r
5737           free(tags);\r
5738       }\r
5739       break;\r
5740 \r
5741     case IDM_MachineBlack:\r
5742       MachineBlackEvent();\r
5743       /*\r
5744        * refresh the tags dialog only if it's visible\r
5745        */\r
5746       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5747           char *tags;\r
5748           tags = PGNTags(&gameInfo);\r
5749           TagsPopUp(tags, CmailMsg());\r
5750           free(tags);\r
5751       }\r
5752       break;\r
5753 \r
5754     case IDM_TwoMachines:\r
5755       TwoMachinesEvent();\r
5756       /*\r
5757        * refresh the tags dialog only if it's visible\r
5758        */\r
5759       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5760           char *tags;\r
5761           tags = PGNTags(&gameInfo);\r
5762           TagsPopUp(tags, CmailMsg());\r
5763           free(tags);\r
5764       }\r
5765       break;\r
5766 \r
5767     case IDM_AnalysisMode:\r
5768       if (!first.analysisSupport) {\r
5769         sprintf(buf, "%s does not support analysis", first.tidy);\r
5770         DisplayError(buf, 0);\r
5771       } else {\r
5772         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5773         if (appData.icsActive) {\r
5774                if (gameMode != IcsObserving) {\r
5775                        sprintf(buf, "You are not observing a game");\r
5776                        DisplayError(buf, 0);\r
5777                        /* secure check */\r
5778                        if (appData.icsEngineAnalyze) {\r
5779                                if (appData.debugMode) \r
5780                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5781                                ExitAnalyzeMode();\r
5782                                ModeHighlight();\r
5783                                break;\r
5784                        }\r
5785                        break;\r
5786                } else {\r
5787                        /* if enable, user want disable icsEngineAnalyze */\r
5788                        if (appData.icsEngineAnalyze) {\r
5789                                ExitAnalyzeMode();\r
5790                                ModeHighlight();\r
5791                                break;\r
5792                        }\r
5793                        appData.icsEngineAnalyze = TRUE;\r
5794                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5795                }\r
5796         } \r
5797         if (!appData.showThinking) ToggleShowThinking();\r
5798         AnalyzeModeEvent();\r
5799       }\r
5800       break;\r
5801 \r
5802     case IDM_AnalyzeFile:\r
5803       if (!first.analysisSupport) {\r
5804         char buf[MSG_SIZ];\r
5805         sprintf(buf, "%s does not support analysis", first.tidy);\r
5806         DisplayError(buf, 0);\r
5807       } else {\r
5808         if (!appData.showThinking) ToggleShowThinking();\r
5809         AnalyzeFileEvent();\r
5810         LoadGameDialog(hwnd, "Analyze Game from File");\r
5811         AnalysisPeriodicEvent(1);\r
5812       }\r
5813       break;\r
5814 \r
5815     case IDM_IcsClient:\r
5816       IcsClientEvent();\r
5817       break;\r
5818 \r
5819     case IDM_EditGame:\r
5820       EditGameEvent();\r
5821       break;\r
5822 \r
5823     case IDM_EditPosition:\r
5824       EditPositionEvent();\r
5825       break;\r
5826 \r
5827     case IDM_Training:\r
5828       TrainingEvent();\r
5829       break;\r
5830 \r
5831     case IDM_ShowGameList:\r
5832       ShowGameListProc();\r
5833       break;\r
5834 \r
5835     case IDM_EditTags:\r
5836       EditTagsProc();\r
5837       break;\r
5838 \r
5839     case IDM_EditComment:\r
5840       if (commentDialogUp && editComment) {\r
5841         CommentPopDown();\r
5842       } else {\r
5843         EditCommentEvent();\r
5844       }\r
5845       break;\r
5846 \r
5847     case IDM_Pause:\r
5848       PauseEvent();\r
5849       break;\r
5850 \r
5851     case IDM_Accept:\r
5852       AcceptEvent();\r
5853       break;\r
5854 \r
5855     case IDM_Decline:\r
5856       DeclineEvent();\r
5857       break;\r
5858 \r
5859     case IDM_Rematch:\r
5860       RematchEvent();\r
5861       break;\r
5862 \r
5863     case IDM_CallFlag:\r
5864       CallFlagEvent();\r
5865       break;\r
5866 \r
5867     case IDM_Draw:\r
5868       DrawEvent();\r
5869       break;\r
5870 \r
5871     case IDM_Adjourn:\r
5872       AdjournEvent();\r
5873       break;\r
5874 \r
5875     case IDM_Abort:\r
5876       AbortEvent();\r
5877       break;\r
5878 \r
5879     case IDM_Resign:\r
5880       ResignEvent();\r
5881       break;\r
5882 \r
5883     case IDM_StopObserving:\r
5884       StopObservingEvent();\r
5885       break;\r
5886 \r
5887     case IDM_StopExamining:\r
5888       StopExaminingEvent();\r
5889       break;\r
5890 \r
5891     case IDM_TypeInMove:\r
5892       PopUpMoveDialog('\000');\r
5893       break;\r
5894 \r
5895     case IDM_TypeInName:\r
5896       PopUpNameDialog('\000');\r
5897       break;\r
5898 \r
5899     case IDM_Backward:\r
5900       BackwardEvent();\r
5901       SetFocus(hwndMain);\r
5902       break;\r
5903 \r
5904     case IDM_Forward:\r
5905       ForwardEvent();\r
5906       SetFocus(hwndMain);\r
5907       break;\r
5908 \r
5909     case IDM_ToStart:\r
5910       ToStartEvent();\r
5911       SetFocus(hwndMain);\r
5912       break;\r
5913 \r
5914     case IDM_ToEnd:\r
5915       ToEndEvent();\r
5916       SetFocus(hwndMain);\r
5917       break;\r
5918 \r
5919     case IDM_Revert:\r
5920       RevertEvent();\r
5921       break;\r
5922 \r
5923     case IDM_TruncateGame:\r
5924       TruncateGameEvent();\r
5925       break;\r
5926 \r
5927     case IDM_MoveNow:\r
5928       MoveNowEvent();\r
5929       break;\r
5930 \r
5931     case IDM_RetractMove:\r
5932       RetractMoveEvent();\r
5933       break;\r
5934 \r
5935     case IDM_FlipView:\r
5936       flipView = !flipView;\r
5937       DrawPosition(FALSE, NULL);\r
5938       break;\r
5939 \r
5940     case IDM_FlipClock:\r
5941       flipClock = !flipClock;\r
5942       DisplayBothClocks();\r
5943       break;\r
5944 \r
5945     case IDM_GeneralOptions:\r
5946       GeneralOptionsPopup(hwnd);\r
5947       DrawPosition(TRUE, NULL);\r
5948       break;\r
5949 \r
5950     case IDM_BoardOptions:\r
5951       BoardOptionsPopup(hwnd);\r
5952       break;\r
5953 \r
5954     case IDM_EnginePlayOptions:\r
5955       EnginePlayOptionsPopup(hwnd);\r
5956       break;\r
5957 \r
5958     case IDM_OptionsUCI:\r
5959       UciOptionsPopup(hwnd);\r
5960       break;\r
5961 \r
5962     case IDM_IcsOptions:\r
5963       IcsOptionsPopup(hwnd);\r
5964       break;\r
5965 \r
5966     case IDM_Fonts:\r
5967       FontsOptionsPopup(hwnd);\r
5968       break;\r
5969 \r
5970     case IDM_Sounds:\r
5971       SoundOptionsPopup(hwnd);\r
5972       break;\r
5973 \r
5974     case IDM_CommPort:\r
5975       CommPortOptionsPopup(hwnd);\r
5976       break;\r
5977 \r
5978     case IDM_LoadOptions:\r
5979       LoadOptionsPopup(hwnd);\r
5980       break;\r
5981 \r
5982     case IDM_SaveOptions:\r
5983       SaveOptionsPopup(hwnd);\r
5984       break;\r
5985 \r
5986     case IDM_TimeControl:\r
5987       TimeControlOptionsPopup(hwnd);\r
5988       break;\r
5989 \r
5990     case IDM_SaveSettings:\r
5991       SaveSettings(settingsFileName);\r
5992       break;\r
5993 \r
5994     case IDM_SaveSettingsOnExit:\r
5995       saveSettingsOnExit = !saveSettingsOnExit;\r
5996       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5997                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5998                                          MF_CHECKED : MF_UNCHECKED));\r
5999       break;\r
6000 \r
6001     case IDM_Hint:\r
6002       HintEvent();\r
6003       break;\r
6004 \r
6005     case IDM_Book:\r
6006       BookEvent();\r
6007       break;\r
6008 \r
6009     case IDM_AboutGame:\r
6010       AboutGameEvent();\r
6011       break;\r
6012 \r
6013     case IDM_Debug:\r
6014       appData.debugMode = !appData.debugMode;\r
6015       if (appData.debugMode) {\r
6016         char dir[MSG_SIZ];\r
6017         GetCurrentDirectory(MSG_SIZ, dir);\r
6018         SetCurrentDirectory(installDir);\r
6019         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6020         SetCurrentDirectory(dir);\r
6021         setbuf(debugFP, NULL);\r
6022       } else {\r
6023         fclose(debugFP);\r
6024         debugFP = NULL;\r
6025       }\r
6026       break;\r
6027 \r
6028     case IDM_HELPCONTENTS:\r
6029       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6030         MessageBox (GetFocus(),\r
6031                     "Unable to activate help",\r
6032                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6033       }\r
6034       break;\r
6035 \r
6036     case IDM_HELPSEARCH:\r
6037       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6038         MessageBox (GetFocus(),\r
6039                     "Unable to activate help",\r
6040                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6041       }\r
6042       break;\r
6043 \r
6044     case IDM_HELPHELP:\r
6045       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6046         MessageBox (GetFocus(),\r
6047                     "Unable to activate help",\r
6048                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6049       }\r
6050       break;\r
6051 \r
6052     case IDM_ABOUT:\r
6053       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6054       DialogBox(hInst, \r
6055         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6056         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6057       FreeProcInstance(lpProc);\r
6058       break;\r
6059 \r
6060     case IDM_DirectCommand1:\r
6061       AskQuestionEvent("Direct Command",\r
6062                        "Send to chess program:", "", "1");\r
6063       break;\r
6064     case IDM_DirectCommand2:\r
6065       AskQuestionEvent("Direct Command",\r
6066                        "Send to second chess program:", "", "2");\r
6067       break;\r
6068 \r
6069     case EP_WhitePawn:\r
6070       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6071       fromX = fromY = -1;\r
6072       break;\r
6073 \r
6074     case EP_WhiteKnight:\r
6075       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6076       fromX = fromY = -1;\r
6077       break;\r
6078 \r
6079     case EP_WhiteBishop:\r
6080       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6081       fromX = fromY = -1;\r
6082       break;\r
6083 \r
6084     case EP_WhiteRook:\r
6085       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6086       fromX = fromY = -1;\r
6087       break;\r
6088 \r
6089     case EP_WhiteQueen:\r
6090       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6091       fromX = fromY = -1;\r
6092       break;\r
6093 \r
6094     case EP_WhiteFerz:\r
6095       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6096       fromX = fromY = -1;\r
6097       break;\r
6098 \r
6099     case EP_WhiteWazir:\r
6100       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6101       fromX = fromY = -1;\r
6102       break;\r
6103 \r
6104     case EP_WhiteAlfil:\r
6105       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6106       fromX = fromY = -1;\r
6107       break;\r
6108 \r
6109     case EP_WhiteCannon:\r
6110       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6111       fromX = fromY = -1;\r
6112       break;\r
6113 \r
6114     case EP_WhiteCardinal:\r
6115       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6116       fromX = fromY = -1;\r
6117       break;\r
6118 \r
6119     case EP_WhiteMarshall:\r
6120       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6121       fromX = fromY = -1;\r
6122       break;\r
6123 \r
6124     case EP_WhiteKing:\r
6125       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6126       fromX = fromY = -1;\r
6127       break;\r
6128 \r
6129     case EP_BlackPawn:\r
6130       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6131       fromX = fromY = -1;\r
6132       break;\r
6133 \r
6134     case EP_BlackKnight:\r
6135       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6136       fromX = fromY = -1;\r
6137       break;\r
6138 \r
6139     case EP_BlackBishop:\r
6140       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6141       fromX = fromY = -1;\r
6142       break;\r
6143 \r
6144     case EP_BlackRook:\r
6145       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6146       fromX = fromY = -1;\r
6147       break;\r
6148 \r
6149     case EP_BlackQueen:\r
6150       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6151       fromX = fromY = -1;\r
6152       break;\r
6153 \r
6154     case EP_BlackFerz:\r
6155       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6156       fromX = fromY = -1;\r
6157       break;\r
6158 \r
6159     case EP_BlackWazir:\r
6160       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6161       fromX = fromY = -1;\r
6162       break;\r
6163 \r
6164     case EP_BlackAlfil:\r
6165       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6166       fromX = fromY = -1;\r
6167       break;\r
6168 \r
6169     case EP_BlackCannon:\r
6170       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6171       fromX = fromY = -1;\r
6172       break;\r
6173 \r
6174     case EP_BlackCardinal:\r
6175       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6176       fromX = fromY = -1;\r
6177       break;\r
6178 \r
6179     case EP_BlackMarshall:\r
6180       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6181       fromX = fromY = -1;\r
6182       break;\r
6183 \r
6184     case EP_BlackKing:\r
6185       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6186       fromX = fromY = -1;\r
6187       break;\r
6188 \r
6189     case EP_EmptySquare:\r
6190       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6191       fromX = fromY = -1;\r
6192       break;\r
6193 \r
6194     case EP_ClearBoard:\r
6195       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6196       fromX = fromY = -1;\r
6197       break;\r
6198 \r
6199     case EP_White:\r
6200       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6201       fromX = fromY = -1;\r
6202       break;\r
6203 \r
6204     case EP_Black:\r
6205       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6206       fromX = fromY = -1;\r
6207       break;\r
6208 \r
6209     case EP_Promote:\r
6210       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6211       fromX = fromY = -1;\r
6212       break;\r
6213 \r
6214     case EP_Demote:\r
6215       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6216       fromX = fromY = -1;\r
6217       break;\r
6218 \r
6219     case DP_Pawn:\r
6220       DropMenuEvent(WhitePawn, fromX, fromY);\r
6221       fromX = fromY = -1;\r
6222       break;\r
6223 \r
6224     case DP_Knight:\r
6225       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6226       fromX = fromY = -1;\r
6227       break;\r
6228 \r
6229     case DP_Bishop:\r
6230       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6231       fromX = fromY = -1;\r
6232       break;\r
6233 \r
6234     case DP_Rook:\r
6235       DropMenuEvent(WhiteRook, fromX, fromY);\r
6236       fromX = fromY = -1;\r
6237       break;\r
6238 \r
6239     case DP_Queen:\r
6240       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6241       fromX = fromY = -1;\r
6242       break;\r
6243 \r
6244     default:\r
6245       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6246     }\r
6247     break;\r
6248 \r
6249   case WM_TIMER:\r
6250     switch (wParam) {\r
6251     case CLOCK_TIMER_ID:\r
6252       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6253       clockTimerEvent = 0;\r
6254       DecrementClocks(); /* call into back end */\r
6255       break;\r
6256     case LOAD_GAME_TIMER_ID:\r
6257       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6258       loadGameTimerEvent = 0;\r
6259       AutoPlayGameLoop(); /* call into back end */\r
6260       break;\r
6261     case ANALYSIS_TIMER_ID:\r
6262       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6263                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6264         AnalysisPeriodicEvent(0);\r
6265       } else {\r
6266         KillTimer(hwnd, analysisTimerEvent);\r
6267         analysisTimerEvent = 0;\r
6268       }\r
6269       break;\r
6270     case DELAYED_TIMER_ID:\r
6271       KillTimer(hwnd, delayedTimerEvent);\r
6272       delayedTimerEvent = 0;\r
6273       delayedTimerCallback();\r
6274       break;\r
6275     }\r
6276     break;\r
6277 \r
6278   case WM_USER_Input:\r
6279     InputEvent(hwnd, message, wParam, lParam);\r
6280     break;\r
6281 \r
6282   /* [AS] Also move "attached" child windows */\r
6283   case WM_WINDOWPOSCHANGING:\r
6284 \r
6285     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6286         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6287 \r
6288         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6289             /* Window is moving */\r
6290             RECT rcMain;\r
6291 \r
6292 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6293             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6294             rcMain.right  = boardX + winWidth;\r
6295             rcMain.top    = boardY;\r
6296             rcMain.bottom = boardY + winHeight;\r
6297             \r
6298             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6299             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6300             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6301             boardX = lpwp->x;\r
6302             boardY = lpwp->y;\r
6303         }\r
6304     }\r
6305     break;\r
6306 \r
6307   /* [AS] Snapping */\r
6308   case WM_ENTERSIZEMOVE:\r
6309     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6310     if (hwnd == hwndMain) {\r
6311       doingSizing = TRUE;\r
6312       lastSizing = 0;\r
6313     }\r
6314     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6315     break;\r
6316 \r
6317   case WM_SIZING:\r
6318     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6319     if (hwnd == hwndMain) {\r
6320       lastSizing = wParam;\r
6321     }\r
6322     break;\r
6323 \r
6324   case WM_MOVING:\r
6325     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6326       return OnMoving( &sd, hwnd, wParam, lParam );\r
6327 \r
6328   case WM_EXITSIZEMOVE:\r
6329     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6330     if (hwnd == hwndMain) {\r
6331       RECT client;\r
6332       doingSizing = FALSE;\r
6333       InvalidateRect(hwnd, &boardRect, FALSE);\r
6334       GetClientRect(hwnd, &client);\r
6335       ResizeBoard(client.right, client.bottom, lastSizing);\r
6336       lastSizing = 0;\r
6337       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6338     }\r
6339     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6340     break;\r
6341 \r
6342   case WM_DESTROY: /* message: window being destroyed */\r
6343     PostQuitMessage(0);\r
6344     break;\r
6345 \r
6346   case WM_CLOSE:\r
6347     if (hwnd == hwndMain) {\r
6348       ExitEvent(0);\r
6349     }\r
6350     break;\r
6351 \r
6352   default:      /* Passes it on if unprocessed */\r
6353     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6354   }\r
6355   return 0;\r
6356 }\r
6357 \r
6358 /*---------------------------------------------------------------------------*\\r
6359  *\r
6360  * Misc utility routines\r
6361  *\r
6362 \*---------------------------------------------------------------------------*/\r
6363 \r
6364 /*\r
6365  * Decent random number generator, at least not as bad as Windows\r
6366  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6367  */\r
6368 unsigned int randstate;\r
6369 \r
6370 int\r
6371 myrandom(void)\r
6372 {\r
6373   randstate = randstate * 1664525 + 1013904223;\r
6374   return (int) randstate & 0x7fffffff;\r
6375 }\r
6376 \r
6377 void\r
6378 mysrandom(unsigned int seed)\r
6379 {\r
6380   randstate = seed;\r
6381 }\r
6382 \r
6383 \r
6384 /* \r
6385  * returns TRUE if user selects a different color, FALSE otherwise \r
6386  */\r
6387 \r
6388 BOOL\r
6389 ChangeColor(HWND hwnd, COLORREF *which)\r
6390 {\r
6391   static BOOL firstTime = TRUE;\r
6392   static DWORD customColors[16];\r
6393   CHOOSECOLOR cc;\r
6394   COLORREF newcolor;\r
6395   int i;\r
6396   ColorClass ccl;\r
6397 \r
6398   if (firstTime) {\r
6399     /* Make initial colors in use available as custom colors */\r
6400     /* Should we put the compiled-in defaults here instead? */\r
6401     i = 0;\r
6402     customColors[i++] = lightSquareColor & 0xffffff;\r
6403     customColors[i++] = darkSquareColor & 0xffffff;\r
6404     customColors[i++] = whitePieceColor & 0xffffff;\r
6405     customColors[i++] = blackPieceColor & 0xffffff;\r
6406     customColors[i++] = highlightSquareColor & 0xffffff;\r
6407     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6408 \r
6409     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6410       customColors[i++] = textAttribs[ccl].color;\r
6411     }\r
6412     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6413     firstTime = FALSE;\r
6414   }\r
6415 \r
6416   cc.lStructSize = sizeof(cc);\r
6417   cc.hwndOwner = hwnd;\r
6418   cc.hInstance = NULL;\r
6419   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6420   cc.lpCustColors = (LPDWORD) customColors;\r
6421   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6422 \r
6423   if (!ChooseColor(&cc)) return FALSE;\r
6424 \r
6425   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6426   if (newcolor == *which) return FALSE;\r
6427   *which = newcolor;\r
6428   return TRUE;\r
6429 \r
6430   /*\r
6431   InitDrawingColors();\r
6432   InvalidateRect(hwnd, &boardRect, FALSE);\r
6433   */\r
6434 }\r
6435 \r
6436 BOOLEAN\r
6437 MyLoadSound(MySound *ms)\r
6438 {\r
6439   BOOL ok = FALSE;\r
6440   struct stat st;\r
6441   FILE *f;\r
6442 \r
6443   if (ms->data) free(ms->data);\r
6444   ms->data = NULL;\r
6445 \r
6446   switch (ms->name[0]) {\r
6447   case NULLCHAR:\r
6448     /* Silence */\r
6449     ok = TRUE;\r
6450     break;\r
6451   case '$':\r
6452     /* System sound from Control Panel.  Don't preload here. */\r
6453     ok = TRUE;\r
6454     break;\r
6455   case '!':\r
6456     if (ms->name[1] == NULLCHAR) {\r
6457       /* "!" alone = silence */\r
6458       ok = TRUE;\r
6459     } else {\r
6460       /* Builtin wave resource.  Error if not found. */\r
6461       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6462       if (h == NULL) break;\r
6463       ms->data = (void *)LoadResource(hInst, h);\r
6464       if (h == NULL) break;\r
6465       ok = TRUE;\r
6466     }\r
6467     break;\r
6468   default:\r
6469     /* .wav file.  Error if not found. */\r
6470     f = fopen(ms->name, "rb");\r
6471     if (f == NULL) break;\r
6472     if (fstat(fileno(f), &st) < 0) break;\r
6473     ms->data = malloc(st.st_size);\r
6474     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6475     fclose(f);\r
6476     ok = TRUE;\r
6477     break;\r
6478   }\r
6479   if (!ok) {\r
6480     char buf[MSG_SIZ];\r
6481     sprintf(buf, "Error loading sound %s", ms->name);\r
6482     DisplayError(buf, GetLastError());\r
6483   }\r
6484   return ok;\r
6485 }\r
6486 \r
6487 BOOLEAN\r
6488 MyPlaySound(MySound *ms)\r
6489 {\r
6490   BOOLEAN ok = FALSE;\r
6491   switch (ms->name[0]) {\r
6492   case NULLCHAR:\r
6493     /* Silence */\r
6494     ok = TRUE;\r
6495     break;\r
6496   case '$':\r
6497     /* System sound from Control Panel (deprecated feature).\r
6498        "$" alone or an unset sound name gets default beep (still in use). */\r
6499     if (ms->name[1]) {\r
6500       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6501     }\r
6502     if (!ok) ok = MessageBeep(MB_OK);\r
6503     break; \r
6504   case '!':\r
6505     /* Builtin wave resource, or "!" alone for silence */\r
6506     if (ms->name[1]) {\r
6507       if (ms->data == NULL) return FALSE;\r
6508       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6509     } else {\r
6510       ok = TRUE;\r
6511     }\r
6512     break;\r
6513   default:\r
6514     /* .wav file.  Error if not found. */\r
6515     if (ms->data == NULL) return FALSE;\r
6516     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6517     break;\r
6518   }\r
6519   /* Don't print an error: this can happen innocently if the sound driver\r
6520      is busy; for instance, if another instance of WinBoard is playing\r
6521      a sound at about the same time. */\r
6522 #if 0\r
6523   if (!ok) {\r
6524     char buf[MSG_SIZ];\r
6525     sprintf(buf, "Error playing sound %s", ms->name);\r
6526     DisplayError(buf, GetLastError());\r
6527   }\r
6528 #endif\r
6529   return ok;\r
6530 }\r
6531 \r
6532 \r
6533 LRESULT CALLBACK\r
6534 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6535 {\r
6536   BOOL ok;\r
6537   OPENFILENAME *ofn;\r
6538   static UINT *number; /* gross that this is static */\r
6539 \r
6540   switch (message) {\r
6541   case WM_INITDIALOG: /* message: initialize dialog box */\r
6542     /* Center the dialog over the application window */\r
6543     ofn = (OPENFILENAME *) lParam;\r
6544     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6545       number = (UINT *) ofn->lCustData;\r
6546       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6547     } else {\r
6548       number = NULL;\r
6549     }\r
6550     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6551     return FALSE;  /* Allow for further processing */\r
6552 \r
6553   case WM_COMMAND:\r
6554     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6555       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6556     }\r
6557     return FALSE;  /* Allow for further processing */\r
6558   }\r
6559   return FALSE;\r
6560 }\r
6561 \r
6562 UINT APIENTRY\r
6563 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6564 {\r
6565   static UINT *number;\r
6566   OPENFILENAME *ofname;\r
6567   OFNOTIFY *ofnot;\r
6568   switch (uiMsg) {\r
6569   case WM_INITDIALOG:\r
6570     ofname = (OPENFILENAME *)lParam;\r
6571     number = (UINT *)(ofname->lCustData);\r
6572     break;\r
6573   case WM_NOTIFY:\r
6574     ofnot = (OFNOTIFY *)lParam;\r
6575     if (ofnot->hdr.code == CDN_FILEOK) {\r
6576       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6577     }\r
6578     break;\r
6579   }\r
6580   return 0;\r
6581 }\r
6582 \r
6583 \r
6584 FILE *\r
6585 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6586                char *nameFilt, char *dlgTitle, UINT *number,\r
6587                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6588 {\r
6589   OPENFILENAME openFileName;\r
6590   char buf1[MSG_SIZ];\r
6591   FILE *f;\r
6592 \r
6593   if (fileName == NULL) fileName = buf1;\r
6594   if (defName == NULL) {\r
6595     strcpy(fileName, "*.");\r
6596     strcat(fileName, defExt);\r
6597   } else {\r
6598     strcpy(fileName, defName);\r
6599   }\r
6600   if (fileTitle) strcpy(fileTitle, "");\r
6601   if (number) *number = 0;\r
6602 \r
6603   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6604   openFileName.hwndOwner         = hwnd;\r
6605   openFileName.hInstance         = (HANDLE) hInst;\r
6606   openFileName.lpstrFilter       = nameFilt;\r
6607   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6608   openFileName.nMaxCustFilter    = 0L;\r
6609   openFileName.nFilterIndex      = 1L;\r
6610   openFileName.lpstrFile         = fileName;\r
6611   openFileName.nMaxFile          = MSG_SIZ;\r
6612   openFileName.lpstrFileTitle    = fileTitle;\r
6613   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6614   openFileName.lpstrInitialDir   = NULL;\r
6615   openFileName.lpstrTitle        = dlgTitle;\r
6616   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6617     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6618     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6619     | (oldDialog ? 0 : OFN_EXPLORER);\r
6620   openFileName.nFileOffset       = 0;\r
6621   openFileName.nFileExtension    = 0;\r
6622   openFileName.lpstrDefExt       = defExt;\r
6623   openFileName.lCustData         = (LONG) number;\r
6624   openFileName.lpfnHook          = oldDialog ?\r
6625     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6626   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6627 \r
6628   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6629                         GetOpenFileName(&openFileName)) {\r
6630     /* open the file */\r
6631     f = fopen(openFileName.lpstrFile, write);\r
6632     if (f == NULL) {\r
6633       MessageBox(hwnd, "File open failed", NULL,\r
6634                  MB_OK|MB_ICONEXCLAMATION);\r
6635       return NULL;\r
6636     }\r
6637   } else {\r
6638     int err = CommDlgExtendedError();\r
6639     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6640     return FALSE;\r
6641   }\r
6642   return f;\r
6643 }\r
6644 \r
6645 \r
6646 \r
6647 VOID APIENTRY\r
6648 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6649 {\r
6650   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6651 \r
6652   /*\r
6653    * Get the first pop-up menu in the menu template. This is the\r
6654    * menu that TrackPopupMenu displays.\r
6655    */\r
6656   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6657 \r
6658   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6659 \r
6660   /*\r
6661    * TrackPopup uses screen coordinates, so convert the\r
6662    * coordinates of the mouse click to screen coordinates.\r
6663    */\r
6664   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6665 \r
6666   /* Draw and track the floating pop-up menu. */\r
6667   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6668                  pt.x, pt.y, 0, hwnd, NULL);\r
6669 \r
6670   /* Destroy the menu.*/\r
6671   DestroyMenu(hmenu);\r
6672 }\r
6673    \r
6674 typedef struct {\r
6675   HWND hDlg, hText;\r
6676   int sizeX, sizeY, newSizeX, newSizeY;\r
6677   HDWP hdwp;\r
6678 } ResizeEditPlusButtonsClosure;\r
6679 \r
6680 BOOL CALLBACK\r
6681 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6682 {\r
6683   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6684   RECT rect;\r
6685   POINT pt;\r
6686 \r
6687   if (hChild == cl->hText) return TRUE;\r
6688   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6689   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6690   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6691   ScreenToClient(cl->hDlg, &pt);\r
6692   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6693     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6694   return TRUE;\r
6695 }\r
6696 \r
6697 /* Resize a dialog that has a (rich) edit field filling most of\r
6698    the top, with a row of buttons below */\r
6699 VOID\r
6700 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6701 {\r
6702   RECT rectText;\r
6703   int newTextHeight, newTextWidth;\r
6704   ResizeEditPlusButtonsClosure cl;\r
6705   \r
6706   /*if (IsIconic(hDlg)) return;*/\r
6707   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6708   \r
6709   cl.hdwp = BeginDeferWindowPos(8);\r
6710 \r
6711   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6712   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6713   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6714   if (newTextHeight < 0) {\r
6715     newSizeY += -newTextHeight;\r
6716     newTextHeight = 0;\r
6717   }\r
6718   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6719     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6720 \r
6721   cl.hDlg = hDlg;\r
6722   cl.hText = hText;\r
6723   cl.sizeX = sizeX;\r
6724   cl.sizeY = sizeY;\r
6725   cl.newSizeX = newSizeX;\r
6726   cl.newSizeY = newSizeY;\r
6727   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6728 \r
6729   EndDeferWindowPos(cl.hdwp);\r
6730 }\r
6731 \r
6732 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6733 {\r
6734     RECT    rChild, rParent;\r
6735     int     wChild, hChild, wParent, hParent;\r
6736     int     wScreen, hScreen, xNew, yNew;\r
6737     HDC     hdc;\r
6738 \r
6739     /* Get the Height and Width of the child window */\r
6740     GetWindowRect (hwndChild, &rChild);\r
6741     wChild = rChild.right - rChild.left;\r
6742     hChild = rChild.bottom - rChild.top;\r
6743 \r
6744     /* Get the Height and Width of the parent window */\r
6745     GetWindowRect (hwndParent, &rParent);\r
6746     wParent = rParent.right - rParent.left;\r
6747     hParent = rParent.bottom - rParent.top;\r
6748 \r
6749     /* Get the display limits */\r
6750     hdc = GetDC (hwndChild);\r
6751     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6752     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6753     ReleaseDC(hwndChild, hdc);\r
6754 \r
6755     /* Calculate new X position, then adjust for screen */\r
6756     xNew = rParent.left + ((wParent - wChild) /2);\r
6757     if (xNew < 0) {\r
6758         xNew = 0;\r
6759     } else if ((xNew+wChild) > wScreen) {\r
6760         xNew = wScreen - wChild;\r
6761     }\r
6762 \r
6763     /* Calculate new Y position, then adjust for screen */\r
6764     if( mode == 0 ) {\r
6765         yNew = rParent.top  + ((hParent - hChild) /2);\r
6766     }\r
6767     else {\r
6768         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6769     }\r
6770 \r
6771     if (yNew < 0) {\r
6772         yNew = 0;\r
6773     } else if ((yNew+hChild) > hScreen) {\r
6774         yNew = hScreen - hChild;\r
6775     }\r
6776 \r
6777     /* Set it, and return */\r
6778     return SetWindowPos (hwndChild, NULL,\r
6779                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6780 }\r
6781 \r
6782 /* Center one window over another */\r
6783 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6784 {\r
6785     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6786 }\r
6787 \r
6788 /*---------------------------------------------------------------------------*\\r
6789  *\r
6790  * Startup Dialog functions\r
6791  *\r
6792 \*---------------------------------------------------------------------------*/\r
6793 void\r
6794 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6795 {\r
6796   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6797 \r
6798   while (*cd != NULL) {\r
6799     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6800     cd++;\r
6801   }\r
6802 }\r
6803 \r
6804 void\r
6805 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6806 {\r
6807   char buf1[ARG_MAX];\r
6808   int len;\r
6809 \r
6810   if (str[0] == '@') {\r
6811     FILE* f = fopen(str + 1, "r");\r
6812     if (f == NULL) {\r
6813       DisplayFatalError(str + 1, errno, 2);\r
6814       return;\r
6815     }\r
6816     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6817     fclose(f);\r
6818     buf1[len] = NULLCHAR;\r
6819     str = buf1;\r
6820   }\r
6821 \r
6822   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6823 \r
6824   for (;;) {\r
6825     char buf[MSG_SIZ];\r
6826     char *end = strchr(str, '\n');\r
6827     if (end == NULL) return;\r
6828     memcpy(buf, str, end - str);\r
6829     buf[end - str] = NULLCHAR;\r
6830     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6831     str = end + 1;\r
6832   }\r
6833 }\r
6834 \r
6835 void\r
6836 SetStartupDialogEnables(HWND hDlg)\r
6837 {\r
6838   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6839     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6840     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6841   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6842     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6843   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6844     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6845   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6846     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6847   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6848     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6849     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6850     IsDlgButtonChecked(hDlg, OPT_View));\r
6851 }\r
6852 \r
6853 char *\r
6854 QuoteForFilename(char *filename)\r
6855 {\r
6856   int dquote, space;\r
6857   dquote = strchr(filename, '"') != NULL;\r
6858   space = strchr(filename, ' ') != NULL;\r
6859   if (dquote || space) {\r
6860     if (dquote) {\r
6861       return "'";\r
6862     } else {\r
6863       return "\"";\r
6864     }\r
6865   } else {\r
6866     return "";\r
6867   }\r
6868 }\r
6869 \r
6870 VOID\r
6871 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6872 {\r
6873   char buf[MSG_SIZ];\r
6874   char *q;\r
6875 \r
6876   InitComboStringsFromOption(hwndCombo, nthnames);\r
6877   q = QuoteForFilename(nthcp);\r
6878   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6879   if (*nthdir != NULLCHAR) {\r
6880     q = QuoteForFilename(nthdir);\r
6881     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6882   }\r
6883   if (*nthcp == NULLCHAR) {\r
6884     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6885   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6886     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6887     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6888   }\r
6889 }\r
6890 \r
6891 LRESULT CALLBACK\r
6892 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6893 {\r
6894   char buf[MSG_SIZ];\r
6895   HANDLE hwndCombo;\r
6896   char *p;\r
6897 \r
6898   switch (message) {\r
6899   case WM_INITDIALOG:\r
6900     /* Center the dialog */\r
6901     CenterWindow (hDlg, GetDesktopWindow());\r
6902     /* Initialize the dialog items */\r
6903     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6904                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6905                   firstChessProgramNames);\r
6906     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6907                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6908                   secondChessProgramNames);\r
6909     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6910     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6911     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6912     if (*appData.icsHelper != NULLCHAR) {\r
6913       char *q = QuoteForFilename(appData.icsHelper);\r
6914       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6915     }\r
6916     if (*appData.icsHost == NULLCHAR) {\r
6917       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6918       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6919     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6920       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6921       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6922     }\r
6923 \r
6924     if (appData.icsActive) {\r
6925       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6926     }\r
6927     else if (appData.noChessProgram) {\r
6928       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6929     }\r
6930     else {\r
6931       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6932     }\r
6933 \r
6934     SetStartupDialogEnables(hDlg);\r
6935     return TRUE;\r
6936 \r
6937   case WM_COMMAND:\r
6938     switch (LOWORD(wParam)) {\r
6939     case IDOK:\r
6940       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6941         strcpy(buf, "/fcp=");\r
6942         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6943         p = buf;\r
6944         ParseArgs(StringGet, &p);\r
6945         strcpy(buf, "/scp=");\r
6946         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6947         p = buf;\r
6948         ParseArgs(StringGet, &p);\r
6949         appData.noChessProgram = FALSE;\r
6950         appData.icsActive = FALSE;\r
6951       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6952         strcpy(buf, "/ics /icshost=");\r
6953         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6954         p = buf;\r
6955         ParseArgs(StringGet, &p);\r
6956         if (appData.zippyPlay) {\r
6957           strcpy(buf, "/fcp=");\r
6958           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6959           p = buf;\r
6960           ParseArgs(StringGet, &p);\r
6961         }\r
6962       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6963         appData.noChessProgram = TRUE;\r
6964         appData.icsActive = FALSE;\r
6965       } else {\r
6966         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6967                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6968         return TRUE;\r
6969       }\r
6970       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6971         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6972         p = buf;\r
6973         ParseArgs(StringGet, &p);\r
6974       }\r
6975       EndDialog(hDlg, TRUE);\r
6976       return TRUE;\r
6977 \r
6978     case IDCANCEL:\r
6979       ExitEvent(0);\r
6980       return TRUE;\r
6981 \r
6982     case IDM_HELPCONTENTS:\r
6983       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6984         MessageBox (GetFocus(),\r
6985                     "Unable to activate help",\r
6986                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6987       }\r
6988       break;\r
6989 \r
6990     default:\r
6991       SetStartupDialogEnables(hDlg);\r
6992       break;\r
6993     }\r
6994     break;\r
6995   }\r
6996   return FALSE;\r
6997 }\r
6998 \r
6999 /*---------------------------------------------------------------------------*\\r
7000  *\r
7001  * About box dialog functions\r
7002  *\r
7003 \*---------------------------------------------------------------------------*/\r
7004 \r
7005 /* Process messages for "About" dialog box */\r
7006 LRESULT CALLBACK\r
7007 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7008 {\r
7009   switch (message) {\r
7010   case WM_INITDIALOG: /* message: initialize dialog box */\r
7011     /* Center the dialog over the application window */\r
7012     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7013     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7014     return (TRUE);\r
7015 \r
7016   case WM_COMMAND: /* message: received a command */\r
7017     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7018         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7019       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7020       return (TRUE);\r
7021     }\r
7022     break;\r
7023   }\r
7024   return (FALSE);\r
7025 }\r
7026 \r
7027 /*---------------------------------------------------------------------------*\\r
7028  *\r
7029  * Comment Dialog functions\r
7030  *\r
7031 \*---------------------------------------------------------------------------*/\r
7032 \r
7033 LRESULT CALLBACK\r
7034 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7035 {\r
7036   static HANDLE hwndText = NULL;\r
7037   int len, newSizeX, newSizeY, flags;\r
7038   static int sizeX, sizeY;\r
7039   char *str;\r
7040   RECT rect;\r
7041   MINMAXINFO *mmi;\r
7042 \r
7043   switch (message) {\r
7044   case WM_INITDIALOG: /* message: initialize dialog box */\r
7045     /* Initialize the dialog items */\r
7046     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7047     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7048     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7049     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7050     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7051     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7052     SetWindowText(hDlg, commentTitle);\r
7053     if (editComment) {\r
7054       SetFocus(hwndText);\r
7055     } else {\r
7056       SetFocus(GetDlgItem(hDlg, IDOK));\r
7057     }\r
7058     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7059                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7060                 MAKELPARAM(FALSE, 0));\r
7061     /* Size and position the dialog */\r
7062     if (!commentDialog) {\r
7063       commentDialog = hDlg;\r
7064       flags = SWP_NOZORDER;\r
7065       GetClientRect(hDlg, &rect);\r
7066       sizeX = rect.right;\r
7067       sizeY = rect.bottom;\r
7068       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7069           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7070         WINDOWPLACEMENT wp;\r
7071         EnsureOnScreen(&commentX, &commentY);\r
7072         wp.length = sizeof(WINDOWPLACEMENT);\r
7073         wp.flags = 0;\r
7074         wp.showCmd = SW_SHOW;\r
7075         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7076         wp.rcNormalPosition.left = commentX;\r
7077         wp.rcNormalPosition.right = commentX + commentW;\r
7078         wp.rcNormalPosition.top = commentY;\r
7079         wp.rcNormalPosition.bottom = commentY + commentH;\r
7080         SetWindowPlacement(hDlg, &wp);\r
7081 \r
7082         GetClientRect(hDlg, &rect);\r
7083         newSizeX = rect.right;\r
7084         newSizeY = rect.bottom;\r
7085         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7086                               newSizeX, newSizeY);\r
7087         sizeX = newSizeX;\r
7088         sizeY = newSizeY;\r
7089       }\r
7090     }\r
7091     return FALSE;\r
7092 \r
7093   case WM_COMMAND: /* message: received a command */\r
7094     switch (LOWORD(wParam)) {\r
7095     case IDOK:\r
7096       if (editComment) {\r
7097         char *p, *q;\r
7098         /* Read changed options from the dialog box */\r
7099         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7100         len = GetWindowTextLength(hwndText);\r
7101         str = (char *) malloc(len + 1);\r
7102         GetWindowText(hwndText, str, len + 1);\r
7103         p = q = str;\r
7104         while (*q) {\r
7105           if (*q == '\r')\r
7106             q++;\r
7107           else\r
7108             *p++ = *q++;\r
7109         }\r
7110         *p = NULLCHAR;\r
7111         ReplaceComment(commentIndex, str);\r
7112         free(str);\r
7113       }\r
7114       CommentPopDown();\r
7115       return TRUE;\r
7116 \r
7117     case IDCANCEL:\r
7118     case OPT_CancelComment:\r
7119       CommentPopDown();\r
7120       return TRUE;\r
7121 \r
7122     case OPT_ClearComment:\r
7123       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7124       break;\r
7125 \r
7126     case OPT_EditComment:\r
7127       EditCommentEvent();\r
7128       return TRUE;\r
7129 \r
7130     default:\r
7131       break;\r
7132     }\r
7133     break;\r
7134 \r
7135   case WM_SIZE:\r
7136     newSizeX = LOWORD(lParam);\r
7137     newSizeY = HIWORD(lParam);\r
7138     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7139     sizeX = newSizeX;\r
7140     sizeY = newSizeY;\r
7141     break;\r
7142 \r
7143   case WM_GETMINMAXINFO:\r
7144     /* Prevent resizing window too small */\r
7145     mmi = (MINMAXINFO *) lParam;\r
7146     mmi->ptMinTrackSize.x = 100;\r
7147     mmi->ptMinTrackSize.y = 100;\r
7148     break;\r
7149   }\r
7150   return FALSE;\r
7151 }\r
7152 \r
7153 VOID\r
7154 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7155 {\r
7156   FARPROC lpProc;\r
7157   char *p, *q;\r
7158 \r
7159   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7160 \r
7161   if (str == NULL) str = "";\r
7162   p = (char *) malloc(2 * strlen(str) + 2);\r
7163   q = p;\r
7164   while (*str) {\r
7165     if (*str == '\n') *q++ = '\r';\r
7166     *q++ = *str++;\r
7167   }\r
7168   *q = NULLCHAR;\r
7169   if (commentText != NULL) free(commentText);\r
7170 \r
7171   commentIndex = index;\r
7172   commentTitle = title;\r
7173   commentText = p;\r
7174   editComment = edit;\r
7175 \r
7176   if (commentDialog) {\r
7177     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7178     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7179   } else {\r
7180     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7181     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7182                  hwndMain, (DLGPROC)lpProc);\r
7183     FreeProcInstance(lpProc);\r
7184   }\r
7185   commentDialogUp = TRUE;\r
7186 }\r
7187 \r
7188 \r
7189 /*---------------------------------------------------------------------------*\\r
7190  *\r
7191  * Type-in move dialog functions\r
7192  * \r
7193 \*---------------------------------------------------------------------------*/\r
7194 \r
7195 LRESULT CALLBACK\r
7196 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7197 {\r
7198   char move[MSG_SIZ];\r
7199   HWND hInput;\r
7200   ChessMove moveType;\r
7201   int fromX, fromY, toX, toY;\r
7202   char promoChar;\r
7203 \r
7204   switch (message) {\r
7205   case WM_INITDIALOG:\r
7206     move[0] = (char) lParam;\r
7207     move[1] = NULLCHAR;\r
7208     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7209     hInput = GetDlgItem(hDlg, OPT_Move);\r
7210     SetWindowText(hInput, move);\r
7211     SetFocus(hInput);\r
7212     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7213     return FALSE;\r
7214 \r
7215   case WM_COMMAND:\r
7216     switch (LOWORD(wParam)) {\r
7217     case IDOK:\r
7218       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7219         gameMode != Training) {\r
7220         DisplayMoveError("Displayed move is not current");\r
7221       } else {\r
7222         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7223         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7224           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7225           if (gameMode != Training)\r
7226               forwardMostMove = currentMove;\r
7227           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7228         } else {\r
7229           DisplayMoveError("Could not parse move");\r
7230         }\r
7231       }\r
7232       EndDialog(hDlg, TRUE);\r
7233       return TRUE;\r
7234     case IDCANCEL:\r
7235       EndDialog(hDlg, FALSE);\r
7236       return TRUE;\r
7237     default:\r
7238       break;\r
7239     }\r
7240     break;\r
7241   }\r
7242   return FALSE;\r
7243 }\r
7244 \r
7245 VOID\r
7246 PopUpMoveDialog(char firstchar)\r
7247 {\r
7248     FARPROC lpProc;\r
7249     \r
7250     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7251         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7252         gameMode == AnalyzeMode || gameMode == EditGame || \r
7253         gameMode == EditPosition || gameMode == IcsExamining ||\r
7254         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7255         gameMode == Training) {\r
7256       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7257       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7258         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7259       FreeProcInstance(lpProc);\r
7260     }\r
7261 }\r
7262 \r
7263 /*---------------------------------------------------------------------------*\\r
7264  *\r
7265  * Type-in name dialog functions\r
7266  * \r
7267 \*---------------------------------------------------------------------------*/\r
7268 \r
7269 LRESULT CALLBACK\r
7270 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7271 {\r
7272   char move[MSG_SIZ];\r
7273   HWND hInput;\r
7274 \r
7275   switch (message) {\r
7276   case WM_INITDIALOG:\r
7277     move[0] = (char) lParam;\r
7278     move[1] = NULLCHAR;\r
7279     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7280     hInput = GetDlgItem(hDlg, OPT_Name);\r
7281     SetWindowText(hInput, move);\r
7282     SetFocus(hInput);\r
7283     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7284     return FALSE;\r
7285 \r
7286   case WM_COMMAND:\r
7287     switch (LOWORD(wParam)) {\r
7288     case IDOK:\r
7289       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7290       appData.userName = strdup(move);\r
7291 \r
7292       EndDialog(hDlg, TRUE);\r
7293       return TRUE;\r
7294     case IDCANCEL:\r
7295       EndDialog(hDlg, FALSE);\r
7296       return TRUE;\r
7297     default:\r
7298       break;\r
7299     }\r
7300     break;\r
7301   }\r
7302   return FALSE;\r
7303 }\r
7304 \r
7305 VOID\r
7306 PopUpNameDialog(char firstchar)\r
7307 {\r
7308     FARPROC lpProc;\r
7309     \r
7310       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7311       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7312         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7313       FreeProcInstance(lpProc);\r
7314 }\r
7315 \r
7316 /*---------------------------------------------------------------------------*\\r
7317  *\r
7318  *  Error dialogs\r
7319  * \r
7320 \*---------------------------------------------------------------------------*/\r
7321 \r
7322 /* Nonmodal error box */\r
7323 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7324                              WPARAM wParam, LPARAM lParam);\r
7325 \r
7326 VOID\r
7327 ErrorPopUp(char *title, char *content)\r
7328 {\r
7329   FARPROC lpProc;\r
7330   char *p, *q;\r
7331   BOOLEAN modal = hwndMain == NULL;\r
7332 \r
7333   p = content;\r
7334   q = errorMessage;\r
7335   while (*p) {\r
7336     if (*p == '\n') {\r
7337       if (modal) {\r
7338         *q++ = ' ';\r
7339         p++;\r
7340       } else {\r
7341         *q++ = '\r';\r
7342         *q++ = *p++;\r
7343       }\r
7344     } else {\r
7345       *q++ = *p++;\r
7346     }\r
7347   }\r
7348   *q = NULLCHAR;\r
7349   strncpy(errorTitle, title, sizeof(errorTitle));\r
7350   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7351   \r
7352   if (modal) {\r
7353     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7354   } else {\r
7355     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7356     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7357                  hwndMain, (DLGPROC)lpProc);\r
7358     FreeProcInstance(lpProc);\r
7359   }\r
7360 }\r
7361 \r
7362 VOID\r
7363 ErrorPopDown()\r
7364 {\r
7365   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7366   if (errorDialog == NULL) return;\r
7367   DestroyWindow(errorDialog);\r
7368   errorDialog = NULL;\r
7369 }\r
7370 \r
7371 LRESULT CALLBACK\r
7372 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7373 {\r
7374   HANDLE hwndText;\r
7375   RECT rChild;\r
7376 \r
7377   switch (message) {\r
7378   case WM_INITDIALOG:\r
7379     GetWindowRect(hDlg, &rChild);\r
7380 \r
7381     /*\r
7382     SetWindowPos(hDlg, NULL, rChild.left,\r
7383       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7384       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7385     */\r
7386 \r
7387     /* \r
7388         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7389         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7390         and it doesn't work when you resize the dialog.\r
7391         For now, just give it a default position.\r
7392     */\r
7393     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7394 \r
7395     errorDialog = hDlg;\r
7396     SetWindowText(hDlg, errorTitle);\r
7397     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7398     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7399     return FALSE;\r
7400 \r
7401   case WM_COMMAND:\r
7402     switch (LOWORD(wParam)) {\r
7403     case IDOK:\r
7404     case IDCANCEL:\r
7405       if (errorDialog == hDlg) errorDialog = NULL;\r
7406       DestroyWindow(hDlg);\r
7407       return TRUE;\r
7408 \r
7409     default:\r
7410       break;\r
7411     }\r
7412     break;\r
7413   }\r
7414   return FALSE;\r
7415 }\r
7416 \r
7417 #ifdef GOTHIC\r
7418 HWND gothicDialog = NULL;\r
7419 \r
7420 LRESULT CALLBACK\r
7421 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7422 {\r
7423   HANDLE hwndText;\r
7424   RECT rChild;\r
7425   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7426 \r
7427   switch (message) {\r
7428   case WM_INITDIALOG:\r
7429     GetWindowRect(hDlg, &rChild);\r
7430 \r
7431     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7432                                                              SWP_NOZORDER);\r
7433 \r
7434     /* \r
7435         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7436         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7437         and it doesn't work when you resize the dialog.\r
7438         For now, just give it a default position.\r
7439     */\r
7440     gothicDialog = hDlg;\r
7441     SetWindowText(hDlg, errorTitle);\r
7442     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7443     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7444     return FALSE;\r
7445 \r
7446   case WM_COMMAND:\r
7447     switch (LOWORD(wParam)) {\r
7448     case IDOK:\r
7449     case IDCANCEL:\r
7450       if (errorDialog == hDlg) errorDialog = NULL;\r
7451       DestroyWindow(hDlg);\r
7452       return TRUE;\r
7453 \r
7454     default:\r
7455       break;\r
7456     }\r
7457     break;\r
7458   }\r
7459   return FALSE;\r
7460 }\r
7461 \r
7462 VOID\r
7463 GothicPopUp(char *title, VariantClass variant)\r
7464 {\r
7465   FARPROC lpProc;\r
7466   static char *lastTitle;\r
7467 \r
7468   strncpy(errorTitle, title, sizeof(errorTitle));\r
7469   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7470 \r
7471   if(lastTitle != title && gothicDialog != NULL) {\r
7472     DestroyWindow(gothicDialog);\r
7473     gothicDialog = NULL;\r
7474   }\r
7475   if(variant != VariantNormal && gothicDialog == NULL) {\r
7476     title = lastTitle;\r
7477     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7478     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7479                  hwndMain, (DLGPROC)lpProc);\r
7480     FreeProcInstance(lpProc);\r
7481   }\r
7482 }\r
7483 #endif\r
7484 \r
7485 /*---------------------------------------------------------------------------*\\r
7486  *\r
7487  *  Ics Interaction console functions\r
7488  *\r
7489 \*---------------------------------------------------------------------------*/\r
7490 \r
7491 #define HISTORY_SIZE 64\r
7492 static char *history[HISTORY_SIZE];\r
7493 int histIn = 0, histP = 0;\r
7494 \r
7495 VOID\r
7496 SaveInHistory(char *cmd)\r
7497 {\r
7498   if (history[histIn] != NULL) {\r
7499     free(history[histIn]);\r
7500     history[histIn] = NULL;\r
7501   }\r
7502   if (*cmd == NULLCHAR) return;\r
7503   history[histIn] = StrSave(cmd);\r
7504   histIn = (histIn + 1) % HISTORY_SIZE;\r
7505   if (history[histIn] != NULL) {\r
7506     free(history[histIn]);\r
7507     history[histIn] = NULL;\r
7508   }\r
7509   histP = histIn;\r
7510 }\r
7511 \r
7512 char *\r
7513 PrevInHistory(char *cmd)\r
7514 {\r
7515   int newhp;\r
7516   if (histP == histIn) {\r
7517     if (history[histIn] != NULL) free(history[histIn]);\r
7518     history[histIn] = StrSave(cmd);\r
7519   }\r
7520   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7521   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7522   histP = newhp;\r
7523   return history[histP];\r
7524 }\r
7525 \r
7526 char *\r
7527 NextInHistory()\r
7528 {\r
7529   if (histP == histIn) return NULL;\r
7530   histP = (histP + 1) % HISTORY_SIZE;\r
7531   return history[histP];\r
7532 }\r
7533 \r
7534 typedef struct {\r
7535   char *item;\r
7536   char *command;\r
7537   BOOLEAN getname;\r
7538   BOOLEAN immediate;\r
7539 } IcsTextMenuEntry;\r
7540 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7541 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7542 \r
7543 void\r
7544 ParseIcsTextMenu(char *icsTextMenuString)\r
7545 {\r
7546 //  int flags = 0;\r
7547   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7548   char *p = icsTextMenuString;\r
7549   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7550     free(e->item);\r
7551     e->item = NULL;\r
7552     if (e->command != NULL) {\r
7553       free(e->command);\r
7554       e->command = NULL;\r
7555     }\r
7556     e++;\r
7557   }\r
7558   e = icsTextMenuEntry;\r
7559   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7560     if (*p == ';' || *p == '\n') {\r
7561       e->item = strdup("-");\r
7562       e->command = NULL;\r
7563       p++;\r
7564     } else if (*p == '-') {\r
7565       e->item = strdup("-");\r
7566       e->command = NULL;\r
7567       p++;\r
7568       if (*p) p++;\r
7569     } else {\r
7570       char *q, *r, *s, *t;\r
7571       char c;\r
7572       q = strchr(p, ',');\r
7573       if (q == NULL) break;\r
7574       *q = NULLCHAR;\r
7575       r = strchr(q + 1, ',');\r
7576       if (r == NULL) break;\r
7577       *r = NULLCHAR;\r
7578       s = strchr(r + 1, ',');\r
7579       if (s == NULL) break;\r
7580       *s = NULLCHAR;\r
7581       c = ';';\r
7582       t = strchr(s + 1, c);\r
7583       if (t == NULL) {\r
7584         c = '\n';\r
7585         t = strchr(s + 1, c);\r
7586       }\r
7587       if (t != NULL) *t = NULLCHAR;\r
7588       e->item = strdup(p);\r
7589       e->command = strdup(q + 1);\r
7590       e->getname = *(r + 1) != '0';\r
7591       e->immediate = *(s + 1) != '0';\r
7592       *q = ',';\r
7593       *r = ',';\r
7594       *s = ',';\r
7595       if (t == NULL) break;\r
7596       *t = c;\r
7597       p = t + 1;\r
7598     }\r
7599     e++;\r
7600   } \r
7601 }\r
7602 \r
7603 HMENU\r
7604 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7605 {\r
7606   HMENU hmenu, h;\r
7607   int i = 0;\r
7608   hmenu = LoadMenu(hInst, "TextMenu");\r
7609   h = GetSubMenu(hmenu, 0);\r
7610   while (e->item) {\r
7611     if (strcmp(e->item, "-") == 0) {\r
7612       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7613     } else {\r
7614       if (e->item[0] == '|') {\r
7615         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7616                    IDM_CommandX + i, &e->item[1]);\r
7617       } else {\r
7618         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7619       }\r
7620     }\r
7621     e++;\r
7622     i++;\r
7623   } \r
7624   return hmenu;\r
7625 }\r
7626 \r
7627 WNDPROC consoleTextWindowProc;\r
7628 \r
7629 void\r
7630 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7631 {\r
7632   char buf[MSG_SIZ], name[MSG_SIZ];\r
7633   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7634   CHARRANGE sel;\r
7635 \r
7636   if (!getname) {\r
7637     SetWindowText(hInput, command);\r
7638     if (immediate) {\r
7639       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7640     } else {\r
7641       sel.cpMin = 999999;\r
7642       sel.cpMax = 999999;\r
7643       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7644       SetFocus(hInput);\r
7645     }\r
7646     return;\r
7647   }    \r
7648   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7649   if (sel.cpMin == sel.cpMax) {\r
7650     /* Expand to surrounding word */\r
7651     TEXTRANGE tr;\r
7652     do {\r
7653       tr.chrg.cpMax = sel.cpMin;\r
7654       tr.chrg.cpMin = --sel.cpMin;\r
7655       if (sel.cpMin < 0) break;\r
7656       tr.lpstrText = name;\r
7657       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7658     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7659     sel.cpMin++;\r
7660 \r
7661     do {\r
7662       tr.chrg.cpMin = sel.cpMax;\r
7663       tr.chrg.cpMax = ++sel.cpMax;\r
7664       tr.lpstrText = name;\r
7665       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7666     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7667     sel.cpMax--;\r
7668 \r
7669     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7670       MessageBeep(MB_ICONEXCLAMATION);\r
7671       return;\r
7672     }\r
7673     tr.chrg = sel;\r
7674     tr.lpstrText = name;\r
7675     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7676   } else {\r
7677     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7678       MessageBeep(MB_ICONEXCLAMATION);\r
7679       return;\r
7680     }\r
7681     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7682   }\r
7683   if (immediate) {\r
7684     sprintf(buf, "%s %s", command, name);\r
7685     SetWindowText(hInput, buf);\r
7686     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7687   } else {\r
7688     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7689     SetWindowText(hInput, buf);\r
7690     sel.cpMin = 999999;\r
7691     sel.cpMax = 999999;\r
7692     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7693     SetFocus(hInput);\r
7694   }\r
7695 }\r
7696 \r
7697 LRESULT CALLBACK \r
7698 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7699 {\r
7700   HWND hInput;\r
7701   CHARRANGE sel;\r
7702 \r
7703   switch (message) {\r
7704   case WM_KEYDOWN:\r
7705     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7706     switch (wParam) {\r
7707     case VK_PRIOR:\r
7708       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7709       return 0;\r
7710     case VK_NEXT:\r
7711       sel.cpMin = 999999;\r
7712       sel.cpMax = 999999;\r
7713       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7714       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7715       return 0;\r
7716     }\r
7717     break;\r
7718   case WM_CHAR:\r
7719     if (wParam == '\t') {\r
7720       if (GetKeyState(VK_SHIFT) < 0) {\r
7721         /* shifted */\r
7722         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7723         if (buttonDesc[0].hwnd) {\r
7724           SetFocus(buttonDesc[0].hwnd);\r
7725         } else {\r
7726           SetFocus(hwndMain);\r
7727         }\r
7728       } else {\r
7729         /* unshifted */\r
7730         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7731       }\r
7732     } else {\r
7733       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7734       SetFocus(hInput);\r
7735       SendMessage(hInput, message, wParam, lParam);\r
7736     }\r
7737     return 0;\r
7738   case WM_PASTE:\r
7739     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7740     SetFocus(hInput);\r
7741     return SendMessage(hInput, message, wParam, lParam);\r
7742   case WM_MBUTTONDOWN:\r
7743     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7744   case WM_RBUTTONDOWN:\r
7745     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7746       /* Move selection here if it was empty */\r
7747       POINT pt;\r
7748       pt.x = LOWORD(lParam);\r
7749       pt.y = HIWORD(lParam);\r
7750       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7751       if (sel.cpMin == sel.cpMax) {\r
7752         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7753         sel.cpMax = sel.cpMin;\r
7754         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7755       }\r
7756       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7757     }\r
7758     return 0;\r
7759   case WM_RBUTTONUP:\r
7760     if (GetKeyState(VK_SHIFT) & ~1) {\r
7761       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7762         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7763     } else {\r
7764       POINT pt;\r
7765       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7766       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7767       if (sel.cpMin == sel.cpMax) {\r
7768         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7769         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7770       }\r
7771       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7772         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7773       }\r
7774       pt.x = LOWORD(lParam);\r
7775       pt.y = HIWORD(lParam);\r
7776       MenuPopup(hwnd, pt, hmenu, -1);\r
7777     }\r
7778     return 0;\r
7779   case WM_COMMAND:\r
7780     switch (LOWORD(wParam)) {\r
7781     case IDM_QuickPaste:\r
7782       {\r
7783         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7784         if (sel.cpMin == sel.cpMax) {\r
7785           MessageBeep(MB_ICONEXCLAMATION);\r
7786           return 0;\r
7787         }\r
7788         SendMessage(hwnd, WM_COPY, 0, 0);\r
7789         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7790         SendMessage(hInput, WM_PASTE, 0, 0);\r
7791         SetFocus(hInput);\r
7792         return 0;\r
7793       }\r
7794     case IDM_Cut:\r
7795       SendMessage(hwnd, WM_CUT, 0, 0);\r
7796       return 0;\r
7797     case IDM_Paste:\r
7798       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7799       return 0;\r
7800     case IDM_Copy:\r
7801       SendMessage(hwnd, WM_COPY, 0, 0);\r
7802       return 0;\r
7803     default:\r
7804       {\r
7805         int i = LOWORD(wParam) - IDM_CommandX;\r
7806         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7807             icsTextMenuEntry[i].command != NULL) {\r
7808           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7809                    icsTextMenuEntry[i].getname,\r
7810                    icsTextMenuEntry[i].immediate);\r
7811           return 0;\r
7812         }\r
7813       }\r
7814       break;\r
7815     }\r
7816     break;\r
7817   }\r
7818   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7819 }\r
7820 \r
7821 WNDPROC consoleInputWindowProc;\r
7822 \r
7823 LRESULT CALLBACK\r
7824 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7825 {\r
7826   char buf[MSG_SIZ];\r
7827   char *p;\r
7828   static BOOL sendNextChar = FALSE;\r
7829   static BOOL quoteNextChar = FALSE;\r
7830   InputSource *is = consoleInputSource;\r
7831   CHARFORMAT cf;\r
7832   CHARRANGE sel;\r
7833 \r
7834   switch (message) {\r
7835   case WM_CHAR:\r
7836     if (!appData.localLineEditing || sendNextChar) {\r
7837       is->buf[0] = (CHAR) wParam;\r
7838       is->count = 1;\r
7839       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7840       sendNextChar = FALSE;\r
7841       return 0;\r
7842     }\r
7843     if (quoteNextChar) {\r
7844       buf[0] = (char) wParam;\r
7845       buf[1] = NULLCHAR;\r
7846       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7847       quoteNextChar = FALSE;\r
7848       return 0;\r
7849     }\r
7850     switch (wParam) {\r
7851     case '\r':   /* Enter key */\r
7852       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7853       if (consoleEcho) SaveInHistory(is->buf);\r
7854       is->buf[is->count++] = '\n';\r
7855       is->buf[is->count] = NULLCHAR;\r
7856       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7857       if (consoleEcho) {\r
7858         ConsoleOutput(is->buf, is->count, TRUE);\r
7859       } else if (appData.localLineEditing) {\r
7860         ConsoleOutput("\n", 1, TRUE);\r
7861       }\r
7862       /* fall thru */\r
7863     case '\033': /* Escape key */\r
7864       SetWindowText(hwnd, "");\r
7865       cf.cbSize = sizeof(CHARFORMAT);\r
7866       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7867       if (consoleEcho) {\r
7868         cf.crTextColor = textAttribs[ColorNormal].color;\r
7869       } else {\r
7870         cf.crTextColor = COLOR_ECHOOFF;\r
7871       }\r
7872       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7873       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7874       return 0;\r
7875     case '\t':   /* Tab key */\r
7876       if (GetKeyState(VK_SHIFT) < 0) {\r
7877         /* shifted */\r
7878         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7879       } else {\r
7880         /* unshifted */\r
7881         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7882         if (buttonDesc[0].hwnd) {\r
7883           SetFocus(buttonDesc[0].hwnd);\r
7884         } else {\r
7885           SetFocus(hwndMain);\r
7886         }\r
7887       }\r
7888       return 0;\r
7889     case '\023': /* Ctrl+S */\r
7890       sendNextChar = TRUE;\r
7891       return 0;\r
7892     case '\021': /* Ctrl+Q */\r
7893       quoteNextChar = TRUE;\r
7894       return 0;\r
7895     default:\r
7896       break;\r
7897     }\r
7898     break;\r
7899   case WM_KEYDOWN:\r
7900     switch (wParam) {\r
7901     case VK_UP:\r
7902       GetWindowText(hwnd, buf, MSG_SIZ);\r
7903       p = PrevInHistory(buf);\r
7904       if (p != NULL) {\r
7905         SetWindowText(hwnd, p);\r
7906         sel.cpMin = 999999;\r
7907         sel.cpMax = 999999;\r
7908         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7909         return 0;\r
7910       }\r
7911       break;\r
7912     case VK_DOWN:\r
7913       p = NextInHistory();\r
7914       if (p != NULL) {\r
7915         SetWindowText(hwnd, p);\r
7916         sel.cpMin = 999999;\r
7917         sel.cpMax = 999999;\r
7918         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7919         return 0;\r
7920       }\r
7921       break;\r
7922     case VK_HOME:\r
7923     case VK_END:\r
7924       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7925       /* fall thru */\r
7926     case VK_PRIOR:\r
7927     case VK_NEXT:\r
7928       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7929       return 0;\r
7930     }\r
7931     break;\r
7932   case WM_MBUTTONDOWN:\r
7933     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7934       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7935     break;\r
7936   case WM_RBUTTONUP:\r
7937     if (GetKeyState(VK_SHIFT) & ~1) {\r
7938       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7939         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7940     } else {\r
7941       POINT pt;\r
7942       HMENU hmenu;\r
7943       hmenu = LoadMenu(hInst, "InputMenu");\r
7944       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7945       if (sel.cpMin == sel.cpMax) {\r
7946         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7947         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7948       }\r
7949       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7950         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7951       }\r
7952       pt.x = LOWORD(lParam);\r
7953       pt.y = HIWORD(lParam);\r
7954       MenuPopup(hwnd, pt, hmenu, -1);\r
7955     }\r
7956     return 0;\r
7957   case WM_COMMAND:\r
7958     switch (LOWORD(wParam)) { \r
7959     case IDM_Undo:\r
7960       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7961       return 0;\r
7962     case IDM_SelectAll:\r
7963       sel.cpMin = 0;\r
7964       sel.cpMax = -1; /*999999?*/\r
7965       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7966       return 0;\r
7967     case IDM_Cut:\r
7968       SendMessage(hwnd, WM_CUT, 0, 0);\r
7969       return 0;\r
7970     case IDM_Paste:\r
7971       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7972       return 0;\r
7973     case IDM_Copy:\r
7974       SendMessage(hwnd, WM_COPY, 0, 0);\r
7975       return 0;\r
7976     }\r
7977     break;\r
7978   }\r
7979   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7980 }\r
7981 \r
7982 #define CO_MAX  100000\r
7983 #define CO_TRIM   1000\r
7984 \r
7985 LRESULT CALLBACK\r
7986 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7987 {\r
7988   static SnapData sd;\r
7989   static HWND hText, hInput /*, hFocus*/;\r
7990 //  InputSource *is = consoleInputSource;\r
7991   RECT rect;\r
7992   static int sizeX, sizeY;\r
7993   int newSizeX, newSizeY;\r
7994   MINMAXINFO *mmi;\r
7995 \r
7996   switch (message) {\r
7997   case WM_INITDIALOG: /* message: initialize dialog box */\r
7998     hwndConsole = hDlg;\r
7999     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8000     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8001     SetFocus(hInput);\r
8002     consoleTextWindowProc = (WNDPROC)\r
8003       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8004     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8005     consoleInputWindowProc = (WNDPROC)\r
8006       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8007     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8008     Colorize(ColorNormal, TRUE);\r
8009     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8010     ChangedConsoleFont();\r
8011     GetClientRect(hDlg, &rect);\r
8012     sizeX = rect.right;\r
8013     sizeY = rect.bottom;\r
8014     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8015         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8016       WINDOWPLACEMENT wp;\r
8017       EnsureOnScreen(&consoleX, &consoleY);\r
8018       wp.length = sizeof(WINDOWPLACEMENT);\r
8019       wp.flags = 0;\r
8020       wp.showCmd = SW_SHOW;\r
8021       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8022       wp.rcNormalPosition.left = consoleX;\r
8023       wp.rcNormalPosition.right = consoleX + consoleW;\r
8024       wp.rcNormalPosition.top = consoleY;\r
8025       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8026       SetWindowPlacement(hDlg, &wp);\r
8027     }\r
8028 #if 0 \r
8029    // [HGM] Chessknight's change 2004-07-13\r
8030    else { /* Determine Defaults */\r
8031        WINDOWPLACEMENT wp;\r
8032        consoleX = winWidth + 1;\r
8033        consoleY = boardY;\r
8034        consoleW = screenWidth -  winWidth;\r
8035        consoleH = winHeight;\r
8036        EnsureOnScreen(&consoleX, &consoleY);\r
8037        wp.length = sizeof(WINDOWPLACEMENT);\r
8038        wp.flags = 0;\r
8039        wp.showCmd = SW_SHOW;\r
8040        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8041        wp.rcNormalPosition.left = consoleX;\r
8042        wp.rcNormalPosition.right = consoleX + consoleW;\r
8043        wp.rcNormalPosition.top = consoleY;\r
8044        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8045        SetWindowPlacement(hDlg, &wp);\r
8046     }\r
8047 #endif\r
8048     return FALSE;\r
8049 \r
8050   case WM_SETFOCUS:\r
8051     SetFocus(hInput);\r
8052     return 0;\r
8053 \r
8054   case WM_CLOSE:\r
8055     ExitEvent(0);\r
8056     /* not reached */\r
8057     break;\r
8058 \r
8059   case WM_SIZE:\r
8060     if (IsIconic(hDlg)) break;\r
8061     newSizeX = LOWORD(lParam);\r
8062     newSizeY = HIWORD(lParam);\r
8063     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8064       RECT rectText, rectInput;\r
8065       POINT pt;\r
8066       int newTextHeight, newTextWidth;\r
8067       GetWindowRect(hText, &rectText);\r
8068       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8069       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8070       if (newTextHeight < 0) {\r
8071         newSizeY += -newTextHeight;\r
8072         newTextHeight = 0;\r
8073       }\r
8074       SetWindowPos(hText, NULL, 0, 0,\r
8075         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8076       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8077       pt.x = rectInput.left;\r
8078       pt.y = rectInput.top + newSizeY - sizeY;\r
8079       ScreenToClient(hDlg, &pt);\r
8080       SetWindowPos(hInput, NULL, \r
8081         pt.x, pt.y, /* needs client coords */   \r
8082         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8083         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8084     }\r
8085     sizeX = newSizeX;\r
8086     sizeY = newSizeY;\r
8087     break;\r
8088 \r
8089   case WM_GETMINMAXINFO:\r
8090     /* Prevent resizing window too small */\r
8091     mmi = (MINMAXINFO *) lParam;\r
8092     mmi->ptMinTrackSize.x = 100;\r
8093     mmi->ptMinTrackSize.y = 100;\r
8094     break;\r
8095 \r
8096   /* [AS] Snapping */\r
8097   case WM_ENTERSIZEMOVE:\r
8098     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8099 \r
8100   case WM_SIZING:\r
8101     return OnSizing( &sd, hDlg, wParam, lParam );\r
8102 \r
8103   case WM_MOVING:\r
8104     return OnMoving( &sd, hDlg, wParam, lParam );\r
8105 \r
8106   case WM_EXITSIZEMOVE:\r
8107     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8108   }\r
8109 \r
8110   return DefWindowProc(hDlg, message, wParam, lParam);\r
8111 }\r
8112 \r
8113 \r
8114 VOID\r
8115 ConsoleCreate()\r
8116 {\r
8117   HWND hCons;\r
8118   if (hwndConsole) return;\r
8119   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8120   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8121 }\r
8122 \r
8123 \r
8124 VOID\r
8125 ConsoleOutput(char* data, int length, int forceVisible)\r
8126 {\r
8127   HWND hText;\r
8128   int trim, exlen;\r
8129   char *p, *q;\r
8130   char buf[CO_MAX+1];\r
8131   POINT pEnd;\r
8132   RECT rect;\r
8133   static int delayLF = 0;\r
8134   CHARRANGE savesel, sel;\r
8135 \r
8136   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8137   p = data;\r
8138   q = buf;\r
8139   if (delayLF) {\r
8140     *q++ = '\r';\r
8141     *q++ = '\n';\r
8142     delayLF = 0;\r
8143   }\r
8144   while (length--) {\r
8145     if (*p == '\n') {\r
8146       if (*++p) {\r
8147         *q++ = '\r';\r
8148         *q++ = '\n';\r
8149       } else {\r
8150         delayLF = 1;\r
8151       }\r
8152     } else if (*p == '\007') {\r
8153        MyPlaySound(&sounds[(int)SoundBell]);\r
8154        p++;\r
8155     } else {\r
8156       *q++ = *p++;\r
8157     }\r
8158   }\r
8159   *q = NULLCHAR;\r
8160   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8161   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8162   /* Save current selection */\r
8163   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8164   exlen = GetWindowTextLength(hText);\r
8165   /* Find out whether current end of text is visible */\r
8166   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8167   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8168   /* Trim existing text if it's too long */\r
8169   if (exlen + (q - buf) > CO_MAX) {\r
8170     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8171     sel.cpMin = 0;\r
8172     sel.cpMax = trim;\r
8173     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8174     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8175     exlen -= trim;\r
8176     savesel.cpMin -= trim;\r
8177     savesel.cpMax -= trim;\r
8178     if (exlen < 0) exlen = 0;\r
8179     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8180     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8181   }\r
8182   /* Append the new text */\r
8183   sel.cpMin = exlen;\r
8184   sel.cpMax = exlen;\r
8185   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8186   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8187   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8188   if (forceVisible || exlen == 0 ||\r
8189       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8190        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8191     /* Scroll to make new end of text visible if old end of text\r
8192        was visible or new text is an echo of user typein */\r
8193     sel.cpMin = 9999999;\r
8194     sel.cpMax = 9999999;\r
8195     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8196     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8197     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8198     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8199   }\r
8200   if (savesel.cpMax == exlen || forceVisible) {\r
8201     /* Move insert point to new end of text if it was at the old\r
8202        end of text or if the new text is an echo of user typein */\r
8203     sel.cpMin = 9999999;\r
8204     sel.cpMax = 9999999;\r
8205     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8206   } else {\r
8207     /* Restore previous selection */\r
8208     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8209   }\r
8210   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8211 }\r
8212 \r
8213 /*---------*/\r
8214 \r
8215 \r
8216 void\r
8217 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8218 {\r
8219   char buf[100];\r
8220   char *str;\r
8221   COLORREF oldFg, oldBg;\r
8222   HFONT oldFont;\r
8223   RECT rect;\r
8224 \r
8225   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8226 \r
8227   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8228   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8229   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8230 \r
8231   rect.left = x;\r
8232   rect.right = x + squareSize;\r
8233   rect.top  = y;\r
8234   rect.bottom = y + squareSize;\r
8235   str = buf;\r
8236 \r
8237   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8238                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8239              y, ETO_CLIPPED|ETO_OPAQUE,\r
8240              &rect, str, strlen(str), NULL);\r
8241 \r
8242   (void) SetTextColor(hdc, oldFg);\r
8243   (void) SetBkColor(hdc, oldBg);\r
8244   (void) SelectObject(hdc, oldFont);\r
8245 }\r
8246 \r
8247 void\r
8248 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8249               RECT *rect, char *color, char *flagFell)\r
8250 {\r
8251   char buf[100];\r
8252   char *str;\r
8253   COLORREF oldFg, oldBg;\r
8254   HFONT oldFont;\r
8255 \r
8256   if (appData.clockMode) {\r
8257     if (tinyLayout)\r
8258       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8259     else\r
8260       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8261     str = buf;\r
8262   } else {\r
8263     str = color;\r
8264   }\r
8265 \r
8266   if (highlight) {\r
8267     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8268     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8269   } else {\r
8270     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8271     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8272   }\r
8273   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8274 \r
8275   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8276              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8277              rect, str, strlen(str), NULL);\r
8278 \r
8279   (void) SetTextColor(hdc, oldFg);\r
8280   (void) SetBkColor(hdc, oldBg);\r
8281   (void) SelectObject(hdc, oldFont);\r
8282 }\r
8283 \r
8284 \r
8285 int\r
8286 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8287            OVERLAPPED *ovl)\r
8288 {\r
8289   int ok, err;\r
8290 \r
8291   /* [AS]  */\r
8292   if( count <= 0 ) {\r
8293     if (appData.debugMode) {\r
8294       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8295     }\r
8296 \r
8297     return ERROR_INVALID_USER_BUFFER;\r
8298   }\r
8299 \r
8300   ResetEvent(ovl->hEvent);\r
8301   ovl->Offset = ovl->OffsetHigh = 0;\r
8302   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8303   if (ok) {\r
8304     err = NO_ERROR;\r
8305   } else {\r
8306     err = GetLastError();\r
8307     if (err == ERROR_IO_PENDING) {\r
8308       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8309       if (ok)\r
8310         err = NO_ERROR;\r
8311       else\r
8312         err = GetLastError();\r
8313     }\r
8314   }\r
8315   return err;\r
8316 }\r
8317 \r
8318 int\r
8319 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8320             OVERLAPPED *ovl)\r
8321 {\r
8322   int ok, err;\r
8323 \r
8324   ResetEvent(ovl->hEvent);\r
8325   ovl->Offset = ovl->OffsetHigh = 0;\r
8326   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8327   if (ok) {\r
8328     err = NO_ERROR;\r
8329   } else {\r
8330     err = GetLastError();\r
8331     if (err == ERROR_IO_PENDING) {\r
8332       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8333       if (ok)\r
8334         err = NO_ERROR;\r
8335       else\r
8336         err = GetLastError();\r
8337     }\r
8338   }\r
8339   return err;\r
8340 }\r
8341 \r
8342 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8343 void CheckForInputBufferFull( InputSource * is )\r
8344 {\r
8345     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8346         /* Look for end of line */\r
8347         char * p = is->buf;\r
8348         \r
8349         while( p < is->next && *p != '\n' ) {\r
8350             p++;\r
8351         }\r
8352 \r
8353         if( p >= is->next ) {\r
8354             if (appData.debugMode) {\r
8355                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8356             }\r
8357 \r
8358             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8359             is->count = (DWORD) -1;\r
8360             is->next = is->buf;\r
8361         }\r
8362     }\r
8363 }\r
8364 \r
8365 DWORD\r
8366 InputThread(LPVOID arg)\r
8367 {\r
8368   InputSource *is;\r
8369   OVERLAPPED ovl;\r
8370 \r
8371   is = (InputSource *) arg;\r
8372   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8373   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8374   while (is->hThread != NULL) {\r
8375     is->error = DoReadFile(is->hFile, is->next,\r
8376                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8377                            &is->count, &ovl);\r
8378     if (is->error == NO_ERROR) {\r
8379       is->next += is->count;\r
8380     } else {\r
8381       if (is->error == ERROR_BROKEN_PIPE) {\r
8382         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8383         is->count = 0;\r
8384       } else {\r
8385         is->count = (DWORD) -1;\r
8386         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8387         break; \r
8388       }\r
8389     }\r
8390 \r
8391     CheckForInputBufferFull( is );\r
8392 \r
8393     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8394 \r
8395     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8396 \r
8397     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8398   }\r
8399 \r
8400   CloseHandle(ovl.hEvent);\r
8401   CloseHandle(is->hFile);\r
8402 \r
8403   if (appData.debugMode) {\r
8404     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8405   }\r
8406 \r
8407   return 0;\r
8408 }\r
8409 \r
8410 \r
8411 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8412 DWORD\r
8413 NonOvlInputThread(LPVOID arg)\r
8414 {\r
8415   InputSource *is;\r
8416   char *p, *q;\r
8417   int i;\r
8418   char prev;\r
8419 \r
8420   is = (InputSource *) arg;\r
8421   while (is->hThread != NULL) {\r
8422     is->error = ReadFile(is->hFile, is->next,\r
8423                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8424                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8425     if (is->error == NO_ERROR) {\r
8426       /* Change CRLF to LF */\r
8427       if (is->next > is->buf) {\r
8428         p = is->next - 1;\r
8429         i = is->count + 1;\r
8430       } else {\r
8431         p = is->next;\r
8432         i = is->count;\r
8433       }\r
8434       q = p;\r
8435       prev = NULLCHAR;\r
8436       while (i > 0) {\r
8437         if (prev == '\r' && *p == '\n') {\r
8438           *(q-1) = '\n';\r
8439           is->count--;\r
8440         } else { \r
8441           *q++ = *p;\r
8442         }\r
8443         prev = *p++;\r
8444         i--;\r
8445       }\r
8446       *q = NULLCHAR;\r
8447       is->next = q;\r
8448     } else {\r
8449       if (is->error == ERROR_BROKEN_PIPE) {\r
8450         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8451         is->count = 0; \r
8452       } else {\r
8453         is->count = (DWORD) -1;\r
8454       }\r
8455     }\r
8456 \r
8457     CheckForInputBufferFull( is );\r
8458 \r
8459     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8460 \r
8461     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8462 \r
8463     if (is->count < 0) break;  /* Quit on error */\r
8464   }\r
8465   CloseHandle(is->hFile);\r
8466   return 0;\r
8467 }\r
8468 \r
8469 DWORD\r
8470 SocketInputThread(LPVOID arg)\r
8471 {\r
8472   InputSource *is;\r
8473 \r
8474   is = (InputSource *) arg;\r
8475   while (is->hThread != NULL) {\r
8476     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8477     if ((int)is->count == SOCKET_ERROR) {\r
8478       is->count = (DWORD) -1;\r
8479       is->error = WSAGetLastError();\r
8480     } else {\r
8481       is->error = NO_ERROR;\r
8482       is->next += is->count;\r
8483       if (is->count == 0 && is->second == is) {\r
8484         /* End of file on stderr; quit with no message */\r
8485         break;\r
8486       }\r
8487     }\r
8488     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8489 \r
8490     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8491 \r
8492     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8493   }\r
8494   return 0;\r
8495 }\r
8496 \r
8497 VOID\r
8498 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8499 {\r
8500   InputSource *is;\r
8501 \r
8502   is = (InputSource *) lParam;\r
8503   if (is->lineByLine) {\r
8504     /* Feed in lines one by one */\r
8505     char *p = is->buf;\r
8506     char *q = p;\r
8507     while (q < is->next) {\r
8508       if (*q++ == '\n') {\r
8509         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8510         p = q;\r
8511       }\r
8512     }\r
8513     \r
8514     /* Move any partial line to the start of the buffer */\r
8515     q = is->buf;\r
8516     while (p < is->next) {\r
8517       *q++ = *p++;\r
8518     }\r
8519     is->next = q;\r
8520 \r
8521     if (is->error != NO_ERROR || is->count == 0) {\r
8522       /* Notify backend of the error.  Note: If there was a partial\r
8523          line at the end, it is not flushed through. */\r
8524       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8525     }\r
8526   } else {\r
8527     /* Feed in the whole chunk of input at once */\r
8528     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8529     is->next = is->buf;\r
8530   }\r
8531 }\r
8532 \r
8533 /*---------------------------------------------------------------------------*\\r
8534  *\r
8535  *  Menu enables. Used when setting various modes.\r
8536  *\r
8537 \*---------------------------------------------------------------------------*/\r
8538 \r
8539 typedef struct {\r
8540   int item;\r
8541   int flags;\r
8542 } Enables;\r
8543 \r
8544 VOID\r
8545 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8546 {\r
8547   while (enab->item > 0) {\r
8548     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8549     enab++;\r
8550   }\r
8551 }\r
8552 \r
8553 Enables gnuEnables[] = {\r
8554   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8560   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8561   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8564   { -1, -1 }\r
8565 };\r
8566 \r
8567 Enables icsEnables[] = {\r
8568   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8570   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8574   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8575   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8576   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8578   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8580   { -1, -1 }\r
8581 };\r
8582 \r
8583 #ifdef ZIPPY\r
8584 Enables zippyEnables[] = {\r
8585   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8586   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8587   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8588   { -1, -1 }\r
8589 };\r
8590 #endif\r
8591 \r
8592 Enables ncpEnables[] = {\r
8593   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8595   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8596   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8597   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8598   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8601   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8602   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8608   { -1, -1 }\r
8609 };\r
8610 \r
8611 Enables trainingOnEnables[] = {\r
8612   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8616   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8617   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8618   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8619   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8620   { -1, -1 }\r
8621 };\r
8622 \r
8623 Enables trainingOffEnables[] = {\r
8624   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8625   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8627   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8628   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8631   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8632   { -1, -1 }\r
8633 };\r
8634 \r
8635 /* These modify either ncpEnables or gnuEnables */\r
8636 Enables cmailEnables[] = {\r
8637   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8638   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8639   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8640   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8641   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8642   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8643   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8644   { -1, -1 }\r
8645 };\r
8646 \r
8647 Enables machineThinkingEnables[] = {\r
8648   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8651   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8652   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8653   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8654   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8656   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8657   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8658   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8659   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8660   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8661   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8662   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8663   { -1, -1 }\r
8664 };\r
8665 \r
8666 Enables userThinkingEnables[] = {\r
8667   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8668   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8669   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8670   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8671   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8672   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8673   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8674   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8675   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8676   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8677   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8678   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8679   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8680   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8681   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8682   { -1, -1 }\r
8683 };\r
8684 \r
8685 /*---------------------------------------------------------------------------*\\r
8686  *\r
8687  *  Front-end interface functions exported by XBoard.\r
8688  *  Functions appear in same order as prototypes in frontend.h.\r
8689  * \r
8690 \*---------------------------------------------------------------------------*/\r
8691 VOID\r
8692 ModeHighlight()\r
8693 {\r
8694   static UINT prevChecked = 0;\r
8695   static int prevPausing = 0;\r
8696   UINT nowChecked;\r
8697 \r
8698   if (pausing != prevPausing) {\r
8699     prevPausing = pausing;\r
8700     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8701                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8702     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8703   }\r
8704 \r
8705   switch (gameMode) {\r
8706   case BeginningOfGame:\r
8707     if (appData.icsActive)\r
8708       nowChecked = IDM_IcsClient;\r
8709     else if (appData.noChessProgram)\r
8710       nowChecked = IDM_EditGame;\r
8711     else\r
8712       nowChecked = IDM_MachineBlack;\r
8713     break;\r
8714   case MachinePlaysBlack:\r
8715     nowChecked = IDM_MachineBlack;\r
8716     break;\r
8717   case MachinePlaysWhite:\r
8718     nowChecked = IDM_MachineWhite;\r
8719     break;\r
8720   case TwoMachinesPlay:\r
8721     nowChecked = IDM_TwoMachines;\r
8722     break;\r
8723   case AnalyzeMode:\r
8724     nowChecked = IDM_AnalysisMode;\r
8725     break;\r
8726   case AnalyzeFile:\r
8727     nowChecked = IDM_AnalyzeFile;\r
8728     break;\r
8729   case EditGame:\r
8730     nowChecked = IDM_EditGame;\r
8731     break;\r
8732   case PlayFromGameFile:\r
8733     nowChecked = IDM_LoadGame;\r
8734     break;\r
8735   case EditPosition:\r
8736     nowChecked = IDM_EditPosition;\r
8737     break;\r
8738   case Training:\r
8739     nowChecked = IDM_Training;\r
8740     break;\r
8741   case IcsPlayingWhite:\r
8742   case IcsPlayingBlack:\r
8743   case IcsObserving:\r
8744   case IcsIdle:\r
8745     nowChecked = IDM_IcsClient;\r
8746     break;\r
8747   default:\r
8748   case EndOfGame:\r
8749     nowChecked = 0;\r
8750     break;\r
8751   }\r
8752   if (prevChecked != 0)\r
8753     (void) CheckMenuItem(GetMenu(hwndMain),\r
8754                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8755   if (nowChecked != 0)\r
8756     (void) CheckMenuItem(GetMenu(hwndMain),\r
8757                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8758 \r
8759   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8760     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8761                           MF_BYCOMMAND|MF_ENABLED);\r
8762   } else {\r
8763     (void) EnableMenuItem(GetMenu(hwndMain), \r
8764                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8765   }\r
8766 \r
8767   prevChecked = nowChecked;\r
8768 \r
8769   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8770   if (appData.icsActive) {\r
8771        if (appData.icsEngineAnalyze) {\r
8772                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8773                        MF_BYCOMMAND|MF_CHECKED);\r
8774        } else {\r
8775                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8776                        MF_BYCOMMAND|MF_UNCHECKED);\r
8777        }\r
8778   }\r
8779 }\r
8780 \r
8781 VOID\r
8782 SetICSMode()\r
8783 {\r
8784   HMENU hmenu = GetMenu(hwndMain);\r
8785   SetMenuEnables(hmenu, icsEnables);\r
8786   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8787     MF_BYPOSITION|MF_ENABLED);\r
8788 #ifdef ZIPPY\r
8789   if (appData.zippyPlay) {\r
8790     SetMenuEnables(hmenu, zippyEnables);\r
8791     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8792          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8793           MF_BYCOMMAND|MF_ENABLED);\r
8794   }\r
8795 #endif\r
8796 }\r
8797 \r
8798 VOID\r
8799 SetGNUMode()\r
8800 {\r
8801   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8802 }\r
8803 \r
8804 VOID\r
8805 SetNCPMode()\r
8806 {\r
8807   HMENU hmenu = GetMenu(hwndMain);\r
8808   SetMenuEnables(hmenu, ncpEnables);\r
8809   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8810     MF_BYPOSITION|MF_GRAYED);\r
8811     DrawMenuBar(hwndMain);\r
8812 }\r
8813 \r
8814 VOID\r
8815 SetCmailMode()\r
8816 {\r
8817   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8818 }\r
8819 \r
8820 VOID \r
8821 SetTrainingModeOn()\r
8822 {\r
8823   int i;\r
8824   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8825   for (i = 0; i < N_BUTTONS; i++) {\r
8826     if (buttonDesc[i].hwnd != NULL)\r
8827       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8828   }\r
8829   CommentPopDown();\r
8830 }\r
8831 \r
8832 VOID SetTrainingModeOff()\r
8833 {\r
8834   int i;\r
8835   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8836   for (i = 0; i < N_BUTTONS; i++) {\r
8837     if (buttonDesc[i].hwnd != NULL)\r
8838       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8839   }\r
8840 }\r
8841 \r
8842 \r
8843 VOID\r
8844 SetUserThinkingEnables()\r
8845 {\r
8846   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8847 }\r
8848 \r
8849 VOID\r
8850 SetMachineThinkingEnables()\r
8851 {\r
8852   HMENU hMenu = GetMenu(hwndMain);\r
8853   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8854 \r
8855   SetMenuEnables(hMenu, machineThinkingEnables);\r
8856 \r
8857   if (gameMode == MachinePlaysBlack) {\r
8858     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8859   } else if (gameMode == MachinePlaysWhite) {\r
8860     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8861   } else if (gameMode == TwoMachinesPlay) {\r
8862     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8863   }\r
8864 }\r
8865 \r
8866 \r
8867 VOID\r
8868 DisplayTitle(char *str)\r
8869 {\r
8870   char title[MSG_SIZ], *host;\r
8871   if (str[0] != NULLCHAR) {\r
8872     strcpy(title, str);\r
8873   } else if (appData.icsActive) {\r
8874     if (appData.icsCommPort[0] != NULLCHAR)\r
8875       host = "ICS";\r
8876     else \r
8877       host = appData.icsHost;\r
8878     sprintf(title, "%s: %s", szTitle, host);\r
8879   } else if (appData.noChessProgram) {\r
8880     strcpy(title, szTitle);\r
8881   } else {\r
8882     strcpy(title, szTitle);\r
8883     strcat(title, ": ");\r
8884     strcat(title, first.tidy);\r
8885   }\r
8886   SetWindowText(hwndMain, title);\r
8887 }\r
8888 \r
8889 \r
8890 VOID\r
8891 DisplayMessage(char *str1, char *str2)\r
8892 {\r
8893   HDC hdc;\r
8894   HFONT oldFont;\r
8895   int remain = MESSAGE_TEXT_MAX - 1;\r
8896   int len;\r
8897 \r
8898   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8899   messageText[0] = NULLCHAR;\r
8900   if (*str1) {\r
8901     len = strlen(str1);\r
8902     if (len > remain) len = remain;\r
8903     strncpy(messageText, str1, len);\r
8904     messageText[len] = NULLCHAR;\r
8905     remain -= len;\r
8906   }\r
8907   if (*str2 && remain >= 2) {\r
8908     if (*str1) {\r
8909       strcat(messageText, "  ");\r
8910       remain -= 2;\r
8911     }\r
8912     len = strlen(str2);\r
8913     if (len > remain) len = remain;\r
8914     strncat(messageText, str2, len);\r
8915   }\r
8916   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8917 \r
8918   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8919   hdc = GetDC(hwndMain);\r
8920   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8921   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8922              &messageRect, messageText, strlen(messageText), NULL);\r
8923   (void) SelectObject(hdc, oldFont);\r
8924   (void) ReleaseDC(hwndMain, hdc);\r
8925 }\r
8926 \r
8927 VOID\r
8928 DisplayError(char *str, int error)\r
8929 {\r
8930   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8931   int len;\r
8932 \r
8933   if (error == 0) {\r
8934     strcpy(buf, str);\r
8935   } else {\r
8936     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8937                         NULL, error, LANG_NEUTRAL,\r
8938                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8939     if (len > 0) {\r
8940       sprintf(buf, "%s:\n%s", str, buf2);\r
8941     } else {\r
8942       ErrorMap *em = errmap;\r
8943       while (em->err != 0 && em->err != error) em++;\r
8944       if (em->err != 0) {\r
8945         sprintf(buf, "%s:\n%s", str, em->msg);\r
8946       } else {\r
8947         sprintf(buf, "%s:\nError code %d", str, error);\r
8948       }\r
8949     }\r
8950   }\r
8951   \r
8952   ErrorPopUp("Error", buf);\r
8953 }\r
8954 \r
8955 \r
8956 VOID\r
8957 DisplayMoveError(char *str)\r
8958 {\r
8959   fromX = fromY = -1;\r
8960   ClearHighlights();\r
8961   DrawPosition(FALSE, NULL);\r
8962   if (appData.popupMoveErrors) {\r
8963     ErrorPopUp("Error", str);\r
8964   } else {\r
8965     DisplayMessage(str, "");\r
8966     moveErrorMessageUp = TRUE;\r
8967   }\r
8968 }\r
8969 \r
8970 VOID\r
8971 DisplayFatalError(char *str, int error, int exitStatus)\r
8972 {\r
8973   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8974   int len;\r
8975   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8976 \r
8977   if (error != 0) {\r
8978     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8979                         NULL, error, LANG_NEUTRAL,\r
8980                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8981     if (len > 0) {\r
8982       sprintf(buf, "%s:\n%s", str, buf2);\r
8983     } else {\r
8984       ErrorMap *em = errmap;\r
8985       while (em->err != 0 && em->err != error) em++;\r
8986       if (em->err != 0) {\r
8987         sprintf(buf, "%s:\n%s", str, em->msg);\r
8988       } else {\r
8989         sprintf(buf, "%s:\nError code %d", str, error);\r
8990       }\r
8991     }\r
8992     str = buf;\r
8993   }\r
8994   if (appData.debugMode) {\r
8995     fprintf(debugFP, "%s: %s\n", label, str);\r
8996   }\r
8997   if (appData.popupExitMessage) {\r
8998     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8999                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9000   }\r
9001   ExitEvent(exitStatus);\r
9002 }\r
9003 \r
9004 \r
9005 VOID\r
9006 DisplayInformation(char *str)\r
9007 {\r
9008   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9009 }\r
9010 \r
9011 \r
9012 VOID\r
9013 DisplayNote(char *str)\r
9014 {\r
9015   ErrorPopUp("Note", str);\r
9016 }\r
9017 \r
9018 \r
9019 typedef struct {\r
9020   char *title, *question, *replyPrefix;\r
9021   ProcRef pr;\r
9022 } QuestionParams;\r
9023 \r
9024 LRESULT CALLBACK\r
9025 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9026 {\r
9027   static QuestionParams *qp;\r
9028   char reply[MSG_SIZ];\r
9029   int len, err;\r
9030 \r
9031   switch (message) {\r
9032   case WM_INITDIALOG:\r
9033     qp = (QuestionParams *) lParam;\r
9034     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9035     SetWindowText(hDlg, qp->title);\r
9036     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9037     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9038     return FALSE;\r
9039 \r
9040   case WM_COMMAND:\r
9041     switch (LOWORD(wParam)) {\r
9042     case IDOK:\r
9043       strcpy(reply, qp->replyPrefix);\r
9044       if (*reply) strcat(reply, " ");\r
9045       len = strlen(reply);\r
9046       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9047       strcat(reply, "\n");\r
9048       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9049       EndDialog(hDlg, TRUE);\r
9050       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9051       return TRUE;\r
9052     case IDCANCEL:\r
9053       EndDialog(hDlg, FALSE);\r
9054       return TRUE;\r
9055     default:\r
9056       break;\r
9057     }\r
9058     break;\r
9059   }\r
9060   return FALSE;\r
9061 }\r
9062 \r
9063 VOID\r
9064 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9065 {\r
9066     QuestionParams qp;\r
9067     FARPROC lpProc;\r
9068     \r
9069     qp.title = title;\r
9070     qp.question = question;\r
9071     qp.replyPrefix = replyPrefix;\r
9072     qp.pr = pr;\r
9073     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9074     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9075       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9076     FreeProcInstance(lpProc);\r
9077 }\r
9078 \r
9079 /* [AS] Pick FRC position */\r
9080 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9081 {\r
9082     static int * lpIndexFRC;\r
9083     BOOL index_is_ok;\r
9084     char buf[16];\r
9085 \r
9086     switch( message )\r
9087     {\r
9088     case WM_INITDIALOG:\r
9089         lpIndexFRC = (int *) lParam;\r
9090 \r
9091         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9092 \r
9093         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9094         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9095         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9096         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9097 \r
9098         break;\r
9099 \r
9100     case WM_COMMAND:\r
9101         switch( LOWORD(wParam) ) {\r
9102         case IDOK:\r
9103             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9104             EndDialog( hDlg, 0 );\r
9105             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9106             return TRUE;\r
9107         case IDCANCEL:\r
9108             EndDialog( hDlg, 1 );   \r
9109             return TRUE;\r
9110         case IDC_NFG_Edit:\r
9111             if( HIWORD(wParam) == EN_CHANGE ) {\r
9112                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9113 \r
9114                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9115             }\r
9116             return TRUE;\r
9117         case IDC_NFG_Random:\r
9118             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9119             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9120             return TRUE;\r
9121         }\r
9122 \r
9123         break;\r
9124     }\r
9125 \r
9126     return FALSE;\r
9127 }\r
9128 \r
9129 int NewGameFRC()\r
9130 {\r
9131     int result;\r
9132     int index = appData.defaultFrcPosition;\r
9133     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9134 \r
9135     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9136 \r
9137     if( result == 0 ) {\r
9138         appData.defaultFrcPosition = index;\r
9139     }\r
9140 \r
9141     return result;\r
9142 }\r
9143 \r
9144 /* [AS] Game list options */\r
9145 typedef struct {\r
9146     char id;\r
9147     char * name;\r
9148 } GLT_Item;\r
9149 \r
9150 static GLT_Item GLT_ItemInfo[] = {\r
9151     { GLT_EVENT,      "Event" },\r
9152     { GLT_SITE,       "Site" },\r
9153     { GLT_DATE,       "Date" },\r
9154     { GLT_ROUND,      "Round" },\r
9155     { GLT_PLAYERS,    "Players" },\r
9156     { GLT_RESULT,     "Result" },\r
9157     { GLT_WHITE_ELO,  "White Rating" },\r
9158     { GLT_BLACK_ELO,  "Black Rating" },\r
9159     { GLT_TIME_CONTROL,"Time Control" },\r
9160     { GLT_VARIANT,    "Variant" },\r
9161     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9162     { 0, 0 }\r
9163 };\r
9164 \r
9165 const char * GLT_FindItem( char id )\r
9166 {\r
9167     const char * result = 0;\r
9168 \r
9169     GLT_Item * list = GLT_ItemInfo;\r
9170 \r
9171     while( list->id != 0 ) {\r
9172         if( list->id == id ) {\r
9173             result = list->name;\r
9174             break;\r
9175         }\r
9176 \r
9177         list++;\r
9178     }\r
9179 \r
9180     return result;\r
9181 }\r
9182 \r
9183 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9184 {\r
9185     const char * name = GLT_FindItem( id );\r
9186 \r
9187     if( name != 0 ) {\r
9188         if( index >= 0 ) {\r
9189             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9190         }\r
9191         else {\r
9192             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9193         }\r
9194     }\r
9195 }\r
9196 \r
9197 void GLT_TagsToList( HWND hDlg, char * tags )\r
9198 {\r
9199     char * pc = tags;\r
9200 \r
9201     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9202 \r
9203     while( *pc ) {\r
9204         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9205         pc++;\r
9206     }\r
9207 \r
9208     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9209 \r
9210     pc = GLT_ALL_TAGS;\r
9211 \r
9212     while( *pc ) {\r
9213         if( strchr( tags, *pc ) == 0 ) {\r
9214             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9215         }\r
9216         pc++;\r
9217     }\r
9218 \r
9219     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9220 }\r
9221 \r
9222 char GLT_ListItemToTag( HWND hDlg, int index )\r
9223 {\r
9224     char result = '\0';\r
9225     char name[128];\r
9226 \r
9227     GLT_Item * list = GLT_ItemInfo;\r
9228 \r
9229     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9230         while( list->id != 0 ) {\r
9231             if( strcmp( list->name, name ) == 0 ) {\r
9232                 result = list->id;\r
9233                 break;\r
9234             }\r
9235 \r
9236             list++;\r
9237         }\r
9238     }\r
9239 \r
9240     return result;\r
9241 }\r
9242 \r
9243 void GLT_MoveSelection( HWND hDlg, int delta )\r
9244 {\r
9245     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9246     int idx2 = idx1 + delta;\r
9247     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9248 \r
9249     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9250         char buf[128];\r
9251 \r
9252         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9253         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9254         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9255         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9256     }\r
9257 }\r
9258 \r
9259 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9260 {\r
9261     static char glt[64];\r
9262     static char * lpUserGLT;\r
9263 \r
9264     switch( message )\r
9265     {\r
9266     case WM_INITDIALOG:\r
9267         lpUserGLT = (char *) lParam;\r
9268         \r
9269         strcpy( glt, lpUserGLT );\r
9270 \r
9271         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9272 \r
9273         /* Initialize list */\r
9274         GLT_TagsToList( hDlg, glt );\r
9275 \r
9276         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9277 \r
9278         break;\r
9279 \r
9280     case WM_COMMAND:\r
9281         switch( LOWORD(wParam) ) {\r
9282         case IDOK:\r
9283             {\r
9284                 char * pc = lpUserGLT;\r
9285                 int idx = 0;\r
9286 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9287                 char id;\r
9288 \r
9289                 do {\r
9290                     id = GLT_ListItemToTag( hDlg, idx );\r
9291 \r
9292                     *pc++ = id;\r
9293                     idx++;\r
9294                 } while( id != '\0' );\r
9295             }\r
9296             EndDialog( hDlg, 0 );\r
9297             return TRUE;\r
9298         case IDCANCEL:\r
9299             EndDialog( hDlg, 1 );\r
9300             return TRUE;\r
9301 \r
9302         case IDC_GLT_Default:\r
9303             strcpy( glt, GLT_DEFAULT_TAGS );\r
9304             GLT_TagsToList( hDlg, glt );\r
9305             return TRUE;\r
9306 \r
9307         case IDC_GLT_Restore:\r
9308             strcpy( glt, lpUserGLT );\r
9309             GLT_TagsToList( hDlg, glt );\r
9310             return TRUE;\r
9311 \r
9312         case IDC_GLT_Up:\r
9313             GLT_MoveSelection( hDlg, -1 );\r
9314             return TRUE;\r
9315 \r
9316         case IDC_GLT_Down:\r
9317             GLT_MoveSelection( hDlg, +1 );\r
9318             return TRUE;\r
9319         }\r
9320 \r
9321         break;\r
9322     }\r
9323 \r
9324     return FALSE;\r
9325 }\r
9326 \r
9327 int GameListOptions()\r
9328 {\r
9329     char glt[64];\r
9330     int result;\r
9331     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9332 \r
9333     strcpy( glt, appData.gameListTags );\r
9334 \r
9335     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9336 \r
9337     if( result == 0 ) {\r
9338         /* [AS] Memory leak here! */\r
9339         appData.gameListTags = strdup( glt ); \r
9340     }\r
9341 \r
9342     return result;\r
9343 }\r
9344 \r
9345 \r
9346 VOID\r
9347 DisplayIcsInteractionTitle(char *str)\r
9348 {\r
9349   char consoleTitle[MSG_SIZ];\r
9350 \r
9351   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9352   SetWindowText(hwndConsole, consoleTitle);\r
9353 }\r
9354 \r
9355 void\r
9356 DrawPosition(int fullRedraw, Board board)\r
9357 {\r
9358   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9359 }\r
9360 \r
9361 \r
9362 VOID\r
9363 ResetFrontEnd()\r
9364 {\r
9365   fromX = fromY = -1;\r
9366   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9367     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9368     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9369     dragInfo.lastpos = dragInfo.pos;\r
9370     dragInfo.start.x = dragInfo.start.y = -1;\r
9371     dragInfo.from = dragInfo.start;\r
9372     ReleaseCapture();\r
9373     DrawPosition(TRUE, NULL);\r
9374   }\r
9375 }\r
9376 \r
9377 \r
9378 VOID\r
9379 CommentPopUp(char *title, char *str)\r
9380 {\r
9381   HWND hwnd = GetActiveWindow();\r
9382   EitherCommentPopUp(0, title, str, FALSE);\r
9383   SetActiveWindow(hwnd);\r
9384 }\r
9385 \r
9386 VOID\r
9387 CommentPopDown(void)\r
9388 {\r
9389   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9390   if (commentDialog) {\r
9391     ShowWindow(commentDialog, SW_HIDE);\r
9392   }\r
9393   commentDialogUp = FALSE;\r
9394 }\r
9395 \r
9396 VOID\r
9397 EditCommentPopUp(int index, char *title, char *str)\r
9398 {\r
9399   EitherCommentPopUp(index, title, str, TRUE);\r
9400 }\r
9401 \r
9402 \r
9403 VOID\r
9404 RingBell()\r
9405 {\r
9406   MyPlaySound(&sounds[(int)SoundMove]);\r
9407 }\r
9408 \r
9409 VOID PlayIcsWinSound()\r
9410 {\r
9411   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9412 }\r
9413 \r
9414 VOID PlayIcsLossSound()\r
9415 {\r
9416   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9417 }\r
9418 \r
9419 VOID PlayIcsDrawSound()\r
9420 {\r
9421   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9422 }\r
9423 \r
9424 VOID PlayIcsUnfinishedSound()\r
9425 {\r
9426   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9427 }\r
9428 \r
9429 VOID\r
9430 PlayAlarmSound()\r
9431 {\r
9432   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9433 }\r
9434 \r
9435 \r
9436 VOID\r
9437 EchoOn()\r
9438 {\r
9439   HWND hInput;\r
9440   consoleEcho = TRUE;\r
9441   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9442   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9443   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9444 }\r
9445 \r
9446 \r
9447 VOID\r
9448 EchoOff()\r
9449 {\r
9450   CHARFORMAT cf;\r
9451   HWND hInput;\r
9452   consoleEcho = FALSE;\r
9453   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9454   /* This works OK: set text and background both to the same color */\r
9455   cf = consoleCF;\r
9456   cf.crTextColor = COLOR_ECHOOFF;\r
9457   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9458   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9459 }\r
9460 \r
9461 /* No Raw()...? */\r
9462 \r
9463 void Colorize(ColorClass cc, int continuation)\r
9464 {\r
9465   currentColorClass = cc;\r
9466   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9467   consoleCF.crTextColor = textAttribs[cc].color;\r
9468   consoleCF.dwEffects = textAttribs[cc].effects;\r
9469   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9470 }\r
9471 \r
9472 char *\r
9473 UserName()\r
9474 {\r
9475   static char buf[MSG_SIZ];\r
9476   DWORD bufsiz = MSG_SIZ;\r
9477 \r
9478   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9479         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9480   }\r
9481   if (!GetUserName(buf, &bufsiz)) {\r
9482     /*DisplayError("Error getting user name", GetLastError());*/\r
9483     strcpy(buf, "User");\r
9484   }\r
9485   return buf;\r
9486 }\r
9487 \r
9488 char *\r
9489 HostName()\r
9490 {\r
9491   static char buf[MSG_SIZ];\r
9492   DWORD bufsiz = MSG_SIZ;\r
9493 \r
9494   if (!GetComputerName(buf, &bufsiz)) {\r
9495     /*DisplayError("Error getting host name", GetLastError());*/\r
9496     strcpy(buf, "Unknown");\r
9497   }\r
9498   return buf;\r
9499 }\r
9500 \r
9501 \r
9502 int\r
9503 ClockTimerRunning()\r
9504 {\r
9505   return clockTimerEvent != 0;\r
9506 }\r
9507 \r
9508 int\r
9509 StopClockTimer()\r
9510 {\r
9511   if (clockTimerEvent == 0) return FALSE;\r
9512   KillTimer(hwndMain, clockTimerEvent);\r
9513   clockTimerEvent = 0;\r
9514   return TRUE;\r
9515 }\r
9516 \r
9517 void\r
9518 StartClockTimer(long millisec)\r
9519 {\r
9520   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9521                              (UINT) millisec, NULL);\r
9522 }\r
9523 \r
9524 void\r
9525 DisplayWhiteClock(long timeRemaining, int highlight)\r
9526 {\r
9527   HDC hdc;\r
9528   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9529 \r
9530   if(appData.noGUI) return;\r
9531   hdc = GetDC(hwndMain);\r
9532   if (!IsIconic(hwndMain)) {\r
9533     DisplayAClock(hdc, timeRemaining, highlight, \r
9534                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9535   }\r
9536   if (highlight && iconCurrent == iconBlack) {\r
9537     iconCurrent = iconWhite;\r
9538     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9539     if (IsIconic(hwndMain)) {\r
9540       DrawIcon(hdc, 2, 2, iconCurrent);\r
9541     }\r
9542   }\r
9543   (void) ReleaseDC(hwndMain, hdc);\r
9544   if (hwndConsole)\r
9545     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9546 }\r
9547 \r
9548 void\r
9549 DisplayBlackClock(long timeRemaining, int highlight)\r
9550 {\r
9551   HDC hdc;\r
9552   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9553 \r
9554   if(appData.noGUI) return;\r
9555   hdc = GetDC(hwndMain);\r
9556   if (!IsIconic(hwndMain)) {\r
9557     DisplayAClock(hdc, timeRemaining, highlight, \r
9558                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9559   }\r
9560   if (highlight && iconCurrent == iconWhite) {\r
9561     iconCurrent = iconBlack;\r
9562     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9563     if (IsIconic(hwndMain)) {\r
9564       DrawIcon(hdc, 2, 2, iconCurrent);\r
9565     }\r
9566   }\r
9567   (void) ReleaseDC(hwndMain, hdc);\r
9568   if (hwndConsole)\r
9569     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9570 }\r
9571 \r
9572 \r
9573 int\r
9574 LoadGameTimerRunning()\r
9575 {\r
9576   return loadGameTimerEvent != 0;\r
9577 }\r
9578 \r
9579 int\r
9580 StopLoadGameTimer()\r
9581 {\r
9582   if (loadGameTimerEvent == 0) return FALSE;\r
9583   KillTimer(hwndMain, loadGameTimerEvent);\r
9584   loadGameTimerEvent = 0;\r
9585   return TRUE;\r
9586 }\r
9587 \r
9588 void\r
9589 StartLoadGameTimer(long millisec)\r
9590 {\r
9591   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9592                                 (UINT) millisec, NULL);\r
9593 }\r
9594 \r
9595 void\r
9596 AutoSaveGame()\r
9597 {\r
9598   char *defName;\r
9599   FILE *f;\r
9600   char fileTitle[MSG_SIZ];\r
9601 \r
9602   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9603   f = OpenFileDialog(hwndMain, "a", defName,\r
9604                      appData.oldSaveStyle ? "gam" : "pgn",\r
9605                      GAME_FILT, \r
9606                      "Save Game to File", NULL, fileTitle, NULL);\r
9607   if (f != NULL) {\r
9608     SaveGame(f, 0, "");\r
9609     fclose(f);\r
9610   }\r
9611 }\r
9612 \r
9613 \r
9614 void\r
9615 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9616 {\r
9617   if (delayedTimerEvent != 0) {\r
9618     if (appData.debugMode) {\r
9619       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9620     }\r
9621     KillTimer(hwndMain, delayedTimerEvent);\r
9622     delayedTimerEvent = 0;\r
9623     delayedTimerCallback();\r
9624   }\r
9625   delayedTimerCallback = cb;\r
9626   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9627                                 (UINT) millisec, NULL);\r
9628 }\r
9629 \r
9630 DelayedEventCallback\r
9631 GetDelayedEvent()\r
9632 {\r
9633   if (delayedTimerEvent) {\r
9634     return delayedTimerCallback;\r
9635   } else {\r
9636     return NULL;\r
9637   }\r
9638 }\r
9639 \r
9640 void\r
9641 CancelDelayedEvent()\r
9642 {\r
9643   if (delayedTimerEvent) {\r
9644     KillTimer(hwndMain, delayedTimerEvent);\r
9645     delayedTimerEvent = 0;\r
9646   }\r
9647 }\r
9648 \r
9649 DWORD GetWin32Priority(int nice)\r
9650 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9651 /*\r
9652 REALTIME_PRIORITY_CLASS     0x00000100\r
9653 HIGH_PRIORITY_CLASS         0x00000080\r
9654 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9655 NORMAL_PRIORITY_CLASS       0x00000020\r
9656 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9657 IDLE_PRIORITY_CLASS         0x00000040\r
9658 */\r
9659         if (nice < -15) return 0x00000080;\r
9660         if (nice < 0)   return 0x00008000;\r
9661         if (nice == 0)  return 0x00000020;\r
9662         if (nice < 15)  return 0x00004000;\r
9663         return 0x00000040;\r
9664 }\r
9665 \r
9666 /* Start a child process running the given program.\r
9667    The process's standard output can be read from "from", and its\r
9668    standard input can be written to "to".\r
9669    Exit with fatal error if anything goes wrong.\r
9670    Returns an opaque pointer that can be used to destroy the process\r
9671    later.\r
9672 */\r
9673 int\r
9674 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9675 {\r
9676 #define BUFSIZE 4096\r
9677 \r
9678   HANDLE hChildStdinRd, hChildStdinWr,\r
9679     hChildStdoutRd, hChildStdoutWr;\r
9680   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9681   SECURITY_ATTRIBUTES saAttr;\r
9682   BOOL fSuccess;\r
9683   PROCESS_INFORMATION piProcInfo;\r
9684   STARTUPINFO siStartInfo;\r
9685   ChildProc *cp;\r
9686   char buf[MSG_SIZ];\r
9687   DWORD err;\r
9688 \r
9689   if (appData.debugMode) {\r
9690     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9691   }\r
9692 \r
9693   *pr = NoProc;\r
9694 \r
9695   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9696   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9697   saAttr.bInheritHandle = TRUE;\r
9698   saAttr.lpSecurityDescriptor = NULL;\r
9699 \r
9700   /*\r
9701    * The steps for redirecting child's STDOUT:\r
9702    *     1. Create anonymous pipe to be STDOUT for child.\r
9703    *     2. Create a noninheritable duplicate of read handle,\r
9704    *         and close the inheritable read handle.\r
9705    */\r
9706 \r
9707   /* Create a pipe for the child's STDOUT. */\r
9708   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9709     return GetLastError();\r
9710   }\r
9711 \r
9712   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9713   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9714                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9715                              FALSE,     /* not inherited */\r
9716                              DUPLICATE_SAME_ACCESS);\r
9717   if (! fSuccess) {\r
9718     return GetLastError();\r
9719   }\r
9720   CloseHandle(hChildStdoutRd);\r
9721 \r
9722   /*\r
9723    * The steps for redirecting child's STDIN:\r
9724    *     1. Create anonymous pipe to be STDIN for child.\r
9725    *     2. Create a noninheritable duplicate of write handle,\r
9726    *         and close the inheritable write handle.\r
9727    */\r
9728 \r
9729   /* Create a pipe for the child's STDIN. */\r
9730   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9731     return GetLastError();\r
9732   }\r
9733 \r
9734   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9735   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9736                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9737                              FALSE,     /* not inherited */\r
9738                              DUPLICATE_SAME_ACCESS);\r
9739   if (! fSuccess) {\r
9740     return GetLastError();\r
9741   }\r
9742   CloseHandle(hChildStdinWr);\r
9743 \r
9744   /* Arrange to (1) look in dir for the child .exe file, and\r
9745    * (2) have dir be the child's working directory.  Interpret\r
9746    * dir relative to the directory WinBoard loaded from. */\r
9747   GetCurrentDirectory(MSG_SIZ, buf);\r
9748   SetCurrentDirectory(installDir);\r
9749   SetCurrentDirectory(dir);\r
9750 \r
9751   /* Now create the child process. */\r
9752 \r
9753   siStartInfo.cb = sizeof(STARTUPINFO);\r
9754   siStartInfo.lpReserved = NULL;\r
9755   siStartInfo.lpDesktop = NULL;\r
9756   siStartInfo.lpTitle = NULL;\r
9757   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9758   siStartInfo.cbReserved2 = 0;\r
9759   siStartInfo.lpReserved2 = NULL;\r
9760   siStartInfo.hStdInput = hChildStdinRd;\r
9761   siStartInfo.hStdOutput = hChildStdoutWr;\r
9762   siStartInfo.hStdError = hChildStdoutWr;\r
9763 \r
9764   fSuccess = CreateProcess(NULL,\r
9765                            cmdLine,        /* command line */\r
9766                            NULL,           /* process security attributes */\r
9767                            NULL,           /* primary thread security attrs */\r
9768                            TRUE,           /* handles are inherited */\r
9769                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9770                            NULL,           /* use parent's environment */\r
9771                            NULL,\r
9772                            &siStartInfo, /* STARTUPINFO pointer */\r
9773                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9774 \r
9775   err = GetLastError();\r
9776   SetCurrentDirectory(buf); /* return to prev directory */\r
9777   if (! fSuccess) {\r
9778     return err;\r
9779   }\r
9780 \r
9781   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9782     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9783     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9784   }\r
9785 \r
9786   /* Close the handles we don't need in the parent */\r
9787   CloseHandle(piProcInfo.hThread);\r
9788   CloseHandle(hChildStdinRd);\r
9789   CloseHandle(hChildStdoutWr);\r
9790 \r
9791   /* Prepare return value */\r
9792   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9793   cp->kind = CPReal;\r
9794   cp->hProcess = piProcInfo.hProcess;\r
9795   cp->pid = piProcInfo.dwProcessId;\r
9796   cp->hFrom = hChildStdoutRdDup;\r
9797   cp->hTo = hChildStdinWrDup;\r
9798 \r
9799   *pr = (void *) cp;\r
9800 \r
9801   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9802      2000 where engines sometimes don't see the initial command(s)\r
9803      from WinBoard and hang.  I don't understand how that can happen,\r
9804      but the Sleep is harmless, so I've put it in.  Others have also\r
9805      reported what may be the same problem, so hopefully this will fix\r
9806      it for them too.  */\r
9807   Sleep(500);\r
9808 \r
9809   return NO_ERROR;\r
9810 }\r
9811 \r
9812 \r
9813 void\r
9814 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9815 {\r
9816   ChildProc *cp; int result;\r
9817 \r
9818   cp = (ChildProc *) pr;\r
9819   if (cp == NULL) return;\r
9820 \r
9821   switch (cp->kind) {\r
9822   case CPReal:\r
9823     /* TerminateProcess is considered harmful, so... */\r
9824     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9825     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9826     /* The following doesn't work because the chess program\r
9827        doesn't "have the same console" as WinBoard.  Maybe\r
9828        we could arrange for this even though neither WinBoard\r
9829        nor the chess program uses a console for stdio? */\r
9830     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9831 \r
9832     /* [AS] Special termination modes for misbehaving programs... */\r
9833     if( signal == 9 ) { \r
9834         result = TerminateProcess( cp->hProcess, 0 );\r
9835 \r
9836         if ( appData.debugMode) {\r
9837             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9838         }\r
9839     }\r
9840     else if( signal == 10 ) {\r
9841         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9842 \r
9843         if( dw != WAIT_OBJECT_0 ) {\r
9844             result = TerminateProcess( cp->hProcess, 0 );\r
9845 \r
9846             if ( appData.debugMode) {\r
9847                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9848             }\r
9849 \r
9850         }\r
9851     }\r
9852 \r
9853     CloseHandle(cp->hProcess);\r
9854     break;\r
9855 \r
9856   case CPComm:\r
9857     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9858     break;\r
9859 \r
9860   case CPSock:\r
9861     closesocket(cp->sock);\r
9862     WSACleanup();\r
9863     break;\r
9864 \r
9865   case CPRcmd:\r
9866     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9867     closesocket(cp->sock);\r
9868     closesocket(cp->sock2);\r
9869     WSACleanup();\r
9870     break;\r
9871   }\r
9872   free(cp);\r
9873 }\r
9874 \r
9875 void\r
9876 InterruptChildProcess(ProcRef pr)\r
9877 {\r
9878   ChildProc *cp;\r
9879 \r
9880   cp = (ChildProc *) pr;\r
9881   if (cp == NULL) return;\r
9882   switch (cp->kind) {\r
9883   case CPReal:\r
9884     /* The following doesn't work because the chess program\r
9885        doesn't "have the same console" as WinBoard.  Maybe\r
9886        we could arrange for this even though neither WinBoard\r
9887        nor the chess program uses a console for stdio */\r
9888     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9889     break;\r
9890 \r
9891   case CPComm:\r
9892   case CPSock:\r
9893     /* Can't interrupt */\r
9894     break;\r
9895 \r
9896   case CPRcmd:\r
9897     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9898     break;\r
9899   }\r
9900 }\r
9901 \r
9902 \r
9903 int\r
9904 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9905 {\r
9906   char cmdLine[MSG_SIZ];\r
9907 \r
9908   if (port[0] == NULLCHAR) {\r
9909     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9910   } else {\r
9911     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9912   }\r
9913   return StartChildProcess(cmdLine, "", pr);\r
9914 }\r
9915 \r
9916 \r
9917 /* Code to open TCP sockets */\r
9918 \r
9919 int\r
9920 OpenTCP(char *host, char *port, ProcRef *pr)\r
9921 {\r
9922   ChildProc *cp;\r
9923   int err;\r
9924   SOCKET s;\r
9925   struct sockaddr_in sa, mysa;\r
9926   struct hostent FAR *hp;\r
9927   unsigned short uport;\r
9928   WORD wVersionRequested;\r
9929   WSADATA wsaData;\r
9930 \r
9931   /* Initialize socket DLL */\r
9932   wVersionRequested = MAKEWORD(1, 1);\r
9933   err = WSAStartup(wVersionRequested, &wsaData);\r
9934   if (err != 0) return err;\r
9935 \r
9936   /* Make socket */\r
9937   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9938     err = WSAGetLastError();\r
9939     WSACleanup();\r
9940     return err;\r
9941   }\r
9942 \r
9943   /* Bind local address using (mostly) don't-care values.\r
9944    */\r
9945   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9946   mysa.sin_family = AF_INET;\r
9947   mysa.sin_addr.s_addr = INADDR_ANY;\r
9948   uport = (unsigned short) 0;\r
9949   mysa.sin_port = htons(uport);\r
9950   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9951       == SOCKET_ERROR) {\r
9952     err = WSAGetLastError();\r
9953     WSACleanup();\r
9954     return err;\r
9955   }\r
9956 \r
9957   /* Resolve remote host name */\r
9958   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9959   if (!(hp = gethostbyname(host))) {\r
9960     unsigned int b0, b1, b2, b3;\r
9961 \r
9962     err = WSAGetLastError();\r
9963 \r
9964     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9965       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9966       hp->h_addrtype = AF_INET;\r
9967       hp->h_length = 4;\r
9968       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9969       hp->h_addr_list[0] = (char *) malloc(4);\r
9970       hp->h_addr_list[0][0] = (char) b0;\r
9971       hp->h_addr_list[0][1] = (char) b1;\r
9972       hp->h_addr_list[0][2] = (char) b2;\r
9973       hp->h_addr_list[0][3] = (char) b3;\r
9974     } else {\r
9975       WSACleanup();\r
9976       return err;\r
9977     }\r
9978   }\r
9979   sa.sin_family = hp->h_addrtype;\r
9980   uport = (unsigned short) atoi(port);\r
9981   sa.sin_port = htons(uport);\r
9982   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9983 \r
9984   /* Make connection */\r
9985   if (connect(s, (struct sockaddr *) &sa,\r
9986               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9987     err = WSAGetLastError();\r
9988     WSACleanup();\r
9989     return err;\r
9990   }\r
9991 \r
9992   /* Prepare return value */\r
9993   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9994   cp->kind = CPSock;\r
9995   cp->sock = s;\r
9996   *pr = (ProcRef *) cp;\r
9997 \r
9998   return NO_ERROR;\r
9999 }\r
10000 \r
10001 int\r
10002 OpenCommPort(char *name, ProcRef *pr)\r
10003 {\r
10004   HANDLE h;\r
10005   COMMTIMEOUTS ct;\r
10006   ChildProc *cp;\r
10007   char fullname[MSG_SIZ];\r
10008 \r
10009   if (*name != '\\')\r
10010     sprintf(fullname, "\\\\.\\%s", name);\r
10011   else\r
10012     strcpy(fullname, name);\r
10013 \r
10014   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10015                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10016   if (h == (HANDLE) -1) {\r
10017     return GetLastError();\r
10018   }\r
10019   hCommPort = h;\r
10020 \r
10021   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10022 \r
10023   /* Accumulate characters until a 100ms pause, then parse */\r
10024   ct.ReadIntervalTimeout = 100;\r
10025   ct.ReadTotalTimeoutMultiplier = 0;\r
10026   ct.ReadTotalTimeoutConstant = 0;\r
10027   ct.WriteTotalTimeoutMultiplier = 0;\r
10028   ct.WriteTotalTimeoutConstant = 0;\r
10029   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10030 \r
10031   /* Prepare return value */\r
10032   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10033   cp->kind = CPComm;\r
10034   cp->hFrom = h;\r
10035   cp->hTo = h;\r
10036   *pr = (ProcRef *) cp;\r
10037 \r
10038   return NO_ERROR;\r
10039 }\r
10040 \r
10041 int\r
10042 OpenLoopback(ProcRef *pr)\r
10043 {\r
10044   DisplayFatalError("Not implemented", 0, 1);\r
10045   return NO_ERROR;\r
10046 }\r
10047 \r
10048 \r
10049 int\r
10050 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10051 {\r
10052   ChildProc *cp;\r
10053   int err;\r
10054   SOCKET s, s2, s3;\r
10055   struct sockaddr_in sa, mysa;\r
10056   struct hostent FAR *hp;\r
10057   unsigned short uport;\r
10058   WORD wVersionRequested;\r
10059   WSADATA wsaData;\r
10060   int fromPort;\r
10061   char stderrPortStr[MSG_SIZ];\r
10062 \r
10063   /* Initialize socket DLL */\r
10064   wVersionRequested = MAKEWORD(1, 1);\r
10065   err = WSAStartup(wVersionRequested, &wsaData);\r
10066   if (err != 0) return err;\r
10067 \r
10068   /* Resolve remote host name */\r
10069   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10070   if (!(hp = gethostbyname(host))) {\r
10071     unsigned int b0, b1, b2, b3;\r
10072 \r
10073     err = WSAGetLastError();\r
10074 \r
10075     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10076       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10077       hp->h_addrtype = AF_INET;\r
10078       hp->h_length = 4;\r
10079       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10080       hp->h_addr_list[0] = (char *) malloc(4);\r
10081       hp->h_addr_list[0][0] = (char) b0;\r
10082       hp->h_addr_list[0][1] = (char) b1;\r
10083       hp->h_addr_list[0][2] = (char) b2;\r
10084       hp->h_addr_list[0][3] = (char) b3;\r
10085     } else {\r
10086       WSACleanup();\r
10087       return err;\r
10088     }\r
10089   }\r
10090   sa.sin_family = hp->h_addrtype;\r
10091   uport = (unsigned short) 514;\r
10092   sa.sin_port = htons(uport);\r
10093   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10094 \r
10095   /* Bind local socket to unused "privileged" port address\r
10096    */\r
10097   s = INVALID_SOCKET;\r
10098   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10099   mysa.sin_family = AF_INET;\r
10100   mysa.sin_addr.s_addr = INADDR_ANY;\r
10101   for (fromPort = 1023;; fromPort--) {\r
10102     if (fromPort < 0) {\r
10103       WSACleanup();\r
10104       return WSAEADDRINUSE;\r
10105     }\r
10106     if (s == INVALID_SOCKET) {\r
10107       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10108         err = WSAGetLastError();\r
10109         WSACleanup();\r
10110         return err;\r
10111       }\r
10112     }\r
10113     uport = (unsigned short) fromPort;\r
10114     mysa.sin_port = htons(uport);\r
10115     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10116         == SOCKET_ERROR) {\r
10117       err = WSAGetLastError();\r
10118       if (err == WSAEADDRINUSE) continue;\r
10119       WSACleanup();\r
10120       return err;\r
10121     }\r
10122     if (connect(s, (struct sockaddr *) &sa,\r
10123       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10124       err = WSAGetLastError();\r
10125       if (err == WSAEADDRINUSE) {\r
10126         closesocket(s);\r
10127         s = -1;\r
10128         continue;\r
10129       }\r
10130       WSACleanup();\r
10131       return err;\r
10132     }\r
10133     break;\r
10134   }\r
10135 \r
10136   /* Bind stderr local socket to unused "privileged" port address\r
10137    */\r
10138   s2 = INVALID_SOCKET;\r
10139   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10140   mysa.sin_family = AF_INET;\r
10141   mysa.sin_addr.s_addr = INADDR_ANY;\r
10142   for (fromPort = 1023;; fromPort--) {\r
10143     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10144     if (fromPort < 0) {\r
10145       (void) closesocket(s);\r
10146       WSACleanup();\r
10147       return WSAEADDRINUSE;\r
10148     }\r
10149     if (s2 == INVALID_SOCKET) {\r
10150       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10151         err = WSAGetLastError();\r
10152         closesocket(s);\r
10153         WSACleanup();\r
10154         return err;\r
10155       }\r
10156     }\r
10157     uport = (unsigned short) fromPort;\r
10158     mysa.sin_port = htons(uport);\r
10159     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10160         == SOCKET_ERROR) {\r
10161       err = WSAGetLastError();\r
10162       if (err == WSAEADDRINUSE) continue;\r
10163       (void) closesocket(s);\r
10164       WSACleanup();\r
10165       return err;\r
10166     }\r
10167     if (listen(s2, 1) == SOCKET_ERROR) {\r
10168       err = WSAGetLastError();\r
10169       if (err == WSAEADDRINUSE) {\r
10170         closesocket(s2);\r
10171         s2 = INVALID_SOCKET;\r
10172         continue;\r
10173       }\r
10174       (void) closesocket(s);\r
10175       (void) closesocket(s2);\r
10176       WSACleanup();\r
10177       return err;\r
10178     }\r
10179     break;\r
10180   }\r
10181   prevStderrPort = fromPort; // remember port used\r
10182   sprintf(stderrPortStr, "%d", fromPort);\r
10183 \r
10184   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10185     err = WSAGetLastError();\r
10186     (void) closesocket(s);\r
10187     (void) closesocket(s2);\r
10188     WSACleanup();\r
10189     return err;\r
10190   }\r
10191 \r
10192   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10193     err = WSAGetLastError();\r
10194     (void) closesocket(s);\r
10195     (void) closesocket(s2);\r
10196     WSACleanup();\r
10197     return err;\r
10198   }\r
10199   if (*user == NULLCHAR) user = UserName();\r
10200   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10201     err = WSAGetLastError();\r
10202     (void) closesocket(s);\r
10203     (void) closesocket(s2);\r
10204     WSACleanup();\r
10205     return err;\r
10206   }\r
10207   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10208     err = WSAGetLastError();\r
10209     (void) closesocket(s);\r
10210     (void) closesocket(s2);\r
10211     WSACleanup();\r
10212     return err;\r
10213   }\r
10214 \r
10215   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10216     err = WSAGetLastError();\r
10217     (void) closesocket(s);\r
10218     (void) closesocket(s2);\r
10219     WSACleanup();\r
10220     return err;\r
10221   }\r
10222   (void) closesocket(s2);  /* Stop listening */\r
10223 \r
10224   /* Prepare return value */\r
10225   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10226   cp->kind = CPRcmd;\r
10227   cp->sock = s;\r
10228   cp->sock2 = s3;\r
10229   *pr = (ProcRef *) cp;\r
10230 \r
10231   return NO_ERROR;\r
10232 }\r
10233 \r
10234 \r
10235 InputSourceRef\r
10236 AddInputSource(ProcRef pr, int lineByLine,\r
10237                InputCallback func, VOIDSTAR closure)\r
10238 {\r
10239   InputSource *is, *is2 = NULL;\r
10240   ChildProc *cp = (ChildProc *) pr;\r
10241 \r
10242   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10243   is->lineByLine = lineByLine;\r
10244   is->func = func;\r
10245   is->closure = closure;\r
10246   is->second = NULL;\r
10247   is->next = is->buf;\r
10248   if (pr == NoProc) {\r
10249     is->kind = CPReal;\r
10250     consoleInputSource = is;\r
10251   } else {\r
10252     is->kind = cp->kind;\r
10253     /* \r
10254         [AS] Try to avoid a race condition if the thread is given control too early:\r
10255         we create all threads suspended so that the is->hThread variable can be\r
10256         safely assigned, then let the threads start with ResumeThread.\r
10257     */\r
10258     switch (cp->kind) {\r
10259     case CPReal:\r
10260       is->hFile = cp->hFrom;\r
10261       cp->hFrom = NULL; /* now owned by InputThread */\r
10262       is->hThread =\r
10263         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10264                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10265       break;\r
10266 \r
10267     case CPComm:\r
10268       is->hFile = cp->hFrom;\r
10269       cp->hFrom = NULL; /* now owned by InputThread */\r
10270       is->hThread =\r
10271         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10272                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10273       break;\r
10274 \r
10275     case CPSock:\r
10276       is->sock = cp->sock;\r
10277       is->hThread =\r
10278         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10279                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10280       break;\r
10281 \r
10282     case CPRcmd:\r
10283       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10284       *is2 = *is;\r
10285       is->sock = cp->sock;\r
10286       is->second = is2;\r
10287       is2->sock = cp->sock2;\r
10288       is2->second = is2;\r
10289       is->hThread =\r
10290         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10291                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10292       is2->hThread =\r
10293         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10294                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10295       break;\r
10296     }\r
10297 \r
10298     if( is->hThread != NULL ) {\r
10299         ResumeThread( is->hThread );\r
10300     }\r
10301 \r
10302     if( is2 != NULL && is2->hThread != NULL ) {\r
10303         ResumeThread( is2->hThread );\r
10304     }\r
10305   }\r
10306 \r
10307   return (InputSourceRef) is;\r
10308 }\r
10309 \r
10310 void\r
10311 RemoveInputSource(InputSourceRef isr)\r
10312 {\r
10313   InputSource *is;\r
10314 \r
10315   is = (InputSource *) isr;\r
10316   is->hThread = NULL;  /* tell thread to stop */\r
10317   CloseHandle(is->hThread);\r
10318   if (is->second != NULL) {\r
10319     is->second->hThread = NULL;\r
10320     CloseHandle(is->second->hThread);\r
10321   }\r
10322 }\r
10323 \r
10324 \r
10325 int\r
10326 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10327 {\r
10328   DWORD dOutCount;\r
10329   int outCount = SOCKET_ERROR;\r
10330   ChildProc *cp = (ChildProc *) pr;\r
10331   static OVERLAPPED ovl;\r
10332 \r
10333   if (pr == NoProc) {\r
10334     ConsoleOutput(message, count, FALSE);\r
10335     return count;\r
10336   } \r
10337 \r
10338   if (ovl.hEvent == NULL) {\r
10339     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10340   }\r
10341   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10342 \r
10343   switch (cp->kind) {\r
10344   case CPSock:\r
10345   case CPRcmd:\r
10346     outCount = send(cp->sock, message, count, 0);\r
10347     if (outCount == SOCKET_ERROR) {\r
10348       *outError = WSAGetLastError();\r
10349     } else {\r
10350       *outError = NO_ERROR;\r
10351     }\r
10352     break;\r
10353 \r
10354   case CPReal:\r
10355     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10356                   &dOutCount, NULL)) {\r
10357       *outError = NO_ERROR;\r
10358       outCount = (int) dOutCount;\r
10359     } else {\r
10360       *outError = GetLastError();\r
10361     }\r
10362     break;\r
10363 \r
10364   case CPComm:\r
10365     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10366                             &dOutCount, &ovl);\r
10367     if (*outError == NO_ERROR) {\r
10368       outCount = (int) dOutCount;\r
10369     }\r
10370     break;\r
10371   }\r
10372   return outCount;\r
10373 }\r
10374 \r
10375 int\r
10376 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10377                        long msdelay)\r
10378 {\r
10379   /* Ignore delay, not implemented for WinBoard */\r
10380   return OutputToProcess(pr, message, count, outError);\r
10381 }\r
10382 \r
10383 \r
10384 void\r
10385 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10386                         char *buf, int count, int error)\r
10387 {\r
10388   DisplayFatalError("Not implemented", 0, 1);\r
10389 }\r
10390 \r
10391 /* see wgamelist.c for Game List functions */\r
10392 /* see wedittags.c for Edit Tags functions */\r
10393 \r
10394 \r
10395 VOID\r
10396 ICSInitScript()\r
10397 {\r
10398   FILE *f;\r
10399   char buf[MSG_SIZ];\r
10400   char *dummy;\r
10401 \r
10402   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10403     f = fopen(buf, "r");\r
10404     if (f != NULL) {\r
10405       ProcessICSInitScript(f);\r
10406       fclose(f);\r
10407     }\r
10408   }\r
10409 }\r
10410 \r
10411 \r
10412 VOID\r
10413 StartAnalysisClock()\r
10414 {\r
10415   if (analysisTimerEvent) return;\r
10416   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10417                                         (UINT) 2000, NULL);\r
10418 }\r
10419 \r
10420 LRESULT CALLBACK\r
10421 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10422 {\r
10423   static HANDLE hwndText;\r
10424   RECT rect;\r
10425   static int sizeX, sizeY;\r
10426   int newSizeX, newSizeY, flags;\r
10427   MINMAXINFO *mmi;\r
10428 \r
10429   switch (message) {\r
10430   case WM_INITDIALOG: /* message: initialize dialog box */\r
10431     /* Initialize the dialog items */\r
10432     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10433     SetWindowText(hDlg, analysisTitle);\r
10434     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10435     /* Size and position the dialog */\r
10436     if (!analysisDialog) {\r
10437       analysisDialog = hDlg;\r
10438       flags = SWP_NOZORDER;\r
10439       GetClientRect(hDlg, &rect);\r
10440       sizeX = rect.right;\r
10441       sizeY = rect.bottom;\r
10442       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10443           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10444         WINDOWPLACEMENT wp;\r
10445         EnsureOnScreen(&analysisX, &analysisY);\r
10446         wp.length = sizeof(WINDOWPLACEMENT);\r
10447         wp.flags = 0;\r
10448         wp.showCmd = SW_SHOW;\r
10449         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10450         wp.rcNormalPosition.left = analysisX;\r
10451         wp.rcNormalPosition.right = analysisX + analysisW;\r
10452         wp.rcNormalPosition.top = analysisY;\r
10453         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10454         SetWindowPlacement(hDlg, &wp);\r
10455 \r
10456         GetClientRect(hDlg, &rect);\r
10457         newSizeX = rect.right;\r
10458         newSizeY = rect.bottom;\r
10459         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10460                               newSizeX, newSizeY);\r
10461         sizeX = newSizeX;\r
10462         sizeY = newSizeY;\r
10463       }\r
10464     }\r
10465     return FALSE;\r
10466 \r
10467   case WM_COMMAND: /* message: received a command */\r
10468     switch (LOWORD(wParam)) {\r
10469     case IDCANCEL:\r
10470       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10471           ExitAnalyzeMode();\r
10472           ModeHighlight();\r
10473           return TRUE;\r
10474       }\r
10475       EditGameEvent();\r
10476       return TRUE;\r
10477     default:\r
10478       break;\r
10479     }\r
10480     break;\r
10481 \r
10482   case WM_SIZE:\r
10483     newSizeX = LOWORD(lParam);\r
10484     newSizeY = HIWORD(lParam);\r
10485     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10486     sizeX = newSizeX;\r
10487     sizeY = newSizeY;\r
10488     break;\r
10489 \r
10490   case WM_GETMINMAXINFO:\r
10491     /* Prevent resizing window too small */\r
10492     mmi = (MINMAXINFO *) lParam;\r
10493     mmi->ptMinTrackSize.x = 100;\r
10494     mmi->ptMinTrackSize.y = 100;\r
10495     break;\r
10496   }\r
10497   return FALSE;\r
10498 }\r
10499 \r
10500 VOID\r
10501 AnalysisPopUp(char* title, char* str)\r
10502 {\r
10503   FARPROC lpProc;\r
10504   char *p, *q;\r
10505 \r
10506   /* [AS] */\r
10507   EngineOutputPopUp();\r
10508   return;\r
10509 \r
10510   if (str == NULL) str = "";\r
10511   p = (char *) malloc(2 * strlen(str) + 2);\r
10512   q = p;\r
10513   while (*str) {\r
10514     if (*str == '\n') *q++ = '\r';\r
10515     *q++ = *str++;\r
10516   }\r
10517   *q = NULLCHAR;\r
10518   if (analysisText != NULL) free(analysisText);\r
10519   analysisText = p;\r
10520 \r
10521   if (analysisDialog) {\r
10522     SetWindowText(analysisDialog, title);\r
10523     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10524     ShowWindow(analysisDialog, SW_SHOW);\r
10525   } else {\r
10526     analysisTitle = title;\r
10527     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10528     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10529                  hwndMain, (DLGPROC)lpProc);\r
10530     FreeProcInstance(lpProc);\r
10531   }\r
10532   analysisDialogUp = TRUE;  \r
10533 }\r
10534 \r
10535 VOID\r
10536 AnalysisPopDown()\r
10537 {\r
10538   if (analysisDialog) {\r
10539     ShowWindow(analysisDialog, SW_HIDE);\r
10540   }\r
10541   analysisDialogUp = FALSE;  \r
10542 }\r
10543 \r
10544 \r
10545 VOID\r
10546 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10547 {\r
10548   highlightInfo.sq[0].x = fromX;\r
10549   highlightInfo.sq[0].y = fromY;\r
10550   highlightInfo.sq[1].x = toX;\r
10551   highlightInfo.sq[1].y = toY;\r
10552 }\r
10553 \r
10554 VOID\r
10555 ClearHighlights()\r
10556 {\r
10557   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10558     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10559 }\r
10560 \r
10561 VOID\r
10562 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10563 {\r
10564   premoveHighlightInfo.sq[0].x = fromX;\r
10565   premoveHighlightInfo.sq[0].y = fromY;\r
10566   premoveHighlightInfo.sq[1].x = toX;\r
10567   premoveHighlightInfo.sq[1].y = toY;\r
10568 }\r
10569 \r
10570 VOID\r
10571 ClearPremoveHighlights()\r
10572 {\r
10573   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10574     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10575 }\r
10576 \r
10577 VOID\r
10578 ShutDownFrontEnd()\r
10579 {\r
10580   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10581   DeleteClipboardTempFiles();\r
10582 }\r
10583 \r
10584 void\r
10585 BoardToTop()\r
10586 {\r
10587     if (IsIconic(hwndMain))\r
10588       ShowWindow(hwndMain, SW_RESTORE);\r
10589 \r
10590     SetActiveWindow(hwndMain);\r
10591 }\r
10592 \r
10593 /*\r
10594  * Prototypes for animation support routines\r
10595  */\r
10596 static void ScreenSquare(int column, int row, POINT * pt);\r
10597 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10598      POINT frames[], int * nFrames);\r
10599 \r
10600 \r
10601 void\r
10602 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10603 {       // [HGM] atomic: animate blast wave\r
10604         int i;\r
10605 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10606         explodeInfo.fromX = fromX;\r
10607         explodeInfo.fromY = fromY;\r
10608         explodeInfo.toX = toX;\r
10609         explodeInfo.toY = toY;\r
10610         for(i=1; i<nFrames; i++) {\r
10611             explodeInfo.radius = (i*180)/(nFrames-1);\r
10612             DrawPosition(FALSE, NULL);\r
10613             Sleep(appData.animSpeed);\r
10614         }\r
10615         explodeInfo.radius = 0;\r
10616         DrawPosition(TRUE, NULL);\r
10617 }\r
10618 \r
10619 #define kFactor 4\r
10620 \r
10621 void\r
10622 AnimateMove(board, fromX, fromY, toX, toY)\r
10623      Board board;\r
10624      int fromX;\r
10625      int fromY;\r
10626      int toX;\r
10627      int toY;\r
10628 {\r
10629   ChessSquare piece;\r
10630   POINT start, finish, mid;\r
10631   POINT frames[kFactor * 2 + 1];\r
10632   int nFrames, n;\r
10633 \r
10634   if (!appData.animate) return;\r
10635   if (doingSizing) return;\r
10636   if (fromY < 0 || fromX < 0) return;\r
10637   piece = board[fromY][fromX];\r
10638   if (piece >= EmptySquare) return;\r
10639 \r
10640   ScreenSquare(fromX, fromY, &start);\r
10641   ScreenSquare(toX, toY, &finish);\r
10642 \r
10643   /* All pieces except knights move in straight line */\r
10644   if (piece != WhiteKnight && piece != BlackKnight) {\r
10645     mid.x = start.x + (finish.x - start.x) / 2;\r
10646     mid.y = start.y + (finish.y - start.y) / 2;\r
10647   } else {\r
10648     /* Knight: make diagonal movement then straight */\r
10649     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10650        mid.x = start.x + (finish.x - start.x) / 2;\r
10651        mid.y = finish.y;\r
10652      } else {\r
10653        mid.x = finish.x;\r
10654        mid.y = start.y + (finish.y - start.y) / 2;\r
10655      }\r
10656   }\r
10657   \r
10658   /* Don't use as many frames for very short moves */\r
10659   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10660     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10661   else\r
10662     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10663 \r
10664   animInfo.from.x = fromX;\r
10665   animInfo.from.y = fromY;\r
10666   animInfo.to.x = toX;\r
10667   animInfo.to.y = toY;\r
10668   animInfo.lastpos = start;\r
10669   animInfo.piece = piece;\r
10670   for (n = 0; n < nFrames; n++) {\r
10671     animInfo.pos = frames[n];\r
10672     DrawPosition(FALSE, NULL);\r
10673     animInfo.lastpos = animInfo.pos;\r
10674     Sleep(appData.animSpeed);\r
10675   }\r
10676   animInfo.pos = finish;\r
10677   DrawPosition(FALSE, NULL);\r
10678   animInfo.piece = EmptySquare;\r
10679   if(gameInfo.variant == VariantAtomic && \r
10680      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10681         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10682 }\r
10683 \r
10684 /*      Convert board position to corner of screen rect and color       */\r
10685 \r
10686 static void\r
10687 ScreenSquare(column, row, pt)\r
10688      int column; int row; POINT * pt;\r
10689 {\r
10690   if (flipView) {\r
10691     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10692     pt->y = lineGap + row * (squareSize + lineGap);\r
10693   } else {\r
10694     pt->x = lineGap + column * (squareSize + lineGap);\r
10695     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10696   }\r
10697 }\r
10698 \r
10699 /*      Generate a series of frame coords from start->mid->finish.\r
10700         The movement rate doubles until the half way point is\r
10701         reached, then halves back down to the final destination,\r
10702         which gives a nice slow in/out effect. The algorithmn\r
10703         may seem to generate too many intermediates for short\r
10704         moves, but remember that the purpose is to attract the\r
10705         viewers attention to the piece about to be moved and\r
10706         then to where it ends up. Too few frames would be less\r
10707         noticeable.                                             */\r
10708 \r
10709 static void\r
10710 Tween(start, mid, finish, factor, frames, nFrames)\r
10711      POINT * start; POINT * mid;\r
10712      POINT * finish; int factor;\r
10713      POINT frames[]; int * nFrames;\r
10714 {\r
10715   int n, fraction = 1, count = 0;\r
10716 \r
10717   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10718   for (n = 0; n < factor; n++)\r
10719     fraction *= 2;\r
10720   for (n = 0; n < factor; n++) {\r
10721     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10722     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10723     count ++;\r
10724     fraction = fraction / 2;\r
10725   }\r
10726   \r
10727   /* Midpoint */\r
10728   frames[count] = *mid;\r
10729   count ++;\r
10730   \r
10731   /* Slow out, stepping 1/2, then 1/4, ... */\r
10732   fraction = 2;\r
10733   for (n = 0; n < factor; n++) {\r
10734     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10735     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10736     count ++;\r
10737     fraction = fraction * 2;\r
10738   }\r
10739   *nFrames = count;\r
10740 }\r
10741 \r
10742 void\r
10743 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10744 {\r
10745 #if 0\r
10746     char buf[256];\r
10747 \r
10748     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10749         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10750 \r
10751     OutputDebugString( buf );\r
10752 #endif\r
10753 \r
10754     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10755 \r
10756     EvalGraphSet( first, last, current, pvInfoList );\r
10757 }\r
10758 \r
10759 void SetProgramStats( FrontEndProgramStats * stats )\r
10760 {\r
10761 #if 0\r
10762     char buf[1024];\r
10763 \r
10764     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10765         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10766 \r
10767     OutputDebugString( buf );\r
10768 #endif\r
10769 \r
10770     EngineOutputUpdate( stats );\r
10771 }\r