animate explosions in variant atomic (if animate moves is on).
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int toX, int toY, int nFrames);\r
107 \r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int x, y, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo = {0, 0, 0};\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "ICS Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
156 static int squareSize, lineGap, minorSize;\r
157 static int winWidth, winHeight;\r
158 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
159 static int logoHeight = 0;\r
160 static char messageText[MESSAGE_TEXT_MAX];\r
161 static int clockTimerEvent = 0;\r
162 static int loadGameTimerEvent = 0;\r
163 static int analysisTimerEvent = 0;\r
164 static DelayedEventCallback delayedTimerCallback;\r
165 static int delayedTimerEvent = 0;\r
166 static int buttonCount = 2;\r
167 char *icsTextMenuString;\r
168 char *icsNames;\r
169 char *firstChessProgramNames;\r
170 char *secondChessProgramNames;\r
171 \r
172 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
173 \r
174 #define PALETTESIZE 256\r
175 \r
176 HINSTANCE hInst;          /* current instance */\r
177 HWND hwndMain = NULL;        /* root window*/\r
178 HWND hwndConsole = NULL;\r
179 BOOLEAN alwaysOnTop = FALSE;\r
180 RECT boardRect;\r
181 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
182   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
183 HPALETTE hPal;\r
184 ColorClass currentColorClass;\r
185 \r
186 HWND hCommPort = NULL;    /* currently open comm port */\r
187 static HWND hwndPause;    /* pause button */\r
188 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
189 static HBRUSH lightSquareBrush, darkSquareBrush,\r
190   blackSquareBrush, /* [HGM] for band between board and holdings */\r
191   explodeBrush,     /* [HGM] atomic */\r
192   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
193 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
194 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
195 static HPEN gridPen = NULL;\r
196 static HPEN highlightPen = NULL;\r
197 static HPEN premovePen = NULL;\r
198 static NPLOGPALETTE pLogPal;\r
199 static BOOL paletteChanged = FALSE;\r
200 static HICON iconWhite, iconBlack, iconCurrent;\r
201 static int doingSizing = FALSE;\r
202 static int lastSizing = 0;\r
203 static int prevStderrPort;\r
204 \r
205 /* [AS] Support for background textures */\r
206 #define BACK_TEXTURE_MODE_DISABLED      0\r
207 #define BACK_TEXTURE_MODE_PLAIN         1\r
208 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
209 \r
210 static HBITMAP liteBackTexture = NULL;\r
211 static HBITMAP darkBackTexture = NULL;\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
214 static int backTextureSquareSize = 0;\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
216 \r
217 #if __GNUC__ && !defined(_winmajor)\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
219 #else\r
220 #define oldDialog (_winmajor < 4)\r
221 #endif\r
222 \r
223 char *defaultTextAttribs[] = \r
224 {\r
225   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
226   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
227   COLOR_NONE\r
228 };\r
229 \r
230 typedef struct {\r
231   char *name;\r
232   int squareSize;\r
233   int lineGap;\r
234   int smallLayout;\r
235   int tinyLayout;\r
236   int cliWidth, cliHeight;\r
237 } SizeInfo;\r
238 \r
239 SizeInfo sizeInfo[] = \r
240 {\r
241   { "tiny",     21, 0, 1, 1, 0, 0 },\r
242   { "teeny",    25, 1, 1, 1, 0, 0 },\r
243   { "dinky",    29, 1, 1, 1, 0, 0 },\r
244   { "petite",   33, 1, 1, 1, 0, 0 },\r
245   { "slim",     37, 2, 1, 0, 0, 0 },\r
246   { "small",    40, 2, 1, 0, 0, 0 },\r
247   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
248   { "middling", 49, 2, 0, 0, 0, 0 },\r
249   { "average",  54, 2, 0, 0, 0, 0 },\r
250   { "moderate", 58, 3, 0, 0, 0, 0 },\r
251   { "medium",   64, 3, 0, 0, 0, 0 },\r
252   { "bulky",    72, 3, 0, 0, 0, 0 },\r
253   { "large",    80, 3, 0, 0, 0, 0 },\r
254   { "big",      87, 3, 0, 0, 0, 0 },\r
255   { "huge",     95, 3, 0, 0, 0, 0 },\r
256   { "giant",    108, 3, 0, 0, 0, 0 },\r
257   { "colossal", 116, 4, 0, 0, 0, 0 },\r
258   { "titanic",  129, 4, 0, 0, 0, 0 },\r
259   { NULL, 0, 0, 0, 0, 0, 0 }\r
260 };\r
261 \r
262 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
263 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
264 {\r
265   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
266   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
267   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
268   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
269   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
270   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
283 };\r
284 \r
285 MyFont *font[NUM_SIZES][NUM_FONTS];\r
286 \r
287 typedef struct {\r
288   char *label;\r
289   int id;\r
290   HWND hwnd;\r
291   WNDPROC wndproc;\r
292 } MyButtonDesc;\r
293 \r
294 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
295 #define N_BUTTONS 5\r
296 \r
297 MyButtonDesc buttonDesc[N_BUTTONS] =\r
298 {\r
299   {"<<", IDM_ToStart, NULL, NULL},\r
300   {"<", IDM_Backward, NULL, NULL},\r
301   {"P", IDM_Pause, NULL, NULL},\r
302   {">", IDM_Forward, NULL, NULL},\r
303   {">>", IDM_ToEnd, NULL, NULL},\r
304 };\r
305 \r
306 int tinyLayout = 0, smallLayout = 0;\r
307 #define MENU_BAR_ITEMS 6\r
308 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
309   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
310   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
311 };\r
312 \r
313 \r
314 MySound sounds[(int)NSoundClasses];\r
315 MyTextAttribs textAttribs[(int)NColorClasses];\r
316 \r
317 MyColorizeAttribs colorizeAttribs[] = {\r
318   { (COLORREF)0, 0, "Shout Text" },\r
319   { (COLORREF)0, 0, "SShout/CShout" },\r
320   { (COLORREF)0, 0, "Channel 1 Text" },\r
321   { (COLORREF)0, 0, "Channel Text" },\r
322   { (COLORREF)0, 0, "Kibitz Text" },\r
323   { (COLORREF)0, 0, "Tell Text" },\r
324   { (COLORREF)0, 0, "Challenge Text" },\r
325   { (COLORREF)0, 0, "Request Text" },\r
326   { (COLORREF)0, 0, "Seek Text" },\r
327   { (COLORREF)0, 0, "Normal Text" },\r
328   { (COLORREF)0, 0, "None" }\r
329 };\r
330 \r
331 \r
332 \r
333 static char *commentTitle;\r
334 static char *commentText;\r
335 static int commentIndex;\r
336 static Boolean editComment = FALSE;\r
337 HWND commentDialog = NULL;\r
338 BOOLEAN commentDialogUp = FALSE;\r
339 static int commentX, commentY, commentH, commentW;\r
340 \r
341 static char *analysisTitle;\r
342 static char *analysisText;\r
343 HWND analysisDialog = NULL;\r
344 BOOLEAN analysisDialogUp = FALSE;\r
345 static int analysisX, analysisY, analysisH, analysisW;\r
346 \r
347 char errorTitle[MSG_SIZ];\r
348 char errorMessage[2*MSG_SIZ];\r
349 HWND errorDialog = NULL;\r
350 BOOLEAN moveErrorMessageUp = FALSE;\r
351 BOOLEAN consoleEcho = TRUE;\r
352 CHARFORMAT consoleCF;\r
353 COLORREF consoleBackgroundColor;\r
354 \r
355 char *programVersion;\r
356 \r
357 #define CPReal 1\r
358 #define CPComm 2\r
359 #define CPSock 3\r
360 #define CPRcmd 4\r
361 typedef int CPKind;\r
362 \r
363 typedef struct {\r
364   CPKind kind;\r
365   HANDLE hProcess;\r
366   DWORD pid;\r
367   HANDLE hTo;\r
368   HANDLE hFrom;\r
369   SOCKET sock;\r
370   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
371 } ChildProc;\r
372 \r
373 #define INPUT_SOURCE_BUF_SIZE 4096\r
374 \r
375 typedef struct _InputSource {\r
376   CPKind kind;\r
377   HANDLE hFile;\r
378   SOCKET sock;\r
379   int lineByLine;\r
380   HANDLE hThread;\r
381   DWORD id;\r
382   char buf[INPUT_SOURCE_BUF_SIZE];\r
383   char *next;\r
384   DWORD count;\r
385   int error;\r
386   InputCallback func;\r
387   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
388   VOIDSTAR closure;\r
389 } InputSource;\r
390 \r
391 InputSource *consoleInputSource;\r
392 \r
393 DCB dcb;\r
394 \r
395 /* forward */\r
396 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
397 VOID ConsoleCreate();\r
398 LRESULT CALLBACK\r
399   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
400 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
401 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
402 VOID ParseCommSettings(char *arg, DCB *dcb);\r
403 LRESULT CALLBACK\r
404   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
406 void ParseIcsTextMenu(char *icsTextMenuString);\r
407 VOID PopUpMoveDialog(char firstchar);\r
408 VOID PopUpNameDialog(char firstchar);\r
409 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
410 \r
411 /* [AS] */\r
412 int NewGameFRC();\r
413 int GameListOptions();\r
414 \r
415 HWND moveHistoryDialog = NULL;\r
416 BOOLEAN moveHistoryDialogUp = FALSE;\r
417 \r
418 WindowPlacement wpMoveHistory;\r
419 \r
420 HWND evalGraphDialog = NULL;\r
421 BOOLEAN evalGraphDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpEvalGraph;\r
424 \r
425 HWND engineOutputDialog = NULL;\r
426 BOOLEAN engineOutputDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEngineOutput;\r
429 \r
430 VOID MoveHistoryPopUp();\r
431 VOID MoveHistoryPopDown();\r
432 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
433 BOOL MoveHistoryIsUp();\r
434 \r
435 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
436 VOID EvalGraphPopUp();\r
437 VOID EvalGraphPopDown();\r
438 BOOL EvalGraphIsUp();\r
439 \r
440 VOID EngineOutputPopUp();\r
441 VOID EngineOutputPopDown();\r
442 BOOL EngineOutputIsUp();\r
443 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
444 \r
445 VOID GothicPopUp(char *title, VariantClass variant);\r
446 /*\r
447  * Setting "frozen" should disable all user input other than deleting\r
448  * the window.  We do this while engines are initializing themselves.\r
449  */\r
450 static int frozen = 0;\r
451 static int oldMenuItemState[MENU_BAR_ITEMS];\r
452 void FreezeUI()\r
453 {\r
454   HMENU hmenu;\r
455   int i;\r
456 \r
457   if (frozen) return;\r
458   frozen = 1;\r
459   hmenu = GetMenu(hwndMain);\r
460   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
461     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
462   }\r
463   DrawMenuBar(hwndMain);\r
464 }\r
465 \r
466 /* Undo a FreezeUI */\r
467 void ThawUI()\r
468 {\r
469   HMENU hmenu;\r
470   int i;\r
471 \r
472   if (!frozen) return;\r
473   frozen = 0;\r
474   hmenu = GetMenu(hwndMain);\r
475   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
476     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
477   }\r
478   DrawMenuBar(hwndMain);\r
479 }\r
480 \r
481 /*---------------------------------------------------------------------------*\\r
482  *\r
483  * WinMain\r
484  *\r
485 \*---------------------------------------------------------------------------*/\r
486 \r
487 int APIENTRY\r
488 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
489         LPSTR lpCmdLine, int nCmdShow)\r
490 {\r
491   MSG msg;\r
492   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
493 //  INITCOMMONCONTROLSEX ex;\r
494 \r
495   debugFP = stderr;\r
496 \r
497   LoadLibrary("RICHED32.DLL");\r
498   consoleCF.cbSize = sizeof(CHARFORMAT);\r
499 \r
500   if (!InitApplication(hInstance)) {\r
501     return (FALSE);\r
502   }\r
503   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
504     return (FALSE);\r
505   }\r
506 \r
507 //  InitCommonControlsEx(&ex);\r
508   InitCommonControls();\r
509 \r
510   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
511   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
512   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
513 \r
514   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
515 \r
516   while (GetMessage(&msg, /* message structure */\r
517                     NULL, /* handle of window receiving the message */\r
518                     0,    /* lowest message to examine */\r
519                     0))   /* highest message to examine */\r
520     {\r
521       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
522           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
523           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
524           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
525           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
526           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
527           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
528           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
529           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
530           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
531         TranslateMessage(&msg); /* Translates virtual key codes */\r
532         DispatchMessage(&msg);  /* Dispatches message to window */\r
533       }\r
534     }\r
535 \r
536 \r
537   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
538 }\r
539 \r
540 /*---------------------------------------------------------------------------*\\r
541  *\r
542  * Initialization functions\r
543  *\r
544 \*---------------------------------------------------------------------------*/\r
545 \r
546 BOOL\r
547 InitApplication(HINSTANCE hInstance)\r
548 {\r
549   WNDCLASS wc;\r
550 \r
551   /* Fill in window class structure with parameters that describe the */\r
552   /* main window. */\r
553 \r
554   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
555   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
556   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
557   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
558   wc.hInstance     = hInstance;         /* Owner of this class */\r
559   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
560   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
561   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
562   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
563   wc.lpszClassName = szAppName;                 /* Name to register as */\r
564 \r
565   /* Register the window class and return success/failure code. */\r
566   if (!RegisterClass(&wc)) return FALSE;\r
567 \r
568   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
569   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
570   wc.cbClsExtra    = 0;\r
571   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
572   wc.hInstance     = hInstance;\r
573   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
574   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
575   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
576   wc.lpszMenuName  = NULL;\r
577   wc.lpszClassName = szConsoleName;\r
578 \r
579   if (!RegisterClass(&wc)) return FALSE;\r
580   return TRUE;\r
581 }\r
582 \r
583 \r
584 /* Set by InitInstance, used by EnsureOnScreen */\r
585 int screenHeight, screenWidth;\r
586 \r
587 void\r
588 EnsureOnScreen(int *x, int *y)\r
589 {\r
590 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
591   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
592   if (*x > screenWidth - 32) *x = 0;\r
593   if (*y > screenHeight - 32) *y = 0;\r
594   if (*x < 0) *x = 0;\r
595   if (*y < 0) *y = 0;\r
596 //  if (*x < 10) *x = 10;\r
597 //  if (*y < gap) *y = gap;\r
598 }\r
599 \r
600 BOOL\r
601 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
602 {\r
603   HWND hwnd; /* Main window handle. */\r
604   int ibs;\r
605   WINDOWPLACEMENT wp;\r
606   char *filepart;\r
607 \r
608   hInst = hInstance;    /* Store instance handle in our global variable */\r
609 \r
610   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
611     *filepart = NULLCHAR;\r
612   } else {\r
613     GetCurrentDirectory(MSG_SIZ, installDir);\r
614   }\r
615   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
616   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
617   if (appData.debugMode) {\r
618     debugFP = fopen(appData.nameOfDebugFile, "w");\r
619     setbuf(debugFP, NULL);\r
620   }\r
621 \r
622   InitBackEnd1();\r
623 \r
624 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
625 //  InitEngineUCI( installDir, &second );\r
626 \r
627   /* Create a main window for this application instance. */\r
628   hwnd = CreateWindow(szAppName, szTitle,\r
629                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
630                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
631                       NULL, NULL, hInstance, NULL);\r
632   hwndMain = hwnd;\r
633 \r
634   /* If window could not be created, return "failure" */\r
635   if (!hwnd) {\r
636     return (FALSE);\r
637   }\r
638 \r
639   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
640   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
641       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
642 \r
643       if (first.programLogo == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
645       }\r
646   } else if(appData.autoLogo) {\r
647       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
648         char buf[MSG_SIZ];\r
649         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
650         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
651       }\r
652   }\r
653 \r
654   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
655       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
656 \r
657       if (second.programLogo == NULL && appData.debugMode) {\r
658           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
659       }\r
660   } else if(appData.autoLogo) {\r
661       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
662         char buf[MSG_SIZ];\r
663         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
664         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
665       }\r
666   }\r
667 \r
668   iconWhite = LoadIcon(hInstance, "icon_white");\r
669   iconBlack = LoadIcon(hInstance, "icon_black");\r
670   iconCurrent = iconWhite;\r
671   InitDrawingColors();\r
672   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
673   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
674   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
675     /* Compute window size for each board size, and use the largest\r
676        size that fits on this screen as the default. */\r
677     InitDrawingSizes((BoardSize)ibs, 0);\r
678     if (boardSize == (BoardSize)-1 &&\r
679         winHeight <= screenHeight\r
680            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
681         && winWidth <= screenWidth) {\r
682       boardSize = (BoardSize)ibs;\r
683     }\r
684   }\r
685 \r
686   InitDrawingSizes(boardSize, 0);\r
687   InitMenuChecks();\r
688   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
689 \r
690   /* [AS] Load textures if specified */\r
691   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
692   \r
693   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
694       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
695       liteBackTextureMode = appData.liteBackTextureMode;\r
696 \r
697       if (liteBackTexture == NULL && appData.debugMode) {\r
698           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
699       }\r
700   }\r
701   \r
702   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
703       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
704       darkBackTextureMode = appData.darkBackTextureMode;\r
705 \r
706       if (darkBackTexture == NULL && appData.debugMode) {\r
707           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
708       }\r
709   }\r
710 \r
711   mysrandom( (unsigned) time(NULL) );\r
712 \r
713   /* [AS] Restore layout */\r
714   if( wpMoveHistory.visible ) {\r
715       MoveHistoryPopUp();\r
716   }\r
717 \r
718   if( wpEvalGraph.visible ) {\r
719       EvalGraphPopUp();\r
720   }\r
721 \r
722   if( wpEngineOutput.visible ) {\r
723       EngineOutputPopUp();\r
724   }\r
725 \r
726   InitBackEnd2();\r
727 \r
728   /* Make the window visible; update its client area; and return "success" */\r
729   EnsureOnScreen(&boardX, &boardY);\r
730   wp.length = sizeof(WINDOWPLACEMENT);\r
731   wp.flags = 0;\r
732   wp.showCmd = nCmdShow;\r
733   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
734   wp.rcNormalPosition.left = boardX;\r
735   wp.rcNormalPosition.right = boardX + winWidth;\r
736   wp.rcNormalPosition.top = boardY;\r
737   wp.rcNormalPosition.bottom = boardY + winHeight;\r
738   SetWindowPlacement(hwndMain, &wp);\r
739 \r
740   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
741                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
742 \r
743 #if 0\r
744   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
745   if( gameInfo.variant != VariantFischeRandom ) {\r
746       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
747   }\r
748 #endif\r
749   if (hwndConsole) {\r
750 #if AOT_CONSOLE\r
751     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
752                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
753 #endif\r
754     ShowWindow(hwndConsole, nCmdShow);\r
755   }\r
756   UpdateWindow(hwnd);\r
757 \r
758   return TRUE;\r
759 \r
760 }\r
761 \r
762 \r
763 typedef enum {\r
764   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
765   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
766   ArgSettingsFilename\r
767 } ArgType;\r
768 \r
769 typedef struct {\r
770   char *argName;\r
771   ArgType argType;\r
772   /***\r
773   union {\r
774     String *pString;       // ArgString\r
775     int *pInt;             // ArgInt\r
776     float *pFloat;         // ArgFloat\r
777     Boolean *pBoolean;     // ArgBoolean\r
778     COLORREF *pColor;      // ArgColor\r
779     ColorClass cc;         // ArgAttribs\r
780     String *pFilename;     // ArgFilename\r
781     BoardSize *pBoardSize; // ArgBoardSize\r
782     int whichFont;         // ArgFont\r
783     DCB *pDCB;             // ArgCommSettings\r
784     String *pFilename;     // ArgSettingsFilename\r
785   } argLoc;\r
786   ***/\r
787   LPVOID argLoc;\r
788   BOOL save;\r
789 } ArgDescriptor;\r
790 \r
791 int junk;\r
792 ArgDescriptor argDescriptors[] = {\r
793   /* positional arguments */\r
794   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
795   { "", ArgNone, NULL },\r
796   /* keyword arguments */\r
797   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
798   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
799   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
800   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
801   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
802   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
803   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
804   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
805   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
806   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
807   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
808   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
809   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
810   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
811   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
812   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
813   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
814   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
815     FALSE },\r
816   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
817     FALSE },\r
818   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
819     FALSE },\r
820   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
821   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
822     FALSE },\r
823   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
824   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
825   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
826   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
827   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
828   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
829   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
830   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
831   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
832   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
833   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
834   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
835   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
836   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
837   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
838   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
839   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
840   /*!!bitmapDirectory?*/\r
841   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
842   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
843   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
844   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
845   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
846   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
847   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
848   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
849   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
850   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
851   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
852   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
853   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
854   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
855   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
856   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
857   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
858   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
859   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
860   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
861   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
862   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
863   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
864   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
865   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
866   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
867   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
868   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
869   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
870   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
871   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
872   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
873   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
874   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
875   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
876   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
877   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
878   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
879   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
880   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
881   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
882   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
883   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
884   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
885   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
886   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
887   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
888   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
889   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
890   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
891   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
892   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
893   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
894   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
895   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
896   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
897   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
898   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
899   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
900   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
901   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
902   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
903   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
904   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
905   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
906   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
907   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
908   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
909   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
910   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
911   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
912   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
913   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
914   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
915   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
916   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
917   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
918   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
919   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
920   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
921   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
922   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
923   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
924   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
925   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
926   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
927   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
928   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
929   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
930   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
931   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
932   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
933   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
934   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
935     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
936   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
937   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
938   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
939   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
940   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
941   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
942   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
943   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
944     TRUE }, /* must come after all fonts */\r
945   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
946   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
947     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
948   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
949   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
950   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
951   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
952   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
953   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
954   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
955   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
956   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
957   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
958   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
959   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
960   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
961   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
962   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
963   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
964   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
965   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
966   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
967   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
968   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
969   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
970   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
971   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
972   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
973   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
974   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
975   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
976 #if 0\r
977   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
978   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
979 #endif\r
980   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
981   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
982   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
983   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
984   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
985   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
986   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
987   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
988   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
989   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
990   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
991   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
992   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
993   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
994   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
995   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
996   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
997   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
998   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
999   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1000   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1001   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1002   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1003   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1004   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1005   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1006   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1007   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1008   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1009   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1010   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1011   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1012   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1013   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1014   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1015   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1016   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1017   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1018   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1019   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1020   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1021   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1022   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1023   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1024   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1025   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1026   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1027   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1028   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1029   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1030   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1031   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1032   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1033   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1034   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1035   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1036   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1037   { "highlightLastMove", ArgBoolean,\r
1038     (LPVOID) &appData.highlightLastMove, TRUE },\r
1039   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1040   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1041   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1042   { "highlightDragging", ArgBoolean,\r
1043     (LPVOID) &appData.highlightDragging, TRUE },\r
1044   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1045   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1046   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1047   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1048   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1049   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1050   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1051   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1052   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1053   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1054   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1055   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1056   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1057   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1058   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1059   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1060   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1061   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1062   { "soundShout", ArgFilename,\r
1063     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1064   { "soundSShout", ArgFilename,\r
1065     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1066   { "soundChannel1", ArgFilename,\r
1067     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1068   { "soundChannel", ArgFilename,\r
1069     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1070   { "soundKibitz", ArgFilename,\r
1071     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1072   { "soundTell", ArgFilename,\r
1073     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1074   { "soundChallenge", ArgFilename,\r
1075     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1076   { "soundRequest", ArgFilename,\r
1077     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1078   { "soundSeek", ArgFilename,\r
1079     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1080   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1081   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1082   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1083   { "soundIcsLoss", ArgFilename, \r
1084     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1085   { "soundIcsDraw", ArgFilename, \r
1086     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1087   { "soundIcsUnfinished", ArgFilename, \r
1088     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1089   { "soundIcsAlarm", ArgFilename, \r
1090     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1091   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1092   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1093   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1094   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1095   { "reuseChessPrograms", ArgBoolean,\r
1096     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1097   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1098   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1099   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1100   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1101   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1102   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1103   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1104   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1105   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1106   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1107   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1108   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1109   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1110   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1111   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1112   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1113   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1114   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1115   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1116   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1117   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1118   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1119   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1120   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1121   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1122   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1123   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1124   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1125   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1126   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1127   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1128   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1129   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1130   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1131   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1132   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1133   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1134     TRUE },\r
1135   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1136     TRUE },\r
1137   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1138   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1139   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1140   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1141   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1142   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1143   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1144   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1145   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1146   /* [AS] New features */\r
1147   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1148   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1149   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1150   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1151   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1152   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1153   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1154   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1155   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1156   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1157   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1158   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1159   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1160   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1161   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1162   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1163   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1164   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1165   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1166   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1167   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1168   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1169   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1170   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1171   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1172   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1173   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1174   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1175   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1176   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1177   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1178   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1179   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1180   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1181   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1182   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1183   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1184   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1185   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1186   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1187   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1188   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1189   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1190   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1191   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1192   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1193   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1194   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1195   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1196   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1197 \r
1198   /* [AS] Layout stuff */\r
1199   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1200   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1201   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1202   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1203   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1204 \r
1205   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1206   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1207   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1208   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1209   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1210 \r
1211   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1212   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1213   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1214   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1215   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1216 \r
1217   /* [HGM] board-size, adjudication and misc. options */\r
1218   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1219   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1220   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1221   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1222   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1223   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1224   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1225   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1226   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1227   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1228   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1229   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1230   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1231   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1232   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1233   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1234   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1235   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1236   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1237   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1238   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1239   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1240   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1241   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1242   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1243   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1244   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1245   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1246   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1247 \r
1248 #ifdef ZIPPY\r
1249   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1250   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1251   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1252   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1253   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1254   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1255   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1256   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1257   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1258   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1259   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1260   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1261   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1262     FALSE },\r
1263   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1264   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1265   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1266   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1267   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1268   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1269   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1270     FALSE },\r
1271   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1272   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1273   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1274   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1275   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1276   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1277   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1278   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1279   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1280   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1281   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1282   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1283   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1284   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1285   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1286   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1287   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1288   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1289 #endif\r
1290   /* [HGM] options for broadcasting and time odds */\r
1291   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1292   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1293   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1294   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1295   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1296   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1297   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1298   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1299   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1300   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1301   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1302   { NULL, ArgNone, NULL, FALSE }\r
1303 };\r
1304 \r
1305 \r
1306 /* Kludge for indirection files on command line */\r
1307 char* lastIndirectionFilename;\r
1308 ArgDescriptor argDescriptorIndirection =\r
1309 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1310 \r
1311 \r
1312 VOID\r
1313 ExitArgError(char *msg, char *badArg)\r
1314 {\r
1315   char buf[MSG_SIZ];\r
1316 \r
1317   sprintf(buf, "%s %s", msg, badArg);\r
1318   DisplayFatalError(buf, 0, 2);\r
1319   exit(2);\r
1320 }\r
1321 \r
1322 /* Command line font name parser.  NULL name means do nothing.\r
1323    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1324    For backward compatibility, syntax without the colon is also\r
1325    accepted, but font names with digits in them won't work in that case.\r
1326 */\r
1327 VOID\r
1328 ParseFontName(char *name, MyFontParams *mfp)\r
1329 {\r
1330   char *p, *q;\r
1331   if (name == NULL) return;\r
1332   p = name;\r
1333   q = strchr(p, ':');\r
1334   if (q) {\r
1335     if (q - p >= sizeof(mfp->faceName))\r
1336       ExitArgError("Font name too long:", name);\r
1337     memcpy(mfp->faceName, p, q - p);\r
1338     mfp->faceName[q - p] = NULLCHAR;\r
1339     p = q + 1;\r
1340   } else {\r
1341     q = mfp->faceName;\r
1342     while (*p && !isdigit(*p)) {\r
1343       *q++ = *p++;\r
1344       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1345         ExitArgError("Font name too long:", name);\r
1346     }\r
1347     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1348     *q = NULLCHAR;\r
1349   }\r
1350   if (!*p) ExitArgError("Font point size missing:", name);\r
1351   mfp->pointSize = (float) atof(p);\r
1352   mfp->bold = (strchr(p, 'b') != NULL);\r
1353   mfp->italic = (strchr(p, 'i') != NULL);\r
1354   mfp->underline = (strchr(p, 'u') != NULL);\r
1355   mfp->strikeout = (strchr(p, 's') != NULL);\r
1356 }\r
1357 \r
1358 /* Color name parser.\r
1359    X version accepts X color names, but this one\r
1360    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1361 COLORREF\r
1362 ParseColorName(char *name)\r
1363 {\r
1364   int red, green, blue, count;\r
1365   char buf[MSG_SIZ];\r
1366 \r
1367   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1368   if (count != 3) {\r
1369     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1370       &red, &green, &blue);\r
1371   }\r
1372   if (count != 3) {\r
1373     sprintf(buf, "Can't parse color name %s", name);\r
1374     DisplayError(buf, 0);\r
1375     return RGB(0, 0, 0);\r
1376   }\r
1377   return PALETTERGB(red, green, blue);\r
1378 }\r
1379 \r
1380 \r
1381 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1382 {\r
1383   char *e = argValue;\r
1384   int eff = 0;\r
1385 \r
1386   while (*e) {\r
1387     if (*e == 'b')      eff |= CFE_BOLD;\r
1388     else if (*e == 'i') eff |= CFE_ITALIC;\r
1389     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1390     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1391     else if (*e == '#' || isdigit(*e)) break;\r
1392     e++;\r
1393   }\r
1394   *effects = eff;\r
1395   *color   = ParseColorName(e);\r
1396 }\r
1397 \r
1398 \r
1399 BoardSize\r
1400 ParseBoardSize(char *name)\r
1401 {\r
1402   BoardSize bs = SizeTiny;\r
1403   while (sizeInfo[bs].name != NULL) {\r
1404     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1405     bs++;\r
1406   }\r
1407   ExitArgError("Unrecognized board size value", name);\r
1408   return bs; /* not reached */\r
1409 }\r
1410 \r
1411 \r
1412 char\r
1413 StringGet(void *getClosure)\r
1414 {\r
1415   char **p = (char **) getClosure;\r
1416   return *((*p)++);\r
1417 }\r
1418 \r
1419 char\r
1420 FileGet(void *getClosure)\r
1421 {\r
1422   int c;\r
1423   FILE* f = (FILE*) getClosure;\r
1424 \r
1425   c = getc(f);\r
1426   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1427   if (c == EOF)\r
1428     return NULLCHAR;\r
1429   else\r
1430     return (char) c;\r
1431 }\r
1432 \r
1433 /* Parse settings file named "name". If file found, return the\r
1434    full name in fullname and return TRUE; else return FALSE */\r
1435 BOOLEAN\r
1436 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1437 {\r
1438   char *dummy;\r
1439   FILE *f;\r
1440   int ok; char buf[MSG_SIZ];\r
1441 \r
1442   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1443   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1444     sprintf(buf, "%s.ini", name);\r
1445     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1446   }\r
1447   if (ok) {\r
1448     f = fopen(fullname, "r");\r
1449     if (f != NULL) {\r
1450       ParseArgs(FileGet, f);\r
1451       fclose(f);\r
1452       return TRUE;\r
1453     }\r
1454   }\r
1455   return FALSE;\r
1456 }\r
1457 \r
1458 VOID\r
1459 ParseArgs(GetFunc get, void *cl)\r
1460 {\r
1461   char argName[ARG_MAX];\r
1462   char argValue[ARG_MAX];\r
1463   ArgDescriptor *ad;\r
1464   char start;\r
1465   char *q;\r
1466   int i, octval;\r
1467   char ch;\r
1468   int posarg = 0;\r
1469 \r
1470   ch = get(cl);\r
1471   for (;;) {\r
1472     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1473     if (ch == NULLCHAR) break;\r
1474     if (ch == ';') {\r
1475       /* Comment to end of line */\r
1476       ch = get(cl);\r
1477       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1478       continue;\r
1479     } else if (ch == '/' || ch == '-') {\r
1480       /* Switch */\r
1481       q = argName;\r
1482       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1483              ch != '\n' && ch != '\t') {\r
1484         *q++ = ch;\r
1485         ch = get(cl);\r
1486       }\r
1487       *q = NULLCHAR;\r
1488 \r
1489       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1490         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1491 \r
1492       if (ad->argName == NULL)\r
1493         ExitArgError("Unrecognized argument", argName);\r
1494 \r
1495     } else if (ch == '@') {\r
1496       /* Indirection file */\r
1497       ad = &argDescriptorIndirection;\r
1498       ch = get(cl);\r
1499     } else {\r
1500       /* Positional argument */\r
1501       ad = &argDescriptors[posarg++];\r
1502       strcpy(argName, ad->argName);\r
1503     }\r
1504 \r
1505     if (ad->argType == ArgTrue) {\r
1506       *(Boolean *) ad->argLoc = TRUE;\r
1507       continue;\r
1508     }\r
1509     if (ad->argType == ArgFalse) {\r
1510       *(Boolean *) ad->argLoc = FALSE;\r
1511       continue;\r
1512     }\r
1513 \r
1514     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1515     if (ch == NULLCHAR || ch == '\n') {\r
1516       ExitArgError("No value provided for argument", argName);\r
1517     }\r
1518     q = argValue;\r
1519     if (ch == '{') {\r
1520       // Quoting with { }.  No characters have to (or can) be escaped.\r
1521       // Thus the string cannot contain a '}' character.\r
1522       start = ch;\r
1523       ch = get(cl);\r
1524       while (start) {\r
1525         switch (ch) {\r
1526         case NULLCHAR:\r
1527           start = NULLCHAR;\r
1528           break;\r
1529           \r
1530         case '}':\r
1531           ch = get(cl);\r
1532           start = NULLCHAR;\r
1533           break;\r
1534 \r
1535         default:\r
1536           *q++ = ch;\r
1537           ch = get(cl);\r
1538           break;\r
1539         }\r
1540       }   \r
1541     } else if (ch == '\'' || ch == '"') {\r
1542       // Quoting with ' ' or " ", with \ as escape character.\r
1543       // Inconvenient for long strings that may contain Windows filenames.\r
1544       start = ch;\r
1545       ch = get(cl);\r
1546       while (start) {\r
1547         switch (ch) {\r
1548         case NULLCHAR:\r
1549           start = NULLCHAR;\r
1550           break;\r
1551 \r
1552         default:\r
1553         not_special:\r
1554           *q++ = ch;\r
1555           ch = get(cl);\r
1556           break;\r
1557 \r
1558         case '\'':\r
1559         case '\"':\r
1560           if (ch == start) {\r
1561             ch = get(cl);\r
1562             start = NULLCHAR;\r
1563             break;\r
1564           } else {\r
1565             goto not_special;\r
1566           }\r
1567 \r
1568         case '\\':\r
1569           if (ad->argType == ArgFilename\r
1570               || ad->argType == ArgSettingsFilename) {\r
1571               goto not_special;\r
1572           }\r
1573           ch = get(cl);\r
1574           switch (ch) {\r
1575           case NULLCHAR:\r
1576             ExitArgError("Incomplete \\ escape in value for", argName);\r
1577             break;\r
1578           case 'n':\r
1579             *q++ = '\n';\r
1580             ch = get(cl);\r
1581             break;\r
1582           case 'r':\r
1583             *q++ = '\r';\r
1584             ch = get(cl);\r
1585             break;\r
1586           case 't':\r
1587             *q++ = '\t';\r
1588             ch = get(cl);\r
1589             break;\r
1590           case 'b':\r
1591             *q++ = '\b';\r
1592             ch = get(cl);\r
1593             break;\r
1594           case 'f':\r
1595             *q++ = '\f';\r
1596             ch = get(cl);\r
1597             break;\r
1598           default:\r
1599             octval = 0;\r
1600             for (i = 0; i < 3; i++) {\r
1601               if (ch >= '0' && ch <= '7') {\r
1602                 octval = octval*8 + (ch - '0');\r
1603                 ch = get(cl);\r
1604               } else {\r
1605                 break;\r
1606               }\r
1607             }\r
1608             if (i > 0) {\r
1609               *q++ = (char) octval;\r
1610             } else {\r
1611               *q++ = ch;\r
1612               ch = get(cl);\r
1613             }\r
1614             break;\r
1615           }\r
1616           break;\r
1617         }\r
1618       }\r
1619     } else {\r
1620       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1621         *q++ = ch;\r
1622         ch = get(cl);\r
1623       }\r
1624     }\r
1625     *q = NULLCHAR;\r
1626 \r
1627     switch (ad->argType) {\r
1628     case ArgInt:\r
1629       *(int *) ad->argLoc = atoi(argValue);\r
1630       break;\r
1631 \r
1632     case ArgFloat:\r
1633       *(float *) ad->argLoc = (float) atof(argValue);\r
1634       break;\r
1635 \r
1636     case ArgString:\r
1637     case ArgFilename:\r
1638       *(char **) ad->argLoc = strdup(argValue);\r
1639       break;\r
1640 \r
1641     case ArgSettingsFilename:\r
1642       {\r
1643         char fullname[MSG_SIZ];\r
1644         if (ParseSettingsFile(argValue, fullname)) {\r
1645           if (ad->argLoc != NULL) {\r
1646             *(char **) ad->argLoc = strdup(fullname);\r
1647           }\r
1648         } else {\r
1649           if (ad->argLoc != NULL) {\r
1650           } else {\r
1651             ExitArgError("Failed to open indirection file", argValue);\r
1652           }\r
1653         }\r
1654       }\r
1655       break;\r
1656 \r
1657     case ArgBoolean:\r
1658       switch (argValue[0]) {\r
1659       case 't':\r
1660       case 'T':\r
1661         *(Boolean *) ad->argLoc = TRUE;\r
1662         break;\r
1663       case 'f':\r
1664       case 'F':\r
1665         *(Boolean *) ad->argLoc = FALSE;\r
1666         break;\r
1667       default:\r
1668         ExitArgError("Unrecognized boolean argument value", argValue);\r
1669         break;\r
1670       }\r
1671       break;\r
1672 \r
1673     case ArgColor:\r
1674       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1675       break;\r
1676 \r
1677     case ArgAttribs: {\r
1678       ColorClass cc = (ColorClass)ad->argLoc;\r
1679       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1680       }\r
1681       break;\r
1682       \r
1683     case ArgBoardSize:\r
1684       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1685       break;\r
1686 \r
1687     case ArgFont:\r
1688       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1689       break;\r
1690 \r
1691     case ArgCommSettings:\r
1692       ParseCommSettings(argValue, &dcb);\r
1693       break;\r
1694 \r
1695     case ArgNone:\r
1696       ExitArgError("Unrecognized argument", argValue);\r
1697       break;\r
1698     case ArgTrue:\r
1699     case ArgFalse: ;\r
1700     }\r
1701   }\r
1702 }\r
1703 \r
1704 VOID\r
1705 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1706 {\r
1707   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1708   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1709   DeleteDC(hdc);\r
1710   lf->lfWidth = 0;\r
1711   lf->lfEscapement = 0;\r
1712   lf->lfOrientation = 0;\r
1713   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1714   lf->lfItalic = mfp->italic;\r
1715   lf->lfUnderline = mfp->underline;\r
1716   lf->lfStrikeOut = mfp->strikeout;\r
1717   lf->lfCharSet = DEFAULT_CHARSET;\r
1718   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1719   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1720   lf->lfQuality = DEFAULT_QUALITY;\r
1721   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1722   strcpy(lf->lfFaceName, mfp->faceName);\r
1723 }\r
1724 \r
1725 VOID\r
1726 CreateFontInMF(MyFont *mf)\r
1727 {\r
1728   LFfromMFP(&mf->lf, &mf->mfp);\r
1729   if (mf->hf) DeleteObject(mf->hf);\r
1730   mf->hf = CreateFontIndirect(&mf->lf);\r
1731 }\r
1732 \r
1733 VOID\r
1734 SetDefaultTextAttribs()\r
1735 {\r
1736   ColorClass cc;\r
1737   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1738     ParseAttribs(&textAttribs[cc].color, \r
1739                  &textAttribs[cc].effects, \r
1740                  defaultTextAttribs[cc]);\r
1741   }\r
1742 }\r
1743 \r
1744 VOID\r
1745 SetDefaultSounds()\r
1746 {\r
1747   ColorClass cc;\r
1748   SoundClass sc;\r
1749   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1750     textAttribs[cc].sound.name = strdup("");\r
1751     textAttribs[cc].sound.data = NULL;\r
1752   }\r
1753   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1754     sounds[sc].name = strdup("");\r
1755     sounds[sc].data = NULL;\r
1756   }\r
1757   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1758 }\r
1759 \r
1760 VOID\r
1761 LoadAllSounds()\r
1762 {\r
1763   ColorClass cc;\r
1764   SoundClass sc;\r
1765   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1766     MyLoadSound(&textAttribs[cc].sound);\r
1767   }\r
1768   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1769     MyLoadSound(&sounds[sc]);\r
1770   }\r
1771 }\r
1772 \r
1773 VOID\r
1774 InitAppData(LPSTR lpCmdLine)\r
1775 {\r
1776   int i, j;\r
1777   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1778   char *dummy, *p;\r
1779 \r
1780   programName = szAppName;\r
1781 \r
1782   /* Initialize to defaults */\r
1783   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1784   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1785   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1786   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1787   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1788   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1789   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1790   SetDefaultTextAttribs();\r
1791   SetDefaultSounds();\r
1792   appData.movesPerSession = MOVES_PER_SESSION;\r
1793   appData.initString = INIT_STRING;\r
1794   appData.secondInitString = INIT_STRING;\r
1795   appData.firstComputerString = COMPUTER_STRING;\r
1796   appData.secondComputerString = COMPUTER_STRING;\r
1797   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1798   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1799   appData.firstPlaysBlack = FALSE;\r
1800   appData.noChessProgram = FALSE;\r
1801   chessProgram = FALSE;\r
1802   appData.firstHost = FIRST_HOST;\r
1803   appData.secondHost = SECOND_HOST;\r
1804   appData.firstDirectory = FIRST_DIRECTORY;\r
1805   appData.secondDirectory = SECOND_DIRECTORY;\r
1806   appData.bitmapDirectory = "";\r
1807   appData.remoteShell = REMOTE_SHELL;\r
1808   appData.remoteUser = "";\r
1809   appData.timeDelay = TIME_DELAY;\r
1810   appData.timeControl = TIME_CONTROL;\r
1811   appData.timeIncrement = TIME_INCREMENT;\r
1812   appData.icsActive = FALSE;\r
1813   appData.icsHost = "";\r
1814   appData.icsPort = ICS_PORT;\r
1815   appData.icsCommPort = ICS_COMM_PORT;\r
1816   appData.icsLogon = ICS_LOGON;\r
1817   appData.icsHelper = "";\r
1818   appData.useTelnet = FALSE;\r
1819   appData.telnetProgram = TELNET_PROGRAM;\r
1820   appData.gateway = "";\r
1821   appData.loadGameFile = "";\r
1822   appData.loadGameIndex = 0;\r
1823   appData.saveGameFile = "";\r
1824   appData.autoSaveGames = FALSE;\r
1825   appData.loadPositionFile = "";\r
1826   appData.loadPositionIndex = 1;\r
1827   appData.savePositionFile = "";\r
1828   appData.matchMode = FALSE;\r
1829   appData.matchGames = 0;\r
1830   appData.monoMode = FALSE;\r
1831   appData.debugMode = FALSE;\r
1832   appData.clockMode = TRUE;\r
1833   boardSize = (BoardSize) -1; /* determine by screen size */\r
1834   appData.Iconic = FALSE; /*unused*/\r
1835   appData.searchTime = "";\r
1836   appData.searchDepth = 0;\r
1837   appData.showCoords = FALSE;\r
1838   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1839   appData.autoCallFlag = FALSE;\r
1840   appData.flipView = FALSE;\r
1841   appData.autoFlipView = TRUE;\r
1842   appData.cmailGameName = "";\r
1843   appData.alwaysPromoteToQueen = FALSE;\r
1844   appData.oldSaveStyle = FALSE;\r
1845   appData.quietPlay = FALSE;\r
1846   appData.showThinking = FALSE;\r
1847   appData.ponderNextMove = TRUE;\r
1848   appData.periodicUpdates = TRUE;\r
1849   appData.popupExitMessage = TRUE;\r
1850   appData.popupMoveErrors = FALSE;\r
1851   appData.autoObserve = FALSE;\r
1852   appData.autoComment = FALSE;\r
1853   appData.animate = TRUE;\r
1854   appData.animSpeed = 10;\r
1855   appData.animateDragging = TRUE;\r
1856   appData.highlightLastMove = TRUE;\r
1857   appData.getMoveList = TRUE;\r
1858   appData.testLegality = TRUE;\r
1859   appData.premove = TRUE;\r
1860   appData.premoveWhite = FALSE;\r
1861   appData.premoveWhiteText = "";\r
1862   appData.premoveBlack = FALSE;\r
1863   appData.premoveBlackText = "";\r
1864   appData.icsAlarm = TRUE;\r
1865   appData.icsAlarmTime = 5000;\r
1866   appData.autoRaiseBoard = TRUE;\r
1867   appData.localLineEditing = TRUE;\r
1868   appData.colorize = TRUE;\r
1869   appData.reuseFirst = TRUE;\r
1870   appData.reuseSecond = TRUE;\r
1871   appData.blindfold = FALSE;\r
1872   appData.icsEngineAnalyze = FALSE;\r
1873   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1874   dcb.DCBlength = sizeof(DCB);\r
1875   dcb.BaudRate = 9600;\r
1876   dcb.fBinary = TRUE;\r
1877   dcb.fParity = FALSE;\r
1878   dcb.fOutxCtsFlow = FALSE;\r
1879   dcb.fOutxDsrFlow = FALSE;\r
1880   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1881   dcb.fDsrSensitivity = FALSE;\r
1882   dcb.fTXContinueOnXoff = TRUE;\r
1883   dcb.fOutX = FALSE;\r
1884   dcb.fInX = FALSE;\r
1885   dcb.fNull = FALSE;\r
1886   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1887   dcb.fAbortOnError = FALSE;\r
1888   dcb.ByteSize = 7;\r
1889   dcb.Parity = SPACEPARITY;\r
1890   dcb.StopBits = ONESTOPBIT;\r
1891   settingsFileName = SETTINGS_FILE;\r
1892   saveSettingsOnExit = TRUE;\r
1893   boardX = CW_USEDEFAULT;\r
1894   boardY = CW_USEDEFAULT;\r
1895   consoleX = CW_USEDEFAULT; \r
1896   consoleY = CW_USEDEFAULT; \r
1897   consoleW = CW_USEDEFAULT;\r
1898   consoleH = CW_USEDEFAULT;\r
1899   analysisX = CW_USEDEFAULT; \r
1900   analysisY = CW_USEDEFAULT; \r
1901   analysisW = CW_USEDEFAULT;\r
1902   analysisH = CW_USEDEFAULT;\r
1903   commentX = CW_USEDEFAULT; \r
1904   commentY = CW_USEDEFAULT; \r
1905   commentW = CW_USEDEFAULT;\r
1906   commentH = CW_USEDEFAULT;\r
1907   editTagsX = CW_USEDEFAULT; \r
1908   editTagsY = CW_USEDEFAULT; \r
1909   editTagsW = CW_USEDEFAULT;\r
1910   editTagsH = CW_USEDEFAULT;\r
1911   gameListX = CW_USEDEFAULT; \r
1912   gameListY = CW_USEDEFAULT; \r
1913   gameListW = CW_USEDEFAULT;\r
1914   gameListH = CW_USEDEFAULT;\r
1915   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1916   icsNames = ICS_NAMES;\r
1917   firstChessProgramNames = FCP_NAMES;\r
1918   secondChessProgramNames = SCP_NAMES;\r
1919   appData.initialMode = "";\r
1920   appData.variant = "normal";\r
1921   appData.firstProtocolVersion = PROTOVER;\r
1922   appData.secondProtocolVersion = PROTOVER;\r
1923   appData.showButtonBar = TRUE;\r
1924 \r
1925    /* [AS] New properties (see comments in header file) */\r
1926   appData.firstScoreIsAbsolute = FALSE;\r
1927   appData.secondScoreIsAbsolute = FALSE;\r
1928   appData.saveExtendedInfoInPGN = FALSE;\r
1929   appData.hideThinkingFromHuman = FALSE;\r
1930   appData.liteBackTextureFile = "";\r
1931   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1932   appData.darkBackTextureFile = "";\r
1933   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1934   appData.renderPiecesWithFont = "";\r
1935   appData.fontToPieceTable = "";\r
1936   appData.fontBackColorWhite = 0;\r
1937   appData.fontForeColorWhite = 0;\r
1938   appData.fontBackColorBlack = 0;\r
1939   appData.fontForeColorBlack = 0;\r
1940   appData.fontPieceSize = 80;\r
1941   appData.overrideLineGap = 1;\r
1942   appData.adjudicateLossThreshold = 0;\r
1943   appData.delayBeforeQuit = 0;\r
1944   appData.delayAfterQuit = 0;\r
1945   appData.nameOfDebugFile = "winboard.debug";\r
1946   appData.pgnEventHeader = "Computer Chess Game";\r
1947   appData.defaultFrcPosition = -1;\r
1948   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1949   appData.saveOutOfBookInfo = TRUE;\r
1950   appData.showEvalInMoveHistory = TRUE;\r
1951   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1952   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1953   appData.highlightMoveWithArrow = FALSE;\r
1954   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1955   appData.useStickyWindows = TRUE;\r
1956   appData.adjudicateDrawMoves = 0;\r
1957   appData.autoDisplayComment = TRUE;\r
1958   appData.autoDisplayTags = TRUE;\r
1959   appData.firstIsUCI = FALSE;\r
1960   appData.secondIsUCI = FALSE;\r
1961   appData.firstHasOwnBookUCI = TRUE;\r
1962   appData.secondHasOwnBookUCI = TRUE;\r
1963   appData.polyglotDir = "";\r
1964   appData.usePolyglotBook = FALSE;\r
1965   appData.polyglotBook = "";\r
1966   appData.defaultHashSize = 64;\r
1967   appData.defaultCacheSizeEGTB = 4;\r
1968   appData.defaultPathEGTB = "c:\\egtb";\r
1969   appData.firstOptions = "";\r
1970   appData.secondOptions = "";\r
1971 \r
1972   InitWindowPlacement( &wpMoveHistory );\r
1973   InitWindowPlacement( &wpEvalGraph );\r
1974   InitWindowPlacement( &wpEngineOutput );\r
1975 \r
1976   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1977   appData.NrFiles      = -1;\r
1978   appData.NrRanks      = -1;\r
1979   appData.holdingsSize = -1;\r
1980   appData.testClaims   = FALSE;\r
1981   appData.checkMates   = FALSE;\r
1982   appData.materialDraws= FALSE;\r
1983   appData.trivialDraws = FALSE;\r
1984   appData.ruleMoves    = 51;\r
1985   appData.drawRepeats  = 6;\r
1986   appData.matchPause   = 10000;\r
1987   appData.alphaRank    = FALSE;\r
1988   appData.allWhite     = FALSE;\r
1989   appData.upsideDown   = FALSE;\r
1990   appData.serverPause  = 15;\r
1991   appData.serverMovesName   = NULL;\r
1992   appData.suppressLoadMoves = FALSE;\r
1993   appData.firstTimeOdds  = 1;\r
1994   appData.secondTimeOdds = 1;\r
1995   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1996   appData.secondAccumulateTC = 1;\r
1997   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1998   appData.secondNPS = -1;\r
1999   appData.engineComments = 1;\r
2000   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2001   appData.egtFormats = "";\r
2002 \r
2003 #ifdef ZIPPY\r
2004   appData.zippyTalk = ZIPPY_TALK;\r
2005   appData.zippyPlay = ZIPPY_PLAY;\r
2006   appData.zippyLines = ZIPPY_LINES;\r
2007   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2008   appData.zippyPassword = ZIPPY_PASSWORD;\r
2009   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2010   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2011   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2012   appData.zippyUseI = ZIPPY_USE_I;\r
2013   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2014   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2015   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2016   appData.zippyGameStart = ZIPPY_GAME_START;\r
2017   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2018   appData.zippyAbort = ZIPPY_ABORT;\r
2019   appData.zippyVariants = ZIPPY_VARIANTS;\r
2020   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2021   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2022 #endif\r
2023 \r
2024   /* Point font array elements to structures and\r
2025      parse default font names */\r
2026   for (i=0; i<NUM_FONTS; i++) {\r
2027     for (j=0; j<NUM_SIZES; j++) {\r
2028       font[j][i] = &fontRec[j][i];\r
2029       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2030     }\r
2031   }\r
2032   \r
2033   /* Parse default settings file if any */\r
2034   if (ParseSettingsFile(settingsFileName, buf)) {\r
2035     settingsFileName = strdup(buf);\r
2036   }\r
2037 \r
2038   /* Parse command line */\r
2039   ParseArgs(StringGet, &lpCmdLine);\r
2040 \r
2041   /* [HGM] make sure board size is acceptable */\r
2042   if(appData.NrFiles > BOARD_SIZE ||\r
2043      appData.NrRanks > BOARD_SIZE   )\r
2044       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2045 \r
2046   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2047    * with options from the command line, we now make an even higher priority\r
2048    * overrule by WB options attached to the engine command line. This so that\r
2049    * tournament managers can use WB options (such as /timeOdds) that follow\r
2050    * the engines.\r
2051    */\r
2052   if(appData.firstChessProgram != NULL) {\r
2053       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2054       static char *f = "first";\r
2055       char buf[MSG_SIZ], *q = buf;\r
2056       if(p != NULL) { // engine command line contains WinBoard options\r
2057           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2058           ParseArgs(StringGet, &q);\r
2059           p[-1] = 0; // cut them offengine command line\r
2060       }\r
2061   }\r
2062   // now do same for second chess program\r
2063   if(appData.secondChessProgram != NULL) {\r
2064       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2065       static char *s = "second";\r
2066       char buf[MSG_SIZ], *q = buf;\r
2067       if(p != NULL) { // engine command line contains WinBoard options\r
2068           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2069           ParseArgs(StringGet, &q);\r
2070           p[-1] = 0; // cut them offengine command line\r
2071       }\r
2072   }\r
2073 \r
2074 \r
2075   /* Propagate options that affect others */\r
2076   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2077   if (appData.icsActive || appData.noChessProgram) {\r
2078      chessProgram = FALSE;  /* not local chess program mode */\r
2079   }\r
2080 \r
2081   /* Open startup dialog if needed */\r
2082   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2083       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2084       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2085                         *appData.secondChessProgram == NULLCHAR))) {\r
2086     FARPROC lpProc;\r
2087     \r
2088     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2089     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2090     FreeProcInstance(lpProc);\r
2091   }\r
2092 \r
2093   /* Make sure save files land in the right (?) directory */\r
2094   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2095     appData.saveGameFile = strdup(buf);\r
2096   }\r
2097   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2098     appData.savePositionFile = strdup(buf);\r
2099   }\r
2100 \r
2101   /* Finish initialization for fonts and sounds */\r
2102   for (i=0; i<NUM_FONTS; i++) {\r
2103     for (j=0; j<NUM_SIZES; j++) {\r
2104       CreateFontInMF(font[j][i]);\r
2105     }\r
2106   }\r
2107   /* xboard, and older WinBoards, controlled the move sound with the\r
2108      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2109      always turn the option on (so that the backend will call us),\r
2110      then let the user turn the sound off by setting it to silence if\r
2111      desired.  To accommodate old winboard.ini files saved by old\r
2112      versions of WinBoard, we also turn off the sound if the option\r
2113      was initially set to false. */\r
2114   if (!appData.ringBellAfterMoves) {\r
2115     sounds[(int)SoundMove].name = strdup("");\r
2116     appData.ringBellAfterMoves = TRUE;\r
2117   }\r
2118   GetCurrentDirectory(MSG_SIZ, currDir);\r
2119   SetCurrentDirectory(installDir);\r
2120   LoadAllSounds();\r
2121   SetCurrentDirectory(currDir);\r
2122 \r
2123   p = icsTextMenuString;\r
2124   if (p[0] == '@') {\r
2125     FILE* f = fopen(p + 1, "r");\r
2126     if (f == NULL) {\r
2127       DisplayFatalError(p + 1, errno, 2);\r
2128       return;\r
2129     }\r
2130     i = fread(buf, 1, sizeof(buf)-1, f);\r
2131     fclose(f);\r
2132     buf[i] = NULLCHAR;\r
2133     p = buf;\r
2134   }\r
2135   ParseIcsTextMenu(strdup(p));\r
2136 }\r
2137 \r
2138 \r
2139 VOID\r
2140 InitMenuChecks()\r
2141 {\r
2142   HMENU hmenu = GetMenu(hwndMain);\r
2143 \r
2144   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2145                         MF_BYCOMMAND|((appData.icsActive &&\r
2146                                        *appData.icsCommPort != NULLCHAR) ?\r
2147                                       MF_ENABLED : MF_GRAYED));\r
2148   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2149                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2150                                      MF_CHECKED : MF_UNCHECKED));\r
2151 }\r
2152 \r
2153 \r
2154 VOID\r
2155 SaveSettings(char* name)\r
2156 {\r
2157   FILE *f;\r
2158   ArgDescriptor *ad;\r
2159   WINDOWPLACEMENT wp;\r
2160   char dir[MSG_SIZ];\r
2161 \r
2162   if (!hwndMain) return;\r
2163 \r
2164   GetCurrentDirectory(MSG_SIZ, dir);\r
2165   SetCurrentDirectory(installDir);\r
2166   f = fopen(name, "w");\r
2167   SetCurrentDirectory(dir);\r
2168   if (f == NULL) {\r
2169     DisplayError(name, errno);\r
2170     return;\r
2171   }\r
2172   fprintf(f, ";\n");\r
2173   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2174   fprintf(f, ";\n");\r
2175   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2176   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2177   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2178   fprintf(f, ";\n");\r
2179 \r
2180   wp.length = sizeof(WINDOWPLACEMENT);\r
2181   GetWindowPlacement(hwndMain, &wp);\r
2182   boardX = wp.rcNormalPosition.left;\r
2183   boardY = wp.rcNormalPosition.top;\r
2184 \r
2185   if (hwndConsole) {\r
2186     GetWindowPlacement(hwndConsole, &wp);\r
2187     consoleX = wp.rcNormalPosition.left;\r
2188     consoleY = wp.rcNormalPosition.top;\r
2189     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2190     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2191   }\r
2192 \r
2193   if (analysisDialog) {\r
2194     GetWindowPlacement(analysisDialog, &wp);\r
2195     analysisX = wp.rcNormalPosition.left;\r
2196     analysisY = wp.rcNormalPosition.top;\r
2197     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2198     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2199   }\r
2200 \r
2201   if (commentDialog) {\r
2202     GetWindowPlacement(commentDialog, &wp);\r
2203     commentX = wp.rcNormalPosition.left;\r
2204     commentY = wp.rcNormalPosition.top;\r
2205     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2206     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2207   }\r
2208 \r
2209   if (editTagsDialog) {\r
2210     GetWindowPlacement(editTagsDialog, &wp);\r
2211     editTagsX = wp.rcNormalPosition.left;\r
2212     editTagsY = wp.rcNormalPosition.top;\r
2213     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2214     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2215   }\r
2216 \r
2217   if (gameListDialog) {\r
2218     GetWindowPlacement(gameListDialog, &wp);\r
2219     gameListX = wp.rcNormalPosition.left;\r
2220     gameListY = wp.rcNormalPosition.top;\r
2221     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2222     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2223   }\r
2224 \r
2225   /* [AS] Move history */\r
2226   wpMoveHistory.visible = MoveHistoryIsUp();\r
2227   \r
2228   if( moveHistoryDialog ) {\r
2229     GetWindowPlacement(moveHistoryDialog, &wp);\r
2230     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2231     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2232     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2233     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2234   }\r
2235 \r
2236   /* [AS] Eval graph */\r
2237   wpEvalGraph.visible = EvalGraphIsUp();\r
2238 \r
2239   if( evalGraphDialog ) {\r
2240     GetWindowPlacement(evalGraphDialog, &wp);\r
2241     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2242     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2243     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2244     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2245   }\r
2246 \r
2247   /* [AS] Engine output */\r
2248   wpEngineOutput.visible = EngineOutputIsUp();\r
2249 \r
2250   if( engineOutputDialog ) {\r
2251     GetWindowPlacement(engineOutputDialog, &wp);\r
2252     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2253     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2254     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2255     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2256   }\r
2257 \r
2258   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2259     if (!ad->save) continue;\r
2260     switch (ad->argType) {\r
2261     case ArgString:\r
2262       {\r
2263         char *p = *(char **)ad->argLoc;\r
2264         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2265           /* Quote multiline values or \-containing values\r
2266              with { } if possible */\r
2267           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2268         } else {\r
2269           /* Else quote with " " */\r
2270           fprintf(f, "/%s=\"", ad->argName);\r
2271           while (*p) {\r
2272             if (*p == '\n') fprintf(f, "\n");\r
2273             else if (*p == '\r') fprintf(f, "\\r");\r
2274             else if (*p == '\t') fprintf(f, "\\t");\r
2275             else if (*p == '\b') fprintf(f, "\\b");\r
2276             else if (*p == '\f') fprintf(f, "\\f");\r
2277             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2278             else if (*p == '\"') fprintf(f, "\\\"");\r
2279             else if (*p == '\\') fprintf(f, "\\\\");\r
2280             else putc(*p, f);\r
2281             p++;\r
2282           }\r
2283           fprintf(f, "\"\n");\r
2284         }\r
2285       }\r
2286       break;\r
2287     case ArgInt:\r
2288       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2289       break;\r
2290     case ArgFloat:\r
2291       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2292       break;\r
2293     case ArgBoolean:\r
2294       fprintf(f, "/%s=%s\n", ad->argName, \r
2295         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2296       break;\r
2297     case ArgTrue:\r
2298       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2299       break;\r
2300     case ArgFalse:\r
2301       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2302       break;\r
2303     case ArgColor:\r
2304       {\r
2305         COLORREF color = *(COLORREF *)ad->argLoc;\r
2306         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2307           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2308       }\r
2309       break;\r
2310     case ArgAttribs:\r
2311       {\r
2312         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2313         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2314           (ta->effects & CFE_BOLD) ? "b" : "",\r
2315           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2316           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2317           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2318           (ta->effects) ? " " : "",\r
2319           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2320       }\r
2321       break;\r
2322     case ArgFilename:\r
2323       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2324         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2325       } else {\r
2326         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2327       }\r
2328       break;\r
2329     case ArgBoardSize:\r
2330       fprintf(f, "/%s=%s\n", ad->argName,\r
2331               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2332       break;\r
2333     case ArgFont:\r
2334       {\r
2335         int bs;\r
2336         for (bs=0; bs<NUM_SIZES; bs++) {\r
2337           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2338           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2339           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2340             ad->argName, mfp->faceName, mfp->pointSize,\r
2341             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2342             mfp->bold ? "b" : "",\r
2343             mfp->italic ? "i" : "",\r
2344             mfp->underline ? "u" : "",\r
2345             mfp->strikeout ? "s" : "");\r
2346         }\r
2347       }\r
2348       break;\r
2349     case ArgCommSettings:\r
2350       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2351     case ArgNone:\r
2352     case ArgSettingsFilename: ;\r
2353     }\r
2354   }\r
2355   fclose(f);\r
2356 }\r
2357 \r
2358 \r
2359 \r
2360 /*---------------------------------------------------------------------------*\\r
2361  *\r
2362  * GDI board drawing routines\r
2363  *\r
2364 \*---------------------------------------------------------------------------*/\r
2365 \r
2366 /* [AS] Draw square using background texture */\r
2367 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2368 {\r
2369     XFORM   x;\r
2370 \r
2371     if( mode == 0 ) {\r
2372         return; /* Should never happen! */\r
2373     }\r
2374 \r
2375     SetGraphicsMode( dst, GM_ADVANCED );\r
2376 \r
2377     switch( mode ) {\r
2378     case 1:\r
2379         /* Identity */\r
2380         break;\r
2381     case 2:\r
2382         /* X reflection */\r
2383         x.eM11 = -1.0;\r
2384         x.eM12 = 0;\r
2385         x.eM21 = 0;\r
2386         x.eM22 = 1.0;\r
2387         x.eDx = (FLOAT) dw + dx - 1;\r
2388         x.eDy = 0;\r
2389         dx = 0;\r
2390         SetWorldTransform( dst, &x );\r
2391         break;\r
2392     case 3:\r
2393         /* Y reflection */\r
2394         x.eM11 = 1.0;\r
2395         x.eM12 = 0;\r
2396         x.eM21 = 0;\r
2397         x.eM22 = -1.0;\r
2398         x.eDx = 0;\r
2399         x.eDy = (FLOAT) dh + dy - 1;\r
2400         dy = 0;\r
2401         SetWorldTransform( dst, &x );\r
2402         break;\r
2403     case 4:\r
2404         /* X/Y flip */\r
2405         x.eM11 = 0;\r
2406         x.eM12 = 1.0;\r
2407         x.eM21 = 1.0;\r
2408         x.eM22 = 0;\r
2409         x.eDx = (FLOAT) dx;\r
2410         x.eDy = (FLOAT) dy;\r
2411         dx = 0;\r
2412         dy = 0;\r
2413         SetWorldTransform( dst, &x );\r
2414         break;\r
2415     }\r
2416 \r
2417     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2418 \r
2419     x.eM11 = 1.0;\r
2420     x.eM12 = 0;\r
2421     x.eM21 = 0;\r
2422     x.eM22 = 1.0;\r
2423     x.eDx = 0;\r
2424     x.eDy = 0;\r
2425     SetWorldTransform( dst, &x );\r
2426 \r
2427     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2428 }\r
2429 \r
2430 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2431 enum {\r
2432     PM_WP = (int) WhitePawn, \r
2433     PM_WN = (int) WhiteKnight, \r
2434     PM_WB = (int) WhiteBishop, \r
2435     PM_WR = (int) WhiteRook, \r
2436     PM_WQ = (int) WhiteQueen, \r
2437     PM_WF = (int) WhiteFerz, \r
2438     PM_WW = (int) WhiteWazir, \r
2439     PM_WE = (int) WhiteAlfil, \r
2440     PM_WM = (int) WhiteMan, \r
2441     PM_WO = (int) WhiteCannon, \r
2442     PM_WU = (int) WhiteUnicorn, \r
2443     PM_WH = (int) WhiteNightrider, \r
2444     PM_WA = (int) WhiteAngel, \r
2445     PM_WC = (int) WhiteMarshall, \r
2446     PM_WAB = (int) WhiteCardinal, \r
2447     PM_WD = (int) WhiteDragon, \r
2448     PM_WL = (int) WhiteLance, \r
2449     PM_WS = (int) WhiteCobra, \r
2450     PM_WV = (int) WhiteFalcon, \r
2451     PM_WSG = (int) WhiteSilver, \r
2452     PM_WG = (int) WhiteGrasshopper, \r
2453     PM_WK = (int) WhiteKing,\r
2454     PM_BP = (int) BlackPawn, \r
2455     PM_BN = (int) BlackKnight, \r
2456     PM_BB = (int) BlackBishop, \r
2457     PM_BR = (int) BlackRook, \r
2458     PM_BQ = (int) BlackQueen, \r
2459     PM_BF = (int) BlackFerz, \r
2460     PM_BW = (int) BlackWazir, \r
2461     PM_BE = (int) BlackAlfil, \r
2462     PM_BM = (int) BlackMan,\r
2463     PM_BO = (int) BlackCannon, \r
2464     PM_BU = (int) BlackUnicorn, \r
2465     PM_BH = (int) BlackNightrider, \r
2466     PM_BA = (int) BlackAngel, \r
2467     PM_BC = (int) BlackMarshall, \r
2468     PM_BG = (int) BlackGrasshopper, \r
2469     PM_BAB = (int) BlackCardinal,\r
2470     PM_BD = (int) BlackDragon,\r
2471     PM_BL = (int) BlackLance,\r
2472     PM_BS = (int) BlackCobra,\r
2473     PM_BV = (int) BlackFalcon,\r
2474     PM_BSG = (int) BlackSilver,\r
2475     PM_BK = (int) BlackKing\r
2476 };\r
2477 \r
2478 static HFONT hPieceFont = NULL;\r
2479 static HBITMAP hPieceMask[(int) EmptySquare];\r
2480 static HBITMAP hPieceFace[(int) EmptySquare];\r
2481 static int fontBitmapSquareSize = 0;\r
2482 static char pieceToFontChar[(int) EmptySquare] =\r
2483                               { 'p', 'n', 'b', 'r', 'q', \r
2484                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2485                       'k', 'o', 'm', 'v', 't', 'w', \r
2486                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2487                                                               'l' };\r
2488 \r
2489 extern BOOL SetCharTable( char *table, const char * map );\r
2490 /* [HGM] moved to backend.c */\r
2491 \r
2492 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2493 {\r
2494     HBRUSH hbrush;\r
2495     BYTE r1 = GetRValue( color );\r
2496     BYTE g1 = GetGValue( color );\r
2497     BYTE b1 = GetBValue( color );\r
2498     BYTE r2 = r1 / 2;\r
2499     BYTE g2 = g1 / 2;\r
2500     BYTE b2 = b1 / 2;\r
2501     RECT rc;\r
2502 \r
2503     /* Create a uniform background first */\r
2504     hbrush = CreateSolidBrush( color );\r
2505     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2506     FillRect( hdc, &rc, hbrush );\r
2507     DeleteObject( hbrush );\r
2508     \r
2509     if( mode == 1 ) {\r
2510         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2511         int steps = squareSize / 2;\r
2512         int i;\r
2513 \r
2514         for( i=0; i<steps; i++ ) {\r
2515             BYTE r = r1 - (r1-r2) * i / steps;\r
2516             BYTE g = g1 - (g1-g2) * i / steps;\r
2517             BYTE b = b1 - (b1-b2) * i / steps;\r
2518 \r
2519             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2520             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2521             FillRect( hdc, &rc, hbrush );\r
2522             DeleteObject(hbrush);\r
2523         }\r
2524     }\r
2525     else if( mode == 2 ) {\r
2526         /* Diagonal gradient, good more or less for every piece */\r
2527         POINT triangle[3];\r
2528         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2529         HBRUSH hbrush_old;\r
2530         int steps = squareSize;\r
2531         int i;\r
2532 \r
2533         triangle[0].x = squareSize - steps;\r
2534         triangle[0].y = squareSize;\r
2535         triangle[1].x = squareSize;\r
2536         triangle[1].y = squareSize;\r
2537         triangle[2].x = squareSize;\r
2538         triangle[2].y = squareSize - steps;\r
2539 \r
2540         for( i=0; i<steps; i++ ) {\r
2541             BYTE r = r1 - (r1-r2) * i / steps;\r
2542             BYTE g = g1 - (g1-g2) * i / steps;\r
2543             BYTE b = b1 - (b1-b2) * i / steps;\r
2544 \r
2545             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2546             hbrush_old = SelectObject( hdc, hbrush );\r
2547             Polygon( hdc, triangle, 3 );\r
2548             SelectObject( hdc, hbrush_old );\r
2549             DeleteObject(hbrush);\r
2550             triangle[0].x++;\r
2551             triangle[2].y++;\r
2552         }\r
2553 \r
2554         SelectObject( hdc, hpen );\r
2555     }\r
2556 }\r
2557 \r
2558 /*\r
2559     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2560     seems to work ok. The main problem here is to find the "inside" of a chess\r
2561     piece: follow the steps as explained below.\r
2562 */\r
2563 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2564 {\r
2565     HBITMAP hbm;\r
2566     HBITMAP hbm_old;\r
2567     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2568     RECT rc;\r
2569     SIZE sz;\r
2570     POINT pt;\r
2571     int backColor = whitePieceColor; \r
2572     int foreColor = blackPieceColor;\r
2573     \r
2574     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2575         backColor = appData.fontBackColorWhite;\r
2576         foreColor = appData.fontForeColorWhite;\r
2577     }\r
2578     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2579         backColor = appData.fontBackColorBlack;\r
2580         foreColor = appData.fontForeColorBlack;\r
2581     }\r
2582 \r
2583     /* Mask */\r
2584     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2585 \r
2586     hbm_old = SelectObject( hdc, hbm );\r
2587 \r
2588     rc.left = 0;\r
2589     rc.top = 0;\r
2590     rc.right = squareSize;\r
2591     rc.bottom = squareSize;\r
2592 \r
2593     /* Step 1: background is now black */\r
2594     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2595 \r
2596     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2597 \r
2598     pt.x = (squareSize - sz.cx) / 2;\r
2599     pt.y = (squareSize - sz.cy) / 2;\r
2600 \r
2601     SetBkMode( hdc, TRANSPARENT );\r
2602     SetTextColor( hdc, chroma );\r
2603     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2604     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2605 \r
2606     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2607     /* Step 3: the area outside the piece is filled with white */\r
2608 //    FloodFill( hdc, 0, 0, chroma );\r
2609     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2610     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2611     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2612     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2613     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2614     /* \r
2615         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2616         but if the start point is not inside the piece we're lost!\r
2617         There should be a better way to do this... if we could create a region or path\r
2618         from the fill operation we would be fine for example.\r
2619     */\r
2620 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2621     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2622 \r
2623     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2624         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2625         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2626 \r
2627         SelectObject( dc2, bm2 );\r
2628         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2629         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2630         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2631         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2632         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2633 \r
2634         DeleteDC( dc2 );\r
2635         DeleteObject( bm2 );\r
2636     }\r
2637 \r
2638     SetTextColor( hdc, 0 );\r
2639     /* \r
2640         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2641         draw the piece again in black for safety.\r
2642     */\r
2643     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2644 \r
2645     SelectObject( hdc, hbm_old );\r
2646 \r
2647     if( hPieceMask[index] != NULL ) {\r
2648         DeleteObject( hPieceMask[index] );\r
2649     }\r
2650 \r
2651     hPieceMask[index] = hbm;\r
2652 \r
2653     /* Face */\r
2654     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2655 \r
2656     SelectObject( hdc, hbm );\r
2657 \r
2658     {\r
2659         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2660         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2661         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2662 \r
2663         SelectObject( dc1, hPieceMask[index] );\r
2664         SelectObject( dc2, bm2 );\r
2665         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2666         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2667         \r
2668         /* \r
2669             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2670             the piece background and deletes (makes transparent) the rest.\r
2671             Thanks to that mask, we are free to paint the background with the greates\r
2672             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2673             We use this, to make gradients and give the pieces a "roundish" look.\r
2674         */\r
2675         SetPieceBackground( hdc, backColor, 2 );\r
2676         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2677 \r
2678         DeleteDC( dc2 );\r
2679         DeleteDC( dc1 );\r
2680         DeleteObject( bm2 );\r
2681     }\r
2682 \r
2683     SetTextColor( hdc, foreColor );\r
2684     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2685 \r
2686     SelectObject( hdc, hbm_old );\r
2687 \r
2688     if( hPieceFace[index] != NULL ) {\r
2689         DeleteObject( hPieceFace[index] );\r
2690     }\r
2691 \r
2692     hPieceFace[index] = hbm;\r
2693 }\r
2694 \r
2695 static int TranslatePieceToFontPiece( int piece )\r
2696 {\r
2697     switch( piece ) {\r
2698     case BlackPawn:\r
2699         return PM_BP;\r
2700     case BlackKnight:\r
2701         return PM_BN;\r
2702     case BlackBishop:\r
2703         return PM_BB;\r
2704     case BlackRook:\r
2705         return PM_BR;\r
2706     case BlackQueen:\r
2707         return PM_BQ;\r
2708     case BlackKing:\r
2709         return PM_BK;\r
2710     case WhitePawn:\r
2711         return PM_WP;\r
2712     case WhiteKnight:\r
2713         return PM_WN;\r
2714     case WhiteBishop:\r
2715         return PM_WB;\r
2716     case WhiteRook:\r
2717         return PM_WR;\r
2718     case WhiteQueen:\r
2719         return PM_WQ;\r
2720     case WhiteKing:\r
2721         return PM_WK;\r
2722 \r
2723     case BlackAngel:\r
2724         return PM_BA;\r
2725     case BlackMarshall:\r
2726         return PM_BC;\r
2727     case BlackFerz:\r
2728         return PM_BF;\r
2729     case BlackNightrider:\r
2730         return PM_BH;\r
2731     case BlackAlfil:\r
2732         return PM_BE;\r
2733     case BlackWazir:\r
2734         return PM_BW;\r
2735     case BlackUnicorn:\r
2736         return PM_BU;\r
2737     case BlackCannon:\r
2738         return PM_BO;\r
2739     case BlackGrasshopper:\r
2740         return PM_BG;\r
2741     case BlackMan:\r
2742         return PM_BM;\r
2743     case BlackSilver:\r
2744         return PM_BSG;\r
2745     case BlackLance:\r
2746         return PM_BL;\r
2747     case BlackFalcon:\r
2748         return PM_BV;\r
2749     case BlackCobra:\r
2750         return PM_BS;\r
2751     case BlackCardinal:\r
2752         return PM_BAB;\r
2753     case BlackDragon:\r
2754         return PM_BD;\r
2755 \r
2756     case WhiteAngel:\r
2757         return PM_WA;\r
2758     case WhiteMarshall:\r
2759         return PM_WC;\r
2760     case WhiteFerz:\r
2761         return PM_WF;\r
2762     case WhiteNightrider:\r
2763         return PM_WH;\r
2764     case WhiteAlfil:\r
2765         return PM_WE;\r
2766     case WhiteWazir:\r
2767         return PM_WW;\r
2768     case WhiteUnicorn:\r
2769         return PM_WU;\r
2770     case WhiteCannon:\r
2771         return PM_WO;\r
2772     case WhiteGrasshopper:\r
2773         return PM_WG;\r
2774     case WhiteMan:\r
2775         return PM_WM;\r
2776     case WhiteSilver:\r
2777         return PM_WSG;\r
2778     case WhiteLance:\r
2779         return PM_WL;\r
2780     case WhiteFalcon:\r
2781         return PM_WV;\r
2782     case WhiteCobra:\r
2783         return PM_WS;\r
2784     case WhiteCardinal:\r
2785         return PM_WAB;\r
2786     case WhiteDragon:\r
2787         return PM_WD;\r
2788     }\r
2789 \r
2790     return 0;\r
2791 }\r
2792 \r
2793 void CreatePiecesFromFont()\r
2794 {\r
2795     LOGFONT lf;\r
2796     HDC hdc_window = NULL;\r
2797     HDC hdc = NULL;\r
2798     HFONT hfont_old;\r
2799     int fontHeight;\r
2800     int i;\r
2801 \r
2802     if( fontBitmapSquareSize < 0 ) {\r
2803         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2804         return;\r
2805     }\r
2806 \r
2807     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2808         fontBitmapSquareSize = -1;\r
2809         return;\r
2810     }\r
2811 \r
2812     if( fontBitmapSquareSize != squareSize ) {\r
2813         hdc_window = GetDC( hwndMain );\r
2814         hdc = CreateCompatibleDC( hdc_window );\r
2815 \r
2816         if( hPieceFont != NULL ) {\r
2817             DeleteObject( hPieceFont );\r
2818         }\r
2819         else {\r
2820             for( i=0; i<=(int)BlackKing; i++ ) {\r
2821                 hPieceMask[i] = NULL;\r
2822                 hPieceFace[i] = NULL;\r
2823             }\r
2824         }\r
2825 \r
2826         fontHeight = 75;\r
2827 \r
2828         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2829             fontHeight = appData.fontPieceSize;\r
2830         }\r
2831 \r
2832         fontHeight = (fontHeight * squareSize) / 100;\r
2833 \r
2834         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2835         lf.lfWidth = 0;\r
2836         lf.lfEscapement = 0;\r
2837         lf.lfOrientation = 0;\r
2838         lf.lfWeight = FW_NORMAL;\r
2839         lf.lfItalic = 0;\r
2840         lf.lfUnderline = 0;\r
2841         lf.lfStrikeOut = 0;\r
2842         lf.lfCharSet = DEFAULT_CHARSET;\r
2843         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2844         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2845         lf.lfQuality = PROOF_QUALITY;\r
2846         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2847         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2848         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2849 \r
2850         hPieceFont = CreateFontIndirect( &lf );\r
2851 \r
2852         if( hPieceFont == NULL ) {\r
2853             fontBitmapSquareSize = -2;\r
2854         }\r
2855         else {\r
2856             /* Setup font-to-piece character table */\r
2857             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2858                 /* No (or wrong) global settings, try to detect the font */\r
2859                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2860                     /* Alpha */\r
2861                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2862                 }\r
2863                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2864                     /* DiagramTT* family */\r
2865                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2866                 }\r
2867                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2868                     /* Fairy symbols */\r
2869                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2870                 }\r
2871                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2872                     /* Good Companion (Some characters get warped as literal :-( */\r
2873                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2874                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2875                     SetCharTable(pieceToFontChar, s);\r
2876                 }\r
2877                 else {\r
2878                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2879                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2880                 }\r
2881             }\r
2882 \r
2883             /* Create bitmaps */\r
2884             hfont_old = SelectObject( hdc, hPieceFont );\r
2885 #if 0\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2898 \r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2912             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2913             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2914             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2915             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2916             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2917             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2918             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2919             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2920             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2921             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2922             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2923             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2924             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2925             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2926             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2927             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2931 #else\r
2932             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2933                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2934                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2935 #endif\r
2936             SelectObject( hdc, hfont_old );\r
2937 \r
2938             fontBitmapSquareSize = squareSize;\r
2939         }\r
2940     }\r
2941 \r
2942     if( hdc != NULL ) {\r
2943         DeleteDC( hdc );\r
2944     }\r
2945 \r
2946     if( hdc_window != NULL ) {\r
2947         ReleaseDC( hwndMain, hdc_window );\r
2948     }\r
2949 }\r
2950 \r
2951 HBITMAP\r
2952 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2953 {\r
2954   char name[128];\r
2955 \r
2956   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2957   if (gameInfo.event &&\r
2958       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2959       strcmp(name, "k80s") == 0) {\r
2960     strcpy(name, "tim");\r
2961   }\r
2962   return LoadBitmap(hinst, name);\r
2963 }\r
2964 \r
2965 \r
2966 /* Insert a color into the program's logical palette\r
2967    structure.  This code assumes the given color is\r
2968    the result of the RGB or PALETTERGB macro, and it\r
2969    knows how those macros work (which is documented).\r
2970 */\r
2971 VOID\r
2972 InsertInPalette(COLORREF color)\r
2973 {\r
2974   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2975 \r
2976   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2977     DisplayFatalError("Too many colors", 0, 1);\r
2978     pLogPal->palNumEntries--;\r
2979     return;\r
2980   }\r
2981 \r
2982   pe->peFlags = (char) 0;\r
2983   pe->peRed = (char) (0xFF & color);\r
2984   pe->peGreen = (char) (0xFF & (color >> 8));\r
2985   pe->peBlue = (char) (0xFF & (color >> 16));\r
2986   return;\r
2987 }\r
2988 \r
2989 \r
2990 VOID\r
2991 InitDrawingColors()\r
2992 {\r
2993   if (pLogPal == NULL) {\r
2994     /* Allocate enough memory for a logical palette with\r
2995      * PALETTESIZE entries and set the size and version fields\r
2996      * of the logical palette structure.\r
2997      */\r
2998     pLogPal = (NPLOGPALETTE)\r
2999       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3000                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3001     pLogPal->palVersion    = 0x300;\r
3002   }\r
3003   pLogPal->palNumEntries = 0;\r
3004 \r
3005   InsertInPalette(lightSquareColor);\r
3006   InsertInPalette(darkSquareColor);\r
3007   InsertInPalette(whitePieceColor);\r
3008   InsertInPalette(blackPieceColor);\r
3009   InsertInPalette(highlightSquareColor);\r
3010   InsertInPalette(premoveHighlightColor);\r
3011 \r
3012   /*  create a logical color palette according the information\r
3013    *  in the LOGPALETTE structure.\r
3014    */\r
3015   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3016 \r
3017   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3018   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3019   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3020   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3021   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3022   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3023   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3024   /* [AS] Force rendering of the font-based pieces */\r
3025   if( fontBitmapSquareSize > 0 ) {\r
3026     fontBitmapSquareSize = 0;\r
3027   }\r
3028 }\r
3029 \r
3030 \r
3031 int\r
3032 BoardWidth(int boardSize, int n)\r
3033 { /* [HGM] argument n added to allow different width and height */\r
3034   int lineGap = sizeInfo[boardSize].lineGap;\r
3035 \r
3036   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3037       lineGap = appData.overrideLineGap;\r
3038   }\r
3039 \r
3040   return (n + 1) * lineGap +\r
3041           n * sizeInfo[boardSize].squareSize;\r
3042 }\r
3043 \r
3044 /* Respond to board resize by dragging edge */\r
3045 VOID\r
3046 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3047 {\r
3048   BoardSize newSize = NUM_SIZES - 1;\r
3049   static int recurse = 0;\r
3050   if (IsIconic(hwndMain)) return;\r
3051   if (recurse > 0) return;\r
3052   recurse++;\r
3053   while (newSize > 0) {\r
3054         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3055         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3056            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3057     newSize--;\r
3058   } \r
3059   boardSize = newSize;\r
3060   InitDrawingSizes(boardSize, flags);\r
3061   recurse--;\r
3062 }\r
3063 \r
3064 \r
3065 \r
3066 VOID\r
3067 InitDrawingSizes(BoardSize boardSize, int flags)\r
3068 {\r
3069   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3070   ChessSquare piece;\r
3071   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3072   HDC hdc;\r
3073   SIZE clockSize, messageSize;\r
3074   HFONT oldFont;\r
3075   char buf[MSG_SIZ];\r
3076   char *str;\r
3077   HMENU hmenu = GetMenu(hwndMain);\r
3078   RECT crect, wrect;\r
3079   int offby;\r
3080   LOGBRUSH logbrush;\r
3081 \r
3082   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3083   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3084 \r
3085   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3086   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3087 \r
3088   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3089   smallLayout = sizeInfo[boardSize].smallLayout;\r
3090   squareSize = sizeInfo[boardSize].squareSize;\r
3091   lineGap = sizeInfo[boardSize].lineGap;\r
3092   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3093 \r
3094   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3095       lineGap = appData.overrideLineGap;\r
3096   }\r
3097 \r
3098   if (tinyLayout != oldTinyLayout) {\r
3099     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3100     if (tinyLayout) {\r
3101       style &= ~WS_SYSMENU;\r
3102       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3103                  "&Minimize\tCtrl+F4");\r
3104     } else {\r
3105       style |= WS_SYSMENU;\r
3106       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3107     }\r
3108     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3109 \r
3110     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3111       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3112         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3113     }\r
3114     DrawMenuBar(hwndMain);\r
3115   }\r
3116 \r
3117   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3118   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3119 \r
3120   /* Get text area sizes */\r
3121   hdc = GetDC(hwndMain);\r
3122   if (appData.clockMode) {\r
3123     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3124   } else {\r
3125     sprintf(buf, "White");\r
3126   }\r
3127   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3128   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3129   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3130   str = "We only care about the height here";\r
3131   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3132   SelectObject(hdc, oldFont);\r
3133   ReleaseDC(hwndMain, hdc);\r
3134 \r
3135   /* Compute where everything goes */\r
3136   if(first.programLogo || second.programLogo) {\r
3137         /* [HGM] logo: if either logo is on, reserve space for it */\r
3138         logoHeight =  2*clockSize.cy;\r
3139         leftLogoRect.left   = OUTER_MARGIN;\r
3140         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3141         leftLogoRect.top    = OUTER_MARGIN;\r
3142         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3143 \r
3144         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3145         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3146         rightLogoRect.top    = OUTER_MARGIN;\r
3147         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3148 \r
3149 \r
3150     blackRect.left = leftLogoRect.right;\r
3151     blackRect.right = rightLogoRect.left;\r
3152     blackRect.top = OUTER_MARGIN;\r
3153     blackRect.bottom = blackRect.top + clockSize.cy;\r
3154 \r
3155     whiteRect.left = blackRect.left ;\r
3156     whiteRect.right = blackRect.right;\r
3157     whiteRect.top = blackRect.bottom;\r
3158     whiteRect.bottom = leftLogoRect.bottom;\r
3159   } else {\r
3160     whiteRect.left = OUTER_MARGIN;\r
3161     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3162     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3163     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3164 \r
3165     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3166     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3167     blackRect.top = whiteRect.top;\r
3168     blackRect.bottom = whiteRect.bottom;\r
3169   }\r
3170 \r
3171   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3172   if (appData.showButtonBar) {\r
3173     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3174       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3175   } else {\r
3176     messageRect.right = OUTER_MARGIN + boardWidth;\r
3177   }\r
3178   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3179   messageRect.bottom = messageRect.top + messageSize.cy;\r
3180 \r
3181   boardRect.left = OUTER_MARGIN;\r
3182   boardRect.right = boardRect.left + boardWidth;\r
3183   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3184   boardRect.bottom = boardRect.top + boardHeight;\r
3185 \r
3186   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3187   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3188   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3189   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3190   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3191     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3192   GetWindowRect(hwndMain, &wrect);\r
3193   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3194                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3195   /* compensate if menu bar wrapped */\r
3196   GetClientRect(hwndMain, &crect);\r
3197   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3198   winHeight += offby;\r
3199   switch (flags) {\r
3200   case WMSZ_TOPLEFT:\r
3201     SetWindowPos(hwndMain, NULL, \r
3202                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3203                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3204     break;\r
3205 \r
3206   case WMSZ_TOPRIGHT:\r
3207   case WMSZ_TOP:\r
3208     SetWindowPos(hwndMain, NULL, \r
3209                  wrect.left, wrect.bottom - winHeight, \r
3210                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3211     break;\r
3212 \r
3213   case WMSZ_BOTTOMLEFT:\r
3214   case WMSZ_LEFT:\r
3215     SetWindowPos(hwndMain, NULL, \r
3216                  wrect.right - winWidth, wrect.top, \r
3217                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3218     break;\r
3219 \r
3220   case WMSZ_BOTTOMRIGHT:\r
3221   case WMSZ_BOTTOM:\r
3222   case WMSZ_RIGHT:\r
3223   default:\r
3224     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3225                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3226     break;\r
3227   }\r
3228 \r
3229   hwndPause = NULL;\r
3230   for (i = 0; i < N_BUTTONS; i++) {\r
3231     if (buttonDesc[i].hwnd != NULL) {\r
3232       DestroyWindow(buttonDesc[i].hwnd);\r
3233       buttonDesc[i].hwnd = NULL;\r
3234     }\r
3235     if (appData.showButtonBar) {\r
3236       buttonDesc[i].hwnd =\r
3237         CreateWindow("BUTTON", buttonDesc[i].label,\r
3238                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3239                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3240                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3241                      (HMENU) buttonDesc[i].id,\r
3242                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3243       if (tinyLayout) {\r
3244         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3245                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3246                     MAKELPARAM(FALSE, 0));\r
3247       }\r
3248       if (buttonDesc[i].id == IDM_Pause)\r
3249         hwndPause = buttonDesc[i].hwnd;\r
3250       buttonDesc[i].wndproc = (WNDPROC)\r
3251         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3252     }\r
3253   }\r
3254   if (gridPen != NULL) DeleteObject(gridPen);\r
3255   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3256   if (premovePen != NULL) DeleteObject(premovePen);\r
3257   if (lineGap != 0) {\r
3258     logbrush.lbStyle = BS_SOLID;\r
3259     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3260     gridPen =\r
3261       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3262                    lineGap, &logbrush, 0, NULL);\r
3263     logbrush.lbColor = highlightSquareColor;\r
3264     highlightPen =\r
3265       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3266                    lineGap, &logbrush, 0, NULL);\r
3267 \r
3268     logbrush.lbColor = premoveHighlightColor; \r
3269     premovePen =\r
3270       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3271                    lineGap, &logbrush, 0, NULL);\r
3272 \r
3273     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3274     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3275       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3276       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3277         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3278       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3279         BOARD_WIDTH * (squareSize + lineGap);\r
3280       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3281     }\r
3282     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3283       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3284       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3285         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3286         lineGap / 2 + (i * (squareSize + lineGap));\r
3287       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3288         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3289       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3290     }\r
3291   }\r
3292 \r
3293   /* [HGM] Licensing requirement */\r
3294 #ifdef GOTHIC\r
3295   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3296 #endif\r
3297 #ifdef FALCON\r
3298   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3299 #endif\r
3300   GothicPopUp( "", VariantNormal);\r
3301 \r
3302 \r
3303 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3304   oldBoardSize = boardSize;\r
3305   oldTinyLayout = tinyLayout;\r
3306 \r
3307   /* Load piece bitmaps for this board size */\r
3308   for (i=0; i<=2; i++) {\r
3309     for (piece = WhitePawn;\r
3310          (int) piece < (int) BlackPawn;\r
3311          piece = (ChessSquare) ((int) piece + 1)) {\r
3312       if (pieceBitmap[i][piece] != NULL)\r
3313         DeleteObject(pieceBitmap[i][piece]);\r
3314     }\r
3315   }\r
3316 \r
3317   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3318   // Orthodox Chess pieces\r
3319   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3320   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3321   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3322   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3323   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3324   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3325   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3326   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3327   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3328   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3329   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3330   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3331   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3332   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3333   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3334   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3335     // in Shogi, Hijack the unused Queen for Lance\r
3336     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3337     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3338     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3339   } else {\r
3340     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3341     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3342     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3343   }\r
3344 \r
3345   if(squareSize <= 72 && squareSize >= 33) { \r
3346     /* A & C are available in most sizes now */\r
3347     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3348       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3349       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3350       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3351       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3352       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3353       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3354       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3355       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3356       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3357       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3358       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3359       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3360     } else { // Smirf-like\r
3361       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3362       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3363       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3364     }\r
3365     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3366       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3367       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3368       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3369     } else { // WinBoard standard\r
3370       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3371       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3372       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3373     }\r
3374   }\r
3375 \r
3376 \r
3377   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3378     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3379     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3380     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3381     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3382     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3383     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3384     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3385     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3386     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3387     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3388     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3389     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3390     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3391     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3392     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3393     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3394     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3395     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3396     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3397     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3398     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3399     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3400     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3401     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3402     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3403     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3404     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3405     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3406     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3407     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3408 \r
3409     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3410       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3411       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3412       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3413       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3414       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3415       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3416       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3417       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3418       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3419       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3420       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3421       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3422     } else {\r
3423       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3424       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3425       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3426       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3427       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3428       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3429       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3430       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3431       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3432       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3433       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3434       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3435     }\r
3436 \r
3437   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3438     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3439     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3440     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3441     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3442     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3443     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3444     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3445     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3446     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3447     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3448     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3449     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3450     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3451     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3452   }\r
3453 \r
3454 \r
3455   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3456   /* special Shogi support in this size */\r
3457   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3458       for (piece = WhitePawn;\r
3459            (int) piece < (int) BlackPawn;\r
3460            piece = (ChessSquare) ((int) piece + 1)) {\r
3461         if (pieceBitmap[i][piece] != NULL)\r
3462           DeleteObject(pieceBitmap[i][piece]);\r
3463       }\r
3464     }\r
3465   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3466   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3467   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3468   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3469   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3470   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3471   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3472   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3473   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3474   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3475   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3476   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3477   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3478   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3479   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3480   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3481   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3482   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3483   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3484   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3485   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3486   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3487   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3488   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3489   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3490   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3491   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3492   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3493   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3494   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3495   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3496   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3497   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3498   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3499   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3500   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3501   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3502   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3503   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3504   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3505   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3506   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3507   minorSize = 0;\r
3508   }\r
3509 }\r
3510 \r
3511 HBITMAP\r
3512 PieceBitmap(ChessSquare p, int kind)\r
3513 {\r
3514   if ((int) p >= (int) BlackPawn)\r
3515     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3516 \r
3517   return pieceBitmap[kind][(int) p];\r
3518 }\r
3519 \r
3520 /***************************************************************/\r
3521 \r
3522 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3523 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3524 /*\r
3525 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3526 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3527 */\r
3528 \r
3529 VOID\r
3530 SquareToPos(int row, int column, int * x, int * y)\r
3531 {\r
3532   if (flipView) {\r
3533     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3534     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3535   } else {\r
3536     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3537     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3538   }\r
3539 }\r
3540 \r
3541 VOID\r
3542 DrawCoordsOnDC(HDC hdc)\r
3543 {\r
3544   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
3545   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
3546   char str[2] = { NULLCHAR, NULLCHAR };\r
3547   int oldMode, oldAlign, x, y, start, i;\r
3548   HFONT oldFont;\r
3549   HBRUSH oldBrush;\r
3550 \r
3551   if (!appData.showCoords)\r
3552     return;\r
3553 \r
3554   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3555 \r
3556   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3557   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3558   oldAlign = GetTextAlign(hdc);\r
3559   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3560 \r
3561   y = boardRect.top + lineGap;\r
3562   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3563 \r
3564   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3565   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3566     str[0] = files[start + i];\r
3567     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3568     y += squareSize + lineGap;\r
3569   }\r
3570 \r
3571   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3572 \r
3573   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3574   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3575     str[0] = ranks[start + i];\r
3576     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3577     x += squareSize + lineGap;\r
3578   }    \r
3579 \r
3580   SelectObject(hdc, oldBrush);\r
3581   SetBkMode(hdc, oldMode);\r
3582   SetTextAlign(hdc, oldAlign);\r
3583   SelectObject(hdc, oldFont);\r
3584 }\r
3585 \r
3586 VOID\r
3587 DrawGridOnDC(HDC hdc)\r
3588 {\r
3589   HPEN oldPen;\r
3590  \r
3591   if (lineGap != 0) {\r
3592     oldPen = SelectObject(hdc, gridPen);\r
3593     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3594     SelectObject(hdc, oldPen);\r
3595   }\r
3596 }\r
3597 \r
3598 #define HIGHLIGHT_PEN 0\r
3599 #define PREMOVE_PEN   1\r
3600 \r
3601 VOID\r
3602 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3603 {\r
3604   int x1, y1;\r
3605   HPEN oldPen, hPen;\r
3606   if (lineGap == 0) return;\r
3607   if (flipView) {\r
3608     x1 = boardRect.left +\r
3609       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3610     y1 = boardRect.top +\r
3611       lineGap/2 + y * (squareSize + lineGap);\r
3612   } else {\r
3613     x1 = boardRect.left +\r
3614       lineGap/2 + x * (squareSize + lineGap);\r
3615     y1 = boardRect.top +\r
3616       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3617   }\r
3618   hPen = pen ? premovePen : highlightPen;\r
3619   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3620   MoveToEx(hdc, x1, y1, NULL);\r
3621   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3622   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3623   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3624   LineTo(hdc, x1, y1);\r
3625   SelectObject(hdc, oldPen);\r
3626 }\r
3627 \r
3628 VOID\r
3629 DrawHighlightsOnDC(HDC hdc)\r
3630 {\r
3631   int i;\r
3632   for (i=0; i<2; i++) {\r
3633     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3634       DrawHighlightOnDC(hdc, TRUE,\r
3635                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3636                         HIGHLIGHT_PEN);\r
3637   }\r
3638   for (i=0; i<2; i++) {\r
3639     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3640         premoveHighlightInfo.sq[i].y >= 0) {\r
3641         DrawHighlightOnDC(hdc, TRUE,\r
3642                           premoveHighlightInfo.sq[i].x, \r
3643                           premoveHighlightInfo.sq[i].y,\r
3644                           PREMOVE_PEN);\r
3645     }\r
3646   }\r
3647 }\r
3648 \r
3649 /* Note: sqcolor is used only in monoMode */\r
3650 /* Note that this code is largely duplicated in woptions.c,\r
3651    function DrawSampleSquare, so that needs to be updated too */\r
3652 VOID\r
3653 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3654 {\r
3655   HBITMAP oldBitmap;\r
3656   HBRUSH oldBrush;\r
3657   int tmpSize;\r
3658 \r
3659   if (appData.blindfold) return;\r
3660 \r
3661   /* [AS] Use font-based pieces if needed */\r
3662   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3663     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3664     CreatePiecesFromFont();\r
3665 \r
3666     if( fontBitmapSquareSize == squareSize ) {\r
3667         int index = TranslatePieceToFontPiece(piece);\r
3668 \r
3669         SelectObject( tmphdc, hPieceMask[ index ] );\r
3670 \r
3671         BitBlt( hdc,\r
3672             x, y,\r
3673             squareSize, squareSize,\r
3674             tmphdc,\r
3675             0, 0,\r
3676             SRCAND );\r
3677 \r
3678         SelectObject( tmphdc, hPieceFace[ index ] );\r
3679 \r
3680         BitBlt( hdc,\r
3681             x, y,\r
3682             squareSize, squareSize,\r
3683             tmphdc,\r
3684             0, 0,\r
3685             SRCPAINT );\r
3686 \r
3687         return;\r
3688     }\r
3689   }\r
3690 \r
3691   if (appData.monoMode) {\r
3692     SelectObject(tmphdc, PieceBitmap(piece, \r
3693       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3694     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3695            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3696   } else {\r
3697     tmpSize = squareSize;\r
3698     if(minorSize &&\r
3699         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3700          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3701       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3702       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3703       x += (squareSize - minorSize)>>1;\r
3704       y += squareSize - minorSize - 2;\r
3705       tmpSize = minorSize;\r
3706     }\r
3707     if (color || appData.allWhite ) {\r
3708       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3709       if( color )\r
3710               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3711       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3712       if(appData.upsideDown && color==flipView)\r
3713         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3714       else\r
3715         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3716 #if 0\r
3717       /* Use black piece color for outline of white pieces */\r
3718       /* Not sure this looks really good (though xboard does it).\r
3719          Maybe better to have another selectable color, default black */\r
3720       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3721       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3722       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3723 #else\r
3724       /* Use black for outline of white pieces */\r
3725       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3726       if(appData.upsideDown && color==flipView)\r
3727         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3728       else\r
3729         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3730 #endif\r
3731     } else {\r
3732 #if 0\r
3733       /* Use white piece color for details of black pieces */\r
3734       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3735          WHITE_PIECE ones aren't always the right shape. */\r
3736       /* Not sure this looks really good (though xboard does it).\r
3737          Maybe better to have another selectable color, default medium gray? */\r
3738       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3739       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3740       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3741       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3742       SelectObject(hdc, blackPieceBrush);\r
3743       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3744 #else\r
3745       /* Use square color for details of black pieces */\r
3746       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3747       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3748       if(appData.upsideDown && !flipView)\r
3749         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3750       else\r
3751         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3752 #endif\r
3753     }\r
3754     SelectObject(hdc, oldBrush);\r
3755     SelectObject(tmphdc, oldBitmap);\r
3756   }\r
3757 }\r
3758 \r
3759 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3760 int GetBackTextureMode( int algo )\r
3761 {\r
3762     int result = BACK_TEXTURE_MODE_DISABLED;\r
3763 \r
3764     switch( algo ) \r
3765     {\r
3766         case BACK_TEXTURE_MODE_PLAIN:\r
3767             result = 1; /* Always use identity map */\r
3768             break;\r
3769         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3770             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3771             break;\r
3772     }\r
3773 \r
3774     return result;\r
3775 }\r
3776 \r
3777 /* \r
3778     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3779     to handle redraws cleanly (as random numbers would always be different).\r
3780 */\r
3781 VOID RebuildTextureSquareInfo()\r
3782 {\r
3783     BITMAP bi;\r
3784     int lite_w = 0;\r
3785     int lite_h = 0;\r
3786     int dark_w = 0;\r
3787     int dark_h = 0;\r
3788     int row;\r
3789     int col;\r
3790 \r
3791     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3792 \r
3793     if( liteBackTexture != NULL ) {\r
3794         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3795             lite_w = bi.bmWidth;\r
3796             lite_h = bi.bmHeight;\r
3797         }\r
3798     }\r
3799 \r
3800     if( darkBackTexture != NULL ) {\r
3801         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3802             dark_w = bi.bmWidth;\r
3803             dark_h = bi.bmHeight;\r
3804         }\r
3805     }\r
3806 \r
3807     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3808         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3809             if( (col + row) & 1 ) {\r
3810                 /* Lite square */\r
3811                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3812                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3813                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3814                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3815                 }\r
3816             }\r
3817             else {\r
3818                 /* Dark square */\r
3819                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3820                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3821                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3822                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3823                 }\r
3824             }\r
3825         }\r
3826     }\r
3827 }\r
3828 \r
3829 /* [AS] Arrow highlighting support */\r
3830 \r
3831 static int A_WIDTH = 5; /* Width of arrow body */\r
3832 \r
3833 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3834 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3835 \r
3836 static double Sqr( double x )\r
3837 {\r
3838     return x*x;\r
3839 }\r
3840 \r
3841 static int Round( double x )\r
3842 {\r
3843     return (int) (x + 0.5);\r
3844 }\r
3845 \r
3846 /* Draw an arrow between two points using current settings */\r
3847 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3848 {\r
3849     POINT arrow[7];\r
3850     double dx, dy, j, k, x, y;\r
3851 \r
3852     if( d_x == s_x ) {\r
3853         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3854 \r
3855         arrow[0].x = s_x + A_WIDTH;\r
3856         arrow[0].y = s_y;\r
3857 \r
3858         arrow[1].x = s_x + A_WIDTH;\r
3859         arrow[1].y = d_y - h;\r
3860 \r
3861         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3862         arrow[2].y = d_y - h;\r
3863 \r
3864         arrow[3].x = d_x;\r
3865         arrow[3].y = d_y;\r
3866 \r
3867         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3868         arrow[4].y = d_y - h;\r
3869 \r
3870         arrow[5].x = s_x - A_WIDTH;\r
3871         arrow[5].y = d_y - h;\r
3872 \r
3873         arrow[6].x = s_x - A_WIDTH;\r
3874         arrow[6].y = s_y;\r
3875     }\r
3876     else if( d_y == s_y ) {\r
3877         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3878 \r
3879         arrow[0].x = s_x;\r
3880         arrow[0].y = s_y + A_WIDTH;\r
3881 \r
3882         arrow[1].x = d_x - w;\r
3883         arrow[1].y = s_y + A_WIDTH;\r
3884 \r
3885         arrow[2].x = d_x - w;\r
3886         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3887 \r
3888         arrow[3].x = d_x;\r
3889         arrow[3].y = d_y;\r
3890 \r
3891         arrow[4].x = d_x - w;\r
3892         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3893 \r
3894         arrow[5].x = d_x - w;\r
3895         arrow[5].y = s_y - A_WIDTH;\r
3896 \r
3897         arrow[6].x = s_x;\r
3898         arrow[6].y = s_y - A_WIDTH;\r
3899     }\r
3900     else {\r
3901         /* [AS] Needed a lot of paper for this! :-) */\r
3902         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3903         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3904   \r
3905         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3906 \r
3907         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3908 \r
3909         x = s_x;\r
3910         y = s_y;\r
3911 \r
3912         arrow[0].x = Round(x - j);\r
3913         arrow[0].y = Round(y + j*dx);\r
3914 \r
3915         arrow[1].x = Round(x + j);\r
3916         arrow[1].y = Round(y - j*dx);\r
3917 \r
3918         if( d_x > s_x ) {\r
3919             x = (double) d_x - k;\r
3920             y = (double) d_y - k*dy;\r
3921         }\r
3922         else {\r
3923             x = (double) d_x + k;\r
3924             y = (double) d_y + k*dy;\r
3925         }\r
3926 \r
3927         arrow[2].x = Round(x + j);\r
3928         arrow[2].y = Round(y - j*dx);\r
3929 \r
3930         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3931         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3932 \r
3933         arrow[4].x = d_x;\r
3934         arrow[4].y = d_y;\r
3935 \r
3936         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3937         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3938 \r
3939         arrow[6].x = Round(x - j);\r
3940         arrow[6].y = Round(y + j*dx);\r
3941     }\r
3942 \r
3943     Polygon( hdc, arrow, 7 );\r
3944 }\r
3945 \r
3946 /* [AS] Draw an arrow between two squares */\r
3947 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3948 {\r
3949     int s_x, s_y, d_x, d_y;\r
3950     HPEN hpen;\r
3951     HPEN holdpen;\r
3952     HBRUSH hbrush;\r
3953     HBRUSH holdbrush;\r
3954     LOGBRUSH stLB;\r
3955 \r
3956     if( s_col == d_col && s_row == d_row ) {\r
3957         return;\r
3958     }\r
3959 \r
3960     /* Get source and destination points */\r
3961     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3962     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3963 \r
3964     if( d_y > s_y ) {\r
3965         d_y += squareSize / 4;\r
3966     }\r
3967     else if( d_y < s_y ) {\r
3968         d_y += 3 * squareSize / 4;\r
3969     }\r
3970     else {\r
3971         d_y += squareSize / 2;\r
3972     }\r
3973 \r
3974     if( d_x > s_x ) {\r
3975         d_x += squareSize / 4;\r
3976     }\r
3977     else if( d_x < s_x ) {\r
3978         d_x += 3 * squareSize / 4;\r
3979     }\r
3980     else {\r
3981         d_x += squareSize / 2;\r
3982     }\r
3983 \r
3984     s_x += squareSize / 2;\r
3985     s_y += squareSize / 2;\r
3986 \r
3987     /* Adjust width */\r
3988     A_WIDTH = squareSize / 14;\r
3989 \r
3990     /* Draw */\r
3991     stLB.lbStyle = BS_SOLID;\r
3992     stLB.lbColor = appData.highlightArrowColor;\r
3993     stLB.lbHatch = 0;\r
3994 \r
3995     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3996     holdpen = SelectObject( hdc, hpen );\r
3997     hbrush = CreateBrushIndirect( &stLB );\r
3998     holdbrush = SelectObject( hdc, hbrush );\r
3999 \r
4000     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4001 \r
4002     SelectObject( hdc, holdpen );\r
4003     SelectObject( hdc, holdbrush );\r
4004     DeleteObject( hpen );\r
4005     DeleteObject( hbrush );\r
4006 }\r
4007 \r
4008 BOOL HasHighlightInfo()\r
4009 {\r
4010     BOOL result = FALSE;\r
4011 \r
4012     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4013         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4014     {\r
4015         result = TRUE;\r
4016     }\r
4017 \r
4018     return result;\r
4019 }\r
4020 \r
4021 BOOL IsDrawArrowEnabled()\r
4022 {\r
4023     BOOL result = FALSE;\r
4024 \r
4025     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4026         result = TRUE;\r
4027     }\r
4028 \r
4029     return result;\r
4030 }\r
4031 \r
4032 VOID DrawArrowHighlight( HDC hdc )\r
4033 {\r
4034     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4035         DrawArrowBetweenSquares( hdc,\r
4036             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4037             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4038     }\r
4039 }\r
4040 \r
4041 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4042 {\r
4043     HRGN result = NULL;\r
4044 \r
4045     if( HasHighlightInfo() ) {\r
4046         int x1, y1, x2, y2;\r
4047         int sx, sy, dx, dy;\r
4048 \r
4049         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4050         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4051 \r
4052         sx = MIN( x1, x2 );\r
4053         sy = MIN( y1, y2 );\r
4054         dx = MAX( x1, x2 ) + squareSize;\r
4055         dy = MAX( y1, y2 ) + squareSize;\r
4056 \r
4057         result = CreateRectRgn( sx, sy, dx, dy );\r
4058     }\r
4059 \r
4060     return result;\r
4061 }\r
4062 \r
4063 /*\r
4064     Warning: this function modifies the behavior of several other functions. \r
4065     \r
4066     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4067     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4068     repaint is scattered all over the place, which is not good for features such as\r
4069     "arrow highlighting" that require a full repaint of the board.\r
4070 \r
4071     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4072     user interaction, when speed is not so important) but especially to avoid errors\r
4073     in the displayed graphics.\r
4074 \r
4075     In such patched places, I always try refer to this function so there is a single\r
4076     place to maintain knowledge.\r
4077     \r
4078     To restore the original behavior, just return FALSE unconditionally.\r
4079 */\r
4080 BOOL IsFullRepaintPreferrable()\r
4081 {\r
4082     BOOL result = FALSE;\r
4083 \r
4084     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4085         /* Arrow may appear on the board */\r
4086         result = TRUE;\r
4087     }\r
4088 \r
4089     return result;\r
4090 }\r
4091 \r
4092 /* \r
4093     This function is called by DrawPosition to know whether a full repaint must\r
4094     be forced or not.\r
4095 \r
4096     Only DrawPosition may directly call this function, which makes use of \r
4097     some state information. Other function should call DrawPosition specifying \r
4098     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4099 */\r
4100 BOOL DrawPositionNeedsFullRepaint()\r
4101 {\r
4102     BOOL result = FALSE;\r
4103 \r
4104     /* \r
4105         Probably a slightly better policy would be to trigger a full repaint\r
4106         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4107         but animation is fast enough that it's difficult to notice.\r
4108     */\r
4109     if( animInfo.piece == EmptySquare ) {\r
4110         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4111             result = TRUE;\r
4112         }\r
4113     }\r
4114 \r
4115     return result;\r
4116 }\r
4117 \r
4118 VOID\r
4119 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4120 {\r
4121   int row, column, x, y, square_color, piece_color;\r
4122   ChessSquare piece;\r
4123   HBRUSH oldBrush;\r
4124   HDC texture_hdc = NULL;\r
4125 \r
4126   /* [AS] Initialize background textures if needed */\r
4127   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4128       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4129       if( backTextureSquareSize != squareSize \r
4130        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4131           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4132           backTextureSquareSize = squareSize;\r
4133           RebuildTextureSquareInfo();\r
4134       }\r
4135 \r
4136       texture_hdc = CreateCompatibleDC( hdc );\r
4137   }\r
4138 \r
4139   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4140     for (column = 0; column < BOARD_WIDTH; column++) {\r
4141   \r
4142       SquareToPos(row, column, &x, &y);\r
4143 \r
4144       piece = board[row][column];\r
4145 \r
4146       square_color = ((column + row) % 2) == 1;\r
4147       if( gameInfo.variant == VariantXiangqi ) {\r
4148           square_color = !InPalace(row, column);\r
4149           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4150           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4151       }\r
4152       piece_color = (int) piece < (int) BlackPawn;\r
4153 \r
4154 \r
4155       /* [HGM] holdings file: light square or black */\r
4156       if(column == BOARD_LEFT-2) {\r
4157             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4158                 square_color = 1;\r
4159             else {\r
4160                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4161                 continue;\r
4162             }\r
4163       } else\r
4164       if(column == BOARD_RGHT + 1 ) {\r
4165             if( row < gameInfo.holdingsSize )\r
4166                 square_color = 1;\r
4167             else {\r
4168                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4169                 continue;\r
4170             }\r
4171       }\r
4172       if(column == BOARD_LEFT-1 ) /* left align */\r
4173             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4174       else if( column == BOARD_RGHT) /* right align */\r
4175             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4176       else\r
4177       if (appData.monoMode) {\r
4178         if (piece == EmptySquare) {\r
4179           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4180                  square_color ? WHITENESS : BLACKNESS);\r
4181         } else {\r
4182           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4183         }\r
4184       } \r
4185       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4186           /* [AS] Draw the square using a texture bitmap */\r
4187           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4188           int r = row, c = column; // [HGM] do not flip board in flipView\r
4189           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4190 \r
4191           DrawTile( x, y, \r
4192               squareSize, squareSize, \r
4193               hdc, \r
4194               texture_hdc,\r
4195               backTextureSquareInfo[r][c].mode,\r
4196               backTextureSquareInfo[r][c].x,\r
4197               backTextureSquareInfo[r][c].y );\r
4198 \r
4199           SelectObject( texture_hdc, hbm );\r
4200 \r
4201           if (piece != EmptySquare) {\r
4202               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4203           }\r
4204       }\r
4205       else {\r
4206         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4207 \r
4208         oldBrush = SelectObject(hdc, brush );\r
4209         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4210         SelectObject(hdc, oldBrush);\r
4211         if (piece != EmptySquare)\r
4212           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4213       }\r
4214     }\r
4215   }\r
4216 \r
4217   if( texture_hdc != NULL ) {\r
4218     DeleteDC( texture_hdc );\r
4219   }\r
4220 }\r
4221 \r
4222 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4223 void fputDW(FILE *f, int x)\r
4224 {\r
4225         fputc(x     & 255, f);\r
4226         fputc(x>>8  & 255, f);\r
4227         fputc(x>>16 & 255, f);\r
4228         fputc(x>>24 & 255, f);\r
4229 }\r
4230 \r
4231 #define MAX_CLIPS 200   /* more than enough */\r
4232 \r
4233 VOID\r
4234 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4235 {\r
4236 //  HBITMAP bufferBitmap;\r
4237   BITMAP bi;\r
4238 //  RECT Rect;\r
4239   HDC tmphdc;\r
4240   HBITMAP hbm;\r
4241   int w = 100, h = 50;\r
4242 \r
4243   if(cps->programLogo == NULL) return;\r
4244 //  GetClientRect(hwndMain, &Rect);\r
4245 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4246 //                                      Rect.bottom-Rect.top+1);\r
4247   tmphdc = CreateCompatibleDC(hdc);\r
4248   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4249   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4250             w = bi.bmWidth;\r
4251             h = bi.bmHeight;\r
4252   }\r
4253   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4254                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4255   SelectObject(tmphdc, hbm);\r
4256   DeleteDC(tmphdc);\r
4257 }\r
4258 \r
4259 VOID\r
4260 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4261 {\r
4262   static Board lastReq, lastDrawn;\r
4263   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4264   static int lastDrawnFlipView = 0;\r
4265   static int lastReqValid = 0, lastDrawnValid = 0;\r
4266   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4267   HDC tmphdc;\r
4268   HDC hdcmem;\r
4269   HBITMAP bufferBitmap;\r
4270   HBITMAP oldBitmap;\r
4271   RECT Rect;\r
4272   HRGN clips[MAX_CLIPS];\r
4273   ChessSquare dragged_piece = EmptySquare;\r
4274 \r
4275   /* I'm undecided on this - this function figures out whether a full\r
4276    * repaint is necessary on its own, so there's no real reason to have the\r
4277    * caller tell it that.  I think this can safely be set to FALSE - but\r
4278    * if we trust the callers not to request full repaints unnessesarily, then\r
4279    * we could skip some clipping work.  In other words, only request a full\r
4280    * redraw when the majority of pieces have changed positions (ie. flip, \r
4281    * gamestart and similar)  --Hawk\r
4282    */\r
4283   Boolean fullrepaint = repaint;\r
4284 \r
4285   if( DrawPositionNeedsFullRepaint() ) {\r
4286       fullrepaint = TRUE;\r
4287   }\r
4288 \r
4289 #if 0\r
4290   if( fullrepaint ) {\r
4291       static int repaint_count = 0;\r
4292       char buf[128];\r
4293 \r
4294       repaint_count++;\r
4295       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4296       OutputDebugString( buf );\r
4297   }\r
4298 #endif\r
4299 \r
4300   if (board == NULL) {\r
4301     if (!lastReqValid) {\r
4302       return;\r
4303     }\r
4304     board = lastReq;\r
4305   } else {\r
4306     CopyBoard(lastReq, board);\r
4307     lastReqValid = 1;\r
4308   }\r
4309 \r
4310   if (doingSizing) {\r
4311     return;\r
4312   }\r
4313 \r
4314   if (IsIconic(hwndMain)) {\r
4315     return;\r
4316   }\r
4317 \r
4318   if (hdc == NULL) {\r
4319     hdc = GetDC(hwndMain);\r
4320     if (!appData.monoMode) {\r
4321       SelectPalette(hdc, hPal, FALSE);\r
4322       RealizePalette(hdc);\r
4323     }\r
4324     releaseDC = TRUE;\r
4325   } else {\r
4326     releaseDC = FALSE;\r
4327   }\r
4328 \r
4329 #if 0\r
4330   fprintf(debugFP, "*******************************\n"\r
4331                    "repaint = %s\n"\r
4332                    "dragInfo.from (%d,%d)\n"\r
4333                    "dragInfo.start (%d,%d)\n"\r
4334                    "dragInfo.pos (%d,%d)\n"\r
4335                    "dragInfo.lastpos (%d,%d)\n", \r
4336                     repaint ? "TRUE" : "FALSE",\r
4337                     dragInfo.from.x, dragInfo.from.y, \r
4338                     dragInfo.start.x, dragInfo.start.y,\r
4339                     dragInfo.pos.x, dragInfo.pos.y,\r
4340                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4341   fprintf(debugFP, "prev:  ");\r
4342   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4343     for (column = 0; column < BOARD_WIDTH; column++) {\r
4344       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4345     }\r
4346   }\r
4347   fprintf(debugFP, "\n");\r
4348   fprintf(debugFP, "board: ");\r
4349   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4350     for (column = 0; column < BOARD_WIDTH; column++) {\r
4351       fprintf(debugFP, "%d ", board[row][column]);\r
4352     }\r
4353   }\r
4354   fprintf(debugFP, "\n");\r
4355   fflush(debugFP);\r
4356 #endif\r
4357 \r
4358   /* Create some work-DCs */\r
4359   hdcmem = CreateCompatibleDC(hdc);\r
4360   tmphdc = CreateCompatibleDC(hdc);\r
4361 \r
4362   /* If dragging is in progress, we temporarely remove the piece */\r
4363   /* [HGM] or temporarily decrease count if stacked              */\r
4364   /*       !! Moved to before board compare !!                   */\r
4365   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4366     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4367     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4368             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4369         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4370     } else \r
4371     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4372             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4373         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4374     } else \r
4375         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4376   }\r
4377 \r
4378   /* Figure out which squares need updating by comparing the \r
4379    * newest board with the last drawn board and checking if\r
4380    * flipping has changed.\r
4381    */\r
4382   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4383     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4384       for (column = 0; column < BOARD_WIDTH; column++) {\r
4385         if (lastDrawn[row][column] != board[row][column]) {\r
4386           SquareToPos(row, column, &x, &y);\r
4387           clips[num_clips++] =\r
4388             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4389         }\r
4390       }\r
4391     }\r
4392     for (i=0; i<2; i++) {\r
4393       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4394           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4395         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4396             lastDrawnHighlight.sq[i].y >= 0) {\r
4397           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4398                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4399           clips[num_clips++] =\r
4400             CreateRectRgn(x - lineGap, y - lineGap, \r
4401                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4402         }\r
4403         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4404           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4405           clips[num_clips++] =\r
4406             CreateRectRgn(x - lineGap, y - lineGap, \r
4407                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4408         }\r
4409       }\r
4410     }\r
4411     for (i=0; i<2; i++) {\r
4412       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4413           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4414         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4415             lastDrawnPremove.sq[i].y >= 0) {\r
4416           SquareToPos(lastDrawnPremove.sq[i].y,\r
4417                       lastDrawnPremove.sq[i].x, &x, &y);\r
4418           clips[num_clips++] =\r
4419             CreateRectRgn(x - lineGap, y - lineGap, \r
4420                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4421         }\r
4422         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4423             premoveHighlightInfo.sq[i].y >= 0) {\r
4424           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4425                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4426           clips[num_clips++] =\r
4427             CreateRectRgn(x - lineGap, y - lineGap, \r
4428                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4429         }\r
4430       }\r
4431     }\r
4432   } else {\r
4433     fullrepaint = TRUE;\r
4434   }\r
4435 \r
4436   /* Create a buffer bitmap - this is the actual bitmap\r
4437    * being written to.  When all the work is done, we can\r
4438    * copy it to the real DC (the screen).  This avoids\r
4439    * the problems with flickering.\r
4440    */\r
4441   GetClientRect(hwndMain, &Rect);\r
4442   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4443                                         Rect.bottom-Rect.top+1);\r
4444   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4445   if (!appData.monoMode) {\r
4446     SelectPalette(hdcmem, hPal, FALSE);\r
4447   }\r
4448 \r
4449   /* Create clips for dragging */\r
4450   if (!fullrepaint) {\r
4451     if (dragInfo.from.x >= 0) {\r
4452       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4453       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4454     }\r
4455     if (dragInfo.start.x >= 0) {\r
4456       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4457       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4458     }\r
4459     if (dragInfo.pos.x >= 0) {\r
4460       x = dragInfo.pos.x - squareSize / 2;\r
4461       y = dragInfo.pos.y - squareSize / 2;\r
4462       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4463     }\r
4464     if (dragInfo.lastpos.x >= 0) {\r
4465       x = dragInfo.lastpos.x - squareSize / 2;\r
4466       y = dragInfo.lastpos.y - squareSize / 2;\r
4467       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4468     }\r
4469   }\r
4470 \r
4471   /* Are we animating a move?  \r
4472    * If so, \r
4473    *   - remove the piece from the board (temporarely)\r
4474    *   - calculate the clipping region\r
4475    */\r
4476   if (!fullrepaint) {\r
4477     if (animInfo.piece != EmptySquare) {\r
4478       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4479       x = boardRect.left + animInfo.lastpos.x;\r
4480       y = boardRect.top + animInfo.lastpos.y;\r
4481       x2 = boardRect.left + animInfo.pos.x;\r
4482       y2 = boardRect.top + animInfo.pos.y;\r
4483       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4484       /* [HGM] old location of "slight kludge" below */\r
4485     }\r
4486   }\r
4487 \r
4488   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4489   if (num_clips == 0)\r
4490     fullrepaint = TRUE;\r
4491 \r
4492   /* Set clipping on the memory DC */\r
4493   if (!fullrepaint) {\r
4494     SelectClipRgn(hdcmem, clips[0]);\r
4495     for (x = 1; x < num_clips; x++) {\r
4496       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4497         abort();  // this should never ever happen!\r
4498     }\r
4499   }\r
4500 \r
4501   /* Do all the drawing to the memory DC */\r
4502   if(explodeInfo.radius) { // [HGM] atomic\r
4503         HBRUSH oldBrush;\r
4504         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4505         SquareToPos(explodeInfo.y, explodeInfo.x, &x, &y);\r
4506         x += squareSize/2;\r
4507         y += squareSize/2;\r
4508         if(!fullrepaint) {\r
4509           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4510           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4511         }\r
4512         DrawGridOnDC(hdcmem);\r
4513         DrawHighlightsOnDC(hdcmem);\r
4514         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4515         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4516         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4517         SelectObject(hdcmem, oldBrush);\r
4518   } else {\r
4519     DrawGridOnDC(hdcmem);\r
4520     DrawHighlightsOnDC(hdcmem);\r
4521     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4522   }\r
4523   if(logoHeight) {\r
4524         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4525         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4526   }\r
4527 \r
4528   if( appData.highlightMoveWithArrow ) {\r
4529     DrawArrowHighlight(hdcmem);\r
4530   }\r
4531 \r
4532   DrawCoordsOnDC(hdcmem);\r
4533 \r
4534   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4535                  /* to make sure lastDrawn contains what is actually drawn */\r
4536 \r
4537   /* Put the dragged piece back into place and draw it (out of place!) */\r
4538     if (dragged_piece != EmptySquare) {\r
4539     /* [HGM] or restack */\r
4540     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4541                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4542     else\r
4543     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4544                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4545     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4546     x = dragInfo.pos.x - squareSize / 2;\r
4547     y = dragInfo.pos.y - squareSize / 2;\r
4548     DrawPieceOnDC(hdcmem, dragged_piece,\r
4549                   ((int) dragged_piece < (int) BlackPawn), \r
4550                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4551   }   \r
4552   \r
4553   /* Put the animated piece back into place and draw it */\r
4554   if (animInfo.piece != EmptySquare) {\r
4555     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4556     x = boardRect.left + animInfo.pos.x;\r
4557     y = boardRect.top + animInfo.pos.y;\r
4558     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4559                   ((int) animInfo.piece < (int) BlackPawn),\r
4560                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4561       /* Slight kludge.  The real problem is that after AnimateMove is\r
4562          done, the position on the screen does not match lastDrawn.\r
4563          This currently causes trouble only on e.p. captures in\r
4564          atomic, where the piece moves to an empty square and then\r
4565          explodes.  The old and new positions both had an empty square\r
4566          at the destination, but animation has drawn a piece there and\r
4567          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4568       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4569   }\r
4570 \r
4571   /* Release the bufferBitmap by selecting in the old bitmap \r
4572    * and delete the memory DC\r
4573    */\r
4574   SelectObject(hdcmem, oldBitmap);\r
4575   DeleteDC(hdcmem);\r
4576 \r
4577   /* Set clipping on the target DC */\r
4578   if (!fullrepaint) {\r
4579     SelectClipRgn(hdc, clips[0]);\r
4580     for (x = 1; x < num_clips; x++) {\r
4581       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4582         abort();   // this should never ever happen!\r
4583     } \r
4584   }\r
4585 \r
4586   /* Copy the new bitmap onto the screen in one go.\r
4587    * This way we avoid any flickering\r
4588    */\r
4589   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4590   BitBlt(hdc, boardRect.left, boardRect.top,\r
4591          boardRect.right - boardRect.left,\r
4592          boardRect.bottom - boardRect.top,\r
4593          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4594   if(saveDiagFlag) { \r
4595     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4596     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4597 \r
4598     GetObject(bufferBitmap, sizeof(b), &b);\r
4599     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4600         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4601         bih.biWidth = b.bmWidth;\r
4602         bih.biHeight = b.bmHeight;\r
4603         bih.biPlanes = 1;\r
4604         bih.biBitCount = b.bmBitsPixel;\r
4605         bih.biCompression = 0;\r
4606         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4607         bih.biXPelsPerMeter = 0;\r
4608         bih.biYPelsPerMeter = 0;\r
4609         bih.biClrUsed = 0;\r
4610         bih.biClrImportant = 0;\r
4611 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4612 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4613         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4614 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4615 \r
4616 #if 1\r
4617         wb = b.bmWidthBytes;\r
4618         // count colors\r
4619         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4620                 int k = ((int*) pData)[i];\r
4621                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4622                 if(j >= 16) break;\r
4623                 color[j] = k;\r
4624                 if(j >= nrColors) nrColors = j+1;\r
4625         }\r
4626         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4627                 INT p = 0;\r
4628                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4629                     for(w=0; w<(wb>>2); w+=2) {\r
4630                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4631                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4632                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4633                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4634                         pData[p++] = m | j<<4;\r
4635                     }\r
4636                     while(p&3) pData[p++] = 0;\r
4637                 }\r
4638                 fac = 3;\r
4639                 wb = ((wb+31)>>5)<<2;\r
4640         }\r
4641         // write BITMAPFILEHEADER\r
4642         fprintf(diagFile, "BM");\r
4643         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4644         fputDW(diagFile, 0);\r
4645         fputDW(diagFile, 0x36 + (fac?64:0));\r
4646         // write BITMAPINFOHEADER\r
4647         fputDW(diagFile, 40);\r
4648         fputDW(diagFile, b.bmWidth);\r
4649         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4650         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4651         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4652         fputDW(diagFile, 0);\r
4653         fputDW(diagFile, 0);\r
4654         fputDW(diagFile, 0);\r
4655         fputDW(diagFile, 0);\r
4656         fputDW(diagFile, 0);\r
4657         fputDW(diagFile, 0);\r
4658         // write color table\r
4659         if(fac)\r
4660         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4661         // write bitmap data\r
4662         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4663                 fputc(pData[i], diagFile);\r
4664 #endif\r
4665      }\r
4666   }\r
4667 \r
4668   SelectObject(tmphdc, oldBitmap);\r
4669 \r
4670   /* Massive cleanup */\r
4671   for (x = 0; x < num_clips; x++)\r
4672     DeleteObject(clips[x]);\r
4673 \r
4674   DeleteDC(tmphdc);\r
4675   DeleteObject(bufferBitmap);\r
4676 \r
4677   if (releaseDC) \r
4678     ReleaseDC(hwndMain, hdc);\r
4679   \r
4680   if (lastDrawnFlipView != flipView) {\r
4681     if (flipView)\r
4682       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4683     else\r
4684       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4685   }\r
4686 \r
4687 /*  CopyBoard(lastDrawn, board);*/\r
4688   lastDrawnHighlight = highlightInfo;\r
4689   lastDrawnPremove   = premoveHighlightInfo;\r
4690   lastDrawnFlipView = flipView;\r
4691   lastDrawnValid = 1;\r
4692 }\r
4693 \r
4694 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4695 int\r
4696 SaveDiagram(f)\r
4697      FILE *f;\r
4698 {\r
4699     saveDiagFlag = 1; diagFile = f;\r
4700     HDCDrawPosition(NULL, TRUE, NULL);\r
4701 \r
4702     saveDiagFlag = 0;\r
4703 \r
4704 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4705     \r
4706     fclose(f);\r
4707     return TRUE;\r
4708 }\r
4709 \r
4710 \r
4711 /*---------------------------------------------------------------------------*\\r
4712 | CLIENT PAINT PROCEDURE\r
4713 |   This is the main event-handler for the WM_PAINT message.\r
4714 |\r
4715 \*---------------------------------------------------------------------------*/\r
4716 VOID\r
4717 PaintProc(HWND hwnd)\r
4718 {\r
4719   HDC         hdc;\r
4720   PAINTSTRUCT ps;\r
4721   HFONT       oldFont;\r
4722 \r
4723   if((hdc = BeginPaint(hwnd, &ps))) {\r
4724     if (IsIconic(hwnd)) {\r
4725       DrawIcon(hdc, 2, 2, iconCurrent);\r
4726     } else {\r
4727       if (!appData.monoMode) {\r
4728         SelectPalette(hdc, hPal, FALSE);\r
4729         RealizePalette(hdc);\r
4730       }\r
4731       HDCDrawPosition(hdc, 1, NULL);\r
4732       oldFont =\r
4733         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4734       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4735                  ETO_CLIPPED|ETO_OPAQUE,\r
4736                  &messageRect, messageText, strlen(messageText), NULL);\r
4737       SelectObject(hdc, oldFont);\r
4738       DisplayBothClocks();\r
4739     }\r
4740     EndPaint(hwnd,&ps);\r
4741   }\r
4742 \r
4743   return;\r
4744 }\r
4745 \r
4746 \r
4747 /*\r
4748  * If the user selects on a border boundary, return -1; if off the board,\r
4749  *   return -2.  Otherwise map the event coordinate to the square.\r
4750  * The offset boardRect.left or boardRect.top must already have been\r
4751  *   subtracted from x.\r
4752  */\r
4753 int\r
4754 EventToSquare(int x)\r
4755 {\r
4756   if (x <= 0)\r
4757     return -2;\r
4758   if (x < lineGap)\r
4759     return -1;\r
4760   x -= lineGap;\r
4761   if ((x % (squareSize + lineGap)) >= squareSize)\r
4762     return -1;\r
4763   x /= (squareSize + lineGap);\r
4764   if (x >= BOARD_SIZE)\r
4765     return -2;\r
4766   return x;\r
4767 }\r
4768 \r
4769 typedef struct {\r
4770   char piece;\r
4771   int command;\r
4772   char* name;\r
4773 } DropEnable;\r
4774 \r
4775 DropEnable dropEnables[] = {\r
4776   { 'P', DP_Pawn, "Pawn" },\r
4777   { 'N', DP_Knight, "Knight" },\r
4778   { 'B', DP_Bishop, "Bishop" },\r
4779   { 'R', DP_Rook, "Rook" },\r
4780   { 'Q', DP_Queen, "Queen" },\r
4781 };\r
4782 \r
4783 VOID\r
4784 SetupDropMenu(HMENU hmenu)\r
4785 {\r
4786   int i, count, enable;\r
4787   char *p;\r
4788   extern char white_holding[], black_holding[];\r
4789   char item[MSG_SIZ];\r
4790 \r
4791   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4792     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4793                dropEnables[i].piece);\r
4794     count = 0;\r
4795     while (p && *p++ == dropEnables[i].piece) count++;\r
4796     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4797     enable = count > 0 || !appData.testLegality\r
4798       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4799                       && !appData.icsActive);\r
4800     ModifyMenu(hmenu, dropEnables[i].command,\r
4801                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4802                dropEnables[i].command, item);\r
4803   }\r
4804 }\r
4805 \r
4806 static int fromX = -1, fromY = -1, toX, toY;\r
4807 \r
4808 /* Event handler for mouse messages */\r
4809 VOID\r
4810 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4811 {\r
4812   int x, y;\r
4813   POINT pt;\r
4814   static int recursive = 0;\r
4815   HMENU hmenu;\r
4816 //  BOOLEAN needsRedraw = FALSE;\r
4817   BOOLEAN saveAnimate;\r
4818   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4819   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4820   ChessMove moveType;\r
4821 \r
4822   if (recursive) {\r
4823     if (message == WM_MBUTTONUP) {\r
4824       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4825          to the middle button: we simulate pressing the left button too!\r
4826          */\r
4827       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4828       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4829     }\r
4830     return;\r
4831   }\r
4832   recursive++;\r
4833   \r
4834   pt.x = LOWORD(lParam);\r
4835   pt.y = HIWORD(lParam);\r
4836   x = EventToSquare(pt.x - boardRect.left);\r
4837   y = EventToSquare(pt.y - boardRect.top);\r
4838   if (!flipView && y >= 0) {\r
4839     y = BOARD_HEIGHT - 1 - y;\r
4840   }\r
4841   if (flipView && x >= 0) {\r
4842     x = BOARD_WIDTH - 1 - x;\r
4843   }\r
4844 \r
4845   switch (message) {\r
4846   case WM_LBUTTONDOWN:\r
4847     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4848         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4849         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4850         if(gameInfo.holdingsWidth && \r
4851                 (WhiteOnMove(currentMove) \r
4852                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4853                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4854             // click in right holdings, for determining promotion piece\r
4855             ChessSquare p = boards[currentMove][y][x];\r
4856             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4857             if(p != EmptySquare) {\r
4858                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4859                 fromX = fromY = -1;\r
4860                 break;\r
4861             }\r
4862         }\r
4863         DrawPosition(FALSE, boards[currentMove]);\r
4864         break;\r
4865     }\r
4866     ErrorPopDown();\r
4867     sameAgain = FALSE;\r
4868     if (y == -2) {\r
4869       /* Downclick vertically off board; check if on clock */\r
4870       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4871         if (gameMode == EditPosition) {\r
4872           SetWhiteToPlayEvent();\r
4873         } else if (gameMode == IcsPlayingBlack ||\r
4874                    gameMode == MachinePlaysWhite) {\r
4875           CallFlagEvent();\r
4876         } else if (gameMode == EditGame) {\r
4877           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4878         }\r
4879       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4880         if (gameMode == EditPosition) {\r
4881           SetBlackToPlayEvent();\r
4882         } else if (gameMode == IcsPlayingWhite ||\r
4883                    gameMode == MachinePlaysBlack) {\r
4884           CallFlagEvent();\r
4885         } else if (gameMode == EditGame) {\r
4886           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4887         }\r
4888       }\r
4889       if (!appData.highlightLastMove) {\r
4890         ClearHighlights();\r
4891         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4892       }\r
4893       fromX = fromY = -1;\r
4894       dragInfo.start.x = dragInfo.start.y = -1;\r
4895       dragInfo.from = dragInfo.start;\r
4896       break;\r
4897     } else if (x < 0 || y < 0\r
4898       /* [HGM] block clicks between board and holdings */\r
4899               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4900               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4901               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4902         /* EditPosition, empty square, or different color piece;\r
4903            click-click move is possible */\r
4904                                ) {\r
4905       break;\r
4906     } else if (fromX == x && fromY == y) {\r
4907       /* Downclick on same square again */\r
4908       ClearHighlights();\r
4909       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4910       sameAgain = TRUE;  \r
4911     } else if (fromX != -1 &&\r
4912                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4913                                                                         ) {\r
4914       /* Downclick on different square. */\r
4915       /* [HGM] if on holdings file, should count as new first click ! */\r
4916       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4917         toX = x;\r
4918         toY = y;\r
4919         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4920            to make sure move is legal before showing promotion popup */\r
4921         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4922         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4923                 fromX = fromY = -1; \r
4924                 ClearHighlights();\r
4925                 DrawPosition(FALSE, boards[currentMove]);\r
4926                 break; \r
4927         } else \r
4928         if(moveType != ImpossibleMove) {\r
4929           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4930           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4931             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4932               appData.alwaysPromoteToQueen)) {\r
4933                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4934                   if (!appData.highlightLastMove) {\r
4935                       ClearHighlights();\r
4936                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4937                   }\r
4938           } else\r
4939           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4940                   SetHighlights(fromX, fromY, toX, toY);\r
4941                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4942                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4943                      If promotion to Q is legal, all are legal! */\r
4944                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4945                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4946                     // kludge to temporarily execute move on display, wthout promotng yet\r
4947                     promotionChoice = TRUE;\r
4948                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4949                     boards[currentMove][toY][toX] = p;\r
4950                     DrawPosition(FALSE, boards[currentMove]);\r
4951                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4952                     boards[currentMove][toY][toX] = q;\r
4953                   } else\r
4954                   PromotionPopup(hwnd);\r
4955           } else {       /* not a promotion */\r
4956              if (appData.animate || appData.highlightLastMove) {\r
4957                  SetHighlights(fromX, fromY, toX, toY);\r
4958              } else {\r
4959                  ClearHighlights();\r
4960              }\r
4961              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4962              fromX = fromY = -1;\r
4963              if (appData.animate && !appData.highlightLastMove) {\r
4964                   ClearHighlights();\r
4965                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4966              }\r
4967           }\r
4968           break;\r
4969         }\r
4970         if (gotPremove) {\r
4971             /* [HGM] it seemed that braces were missing here */\r
4972             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4973             fromX = fromY = -1;\r
4974             break;\r
4975         }\r
4976       }\r
4977       ClearHighlights();\r
4978       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4979     }\r
4980     /* First downclick, or restart on a square with same color piece */\r
4981     if (!frozen && OKToStartUserMove(x, y)) {\r
4982       fromX = x;\r
4983       fromY = y;\r
4984       dragInfo.lastpos = pt;\r
4985       dragInfo.from.x = fromX;\r
4986       dragInfo.from.y = fromY;\r
4987       dragInfo.start = dragInfo.from;\r
4988       SetCapture(hwndMain);\r
4989     } else {\r
4990       fromX = fromY = -1;\r
4991       dragInfo.start.x = dragInfo.start.y = -1;\r
4992       dragInfo.from = dragInfo.start;\r
4993       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4994     }\r
4995     break;\r
4996 \r
4997   case WM_LBUTTONUP:\r
4998     ReleaseCapture();\r
4999     if (fromX == -1) break;\r
5000     if (x == fromX && y == fromY) {\r
5001       dragInfo.from.x = dragInfo.from.y = -1;\r
5002       /* Upclick on same square */\r
5003       if (sameAgain) {\r
5004         /* Clicked same square twice: abort click-click move */\r
5005         fromX = fromY = -1;\r
5006         gotPremove = 0;\r
5007         ClearPremoveHighlights();\r
5008       } else {\r
5009         /* First square clicked: start click-click move */\r
5010         SetHighlights(fromX, fromY, -1, -1);\r
5011       }\r
5012       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5013     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5014       /* Errant click; ignore */\r
5015       break;\r
5016     } else {\r
5017       /* Finish drag move. */\r
5018     if (appData.debugMode) {\r
5019         fprintf(debugFP, "release\n");\r
5020     }\r
5021       dragInfo.from.x = dragInfo.from.y = -1;\r
5022       toX = x;\r
5023       toY = y;\r
5024       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5025       appData.animate = appData.animate && !appData.animateDragging;\r
5026       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5027       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5028                 fromX = fromY = -1; \r
5029                 ClearHighlights();\r
5030                 DrawPosition(FALSE, boards[currentMove]);\r
5031                 break; \r
5032       } else \r
5033       if(moveType != ImpossibleMove) {\r
5034           /* [HGM] use move type to determine if move is promotion.\r
5035              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5036           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5037             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5038               appData.alwaysPromoteToQueen)) \r
5039                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5040           else \r
5041           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5042                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5043                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5044                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5045                     // kludge to temporarily execute move on display, wthout promotng yet\r
5046                     promotionChoice = TRUE;\r
5047                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5048                     boards[currentMove][toY][toX] = p;\r
5049                     DrawPosition(FALSE, boards[currentMove]);\r
5050                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5051                     boards[currentMove][toY][toX] = q;\r
5052                     break;\r
5053                   } else\r
5054                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5055           } else {\r
5056             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5057                         && boards[currentMove][toY][toX] != EmptySquare) AnimateAtomicCapture(toX, toY, 20);\r
5058             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5059           }\r
5060       }\r
5061       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5062       appData.animate = saveAnimate;\r
5063       fromX = fromY = -1;\r
5064       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5065         ClearHighlights();\r
5066       }\r
5067       if (appData.animate || appData.animateDragging ||\r
5068           appData.highlightDragging || gotPremove) {\r
5069         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5070       }\r
5071     }\r
5072     dragInfo.start.x = dragInfo.start.y = -1; \r
5073     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5074     break;\r
5075 \r
5076   case WM_MOUSEMOVE:\r
5077     if ((appData.animateDragging || appData.highlightDragging)\r
5078         && (wParam & MK_LBUTTON)\r
5079         && dragInfo.from.x >= 0) \r
5080     {\r
5081       BOOL full_repaint = FALSE;\r
5082 \r
5083       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5084       if (appData.animateDragging) {\r
5085         dragInfo.pos = pt;\r
5086       }\r
5087       if (appData.highlightDragging) {\r
5088         SetHighlights(fromX, fromY, x, y);\r
5089         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5090             full_repaint = TRUE;\r
5091         }\r
5092       }\r
5093       \r
5094       DrawPosition( full_repaint, NULL);\r
5095       \r
5096       dragInfo.lastpos = dragInfo.pos;\r
5097     }\r
5098     break;\r
5099 \r
5100   case WM_MOUSEWHEEL: // [DM]\r
5101     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5102        /* Mouse Wheel is being rolled forward\r
5103         * Play moves forward\r
5104         */\r
5105        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5106                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5107        /* Mouse Wheel is being rolled backward\r
5108         * Play moves backward\r
5109         */\r
5110        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5111                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5112     }\r
5113     break;\r
5114 \r
5115   case WM_MBUTTONDOWN:\r
5116   case WM_RBUTTONDOWN:\r
5117     ErrorPopDown();\r
5118     ReleaseCapture();\r
5119     fromX = fromY = -1;\r
5120     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5121     dragInfo.start.x = dragInfo.start.y = -1;\r
5122     dragInfo.from = dragInfo.start;\r
5123     dragInfo.lastpos = dragInfo.pos;\r
5124     if (appData.highlightDragging) {\r
5125       ClearHighlights();\r
5126     }\r
5127     if(y == -2) {\r
5128       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5129       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5130           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5131       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5132           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5133       }\r
5134     }\r
5135     DrawPosition(TRUE, NULL);\r
5136 \r
5137     switch (gameMode) {\r
5138     case EditPosition:\r
5139     case IcsExamining:\r
5140       if (x < 0 || y < 0) break;\r
5141       fromX = x;\r
5142       fromY = y;\r
5143       if (message == WM_MBUTTONDOWN) {\r
5144         buttonCount = 3;  /* even if system didn't think so */\r
5145         if (wParam & MK_SHIFT) \r
5146           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5147         else\r
5148           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5149       } else { /* message == WM_RBUTTONDOWN */\r
5150 #if 0\r
5151         if (buttonCount == 3) {\r
5152           if (wParam & MK_SHIFT) \r
5153             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5154           else\r
5155             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5156         } else {\r
5157           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5158         }\r
5159 #else\r
5160         /* Just have one menu, on the right button.  Windows users don't\r
5161            think to try the middle one, and sometimes other software steals\r
5162            it, or it doesn't really exist. */\r
5163         if(gameInfo.variant != VariantShogi)\r
5164             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5165         else\r
5166             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5167 #endif\r
5168       }\r
5169       break;\r
5170     case IcsPlayingWhite:\r
5171     case IcsPlayingBlack:\r
5172     case EditGame:\r
5173     case MachinePlaysWhite:\r
5174     case MachinePlaysBlack:\r
5175       if (appData.testLegality &&\r
5176           gameInfo.variant != VariantBughouse &&\r
5177           gameInfo.variant != VariantCrazyhouse) break;\r
5178       if (x < 0 || y < 0) break;\r
5179       fromX = x;\r
5180       fromY = y;\r
5181       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5182       SetupDropMenu(hmenu);\r
5183       MenuPopup(hwnd, pt, hmenu, -1);\r
5184       break;\r
5185     default:\r
5186       break;\r
5187     }\r
5188     break;\r
5189   }\r
5190 \r
5191   recursive--;\r
5192 }\r
5193 \r
5194 /* Preprocess messages for buttons in main window */\r
5195 LRESULT CALLBACK\r
5196 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5197 {\r
5198   int id = GetWindowLong(hwnd, GWL_ID);\r
5199   int i, dir;\r
5200 \r
5201   for (i=0; i<N_BUTTONS; i++) {\r
5202     if (buttonDesc[i].id == id) break;\r
5203   }\r
5204   if (i == N_BUTTONS) return 0;\r
5205   switch (message) {\r
5206   case WM_KEYDOWN:\r
5207     switch (wParam) {\r
5208     case VK_LEFT:\r
5209     case VK_RIGHT:\r
5210       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5211       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5212       return TRUE;\r
5213     }\r
5214     break;\r
5215   case WM_CHAR:\r
5216     switch (wParam) {\r
5217     case '\r':\r
5218       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5219       return TRUE;\r
5220     case '\t':\r
5221       if (appData.icsActive) {\r
5222         if (GetKeyState(VK_SHIFT) < 0) {\r
5223           /* shifted */\r
5224           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5225           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5226           SetFocus(h);\r
5227         } else {\r
5228           /* unshifted */\r
5229           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5230           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5231           SetFocus(h);\r
5232         }\r
5233         return TRUE;\r
5234       }\r
5235       break;\r
5236     default:\r
5237       if (appData.icsActive) {\r
5238         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5239         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5240         SetFocus(h);\r
5241         SendMessage(h, WM_CHAR, wParam, lParam);\r
5242         return TRUE;\r
5243       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5244         PopUpMoveDialog((char)wParam);\r
5245       }\r
5246       break;\r
5247     }\r
5248     break;\r
5249   }\r
5250   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5251 }\r
5252 \r
5253 /* Process messages for Promotion dialog box */\r
5254 LRESULT CALLBACK\r
5255 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5256 {\r
5257   char promoChar;\r
5258 \r
5259   switch (message) {\r
5260   case WM_INITDIALOG: /* message: initialize dialog box */\r
5261     /* Center the dialog over the application window */\r
5262     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5263     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5264       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5265        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5266                SW_SHOW : SW_HIDE);\r
5267     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5268     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5269        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5270          PieceToChar(WhiteAngel) != '~') ||\r
5271         (PieceToChar(BlackAngel) >= 'A' &&\r
5272          PieceToChar(BlackAngel) != '~')   ) ?\r
5273                SW_SHOW : SW_HIDE);\r
5274     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5275        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5276          PieceToChar(WhiteMarshall) != '~') ||\r
5277         (PieceToChar(BlackMarshall) >= 'A' &&\r
5278          PieceToChar(BlackMarshall) != '~')   ) ?\r
5279                SW_SHOW : SW_HIDE);\r
5280     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5281     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5282        gameInfo.variant != VariantShogi ?\r
5283                SW_SHOW : SW_HIDE);\r
5284     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5285        gameInfo.variant != VariantShogi ?\r
5286                SW_SHOW : SW_HIDE);\r
5287     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5288        gameInfo.variant == VariantShogi ?\r
5289                SW_SHOW : SW_HIDE);\r
5290     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5291        gameInfo.variant == VariantShogi ?\r
5292                SW_SHOW : SW_HIDE);\r
5293     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5294        gameInfo.variant == VariantSuper ?\r
5295                SW_SHOW : SW_HIDE);\r
5296     return TRUE;\r
5297 \r
5298   case WM_COMMAND: /* message: received a command */\r
5299     switch (LOWORD(wParam)) {\r
5300     case IDCANCEL:\r
5301       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5302       ClearHighlights();\r
5303       DrawPosition(FALSE, NULL);\r
5304       return TRUE;\r
5305     case PB_King:\r
5306       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5307       break;\r
5308     case PB_Queen:\r
5309       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5310       break;\r
5311     case PB_Rook:\r
5312       promoChar = PieceToChar(BlackRook);\r
5313       break;\r
5314     case PB_Bishop:\r
5315       promoChar = PieceToChar(BlackBishop);\r
5316       break;\r
5317     case PB_Chancellor:\r
5318       promoChar = PieceToChar(BlackMarshall);\r
5319       break;\r
5320     case PB_Archbishop:\r
5321       promoChar = PieceToChar(BlackAngel);\r
5322       break;\r
5323     case PB_Knight:\r
5324       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5325       break;\r
5326     default:\r
5327       return FALSE;\r
5328     }\r
5329     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5330     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5331        only show the popup when we are already sure the move is valid or\r
5332        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5333        will figure out it is a promotion from the promoChar. */\r
5334     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5335     if (!appData.highlightLastMove) {\r
5336       ClearHighlights();\r
5337       DrawPosition(FALSE, NULL);\r
5338     }\r
5339     return TRUE;\r
5340   }\r
5341   return FALSE;\r
5342 }\r
5343 \r
5344 /* Pop up promotion dialog */\r
5345 VOID\r
5346 PromotionPopup(HWND hwnd)\r
5347 {\r
5348   FARPROC lpProc;\r
5349 \r
5350   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5351   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5352     hwnd, (DLGPROC)lpProc);\r
5353   FreeProcInstance(lpProc);\r
5354 }\r
5355 \r
5356 /* Toggle ShowThinking */\r
5357 VOID\r
5358 ToggleShowThinking()\r
5359 {\r
5360   appData.showThinking = !appData.showThinking;\r
5361   ShowThinkingEvent();\r
5362 }\r
5363 \r
5364 VOID\r
5365 LoadGameDialog(HWND hwnd, char* title)\r
5366 {\r
5367   UINT number = 0;\r
5368   FILE *f;\r
5369   char fileTitle[MSG_SIZ];\r
5370   f = OpenFileDialog(hwnd, "rb", "",\r
5371                      appData.oldSaveStyle ? "gam" : "pgn",\r
5372                      GAME_FILT,\r
5373                      title, &number, fileTitle, NULL);\r
5374   if (f != NULL) {\r
5375     cmailMsgLoaded = FALSE;\r
5376     if (number == 0) {\r
5377       int error = GameListBuild(f);\r
5378       if (error) {\r
5379         DisplayError("Cannot build game list", error);\r
5380       } else if (!ListEmpty(&gameList) &&\r
5381                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5382         GameListPopUp(f, fileTitle);\r
5383         return;\r
5384       }\r
5385       GameListDestroy();\r
5386       number = 1;\r
5387     }\r
5388     LoadGame(f, number, fileTitle, FALSE);\r
5389   }\r
5390 }\r
5391 \r
5392 VOID\r
5393 ChangedConsoleFont()\r
5394 {\r
5395   CHARFORMAT cfmt;\r
5396   CHARRANGE tmpsel, sel;\r
5397   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5398   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5399   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5400   PARAFORMAT paraf;\r
5401 \r
5402   cfmt.cbSize = sizeof(CHARFORMAT);\r
5403   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5404   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5405   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5406    * size.  This was undocumented in the version of MSVC++ that I had\r
5407    * when I wrote the code, but is apparently documented now.\r
5408    */\r
5409   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5410   cfmt.bCharSet = f->lf.lfCharSet;\r
5411   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5412   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5413   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5414   /* Why are the following seemingly needed too? */\r
5415   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5416   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5417   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5418   tmpsel.cpMin = 0;\r
5419   tmpsel.cpMax = -1; /*999999?*/\r
5420   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5421   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5422   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5423    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5424    */\r
5425   paraf.cbSize = sizeof(paraf);\r
5426   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5427   paraf.dxStartIndent = 0;\r
5428   paraf.dxOffset = WRAP_INDENT;\r
5429   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5430   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5431 }\r
5432 \r
5433 /*---------------------------------------------------------------------------*\\r
5434  *\r
5435  * Window Proc for main window\r
5436  *\r
5437 \*---------------------------------------------------------------------------*/\r
5438 \r
5439 /* Process messages for main window, etc. */\r
5440 LRESULT CALLBACK\r
5441 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5442 {\r
5443   FARPROC lpProc;\r
5444   int wmId, wmEvent;\r
5445   char *defName;\r
5446   FILE *f;\r
5447   UINT number;\r
5448   char fileTitle[MSG_SIZ];\r
5449   char buf[MSG_SIZ];\r
5450   static SnapData sd;\r
5451 \r
5452   switch (message) {\r
5453 \r
5454   case WM_PAINT: /* message: repaint portion of window */\r
5455     PaintProc(hwnd);\r
5456     break;\r
5457 \r
5458   case WM_ERASEBKGND:\r
5459     if (IsIconic(hwnd)) {\r
5460       /* Cheat; change the message */\r
5461       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5462     } else {\r
5463       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5464     }\r
5465     break;\r
5466 \r
5467   case WM_LBUTTONDOWN:\r
5468   case WM_MBUTTONDOWN:\r
5469   case WM_RBUTTONDOWN:\r
5470   case WM_LBUTTONUP:\r
5471   case WM_MBUTTONUP:\r
5472   case WM_RBUTTONUP:\r
5473   case WM_MOUSEMOVE:\r
5474   case WM_MOUSEWHEEL:\r
5475     MouseEvent(hwnd, message, wParam, lParam);\r
5476     break;\r
5477 \r
5478   case WM_CHAR:\r
5479     \r
5480     if (appData.icsActive) {\r
5481       if (wParam == '\t') {\r
5482         if (GetKeyState(VK_SHIFT) < 0) {\r
5483           /* shifted */\r
5484           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5485           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5486           SetFocus(h);\r
5487         } else {\r
5488           /* unshifted */\r
5489           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5490           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5491           SetFocus(h);\r
5492         }\r
5493       } else {\r
5494         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5495         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5496         SetFocus(h);\r
5497         SendMessage(h, message, wParam, lParam);\r
5498       }\r
5499     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5500       PopUpMoveDialog((char)wParam);\r
5501     }\r
5502     break;\r
5503 \r
5504   case WM_PALETTECHANGED:\r
5505     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5506       int nnew;\r
5507       HDC hdc = GetDC(hwndMain);\r
5508       SelectPalette(hdc, hPal, TRUE);\r
5509       nnew = RealizePalette(hdc);\r
5510       if (nnew > 0) {\r
5511         paletteChanged = TRUE;\r
5512 #if 0\r
5513         UpdateColors(hdc);\r
5514 #else\r
5515         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5516 #endif\r
5517       }\r
5518       ReleaseDC(hwnd, hdc);\r
5519     }\r
5520     break;\r
5521 \r
5522   case WM_QUERYNEWPALETTE:\r
5523     if (!appData.monoMode /*&& paletteChanged*/) {\r
5524       int nnew;\r
5525       HDC hdc = GetDC(hwndMain);\r
5526       paletteChanged = FALSE;\r
5527       SelectPalette(hdc, hPal, FALSE);\r
5528       nnew = RealizePalette(hdc);\r
5529       if (nnew > 0) {\r
5530         InvalidateRect(hwnd, &boardRect, FALSE);\r
5531       }\r
5532       ReleaseDC(hwnd, hdc);\r
5533       return TRUE;\r
5534     }\r
5535     return FALSE;\r
5536 \r
5537   case WM_COMMAND: /* message: command from application menu */\r
5538     wmId    = LOWORD(wParam);\r
5539     wmEvent = HIWORD(wParam);\r
5540 \r
5541     switch (wmId) {\r
5542     case IDM_NewGame:\r
5543       ResetGameEvent();\r
5544       AnalysisPopDown();\r
5545       break;\r
5546 \r
5547     case IDM_NewGameFRC:\r
5548       if( NewGameFRC() == 0 ) {\r
5549         ResetGameEvent();\r
5550         AnalysisPopDown();\r
5551       }\r
5552       break;\r
5553 \r
5554     case IDM_NewVariant:\r
5555       NewVariantPopup(hwnd);\r
5556       break;\r
5557 \r
5558     case IDM_LoadGame:\r
5559       LoadGameDialog(hwnd, "Load Game from File");\r
5560       break;\r
5561 \r
5562     case IDM_LoadNextGame:\r
5563       ReloadGame(1);\r
5564       break;\r
5565 \r
5566     case IDM_LoadPrevGame:\r
5567       ReloadGame(-1);\r
5568       break;\r
5569 \r
5570     case IDM_ReloadGame:\r
5571       ReloadGame(0);\r
5572       break;\r
5573 \r
5574     case IDM_LoadPosition:\r
5575       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5576         Reset(FALSE, TRUE);\r
5577       }\r
5578       number = 1;\r
5579       f = OpenFileDialog(hwnd, "rb", "",\r
5580                          appData.oldSaveStyle ? "pos" : "fen",\r
5581                          POSITION_FILT,\r
5582                          "Load Position from File", &number, fileTitle, NULL);\r
5583       if (f != NULL) {\r
5584         LoadPosition(f, number, fileTitle);\r
5585       }\r
5586       break;\r
5587 \r
5588     case IDM_LoadNextPosition:\r
5589       ReloadPosition(1);\r
5590       break;\r
5591 \r
5592     case IDM_LoadPrevPosition:\r
5593       ReloadPosition(-1);\r
5594       break;\r
5595 \r
5596     case IDM_ReloadPosition:\r
5597       ReloadPosition(0);\r
5598       break;\r
5599 \r
5600     case IDM_SaveGame:\r
5601       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5602       f = OpenFileDialog(hwnd, "a", defName,\r
5603                          appData.oldSaveStyle ? "gam" : "pgn",\r
5604                          GAME_FILT,\r
5605                          "Save Game to File", NULL, fileTitle, NULL);\r
5606       if (f != NULL) {\r
5607         SaveGame(f, 0, "");\r
5608       }\r
5609       break;\r
5610 \r
5611     case IDM_SavePosition:\r
5612       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5613       f = OpenFileDialog(hwnd, "a", defName,\r
5614                          appData.oldSaveStyle ? "pos" : "fen",\r
5615                          POSITION_FILT,\r
5616                          "Save Position to File", NULL, fileTitle, NULL);\r
5617       if (f != NULL) {\r
5618         SavePosition(f, 0, "");\r
5619       }\r
5620       break;\r
5621 \r
5622     case IDM_SaveDiagram:\r
5623       defName = "diagram";\r
5624       f = OpenFileDialog(hwnd, "wb", defName,\r
5625                          "bmp",\r
5626                          DIAGRAM_FILT,\r
5627                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5628       if (f != NULL) {\r
5629         SaveDiagram(f);\r
5630       }\r
5631       break;\r
5632 \r
5633     case IDM_CopyGame:\r
5634       CopyGameToClipboard();\r
5635       break;\r
5636 \r
5637     case IDM_PasteGame:\r
5638       PasteGameFromClipboard();\r
5639       break;\r
5640 \r
5641     case IDM_CopyGameListToClipboard:\r
5642       CopyGameListToClipboard();\r
5643       break;\r
5644 \r
5645     /* [AS] Autodetect FEN or PGN data */\r
5646     case IDM_PasteAny:\r
5647       PasteGameOrFENFromClipboard();\r
5648       break;\r
5649 \r
5650     /* [AS] Move history */\r
5651     case IDM_ShowMoveHistory:\r
5652         if( MoveHistoryIsUp() ) {\r
5653             MoveHistoryPopDown();\r
5654         }\r
5655         else {\r
5656             MoveHistoryPopUp();\r
5657         }\r
5658         break;\r
5659 \r
5660     /* [AS] Eval graph */\r
5661     case IDM_ShowEvalGraph:\r
5662         if( EvalGraphIsUp() ) {\r
5663             EvalGraphPopDown();\r
5664         }\r
5665         else {\r
5666             EvalGraphPopUp();\r
5667         }\r
5668         break;\r
5669 \r
5670     /* [AS] Engine output */\r
5671     case IDM_ShowEngineOutput:\r
5672         if( EngineOutputIsUp() ) {\r
5673             EngineOutputPopDown();\r
5674         }\r
5675         else {\r
5676             EngineOutputPopUp();\r
5677         }\r
5678         break;\r
5679 \r
5680     /* [AS] User adjudication */\r
5681     case IDM_UserAdjudication_White:\r
5682         UserAdjudicationEvent( +1 );\r
5683         break;\r
5684 \r
5685     case IDM_UserAdjudication_Black:\r
5686         UserAdjudicationEvent( -1 );\r
5687         break;\r
5688 \r
5689     case IDM_UserAdjudication_Draw:\r
5690         UserAdjudicationEvent( 0 );\r
5691         break;\r
5692 \r
5693     /* [AS] Game list options dialog */\r
5694     case IDM_GameListOptions:\r
5695       GameListOptions();\r
5696       break;\r
5697 \r
5698     case IDM_CopyPosition:\r
5699       CopyFENToClipboard();\r
5700       break;\r
5701 \r
5702     case IDM_PastePosition:\r
5703       PasteFENFromClipboard();\r
5704       break;\r
5705 \r
5706     case IDM_MailMove:\r
5707       MailMoveEvent();\r
5708       break;\r
5709 \r
5710     case IDM_ReloadCMailMsg:\r
5711       Reset(TRUE, TRUE);\r
5712       ReloadCmailMsgEvent(FALSE);\r
5713       break;\r
5714 \r
5715     case IDM_Minimize:\r
5716       ShowWindow(hwnd, SW_MINIMIZE);\r
5717       break;\r
5718 \r
5719     case IDM_Exit:\r
5720       ExitEvent(0);\r
5721       break;\r
5722 \r
5723     case IDM_MachineWhite:\r
5724       MachineWhiteEvent();\r
5725       /*\r
5726        * refresh the tags dialog only if it's visible\r
5727        */\r
5728       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5729           char *tags;\r
5730           tags = PGNTags(&gameInfo);\r
5731           TagsPopUp(tags, CmailMsg());\r
5732           free(tags);\r
5733       }\r
5734       break;\r
5735 \r
5736     case IDM_MachineBlack:\r
5737       MachineBlackEvent();\r
5738       /*\r
5739        * refresh the tags dialog only if it's visible\r
5740        */\r
5741       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5742           char *tags;\r
5743           tags = PGNTags(&gameInfo);\r
5744           TagsPopUp(tags, CmailMsg());\r
5745           free(tags);\r
5746       }\r
5747       break;\r
5748 \r
5749     case IDM_TwoMachines:\r
5750       TwoMachinesEvent();\r
5751       /*\r
5752        * refresh the tags dialog only if it's visible\r
5753        */\r
5754       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5755           char *tags;\r
5756           tags = PGNTags(&gameInfo);\r
5757           TagsPopUp(tags, CmailMsg());\r
5758           free(tags);\r
5759       }\r
5760       break;\r
5761 \r
5762     case IDM_AnalysisMode:\r
5763       if (!first.analysisSupport) {\r
5764         sprintf(buf, "%s does not support analysis", first.tidy);\r
5765         DisplayError(buf, 0);\r
5766       } else {\r
5767         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5768         if (appData.icsActive) {\r
5769                if (gameMode != IcsObserving) {\r
5770                        sprintf(buf, "You are not observing a game");\r
5771                        DisplayError(buf, 0);\r
5772                        /* secure check */\r
5773                        if (appData.icsEngineAnalyze) {\r
5774                                if (appData.debugMode) \r
5775                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5776                                ExitAnalyzeMode();\r
5777                                ModeHighlight();\r
5778                                break;\r
5779                        }\r
5780                        break;\r
5781                } else {\r
5782                        /* if enable, user want disable icsEngineAnalyze */\r
5783                        if (appData.icsEngineAnalyze) {\r
5784                                ExitAnalyzeMode();\r
5785                                ModeHighlight();\r
5786                                break;\r
5787                        }\r
5788                        appData.icsEngineAnalyze = TRUE;\r
5789                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5790                }\r
5791         } \r
5792         if (!appData.showThinking) ToggleShowThinking();\r
5793         AnalyzeModeEvent();\r
5794       }\r
5795       break;\r
5796 \r
5797     case IDM_AnalyzeFile:\r
5798       if (!first.analysisSupport) {\r
5799         char buf[MSG_SIZ];\r
5800         sprintf(buf, "%s does not support analysis", first.tidy);\r
5801         DisplayError(buf, 0);\r
5802       } else {\r
5803         if (!appData.showThinking) ToggleShowThinking();\r
5804         AnalyzeFileEvent();\r
5805         LoadGameDialog(hwnd, "Analyze Game from File");\r
5806         AnalysisPeriodicEvent(1);\r
5807       }\r
5808       break;\r
5809 \r
5810     case IDM_IcsClient:\r
5811       IcsClientEvent();\r
5812       break;\r
5813 \r
5814     case IDM_EditGame:\r
5815       EditGameEvent();\r
5816       break;\r
5817 \r
5818     case IDM_EditPosition:\r
5819       EditPositionEvent();\r
5820       break;\r
5821 \r
5822     case IDM_Training:\r
5823       TrainingEvent();\r
5824       break;\r
5825 \r
5826     case IDM_ShowGameList:\r
5827       ShowGameListProc();\r
5828       break;\r
5829 \r
5830     case IDM_EditTags:\r
5831       EditTagsProc();\r
5832       break;\r
5833 \r
5834     case IDM_EditComment:\r
5835       if (commentDialogUp && editComment) {\r
5836         CommentPopDown();\r
5837       } else {\r
5838         EditCommentEvent();\r
5839       }\r
5840       break;\r
5841 \r
5842     case IDM_Pause:\r
5843       PauseEvent();\r
5844       break;\r
5845 \r
5846     case IDM_Accept:\r
5847       AcceptEvent();\r
5848       break;\r
5849 \r
5850     case IDM_Decline:\r
5851       DeclineEvent();\r
5852       break;\r
5853 \r
5854     case IDM_Rematch:\r
5855       RematchEvent();\r
5856       break;\r
5857 \r
5858     case IDM_CallFlag:\r
5859       CallFlagEvent();\r
5860       break;\r
5861 \r
5862     case IDM_Draw:\r
5863       DrawEvent();\r
5864       break;\r
5865 \r
5866     case IDM_Adjourn:\r
5867       AdjournEvent();\r
5868       break;\r
5869 \r
5870     case IDM_Abort:\r
5871       AbortEvent();\r
5872       break;\r
5873 \r
5874     case IDM_Resign:\r
5875       ResignEvent();\r
5876       break;\r
5877 \r
5878     case IDM_StopObserving:\r
5879       StopObservingEvent();\r
5880       break;\r
5881 \r
5882     case IDM_StopExamining:\r
5883       StopExaminingEvent();\r
5884       break;\r
5885 \r
5886     case IDM_TypeInMove:\r
5887       PopUpMoveDialog('\000');\r
5888       break;\r
5889 \r
5890     case IDM_TypeInName:\r
5891       PopUpNameDialog('\000');\r
5892       break;\r
5893 \r
5894     case IDM_Backward:\r
5895       BackwardEvent();\r
5896       SetFocus(hwndMain);\r
5897       break;\r
5898 \r
5899     case IDM_Forward:\r
5900       ForwardEvent();\r
5901       SetFocus(hwndMain);\r
5902       break;\r
5903 \r
5904     case IDM_ToStart:\r
5905       ToStartEvent();\r
5906       SetFocus(hwndMain);\r
5907       break;\r
5908 \r
5909     case IDM_ToEnd:\r
5910       ToEndEvent();\r
5911       SetFocus(hwndMain);\r
5912       break;\r
5913 \r
5914     case IDM_Revert:\r
5915       RevertEvent();\r
5916       break;\r
5917 \r
5918     case IDM_TruncateGame:\r
5919       TruncateGameEvent();\r
5920       break;\r
5921 \r
5922     case IDM_MoveNow:\r
5923       MoveNowEvent();\r
5924       break;\r
5925 \r
5926     case IDM_RetractMove:\r
5927       RetractMoveEvent();\r
5928       break;\r
5929 \r
5930     case IDM_FlipView:\r
5931       flipView = !flipView;\r
5932       DrawPosition(FALSE, NULL);\r
5933       break;\r
5934 \r
5935     case IDM_FlipClock:\r
5936       flipClock = !flipClock;\r
5937       DisplayBothClocks();\r
5938       break;\r
5939 \r
5940     case IDM_GeneralOptions:\r
5941       GeneralOptionsPopup(hwnd);\r
5942       DrawPosition(TRUE, NULL);\r
5943       break;\r
5944 \r
5945     case IDM_BoardOptions:\r
5946       BoardOptionsPopup(hwnd);\r
5947       break;\r
5948 \r
5949     case IDM_EnginePlayOptions:\r
5950       EnginePlayOptionsPopup(hwnd);\r
5951       break;\r
5952 \r
5953     case IDM_OptionsUCI:\r
5954       UciOptionsPopup(hwnd);\r
5955       break;\r
5956 \r
5957     case IDM_IcsOptions:\r
5958       IcsOptionsPopup(hwnd);\r
5959       break;\r
5960 \r
5961     case IDM_Fonts:\r
5962       FontsOptionsPopup(hwnd);\r
5963       break;\r
5964 \r
5965     case IDM_Sounds:\r
5966       SoundOptionsPopup(hwnd);\r
5967       break;\r
5968 \r
5969     case IDM_CommPort:\r
5970       CommPortOptionsPopup(hwnd);\r
5971       break;\r
5972 \r
5973     case IDM_LoadOptions:\r
5974       LoadOptionsPopup(hwnd);\r
5975       break;\r
5976 \r
5977     case IDM_SaveOptions:\r
5978       SaveOptionsPopup(hwnd);\r
5979       break;\r
5980 \r
5981     case IDM_TimeControl:\r
5982       TimeControlOptionsPopup(hwnd);\r
5983       break;\r
5984 \r
5985     case IDM_SaveSettings:\r
5986       SaveSettings(settingsFileName);\r
5987       break;\r
5988 \r
5989     case IDM_SaveSettingsOnExit:\r
5990       saveSettingsOnExit = !saveSettingsOnExit;\r
5991       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5992                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5993                                          MF_CHECKED : MF_UNCHECKED));\r
5994       break;\r
5995 \r
5996     case IDM_Hint:\r
5997       HintEvent();\r
5998       break;\r
5999 \r
6000     case IDM_Book:\r
6001       BookEvent();\r
6002       break;\r
6003 \r
6004     case IDM_AboutGame:\r
6005       AboutGameEvent();\r
6006       break;\r
6007 \r
6008     case IDM_Debug:\r
6009       appData.debugMode = !appData.debugMode;\r
6010       if (appData.debugMode) {\r
6011         char dir[MSG_SIZ];\r
6012         GetCurrentDirectory(MSG_SIZ, dir);\r
6013         SetCurrentDirectory(installDir);\r
6014         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6015         SetCurrentDirectory(dir);\r
6016         setbuf(debugFP, NULL);\r
6017       } else {\r
6018         fclose(debugFP);\r
6019         debugFP = NULL;\r
6020       }\r
6021       break;\r
6022 \r
6023     case IDM_HELPCONTENTS:\r
6024       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6025         MessageBox (GetFocus(),\r
6026                     "Unable to activate help",\r
6027                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6028       }\r
6029       break;\r
6030 \r
6031     case IDM_HELPSEARCH:\r
6032       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6033         MessageBox (GetFocus(),\r
6034                     "Unable to activate help",\r
6035                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6036       }\r
6037       break;\r
6038 \r
6039     case IDM_HELPHELP:\r
6040       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6041         MessageBox (GetFocus(),\r
6042                     "Unable to activate help",\r
6043                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6044       }\r
6045       break;\r
6046 \r
6047     case IDM_ABOUT:\r
6048       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6049       DialogBox(hInst, \r
6050         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6051         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6052       FreeProcInstance(lpProc);\r
6053       break;\r
6054 \r
6055     case IDM_DirectCommand1:\r
6056       AskQuestionEvent("Direct Command",\r
6057                        "Send to chess program:", "", "1");\r
6058       break;\r
6059     case IDM_DirectCommand2:\r
6060       AskQuestionEvent("Direct Command",\r
6061                        "Send to second chess program:", "", "2");\r
6062       break;\r
6063 \r
6064     case EP_WhitePawn:\r
6065       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6066       fromX = fromY = -1;\r
6067       break;\r
6068 \r
6069     case EP_WhiteKnight:\r
6070       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6071       fromX = fromY = -1;\r
6072       break;\r
6073 \r
6074     case EP_WhiteBishop:\r
6075       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6076       fromX = fromY = -1;\r
6077       break;\r
6078 \r
6079     case EP_WhiteRook:\r
6080       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6081       fromX = fromY = -1;\r
6082       break;\r
6083 \r
6084     case EP_WhiteQueen:\r
6085       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6086       fromX = fromY = -1;\r
6087       break;\r
6088 \r
6089     case EP_WhiteFerz:\r
6090       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6091       fromX = fromY = -1;\r
6092       break;\r
6093 \r
6094     case EP_WhiteWazir:\r
6095       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6096       fromX = fromY = -1;\r
6097       break;\r
6098 \r
6099     case EP_WhiteAlfil:\r
6100       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6101       fromX = fromY = -1;\r
6102       break;\r
6103 \r
6104     case EP_WhiteCannon:\r
6105       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6106       fromX = fromY = -1;\r
6107       break;\r
6108 \r
6109     case EP_WhiteCardinal:\r
6110       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6111       fromX = fromY = -1;\r
6112       break;\r
6113 \r
6114     case EP_WhiteMarshall:\r
6115       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6116       fromX = fromY = -1;\r
6117       break;\r
6118 \r
6119     case EP_WhiteKing:\r
6120       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6121       fromX = fromY = -1;\r
6122       break;\r
6123 \r
6124     case EP_BlackPawn:\r
6125       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6126       fromX = fromY = -1;\r
6127       break;\r
6128 \r
6129     case EP_BlackKnight:\r
6130       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6131       fromX = fromY = -1;\r
6132       break;\r
6133 \r
6134     case EP_BlackBishop:\r
6135       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6136       fromX = fromY = -1;\r
6137       break;\r
6138 \r
6139     case EP_BlackRook:\r
6140       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6141       fromX = fromY = -1;\r
6142       break;\r
6143 \r
6144     case EP_BlackQueen:\r
6145       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6146       fromX = fromY = -1;\r
6147       break;\r
6148 \r
6149     case EP_BlackFerz:\r
6150       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6151       fromX = fromY = -1;\r
6152       break;\r
6153 \r
6154     case EP_BlackWazir:\r
6155       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6156       fromX = fromY = -1;\r
6157       break;\r
6158 \r
6159     case EP_BlackAlfil:\r
6160       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6161       fromX = fromY = -1;\r
6162       break;\r
6163 \r
6164     case EP_BlackCannon:\r
6165       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6166       fromX = fromY = -1;\r
6167       break;\r
6168 \r
6169     case EP_BlackCardinal:\r
6170       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6171       fromX = fromY = -1;\r
6172       break;\r
6173 \r
6174     case EP_BlackMarshall:\r
6175       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6176       fromX = fromY = -1;\r
6177       break;\r
6178 \r
6179     case EP_BlackKing:\r
6180       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6181       fromX = fromY = -1;\r
6182       break;\r
6183 \r
6184     case EP_EmptySquare:\r
6185       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6186       fromX = fromY = -1;\r
6187       break;\r
6188 \r
6189     case EP_ClearBoard:\r
6190       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6191       fromX = fromY = -1;\r
6192       break;\r
6193 \r
6194     case EP_White:\r
6195       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6196       fromX = fromY = -1;\r
6197       break;\r
6198 \r
6199     case EP_Black:\r
6200       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6201       fromX = fromY = -1;\r
6202       break;\r
6203 \r
6204     case EP_Promote:\r
6205       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6206       fromX = fromY = -1;\r
6207       break;\r
6208 \r
6209     case EP_Demote:\r
6210       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6211       fromX = fromY = -1;\r
6212       break;\r
6213 \r
6214     case DP_Pawn:\r
6215       DropMenuEvent(WhitePawn, fromX, fromY);\r
6216       fromX = fromY = -1;\r
6217       break;\r
6218 \r
6219     case DP_Knight:\r
6220       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6221       fromX = fromY = -1;\r
6222       break;\r
6223 \r
6224     case DP_Bishop:\r
6225       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6226       fromX = fromY = -1;\r
6227       break;\r
6228 \r
6229     case DP_Rook:\r
6230       DropMenuEvent(WhiteRook, fromX, fromY);\r
6231       fromX = fromY = -1;\r
6232       break;\r
6233 \r
6234     case DP_Queen:\r
6235       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6236       fromX = fromY = -1;\r
6237       break;\r
6238 \r
6239     default:\r
6240       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6241     }\r
6242     break;\r
6243 \r
6244   case WM_TIMER:\r
6245     switch (wParam) {\r
6246     case CLOCK_TIMER_ID:\r
6247       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6248       clockTimerEvent = 0;\r
6249       DecrementClocks(); /* call into back end */\r
6250       break;\r
6251     case LOAD_GAME_TIMER_ID:\r
6252       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6253       loadGameTimerEvent = 0;\r
6254       AutoPlayGameLoop(); /* call into back end */\r
6255       break;\r
6256     case ANALYSIS_TIMER_ID:\r
6257       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6258                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6259         AnalysisPeriodicEvent(0);\r
6260       } else {\r
6261         KillTimer(hwnd, analysisTimerEvent);\r
6262         analysisTimerEvent = 0;\r
6263       }\r
6264       break;\r
6265     case DELAYED_TIMER_ID:\r
6266       KillTimer(hwnd, delayedTimerEvent);\r
6267       delayedTimerEvent = 0;\r
6268       delayedTimerCallback();\r
6269       break;\r
6270     }\r
6271     break;\r
6272 \r
6273   case WM_USER_Input:\r
6274     InputEvent(hwnd, message, wParam, lParam);\r
6275     break;\r
6276 \r
6277   /* [AS] Also move "attached" child windows */\r
6278   case WM_WINDOWPOSCHANGING:\r
6279     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6280         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6281 \r
6282         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6283             /* Window is moving */\r
6284             RECT rcMain;\r
6285 \r
6286             GetWindowRect( hwnd, &rcMain );\r
6287             \r
6288             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6289             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6290             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6291         }\r
6292     }\r
6293     break;\r
6294 \r
6295   /* [AS] Snapping */\r
6296   case WM_ENTERSIZEMOVE:\r
6297     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6298     if (hwnd == hwndMain) {\r
6299       doingSizing = TRUE;\r
6300       lastSizing = 0;\r
6301     }\r
6302     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6303     break;\r
6304 \r
6305   case WM_SIZING:\r
6306     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6307     if (hwnd == hwndMain) {\r
6308       lastSizing = wParam;\r
6309     }\r
6310     break;\r
6311 \r
6312   case WM_MOVING:\r
6313     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6314       return OnMoving( &sd, hwnd, wParam, lParam );\r
6315 \r
6316   case WM_EXITSIZEMOVE:\r
6317     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6318     if (hwnd == hwndMain) {\r
6319       RECT client;\r
6320       doingSizing = FALSE;\r
6321       InvalidateRect(hwnd, &boardRect, FALSE);\r
6322       GetClientRect(hwnd, &client);\r
6323       ResizeBoard(client.right, client.bottom, lastSizing);\r
6324       lastSizing = 0;\r
6325       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6326     }\r
6327     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6328     break;\r
6329 \r
6330   case WM_DESTROY: /* message: window being destroyed */\r
6331     PostQuitMessage(0);\r
6332     break;\r
6333 \r
6334   case WM_CLOSE:\r
6335     if (hwnd == hwndMain) {\r
6336       ExitEvent(0);\r
6337     }\r
6338     break;\r
6339 \r
6340   default:      /* Passes it on if unprocessed */\r
6341     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6342   }\r
6343   return 0;\r
6344 }\r
6345 \r
6346 /*---------------------------------------------------------------------------*\\r
6347  *\r
6348  * Misc utility routines\r
6349  *\r
6350 \*---------------------------------------------------------------------------*/\r
6351 \r
6352 /*\r
6353  * Decent random number generator, at least not as bad as Windows\r
6354  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6355  */\r
6356 unsigned int randstate;\r
6357 \r
6358 int\r
6359 myrandom(void)\r
6360 {\r
6361   randstate = randstate * 1664525 + 1013904223;\r
6362   return (int) randstate & 0x7fffffff;\r
6363 }\r
6364 \r
6365 void\r
6366 mysrandom(unsigned int seed)\r
6367 {\r
6368   randstate = seed;\r
6369 }\r
6370 \r
6371 \r
6372 /* \r
6373  * returns TRUE if user selects a different color, FALSE otherwise \r
6374  */\r
6375 \r
6376 BOOL\r
6377 ChangeColor(HWND hwnd, COLORREF *which)\r
6378 {\r
6379   static BOOL firstTime = TRUE;\r
6380   static DWORD customColors[16];\r
6381   CHOOSECOLOR cc;\r
6382   COLORREF newcolor;\r
6383   int i;\r
6384   ColorClass ccl;\r
6385 \r
6386   if (firstTime) {\r
6387     /* Make initial colors in use available as custom colors */\r
6388     /* Should we put the compiled-in defaults here instead? */\r
6389     i = 0;\r
6390     customColors[i++] = lightSquareColor & 0xffffff;\r
6391     customColors[i++] = darkSquareColor & 0xffffff;\r
6392     customColors[i++] = whitePieceColor & 0xffffff;\r
6393     customColors[i++] = blackPieceColor & 0xffffff;\r
6394     customColors[i++] = highlightSquareColor & 0xffffff;\r
6395     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6396 \r
6397     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6398       customColors[i++] = textAttribs[ccl].color;\r
6399     }\r
6400     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6401     firstTime = FALSE;\r
6402   }\r
6403 \r
6404   cc.lStructSize = sizeof(cc);\r
6405   cc.hwndOwner = hwnd;\r
6406   cc.hInstance = NULL;\r
6407   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6408   cc.lpCustColors = (LPDWORD) customColors;\r
6409   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6410 \r
6411   if (!ChooseColor(&cc)) return FALSE;\r
6412 \r
6413   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6414   if (newcolor == *which) return FALSE;\r
6415   *which = newcolor;\r
6416   return TRUE;\r
6417 \r
6418   /*\r
6419   InitDrawingColors();\r
6420   InvalidateRect(hwnd, &boardRect, FALSE);\r
6421   */\r
6422 }\r
6423 \r
6424 BOOLEAN\r
6425 MyLoadSound(MySound *ms)\r
6426 {\r
6427   BOOL ok = FALSE;\r
6428   struct stat st;\r
6429   FILE *f;\r
6430 \r
6431   if (ms->data) free(ms->data);\r
6432   ms->data = NULL;\r
6433 \r
6434   switch (ms->name[0]) {\r
6435   case NULLCHAR:\r
6436     /* Silence */\r
6437     ok = TRUE;\r
6438     break;\r
6439   case '$':\r
6440     /* System sound from Control Panel.  Don't preload here. */\r
6441     ok = TRUE;\r
6442     break;\r
6443   case '!':\r
6444     if (ms->name[1] == NULLCHAR) {\r
6445       /* "!" alone = silence */\r
6446       ok = TRUE;\r
6447     } else {\r
6448       /* Builtin wave resource.  Error if not found. */\r
6449       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6450       if (h == NULL) break;\r
6451       ms->data = (void *)LoadResource(hInst, h);\r
6452       if (h == NULL) break;\r
6453       ok = TRUE;\r
6454     }\r
6455     break;\r
6456   default:\r
6457     /* .wav file.  Error if not found. */\r
6458     f = fopen(ms->name, "rb");\r
6459     if (f == NULL) break;\r
6460     if (fstat(fileno(f), &st) < 0) break;\r
6461     ms->data = malloc(st.st_size);\r
6462     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6463     fclose(f);\r
6464     ok = TRUE;\r
6465     break;\r
6466   }\r
6467   if (!ok) {\r
6468     char buf[MSG_SIZ];\r
6469     sprintf(buf, "Error loading sound %s", ms->name);\r
6470     DisplayError(buf, GetLastError());\r
6471   }\r
6472   return ok;\r
6473 }\r
6474 \r
6475 BOOLEAN\r
6476 MyPlaySound(MySound *ms)\r
6477 {\r
6478   BOOLEAN ok = FALSE;\r
6479   switch (ms->name[0]) {\r
6480   case NULLCHAR:\r
6481     /* Silence */\r
6482     ok = TRUE;\r
6483     break;\r
6484   case '$':\r
6485     /* System sound from Control Panel (deprecated feature).\r
6486        "$" alone or an unset sound name gets default beep (still in use). */\r
6487     if (ms->name[1]) {\r
6488       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6489     }\r
6490     if (!ok) ok = MessageBeep(MB_OK);\r
6491     break; \r
6492   case '!':\r
6493     /* Builtin wave resource, or "!" alone for silence */\r
6494     if (ms->name[1]) {\r
6495       if (ms->data == NULL) return FALSE;\r
6496       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6497     } else {\r
6498       ok = TRUE;\r
6499     }\r
6500     break;\r
6501   default:\r
6502     /* .wav file.  Error if not found. */\r
6503     if (ms->data == NULL) return FALSE;\r
6504     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6505     break;\r
6506   }\r
6507   /* Don't print an error: this can happen innocently if the sound driver\r
6508      is busy; for instance, if another instance of WinBoard is playing\r
6509      a sound at about the same time. */\r
6510 #if 0\r
6511   if (!ok) {\r
6512     char buf[MSG_SIZ];\r
6513     sprintf(buf, "Error playing sound %s", ms->name);\r
6514     DisplayError(buf, GetLastError());\r
6515   }\r
6516 #endif\r
6517   return ok;\r
6518 }\r
6519 \r
6520 \r
6521 LRESULT CALLBACK\r
6522 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6523 {\r
6524   BOOL ok;\r
6525   OPENFILENAME *ofn;\r
6526   static UINT *number; /* gross that this is static */\r
6527 \r
6528   switch (message) {\r
6529   case WM_INITDIALOG: /* message: initialize dialog box */\r
6530     /* Center the dialog over the application window */\r
6531     ofn = (OPENFILENAME *) lParam;\r
6532     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6533       number = (UINT *) ofn->lCustData;\r
6534       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6535     } else {\r
6536       number = NULL;\r
6537     }\r
6538     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6539     return FALSE;  /* Allow for further processing */\r
6540 \r
6541   case WM_COMMAND:\r
6542     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6543       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6544     }\r
6545     return FALSE;  /* Allow for further processing */\r
6546   }\r
6547   return FALSE;\r
6548 }\r
6549 \r
6550 UINT APIENTRY\r
6551 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6552 {\r
6553   static UINT *number;\r
6554   OPENFILENAME *ofname;\r
6555   OFNOTIFY *ofnot;\r
6556   switch (uiMsg) {\r
6557   case WM_INITDIALOG:\r
6558     ofname = (OPENFILENAME *)lParam;\r
6559     number = (UINT *)(ofname->lCustData);\r
6560     break;\r
6561   case WM_NOTIFY:\r
6562     ofnot = (OFNOTIFY *)lParam;\r
6563     if (ofnot->hdr.code == CDN_FILEOK) {\r
6564       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6565     }\r
6566     break;\r
6567   }\r
6568   return 0;\r
6569 }\r
6570 \r
6571 \r
6572 FILE *\r
6573 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6574                char *nameFilt, char *dlgTitle, UINT *number,\r
6575                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6576 {\r
6577   OPENFILENAME openFileName;\r
6578   char buf1[MSG_SIZ];\r
6579   FILE *f;\r
6580 \r
6581   if (fileName == NULL) fileName = buf1;\r
6582   if (defName == NULL) {\r
6583     strcpy(fileName, "*.");\r
6584     strcat(fileName, defExt);\r
6585   } else {\r
6586     strcpy(fileName, defName);\r
6587   }\r
6588   if (fileTitle) strcpy(fileTitle, "");\r
6589   if (number) *number = 0;\r
6590 \r
6591   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6592   openFileName.hwndOwner         = hwnd;\r
6593   openFileName.hInstance         = (HANDLE) hInst;\r
6594   openFileName.lpstrFilter       = nameFilt;\r
6595   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6596   openFileName.nMaxCustFilter    = 0L;\r
6597   openFileName.nFilterIndex      = 1L;\r
6598   openFileName.lpstrFile         = fileName;\r
6599   openFileName.nMaxFile          = MSG_SIZ;\r
6600   openFileName.lpstrFileTitle    = fileTitle;\r
6601   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6602   openFileName.lpstrInitialDir   = NULL;\r
6603   openFileName.lpstrTitle        = dlgTitle;\r
6604   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6605     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6606     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6607     | (oldDialog ? 0 : OFN_EXPLORER);\r
6608   openFileName.nFileOffset       = 0;\r
6609   openFileName.nFileExtension    = 0;\r
6610   openFileName.lpstrDefExt       = defExt;\r
6611   openFileName.lCustData         = (LONG) number;\r
6612   openFileName.lpfnHook          = oldDialog ?\r
6613     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6614   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6615 \r
6616   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6617                         GetOpenFileName(&openFileName)) {\r
6618     /* open the file */\r
6619     f = fopen(openFileName.lpstrFile, write);\r
6620     if (f == NULL) {\r
6621       MessageBox(hwnd, "File open failed", NULL,\r
6622                  MB_OK|MB_ICONEXCLAMATION);\r
6623       return NULL;\r
6624     }\r
6625   } else {\r
6626     int err = CommDlgExtendedError();\r
6627     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6628     return FALSE;\r
6629   }\r
6630   return f;\r
6631 }\r
6632 \r
6633 \r
6634 \r
6635 VOID APIENTRY\r
6636 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6637 {\r
6638   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6639 \r
6640   /*\r
6641    * Get the first pop-up menu in the menu template. This is the\r
6642    * menu that TrackPopupMenu displays.\r
6643    */\r
6644   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6645 \r
6646   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6647 \r
6648   /*\r
6649    * TrackPopup uses screen coordinates, so convert the\r
6650    * coordinates of the mouse click to screen coordinates.\r
6651    */\r
6652   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6653 \r
6654   /* Draw and track the floating pop-up menu. */\r
6655   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6656                  pt.x, pt.y, 0, hwnd, NULL);\r
6657 \r
6658   /* Destroy the menu.*/\r
6659   DestroyMenu(hmenu);\r
6660 }\r
6661    \r
6662 typedef struct {\r
6663   HWND hDlg, hText;\r
6664   int sizeX, sizeY, newSizeX, newSizeY;\r
6665   HDWP hdwp;\r
6666 } ResizeEditPlusButtonsClosure;\r
6667 \r
6668 BOOL CALLBACK\r
6669 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6670 {\r
6671   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6672   RECT rect;\r
6673   POINT pt;\r
6674 \r
6675   if (hChild == cl->hText) return TRUE;\r
6676   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6677   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6678   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6679   ScreenToClient(cl->hDlg, &pt);\r
6680   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6681     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6682   return TRUE;\r
6683 }\r
6684 \r
6685 /* Resize a dialog that has a (rich) edit field filling most of\r
6686    the top, with a row of buttons below */\r
6687 VOID\r
6688 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6689 {\r
6690   RECT rectText;\r
6691   int newTextHeight, newTextWidth;\r
6692   ResizeEditPlusButtonsClosure cl;\r
6693   \r
6694   /*if (IsIconic(hDlg)) return;*/\r
6695   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6696   \r
6697   cl.hdwp = BeginDeferWindowPos(8);\r
6698 \r
6699   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6700   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6701   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6702   if (newTextHeight < 0) {\r
6703     newSizeY += -newTextHeight;\r
6704     newTextHeight = 0;\r
6705   }\r
6706   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6707     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6708 \r
6709   cl.hDlg = hDlg;\r
6710   cl.hText = hText;\r
6711   cl.sizeX = sizeX;\r
6712   cl.sizeY = sizeY;\r
6713   cl.newSizeX = newSizeX;\r
6714   cl.newSizeY = newSizeY;\r
6715   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6716 \r
6717   EndDeferWindowPos(cl.hdwp);\r
6718 }\r
6719 \r
6720 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6721 {\r
6722     RECT    rChild, rParent;\r
6723     int     wChild, hChild, wParent, hParent;\r
6724     int     wScreen, hScreen, xNew, yNew;\r
6725     HDC     hdc;\r
6726 \r
6727     /* Get the Height and Width of the child window */\r
6728     GetWindowRect (hwndChild, &rChild);\r
6729     wChild = rChild.right - rChild.left;\r
6730     hChild = rChild.bottom - rChild.top;\r
6731 \r
6732     /* Get the Height and Width of the parent window */\r
6733     GetWindowRect (hwndParent, &rParent);\r
6734     wParent = rParent.right - rParent.left;\r
6735     hParent = rParent.bottom - rParent.top;\r
6736 \r
6737     /* Get the display limits */\r
6738     hdc = GetDC (hwndChild);\r
6739     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6740     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6741     ReleaseDC(hwndChild, hdc);\r
6742 \r
6743     /* Calculate new X position, then adjust for screen */\r
6744     xNew = rParent.left + ((wParent - wChild) /2);\r
6745     if (xNew < 0) {\r
6746         xNew = 0;\r
6747     } else if ((xNew+wChild) > wScreen) {\r
6748         xNew = wScreen - wChild;\r
6749     }\r
6750 \r
6751     /* Calculate new Y position, then adjust for screen */\r
6752     if( mode == 0 ) {\r
6753         yNew = rParent.top  + ((hParent - hChild) /2);\r
6754     }\r
6755     else {\r
6756         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6757     }\r
6758 \r
6759     if (yNew < 0) {\r
6760         yNew = 0;\r
6761     } else if ((yNew+hChild) > hScreen) {\r
6762         yNew = hScreen - hChild;\r
6763     }\r
6764 \r
6765     /* Set it, and return */\r
6766     return SetWindowPos (hwndChild, NULL,\r
6767                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6768 }\r
6769 \r
6770 /* Center one window over another */\r
6771 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6772 {\r
6773     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6774 }\r
6775 \r
6776 /*---------------------------------------------------------------------------*\\r
6777  *\r
6778  * Startup Dialog functions\r
6779  *\r
6780 \*---------------------------------------------------------------------------*/\r
6781 void\r
6782 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6783 {\r
6784   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6785 \r
6786   while (*cd != NULL) {\r
6787     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6788     cd++;\r
6789   }\r
6790 }\r
6791 \r
6792 void\r
6793 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6794 {\r
6795   char buf1[ARG_MAX];\r
6796   int len;\r
6797 \r
6798   if (str[0] == '@') {\r
6799     FILE* f = fopen(str + 1, "r");\r
6800     if (f == NULL) {\r
6801       DisplayFatalError(str + 1, errno, 2);\r
6802       return;\r
6803     }\r
6804     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6805     fclose(f);\r
6806     buf1[len] = NULLCHAR;\r
6807     str = buf1;\r
6808   }\r
6809 \r
6810   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6811 \r
6812   for (;;) {\r
6813     char buf[MSG_SIZ];\r
6814     char *end = strchr(str, '\n');\r
6815     if (end == NULL) return;\r
6816     memcpy(buf, str, end - str);\r
6817     buf[end - str] = NULLCHAR;\r
6818     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6819     str = end + 1;\r
6820   }\r
6821 }\r
6822 \r
6823 void\r
6824 SetStartupDialogEnables(HWND hDlg)\r
6825 {\r
6826   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6827     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6828     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6829   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6830     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6831   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6832     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6833   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6834     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6835   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6836     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6837     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6838     IsDlgButtonChecked(hDlg, OPT_View));\r
6839 }\r
6840 \r
6841 char *\r
6842 QuoteForFilename(char *filename)\r
6843 {\r
6844   int dquote, space;\r
6845   dquote = strchr(filename, '"') != NULL;\r
6846   space = strchr(filename, ' ') != NULL;\r
6847   if (dquote || space) {\r
6848     if (dquote) {\r
6849       return "'";\r
6850     } else {\r
6851       return "\"";\r
6852     }\r
6853   } else {\r
6854     return "";\r
6855   }\r
6856 }\r
6857 \r
6858 VOID\r
6859 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6860 {\r
6861   char buf[MSG_SIZ];\r
6862   char *q;\r
6863 \r
6864   InitComboStringsFromOption(hwndCombo, nthnames);\r
6865   q = QuoteForFilename(nthcp);\r
6866   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6867   if (*nthdir != NULLCHAR) {\r
6868     q = QuoteForFilename(nthdir);\r
6869     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6870   }\r
6871   if (*nthcp == NULLCHAR) {\r
6872     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6873   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6874     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6875     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6876   }\r
6877 }\r
6878 \r
6879 LRESULT CALLBACK\r
6880 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6881 {\r
6882   char buf[MSG_SIZ];\r
6883   HANDLE hwndCombo;\r
6884   char *p;\r
6885 \r
6886   switch (message) {\r
6887   case WM_INITDIALOG:\r
6888     /* Center the dialog */\r
6889     CenterWindow (hDlg, GetDesktopWindow());\r
6890     /* Initialize the dialog items */\r
6891     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6892                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6893                   firstChessProgramNames);\r
6894     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6895                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6896                   secondChessProgramNames);\r
6897     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6898     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6899     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6900     if (*appData.icsHelper != NULLCHAR) {\r
6901       char *q = QuoteForFilename(appData.icsHelper);\r
6902       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6903     }\r
6904     if (*appData.icsHost == NULLCHAR) {\r
6905       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6906       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6907     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6908       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6909       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6910     }\r
6911 \r
6912     if (appData.icsActive) {\r
6913       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6914     }\r
6915     else if (appData.noChessProgram) {\r
6916       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6917     }\r
6918     else {\r
6919       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6920     }\r
6921 \r
6922     SetStartupDialogEnables(hDlg);\r
6923     return TRUE;\r
6924 \r
6925   case WM_COMMAND:\r
6926     switch (LOWORD(wParam)) {\r
6927     case IDOK:\r
6928       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6929         strcpy(buf, "/fcp=");\r
6930         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6931         p = buf;\r
6932         ParseArgs(StringGet, &p);\r
6933         strcpy(buf, "/scp=");\r
6934         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6935         p = buf;\r
6936         ParseArgs(StringGet, &p);\r
6937         appData.noChessProgram = FALSE;\r
6938         appData.icsActive = FALSE;\r
6939       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6940         strcpy(buf, "/ics /icshost=");\r
6941         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6942         p = buf;\r
6943         ParseArgs(StringGet, &p);\r
6944         if (appData.zippyPlay) {\r
6945           strcpy(buf, "/fcp=");\r
6946           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6947           p = buf;\r
6948           ParseArgs(StringGet, &p);\r
6949         }\r
6950       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6951         appData.noChessProgram = TRUE;\r
6952         appData.icsActive = FALSE;\r
6953       } else {\r
6954         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6955                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6956         return TRUE;\r
6957       }\r
6958       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6959         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6960         p = buf;\r
6961         ParseArgs(StringGet, &p);\r
6962       }\r
6963       EndDialog(hDlg, TRUE);\r
6964       return TRUE;\r
6965 \r
6966     case IDCANCEL:\r
6967       ExitEvent(0);\r
6968       return TRUE;\r
6969 \r
6970     case IDM_HELPCONTENTS:\r
6971       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6972         MessageBox (GetFocus(),\r
6973                     "Unable to activate help",\r
6974                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6975       }\r
6976       break;\r
6977 \r
6978     default:\r
6979       SetStartupDialogEnables(hDlg);\r
6980       break;\r
6981     }\r
6982     break;\r
6983   }\r
6984   return FALSE;\r
6985 }\r
6986 \r
6987 /*---------------------------------------------------------------------------*\\r
6988  *\r
6989  * About box dialog functions\r
6990  *\r
6991 \*---------------------------------------------------------------------------*/\r
6992 \r
6993 /* Process messages for "About" dialog box */\r
6994 LRESULT CALLBACK\r
6995 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6996 {\r
6997   switch (message) {\r
6998   case WM_INITDIALOG: /* message: initialize dialog box */\r
6999     /* Center the dialog over the application window */\r
7000     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7001     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7002     return (TRUE);\r
7003 \r
7004   case WM_COMMAND: /* message: received a command */\r
7005     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7006         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7007       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7008       return (TRUE);\r
7009     }\r
7010     break;\r
7011   }\r
7012   return (FALSE);\r
7013 }\r
7014 \r
7015 /*---------------------------------------------------------------------------*\\r
7016  *\r
7017  * Comment Dialog functions\r
7018  *\r
7019 \*---------------------------------------------------------------------------*/\r
7020 \r
7021 LRESULT CALLBACK\r
7022 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7023 {\r
7024   static HANDLE hwndText = NULL;\r
7025   int len, newSizeX, newSizeY, flags;\r
7026   static int sizeX, sizeY;\r
7027   char *str;\r
7028   RECT rect;\r
7029   MINMAXINFO *mmi;\r
7030 \r
7031   switch (message) {\r
7032   case WM_INITDIALOG: /* message: initialize dialog box */\r
7033     /* Initialize the dialog items */\r
7034     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7035     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7036     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7037     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7038     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7039     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7040     SetWindowText(hDlg, commentTitle);\r
7041     if (editComment) {\r
7042       SetFocus(hwndText);\r
7043     } else {\r
7044       SetFocus(GetDlgItem(hDlg, IDOK));\r
7045     }\r
7046     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7047                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7048                 MAKELPARAM(FALSE, 0));\r
7049     /* Size and position the dialog */\r
7050     if (!commentDialog) {\r
7051       commentDialog = hDlg;\r
7052       flags = SWP_NOZORDER;\r
7053       GetClientRect(hDlg, &rect);\r
7054       sizeX = rect.right;\r
7055       sizeY = rect.bottom;\r
7056       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7057           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7058         WINDOWPLACEMENT wp;\r
7059         EnsureOnScreen(&commentX, &commentY);\r
7060         wp.length = sizeof(WINDOWPLACEMENT);\r
7061         wp.flags = 0;\r
7062         wp.showCmd = SW_SHOW;\r
7063         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7064         wp.rcNormalPosition.left = commentX;\r
7065         wp.rcNormalPosition.right = commentX + commentW;\r
7066         wp.rcNormalPosition.top = commentY;\r
7067         wp.rcNormalPosition.bottom = commentY + commentH;\r
7068         SetWindowPlacement(hDlg, &wp);\r
7069 \r
7070         GetClientRect(hDlg, &rect);\r
7071         newSizeX = rect.right;\r
7072         newSizeY = rect.bottom;\r
7073         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7074                               newSizeX, newSizeY);\r
7075         sizeX = newSizeX;\r
7076         sizeY = newSizeY;\r
7077       }\r
7078     }\r
7079     return FALSE;\r
7080 \r
7081   case WM_COMMAND: /* message: received a command */\r
7082     switch (LOWORD(wParam)) {\r
7083     case IDOK:\r
7084       if (editComment) {\r
7085         char *p, *q;\r
7086         /* Read changed options from the dialog box */\r
7087         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7088         len = GetWindowTextLength(hwndText);\r
7089         str = (char *) malloc(len + 1);\r
7090         GetWindowText(hwndText, str, len + 1);\r
7091         p = q = str;\r
7092         while (*q) {\r
7093           if (*q == '\r')\r
7094             q++;\r
7095           else\r
7096             *p++ = *q++;\r
7097         }\r
7098         *p = NULLCHAR;\r
7099         ReplaceComment(commentIndex, str);\r
7100         free(str);\r
7101       }\r
7102       CommentPopDown();\r
7103       return TRUE;\r
7104 \r
7105     case IDCANCEL:\r
7106     case OPT_CancelComment:\r
7107       CommentPopDown();\r
7108       return TRUE;\r
7109 \r
7110     case OPT_ClearComment:\r
7111       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7112       break;\r
7113 \r
7114     case OPT_EditComment:\r
7115       EditCommentEvent();\r
7116       return TRUE;\r
7117 \r
7118     default:\r
7119       break;\r
7120     }\r
7121     break;\r
7122 \r
7123   case WM_SIZE:\r
7124     newSizeX = LOWORD(lParam);\r
7125     newSizeY = HIWORD(lParam);\r
7126     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7127     sizeX = newSizeX;\r
7128     sizeY = newSizeY;\r
7129     break;\r
7130 \r
7131   case WM_GETMINMAXINFO:\r
7132     /* Prevent resizing window too small */\r
7133     mmi = (MINMAXINFO *) lParam;\r
7134     mmi->ptMinTrackSize.x = 100;\r
7135     mmi->ptMinTrackSize.y = 100;\r
7136     break;\r
7137   }\r
7138   return FALSE;\r
7139 }\r
7140 \r
7141 VOID\r
7142 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7143 {\r
7144   FARPROC lpProc;\r
7145   char *p, *q;\r
7146 \r
7147   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7148 \r
7149   if (str == NULL) str = "";\r
7150   p = (char *) malloc(2 * strlen(str) + 2);\r
7151   q = p;\r
7152   while (*str) {\r
7153     if (*str == '\n') *q++ = '\r';\r
7154     *q++ = *str++;\r
7155   }\r
7156   *q = NULLCHAR;\r
7157   if (commentText != NULL) free(commentText);\r
7158 \r
7159   commentIndex = index;\r
7160   commentTitle = title;\r
7161   commentText = p;\r
7162   editComment = edit;\r
7163 \r
7164   if (commentDialog) {\r
7165     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7166     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7167   } else {\r
7168     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7169     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7170                  hwndMain, (DLGPROC)lpProc);\r
7171     FreeProcInstance(lpProc);\r
7172   }\r
7173   commentDialogUp = TRUE;\r
7174 }\r
7175 \r
7176 \r
7177 /*---------------------------------------------------------------------------*\\r
7178  *\r
7179  * Type-in move dialog functions\r
7180  * \r
7181 \*---------------------------------------------------------------------------*/\r
7182 \r
7183 LRESULT CALLBACK\r
7184 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7185 {\r
7186   char move[MSG_SIZ];\r
7187   HWND hInput;\r
7188   ChessMove moveType;\r
7189   int fromX, fromY, toX, toY;\r
7190   char promoChar;\r
7191 \r
7192   switch (message) {\r
7193   case WM_INITDIALOG:\r
7194     move[0] = (char) lParam;\r
7195     move[1] = NULLCHAR;\r
7196     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7197     hInput = GetDlgItem(hDlg, OPT_Move);\r
7198     SetWindowText(hInput, move);\r
7199     SetFocus(hInput);\r
7200     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7201     return FALSE;\r
7202 \r
7203   case WM_COMMAND:\r
7204     switch (LOWORD(wParam)) {\r
7205     case IDOK:\r
7206       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7207         gameMode != Training) {\r
7208         DisplayMoveError("Displayed move is not current");\r
7209       } else {\r
7210         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7211         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7212           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7213           if (gameMode != Training)\r
7214               forwardMostMove = currentMove;\r
7215           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7216         } else {\r
7217           DisplayMoveError("Could not parse move");\r
7218         }\r
7219       }\r
7220       EndDialog(hDlg, TRUE);\r
7221       return TRUE;\r
7222     case IDCANCEL:\r
7223       EndDialog(hDlg, FALSE);\r
7224       return TRUE;\r
7225     default:\r
7226       break;\r
7227     }\r
7228     break;\r
7229   }\r
7230   return FALSE;\r
7231 }\r
7232 \r
7233 VOID\r
7234 PopUpMoveDialog(char firstchar)\r
7235 {\r
7236     FARPROC lpProc;\r
7237     \r
7238     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7239         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7240         gameMode == AnalyzeMode || gameMode == EditGame || \r
7241         gameMode == EditPosition || gameMode == IcsExamining ||\r
7242         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7243         gameMode == Training) {\r
7244       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7245       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7246         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7247       FreeProcInstance(lpProc);\r
7248     }\r
7249 }\r
7250 \r
7251 /*---------------------------------------------------------------------------*\\r
7252  *\r
7253  * Type-in name dialog functions\r
7254  * \r
7255 \*---------------------------------------------------------------------------*/\r
7256 \r
7257 LRESULT CALLBACK\r
7258 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7259 {\r
7260   char move[MSG_SIZ];\r
7261   HWND hInput;\r
7262 \r
7263   switch (message) {\r
7264   case WM_INITDIALOG:\r
7265     move[0] = (char) lParam;\r
7266     move[1] = NULLCHAR;\r
7267     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7268     hInput = GetDlgItem(hDlg, OPT_Name);\r
7269     SetWindowText(hInput, move);\r
7270     SetFocus(hInput);\r
7271     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7272     return FALSE;\r
7273 \r
7274   case WM_COMMAND:\r
7275     switch (LOWORD(wParam)) {\r
7276     case IDOK:\r
7277       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7278       appData.userName = strdup(move);\r
7279 \r
7280       EndDialog(hDlg, TRUE);\r
7281       return TRUE;\r
7282     case IDCANCEL:\r
7283       EndDialog(hDlg, FALSE);\r
7284       return TRUE;\r
7285     default:\r
7286       break;\r
7287     }\r
7288     break;\r
7289   }\r
7290   return FALSE;\r
7291 }\r
7292 \r
7293 VOID\r
7294 PopUpNameDialog(char firstchar)\r
7295 {\r
7296     FARPROC lpProc;\r
7297     \r
7298       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7299       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7300         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7301       FreeProcInstance(lpProc);\r
7302 }\r
7303 \r
7304 /*---------------------------------------------------------------------------*\\r
7305  *\r
7306  *  Error dialogs\r
7307  * \r
7308 \*---------------------------------------------------------------------------*/\r
7309 \r
7310 /* Nonmodal error box */\r
7311 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7312                              WPARAM wParam, LPARAM lParam);\r
7313 \r
7314 VOID\r
7315 ErrorPopUp(char *title, char *content)\r
7316 {\r
7317   FARPROC lpProc;\r
7318   char *p, *q;\r
7319   BOOLEAN modal = hwndMain == NULL;\r
7320 \r
7321   p = content;\r
7322   q = errorMessage;\r
7323   while (*p) {\r
7324     if (*p == '\n') {\r
7325       if (modal) {\r
7326         *q++ = ' ';\r
7327         p++;\r
7328       } else {\r
7329         *q++ = '\r';\r
7330         *q++ = *p++;\r
7331       }\r
7332     } else {\r
7333       *q++ = *p++;\r
7334     }\r
7335   }\r
7336   *q = NULLCHAR;\r
7337   strncpy(errorTitle, title, sizeof(errorTitle));\r
7338   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7339   \r
7340   if (modal) {\r
7341     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7342   } else {\r
7343     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7344     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7345                  hwndMain, (DLGPROC)lpProc);\r
7346     FreeProcInstance(lpProc);\r
7347   }\r
7348 }\r
7349 \r
7350 VOID\r
7351 ErrorPopDown()\r
7352 {\r
7353   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7354   if (errorDialog == NULL) return;\r
7355   DestroyWindow(errorDialog);\r
7356   errorDialog = NULL;\r
7357 }\r
7358 \r
7359 LRESULT CALLBACK\r
7360 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7361 {\r
7362   HANDLE hwndText;\r
7363   RECT rChild;\r
7364 \r
7365   switch (message) {\r
7366   case WM_INITDIALOG:\r
7367     GetWindowRect(hDlg, &rChild);\r
7368 \r
7369     /*\r
7370     SetWindowPos(hDlg, NULL, rChild.left,\r
7371       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7372       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7373     */\r
7374 \r
7375     /* \r
7376         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7377         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7378         and it doesn't work when you resize the dialog.\r
7379         For now, just give it a default position.\r
7380     */\r
7381     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7382 \r
7383     errorDialog = hDlg;\r
7384     SetWindowText(hDlg, errorTitle);\r
7385     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7386     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7387     return FALSE;\r
7388 \r
7389   case WM_COMMAND:\r
7390     switch (LOWORD(wParam)) {\r
7391     case IDOK:\r
7392     case IDCANCEL:\r
7393       if (errorDialog == hDlg) errorDialog = NULL;\r
7394       DestroyWindow(hDlg);\r
7395       return TRUE;\r
7396 \r
7397     default:\r
7398       break;\r
7399     }\r
7400     break;\r
7401   }\r
7402   return FALSE;\r
7403 }\r
7404 \r
7405 #ifdef GOTHIC\r
7406 HWND gothicDialog = NULL;\r
7407 \r
7408 LRESULT CALLBACK\r
7409 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7410 {\r
7411   HANDLE hwndText;\r
7412   RECT rChild;\r
7413   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7414 \r
7415   switch (message) {\r
7416   case WM_INITDIALOG:\r
7417     GetWindowRect(hDlg, &rChild);\r
7418 \r
7419     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7420                                                              SWP_NOZORDER);\r
7421 \r
7422     /* \r
7423         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7424         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7425         and it doesn't work when you resize the dialog.\r
7426         For now, just give it a default position.\r
7427     */\r
7428     gothicDialog = hDlg;\r
7429     SetWindowText(hDlg, errorTitle);\r
7430     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7431     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7432     return FALSE;\r
7433 \r
7434   case WM_COMMAND:\r
7435     switch (LOWORD(wParam)) {\r
7436     case IDOK:\r
7437     case IDCANCEL:\r
7438       if (errorDialog == hDlg) errorDialog = NULL;\r
7439       DestroyWindow(hDlg);\r
7440       return TRUE;\r
7441 \r
7442     default:\r
7443       break;\r
7444     }\r
7445     break;\r
7446   }\r
7447   return FALSE;\r
7448 }\r
7449 \r
7450 VOID\r
7451 GothicPopUp(char *title, VariantClass variant)\r
7452 {\r
7453   FARPROC lpProc;\r
7454   static char *lastTitle;\r
7455 \r
7456   strncpy(errorTitle, title, sizeof(errorTitle));\r
7457   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7458 \r
7459   if(lastTitle != title && gothicDialog != NULL) {\r
7460     DestroyWindow(gothicDialog);\r
7461     gothicDialog = NULL;\r
7462   }\r
7463   if(variant != VariantNormal && gothicDialog == NULL) {\r
7464     title = lastTitle;\r
7465     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7466     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7467                  hwndMain, (DLGPROC)lpProc);\r
7468     FreeProcInstance(lpProc);\r
7469   }\r
7470 }\r
7471 #endif\r
7472 \r
7473 /*---------------------------------------------------------------------------*\\r
7474  *\r
7475  *  Ics Interaction console functions\r
7476  *\r
7477 \*---------------------------------------------------------------------------*/\r
7478 \r
7479 #define HISTORY_SIZE 64\r
7480 static char *history[HISTORY_SIZE];\r
7481 int histIn = 0, histP = 0;\r
7482 \r
7483 VOID\r
7484 SaveInHistory(char *cmd)\r
7485 {\r
7486   if (history[histIn] != NULL) {\r
7487     free(history[histIn]);\r
7488     history[histIn] = NULL;\r
7489   }\r
7490   if (*cmd == NULLCHAR) return;\r
7491   history[histIn] = StrSave(cmd);\r
7492   histIn = (histIn + 1) % HISTORY_SIZE;\r
7493   if (history[histIn] != NULL) {\r
7494     free(history[histIn]);\r
7495     history[histIn] = NULL;\r
7496   }\r
7497   histP = histIn;\r
7498 }\r
7499 \r
7500 char *\r
7501 PrevInHistory(char *cmd)\r
7502 {\r
7503   int newhp;\r
7504   if (histP == histIn) {\r
7505     if (history[histIn] != NULL) free(history[histIn]);\r
7506     history[histIn] = StrSave(cmd);\r
7507   }\r
7508   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7509   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7510   histP = newhp;\r
7511   return history[histP];\r
7512 }\r
7513 \r
7514 char *\r
7515 NextInHistory()\r
7516 {\r
7517   if (histP == histIn) return NULL;\r
7518   histP = (histP + 1) % HISTORY_SIZE;\r
7519   return history[histP];\r
7520 }\r
7521 \r
7522 typedef struct {\r
7523   char *item;\r
7524   char *command;\r
7525   BOOLEAN getname;\r
7526   BOOLEAN immediate;\r
7527 } IcsTextMenuEntry;\r
7528 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7529 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7530 \r
7531 void\r
7532 ParseIcsTextMenu(char *icsTextMenuString)\r
7533 {\r
7534 //  int flags = 0;\r
7535   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7536   char *p = icsTextMenuString;\r
7537   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7538     free(e->item);\r
7539     e->item = NULL;\r
7540     if (e->command != NULL) {\r
7541       free(e->command);\r
7542       e->command = NULL;\r
7543     }\r
7544     e++;\r
7545   }\r
7546   e = icsTextMenuEntry;\r
7547   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7548     if (*p == ';' || *p == '\n') {\r
7549       e->item = strdup("-");\r
7550       e->command = NULL;\r
7551       p++;\r
7552     } else if (*p == '-') {\r
7553       e->item = strdup("-");\r
7554       e->command = NULL;\r
7555       p++;\r
7556       if (*p) p++;\r
7557     } else {\r
7558       char *q, *r, *s, *t;\r
7559       char c;\r
7560       q = strchr(p, ',');\r
7561       if (q == NULL) break;\r
7562       *q = NULLCHAR;\r
7563       r = strchr(q + 1, ',');\r
7564       if (r == NULL) break;\r
7565       *r = NULLCHAR;\r
7566       s = strchr(r + 1, ',');\r
7567       if (s == NULL) break;\r
7568       *s = NULLCHAR;\r
7569       c = ';';\r
7570       t = strchr(s + 1, c);\r
7571       if (t == NULL) {\r
7572         c = '\n';\r
7573         t = strchr(s + 1, c);\r
7574       }\r
7575       if (t != NULL) *t = NULLCHAR;\r
7576       e->item = strdup(p);\r
7577       e->command = strdup(q + 1);\r
7578       e->getname = *(r + 1) != '0';\r
7579       e->immediate = *(s + 1) != '0';\r
7580       *q = ',';\r
7581       *r = ',';\r
7582       *s = ',';\r
7583       if (t == NULL) break;\r
7584       *t = c;\r
7585       p = t + 1;\r
7586     }\r
7587     e++;\r
7588   } \r
7589 }\r
7590 \r
7591 HMENU\r
7592 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7593 {\r
7594   HMENU hmenu, h;\r
7595   int i = 0;\r
7596   hmenu = LoadMenu(hInst, "TextMenu");\r
7597   h = GetSubMenu(hmenu, 0);\r
7598   while (e->item) {\r
7599     if (strcmp(e->item, "-") == 0) {\r
7600       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7601     } else {\r
7602       if (e->item[0] == '|') {\r
7603         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7604                    IDM_CommandX + i, &e->item[1]);\r
7605       } else {\r
7606         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7607       }\r
7608     }\r
7609     e++;\r
7610     i++;\r
7611   } \r
7612   return hmenu;\r
7613 }\r
7614 \r
7615 WNDPROC consoleTextWindowProc;\r
7616 \r
7617 void\r
7618 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7619 {\r
7620   char buf[MSG_SIZ], name[MSG_SIZ];\r
7621   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7622   CHARRANGE sel;\r
7623 \r
7624   if (!getname) {\r
7625     SetWindowText(hInput, command);\r
7626     if (immediate) {\r
7627       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7628     } else {\r
7629       sel.cpMin = 999999;\r
7630       sel.cpMax = 999999;\r
7631       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7632       SetFocus(hInput);\r
7633     }\r
7634     return;\r
7635   }    \r
7636   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7637   if (sel.cpMin == sel.cpMax) {\r
7638     /* Expand to surrounding word */\r
7639     TEXTRANGE tr;\r
7640     do {\r
7641       tr.chrg.cpMax = sel.cpMin;\r
7642       tr.chrg.cpMin = --sel.cpMin;\r
7643       if (sel.cpMin < 0) break;\r
7644       tr.lpstrText = name;\r
7645       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7646     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7647     sel.cpMin++;\r
7648 \r
7649     do {\r
7650       tr.chrg.cpMin = sel.cpMax;\r
7651       tr.chrg.cpMax = ++sel.cpMax;\r
7652       tr.lpstrText = name;\r
7653       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7654     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7655     sel.cpMax--;\r
7656 \r
7657     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7658       MessageBeep(MB_ICONEXCLAMATION);\r
7659       return;\r
7660     }\r
7661     tr.chrg = sel;\r
7662     tr.lpstrText = name;\r
7663     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7664   } else {\r
7665     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7666       MessageBeep(MB_ICONEXCLAMATION);\r
7667       return;\r
7668     }\r
7669     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7670   }\r
7671   if (immediate) {\r
7672     sprintf(buf, "%s %s", command, name);\r
7673     SetWindowText(hInput, buf);\r
7674     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7675   } else {\r
7676     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7677     SetWindowText(hInput, buf);\r
7678     sel.cpMin = 999999;\r
7679     sel.cpMax = 999999;\r
7680     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7681     SetFocus(hInput);\r
7682   }\r
7683 }\r
7684 \r
7685 LRESULT CALLBACK \r
7686 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7687 {\r
7688   HWND hInput;\r
7689   CHARRANGE sel;\r
7690 \r
7691   switch (message) {\r
7692   case WM_KEYDOWN:\r
7693     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7694     switch (wParam) {\r
7695     case VK_PRIOR:\r
7696       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7697       return 0;\r
7698     case VK_NEXT:\r
7699       sel.cpMin = 999999;\r
7700       sel.cpMax = 999999;\r
7701       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7702       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7703       return 0;\r
7704     }\r
7705     break;\r
7706   case WM_CHAR:\r
7707     if (wParam == '\t') {\r
7708       if (GetKeyState(VK_SHIFT) < 0) {\r
7709         /* shifted */\r
7710         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7711         if (buttonDesc[0].hwnd) {\r
7712           SetFocus(buttonDesc[0].hwnd);\r
7713         } else {\r
7714           SetFocus(hwndMain);\r
7715         }\r
7716       } else {\r
7717         /* unshifted */\r
7718         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7719       }\r
7720     } else {\r
7721       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7722       SetFocus(hInput);\r
7723       SendMessage(hInput, message, wParam, lParam);\r
7724     }\r
7725     return 0;\r
7726   case WM_PASTE:\r
7727     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7728     SetFocus(hInput);\r
7729     return SendMessage(hInput, message, wParam, lParam);\r
7730   case WM_MBUTTONDOWN:\r
7731     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7732   case WM_RBUTTONDOWN:\r
7733     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7734       /* Move selection here if it was empty */\r
7735       POINT pt;\r
7736       pt.x = LOWORD(lParam);\r
7737       pt.y = HIWORD(lParam);\r
7738       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7739       if (sel.cpMin == sel.cpMax) {\r
7740         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7741         sel.cpMax = sel.cpMin;\r
7742         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7743       }\r
7744       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7745     }\r
7746     return 0;\r
7747   case WM_RBUTTONUP:\r
7748     if (GetKeyState(VK_SHIFT) & ~1) {\r
7749       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7750         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7751     } else {\r
7752       POINT pt;\r
7753       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7754       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7755       if (sel.cpMin == sel.cpMax) {\r
7756         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7757         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7758       }\r
7759       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7760         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7761       }\r
7762       pt.x = LOWORD(lParam);\r
7763       pt.y = HIWORD(lParam);\r
7764       MenuPopup(hwnd, pt, hmenu, -1);\r
7765     }\r
7766     return 0;\r
7767   case WM_COMMAND:\r
7768     switch (LOWORD(wParam)) {\r
7769     case IDM_QuickPaste:\r
7770       {\r
7771         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7772         if (sel.cpMin == sel.cpMax) {\r
7773           MessageBeep(MB_ICONEXCLAMATION);\r
7774           return 0;\r
7775         }\r
7776         SendMessage(hwnd, WM_COPY, 0, 0);\r
7777         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7778         SendMessage(hInput, WM_PASTE, 0, 0);\r
7779         SetFocus(hInput);\r
7780         return 0;\r
7781       }\r
7782     case IDM_Cut:\r
7783       SendMessage(hwnd, WM_CUT, 0, 0);\r
7784       return 0;\r
7785     case IDM_Paste:\r
7786       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7787       return 0;\r
7788     case IDM_Copy:\r
7789       SendMessage(hwnd, WM_COPY, 0, 0);\r
7790       return 0;\r
7791     default:\r
7792       {\r
7793         int i = LOWORD(wParam) - IDM_CommandX;\r
7794         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7795             icsTextMenuEntry[i].command != NULL) {\r
7796           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7797                    icsTextMenuEntry[i].getname,\r
7798                    icsTextMenuEntry[i].immediate);\r
7799           return 0;\r
7800         }\r
7801       }\r
7802       break;\r
7803     }\r
7804     break;\r
7805   }\r
7806   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7807 }\r
7808 \r
7809 WNDPROC consoleInputWindowProc;\r
7810 \r
7811 LRESULT CALLBACK\r
7812 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7813 {\r
7814   char buf[MSG_SIZ];\r
7815   char *p;\r
7816   static BOOL sendNextChar = FALSE;\r
7817   static BOOL quoteNextChar = FALSE;\r
7818   InputSource *is = consoleInputSource;\r
7819   CHARFORMAT cf;\r
7820   CHARRANGE sel;\r
7821 \r
7822   switch (message) {\r
7823   case WM_CHAR:\r
7824     if (!appData.localLineEditing || sendNextChar) {\r
7825       is->buf[0] = (CHAR) wParam;\r
7826       is->count = 1;\r
7827       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7828       sendNextChar = FALSE;\r
7829       return 0;\r
7830     }\r
7831     if (quoteNextChar) {\r
7832       buf[0] = (char) wParam;\r
7833       buf[1] = NULLCHAR;\r
7834       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7835       quoteNextChar = FALSE;\r
7836       return 0;\r
7837     }\r
7838     switch (wParam) {\r
7839     case '\r':   /* Enter key */\r
7840       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7841       if (consoleEcho) SaveInHistory(is->buf);\r
7842       is->buf[is->count++] = '\n';\r
7843       is->buf[is->count] = NULLCHAR;\r
7844       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7845       if (consoleEcho) {\r
7846         ConsoleOutput(is->buf, is->count, TRUE);\r
7847       } else if (appData.localLineEditing) {\r
7848         ConsoleOutput("\n", 1, TRUE);\r
7849       }\r
7850       /* fall thru */\r
7851     case '\033': /* Escape key */\r
7852       SetWindowText(hwnd, "");\r
7853       cf.cbSize = sizeof(CHARFORMAT);\r
7854       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7855       if (consoleEcho) {\r
7856         cf.crTextColor = textAttribs[ColorNormal].color;\r
7857       } else {\r
7858         cf.crTextColor = COLOR_ECHOOFF;\r
7859       }\r
7860       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7861       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7862       return 0;\r
7863     case '\t':   /* Tab key */\r
7864       if (GetKeyState(VK_SHIFT) < 0) {\r
7865         /* shifted */\r
7866         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7867       } else {\r
7868         /* unshifted */\r
7869         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7870         if (buttonDesc[0].hwnd) {\r
7871           SetFocus(buttonDesc[0].hwnd);\r
7872         } else {\r
7873           SetFocus(hwndMain);\r
7874         }\r
7875       }\r
7876       return 0;\r
7877     case '\023': /* Ctrl+S */\r
7878       sendNextChar = TRUE;\r
7879       return 0;\r
7880     case '\021': /* Ctrl+Q */\r
7881       quoteNextChar = TRUE;\r
7882       return 0;\r
7883     default:\r
7884       break;\r
7885     }\r
7886     break;\r
7887   case WM_KEYDOWN:\r
7888     switch (wParam) {\r
7889     case VK_UP:\r
7890       GetWindowText(hwnd, buf, MSG_SIZ);\r
7891       p = PrevInHistory(buf);\r
7892       if (p != NULL) {\r
7893         SetWindowText(hwnd, p);\r
7894         sel.cpMin = 999999;\r
7895         sel.cpMax = 999999;\r
7896         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7897         return 0;\r
7898       }\r
7899       break;\r
7900     case VK_DOWN:\r
7901       p = NextInHistory();\r
7902       if (p != NULL) {\r
7903         SetWindowText(hwnd, p);\r
7904         sel.cpMin = 999999;\r
7905         sel.cpMax = 999999;\r
7906         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7907         return 0;\r
7908       }\r
7909       break;\r
7910     case VK_HOME:\r
7911     case VK_END:\r
7912       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7913       /* fall thru */\r
7914     case VK_PRIOR:\r
7915     case VK_NEXT:\r
7916       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7917       return 0;\r
7918     }\r
7919     break;\r
7920   case WM_MBUTTONDOWN:\r
7921     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7922       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7923     break;\r
7924   case WM_RBUTTONUP:\r
7925     if (GetKeyState(VK_SHIFT) & ~1) {\r
7926       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7927         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7928     } else {\r
7929       POINT pt;\r
7930       HMENU hmenu;\r
7931       hmenu = LoadMenu(hInst, "InputMenu");\r
7932       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7933       if (sel.cpMin == sel.cpMax) {\r
7934         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7935         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7936       }\r
7937       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7938         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7939       }\r
7940       pt.x = LOWORD(lParam);\r
7941       pt.y = HIWORD(lParam);\r
7942       MenuPopup(hwnd, pt, hmenu, -1);\r
7943     }\r
7944     return 0;\r
7945   case WM_COMMAND:\r
7946     switch (LOWORD(wParam)) { \r
7947     case IDM_Undo:\r
7948       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7949       return 0;\r
7950     case IDM_SelectAll:\r
7951       sel.cpMin = 0;\r
7952       sel.cpMax = -1; /*999999?*/\r
7953       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7954       return 0;\r
7955     case IDM_Cut:\r
7956       SendMessage(hwnd, WM_CUT, 0, 0);\r
7957       return 0;\r
7958     case IDM_Paste:\r
7959       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7960       return 0;\r
7961     case IDM_Copy:\r
7962       SendMessage(hwnd, WM_COPY, 0, 0);\r
7963       return 0;\r
7964     }\r
7965     break;\r
7966   }\r
7967   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7968 }\r
7969 \r
7970 #define CO_MAX  100000\r
7971 #define CO_TRIM   1000\r
7972 \r
7973 LRESULT CALLBACK\r
7974 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7975 {\r
7976   static SnapData sd;\r
7977   static HWND hText, hInput /*, hFocus*/;\r
7978 //  InputSource *is = consoleInputSource;\r
7979   RECT rect;\r
7980   static int sizeX, sizeY;\r
7981   int newSizeX, newSizeY;\r
7982   MINMAXINFO *mmi;\r
7983 \r
7984   switch (message) {\r
7985   case WM_INITDIALOG: /* message: initialize dialog box */\r
7986     hwndConsole = hDlg;\r
7987     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7988     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7989     SetFocus(hInput);\r
7990     consoleTextWindowProc = (WNDPROC)\r
7991       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7992     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7993     consoleInputWindowProc = (WNDPROC)\r
7994       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7995     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7996     Colorize(ColorNormal, TRUE);\r
7997     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7998     ChangedConsoleFont();\r
7999     GetClientRect(hDlg, &rect);\r
8000     sizeX = rect.right;\r
8001     sizeY = rect.bottom;\r
8002     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
8003         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
8004       WINDOWPLACEMENT wp;\r
8005       EnsureOnScreen(&consoleX, &consoleY);\r
8006       wp.length = sizeof(WINDOWPLACEMENT);\r
8007       wp.flags = 0;\r
8008       wp.showCmd = SW_SHOW;\r
8009       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8010       wp.rcNormalPosition.left = consoleX;\r
8011       wp.rcNormalPosition.right = consoleX + consoleW;\r
8012       wp.rcNormalPosition.top = consoleY;\r
8013       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8014       SetWindowPlacement(hDlg, &wp);\r
8015     }\r
8016 #if 0 \r
8017    // [HGM] Chessknight's change 2004-07-13\r
8018    else { /* Determine Defaults */\r
8019        WINDOWPLACEMENT wp;\r
8020        consoleX = winWidth + 1;\r
8021        consoleY = boardY;\r
8022        consoleW = screenWidth -  winWidth;\r
8023        consoleH = winHeight;\r
8024        EnsureOnScreen(&consoleX, &consoleY);\r
8025        wp.length = sizeof(WINDOWPLACEMENT);\r
8026        wp.flags = 0;\r
8027        wp.showCmd = SW_SHOW;\r
8028        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8029        wp.rcNormalPosition.left = consoleX;\r
8030        wp.rcNormalPosition.right = consoleX + consoleW;\r
8031        wp.rcNormalPosition.top = consoleY;\r
8032        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
8033        SetWindowPlacement(hDlg, &wp);\r
8034     }\r
8035 #endif\r
8036     return FALSE;\r
8037 \r
8038   case WM_SETFOCUS:\r
8039     SetFocus(hInput);\r
8040     return 0;\r
8041 \r
8042   case WM_CLOSE:\r
8043     ExitEvent(0);\r
8044     /* not reached */\r
8045     break;\r
8046 \r
8047   case WM_SIZE:\r
8048     if (IsIconic(hDlg)) break;\r
8049     newSizeX = LOWORD(lParam);\r
8050     newSizeY = HIWORD(lParam);\r
8051     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8052       RECT rectText, rectInput;\r
8053       POINT pt;\r
8054       int newTextHeight, newTextWidth;\r
8055       GetWindowRect(hText, &rectText);\r
8056       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8057       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8058       if (newTextHeight < 0) {\r
8059         newSizeY += -newTextHeight;\r
8060         newTextHeight = 0;\r
8061       }\r
8062       SetWindowPos(hText, NULL, 0, 0,\r
8063         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8064       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8065       pt.x = rectInput.left;\r
8066       pt.y = rectInput.top + newSizeY - sizeY;\r
8067       ScreenToClient(hDlg, &pt);\r
8068       SetWindowPos(hInput, NULL, \r
8069         pt.x, pt.y, /* needs client coords */   \r
8070         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8071         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8072     }\r
8073     sizeX = newSizeX;\r
8074     sizeY = newSizeY;\r
8075     break;\r
8076 \r
8077   case WM_GETMINMAXINFO:\r
8078     /* Prevent resizing window too small */\r
8079     mmi = (MINMAXINFO *) lParam;\r
8080     mmi->ptMinTrackSize.x = 100;\r
8081     mmi->ptMinTrackSize.y = 100;\r
8082     break;\r
8083 \r
8084   /* [AS] Snapping */\r
8085   case WM_ENTERSIZEMOVE:\r
8086     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8087 \r
8088   case WM_SIZING:\r
8089     return OnSizing( &sd, hDlg, wParam, lParam );\r
8090 \r
8091   case WM_MOVING:\r
8092     return OnMoving( &sd, hDlg, wParam, lParam );\r
8093 \r
8094   case WM_EXITSIZEMOVE:\r
8095     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8096   }\r
8097 \r
8098   return DefWindowProc(hDlg, message, wParam, lParam);\r
8099 }\r
8100 \r
8101 \r
8102 VOID\r
8103 ConsoleCreate()\r
8104 {\r
8105   HWND hCons;\r
8106   if (hwndConsole) return;\r
8107   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8108   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8109 }\r
8110 \r
8111 \r
8112 VOID\r
8113 ConsoleOutput(char* data, int length, int forceVisible)\r
8114 {\r
8115   HWND hText;\r
8116   int trim, exlen;\r
8117   char *p, *q;\r
8118   char buf[CO_MAX+1];\r
8119   POINT pEnd;\r
8120   RECT rect;\r
8121   static int delayLF = 0;\r
8122   CHARRANGE savesel, sel;\r
8123 \r
8124   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8125   p = data;\r
8126   q = buf;\r
8127   if (delayLF) {\r
8128     *q++ = '\r';\r
8129     *q++ = '\n';\r
8130     delayLF = 0;\r
8131   }\r
8132   while (length--) {\r
8133     if (*p == '\n') {\r
8134       if (*++p) {\r
8135         *q++ = '\r';\r
8136         *q++ = '\n';\r
8137       } else {\r
8138         delayLF = 1;\r
8139       }\r
8140     } else if (*p == '\007') {\r
8141        MyPlaySound(&sounds[(int)SoundBell]);\r
8142        p++;\r
8143     } else {\r
8144       *q++ = *p++;\r
8145     }\r
8146   }\r
8147   *q = NULLCHAR;\r
8148   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8149   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8150   /* Save current selection */\r
8151   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8152   exlen = GetWindowTextLength(hText);\r
8153   /* Find out whether current end of text is visible */\r
8154   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8155   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8156   /* Trim existing text if it's too long */\r
8157   if (exlen + (q - buf) > CO_MAX) {\r
8158     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8159     sel.cpMin = 0;\r
8160     sel.cpMax = trim;\r
8161     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8162     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8163     exlen -= trim;\r
8164     savesel.cpMin -= trim;\r
8165     savesel.cpMax -= trim;\r
8166     if (exlen < 0) exlen = 0;\r
8167     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8168     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8169   }\r
8170   /* Append the new text */\r
8171   sel.cpMin = exlen;\r
8172   sel.cpMax = exlen;\r
8173   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8174   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8175   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8176   if (forceVisible || exlen == 0 ||\r
8177       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8178        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8179     /* Scroll to make new end of text visible if old end of text\r
8180        was visible or new text is an echo of user typein */\r
8181     sel.cpMin = 9999999;\r
8182     sel.cpMax = 9999999;\r
8183     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8184     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8185     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8186     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8187   }\r
8188   if (savesel.cpMax == exlen || forceVisible) {\r
8189     /* Move insert point to new end of text if it was at the old\r
8190        end of text or if the new text is an echo of user typein */\r
8191     sel.cpMin = 9999999;\r
8192     sel.cpMax = 9999999;\r
8193     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8194   } else {\r
8195     /* Restore previous selection */\r
8196     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8197   }\r
8198   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8199 }\r
8200 \r
8201 /*---------*/\r
8202 \r
8203 \r
8204 void\r
8205 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8206 {\r
8207   char buf[100];\r
8208   char *str;\r
8209   COLORREF oldFg, oldBg;\r
8210   HFONT oldFont;\r
8211   RECT rect;\r
8212 \r
8213   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8214 \r
8215   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8216   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8217   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8218 \r
8219   rect.left = x;\r
8220   rect.right = x + squareSize;\r
8221   rect.top  = y;\r
8222   rect.bottom = y + squareSize;\r
8223   str = buf;\r
8224 \r
8225   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8226                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8227              y, ETO_CLIPPED|ETO_OPAQUE,\r
8228              &rect, str, strlen(str), NULL);\r
8229 \r
8230   (void) SetTextColor(hdc, oldFg);\r
8231   (void) SetBkColor(hdc, oldBg);\r
8232   (void) SelectObject(hdc, oldFont);\r
8233 }\r
8234 \r
8235 void\r
8236 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8237               RECT *rect, char *color, char *flagFell)\r
8238 {\r
8239   char buf[100];\r
8240   char *str;\r
8241   COLORREF oldFg, oldBg;\r
8242   HFONT oldFont;\r
8243 \r
8244   if (appData.clockMode) {\r
8245     if (tinyLayout)\r
8246       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8247     else\r
8248       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8249     str = buf;\r
8250   } else {\r
8251     str = color;\r
8252   }\r
8253 \r
8254   if (highlight) {\r
8255     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8256     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8257   } else {\r
8258     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8259     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8260   }\r
8261   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8262 \r
8263   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8264              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8265              rect, str, strlen(str), NULL);\r
8266 \r
8267   (void) SetTextColor(hdc, oldFg);\r
8268   (void) SetBkColor(hdc, oldBg);\r
8269   (void) SelectObject(hdc, oldFont);\r
8270 }\r
8271 \r
8272 \r
8273 int\r
8274 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8275            OVERLAPPED *ovl)\r
8276 {\r
8277   int ok, err;\r
8278 \r
8279   /* [AS]  */\r
8280   if( count <= 0 ) {\r
8281     if (appData.debugMode) {\r
8282       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8283     }\r
8284 \r
8285     return ERROR_INVALID_USER_BUFFER;\r
8286   }\r
8287 \r
8288   ResetEvent(ovl->hEvent);\r
8289   ovl->Offset = ovl->OffsetHigh = 0;\r
8290   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8291   if (ok) {\r
8292     err = NO_ERROR;\r
8293   } else {\r
8294     err = GetLastError();\r
8295     if (err == ERROR_IO_PENDING) {\r
8296       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8297       if (ok)\r
8298         err = NO_ERROR;\r
8299       else\r
8300         err = GetLastError();\r
8301     }\r
8302   }\r
8303   return err;\r
8304 }\r
8305 \r
8306 int\r
8307 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8308             OVERLAPPED *ovl)\r
8309 {\r
8310   int ok, err;\r
8311 \r
8312   ResetEvent(ovl->hEvent);\r
8313   ovl->Offset = ovl->OffsetHigh = 0;\r
8314   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8315   if (ok) {\r
8316     err = NO_ERROR;\r
8317   } else {\r
8318     err = GetLastError();\r
8319     if (err == ERROR_IO_PENDING) {\r
8320       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8321       if (ok)\r
8322         err = NO_ERROR;\r
8323       else\r
8324         err = GetLastError();\r
8325     }\r
8326   }\r
8327   return err;\r
8328 }\r
8329 \r
8330 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8331 void CheckForInputBufferFull( InputSource * is )\r
8332 {\r
8333     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8334         /* Look for end of line */\r
8335         char * p = is->buf;\r
8336         \r
8337         while( p < is->next && *p != '\n' ) {\r
8338             p++;\r
8339         }\r
8340 \r
8341         if( p >= is->next ) {\r
8342             if (appData.debugMode) {\r
8343                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8344             }\r
8345 \r
8346             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8347             is->count = (DWORD) -1;\r
8348             is->next = is->buf;\r
8349         }\r
8350     }\r
8351 }\r
8352 \r
8353 DWORD\r
8354 InputThread(LPVOID arg)\r
8355 {\r
8356   InputSource *is;\r
8357   OVERLAPPED ovl;\r
8358 \r
8359   is = (InputSource *) arg;\r
8360   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8361   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8362   while (is->hThread != NULL) {\r
8363     is->error = DoReadFile(is->hFile, is->next,\r
8364                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8365                            &is->count, &ovl);\r
8366     if (is->error == NO_ERROR) {\r
8367       is->next += is->count;\r
8368     } else {\r
8369       if (is->error == ERROR_BROKEN_PIPE) {\r
8370         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8371         is->count = 0;\r
8372       } else {\r
8373         is->count = (DWORD) -1;\r
8374         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8375         break; \r
8376       }\r
8377     }\r
8378 \r
8379     CheckForInputBufferFull( is );\r
8380 \r
8381     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8382 \r
8383     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8384 \r
8385     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8386   }\r
8387 \r
8388   CloseHandle(ovl.hEvent);\r
8389   CloseHandle(is->hFile);\r
8390 \r
8391   if (appData.debugMode) {\r
8392     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8393   }\r
8394 \r
8395   return 0;\r
8396 }\r
8397 \r
8398 \r
8399 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8400 DWORD\r
8401 NonOvlInputThread(LPVOID arg)\r
8402 {\r
8403   InputSource *is;\r
8404   char *p, *q;\r
8405   int i;\r
8406   char prev;\r
8407 \r
8408   is = (InputSource *) arg;\r
8409   while (is->hThread != NULL) {\r
8410     is->error = ReadFile(is->hFile, is->next,\r
8411                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8412                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8413     if (is->error == NO_ERROR) {\r
8414       /* Change CRLF to LF */\r
8415       if (is->next > is->buf) {\r
8416         p = is->next - 1;\r
8417         i = is->count + 1;\r
8418       } else {\r
8419         p = is->next;\r
8420         i = is->count;\r
8421       }\r
8422       q = p;\r
8423       prev = NULLCHAR;\r
8424       while (i > 0) {\r
8425         if (prev == '\r' && *p == '\n') {\r
8426           *(q-1) = '\n';\r
8427           is->count--;\r
8428         } else { \r
8429           *q++ = *p;\r
8430         }\r
8431         prev = *p++;\r
8432         i--;\r
8433       }\r
8434       *q = NULLCHAR;\r
8435       is->next = q;\r
8436     } else {\r
8437       if (is->error == ERROR_BROKEN_PIPE) {\r
8438         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8439         is->count = 0; \r
8440       } else {\r
8441         is->count = (DWORD) -1;\r
8442       }\r
8443     }\r
8444 \r
8445     CheckForInputBufferFull( is );\r
8446 \r
8447     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8448 \r
8449     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8450 \r
8451     if (is->count < 0) break;  /* Quit on error */\r
8452   }\r
8453   CloseHandle(is->hFile);\r
8454   return 0;\r
8455 }\r
8456 \r
8457 DWORD\r
8458 SocketInputThread(LPVOID arg)\r
8459 {\r
8460   InputSource *is;\r
8461 \r
8462   is = (InputSource *) arg;\r
8463   while (is->hThread != NULL) {\r
8464     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8465     if ((int)is->count == SOCKET_ERROR) {\r
8466       is->count = (DWORD) -1;\r
8467       is->error = WSAGetLastError();\r
8468     } else {\r
8469       is->error = NO_ERROR;\r
8470       is->next += is->count;\r
8471       if (is->count == 0 && is->second == is) {\r
8472         /* End of file on stderr; quit with no message */\r
8473         break;\r
8474       }\r
8475     }\r
8476     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8477 \r
8478     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8479 \r
8480     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8481   }\r
8482   return 0;\r
8483 }\r
8484 \r
8485 VOID\r
8486 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8487 {\r
8488   InputSource *is;\r
8489 \r
8490   is = (InputSource *) lParam;\r
8491   if (is->lineByLine) {\r
8492     /* Feed in lines one by one */\r
8493     char *p = is->buf;\r
8494     char *q = p;\r
8495     while (q < is->next) {\r
8496       if (*q++ == '\n') {\r
8497         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8498         p = q;\r
8499       }\r
8500     }\r
8501     \r
8502     /* Move any partial line to the start of the buffer */\r
8503     q = is->buf;\r
8504     while (p < is->next) {\r
8505       *q++ = *p++;\r
8506     }\r
8507     is->next = q;\r
8508 \r
8509     if (is->error != NO_ERROR || is->count == 0) {\r
8510       /* Notify backend of the error.  Note: If there was a partial\r
8511          line at the end, it is not flushed through. */\r
8512       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8513     }\r
8514   } else {\r
8515     /* Feed in the whole chunk of input at once */\r
8516     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8517     is->next = is->buf;\r
8518   }\r
8519 }\r
8520 \r
8521 /*---------------------------------------------------------------------------*\\r
8522  *\r
8523  *  Menu enables. Used when setting various modes.\r
8524  *\r
8525 \*---------------------------------------------------------------------------*/\r
8526 \r
8527 typedef struct {\r
8528   int item;\r
8529   int flags;\r
8530 } Enables;\r
8531 \r
8532 VOID\r
8533 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8534 {\r
8535   while (enab->item > 0) {\r
8536     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8537     enab++;\r
8538   }\r
8539 }\r
8540 \r
8541 Enables gnuEnables[] = {\r
8542   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8543   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8544   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8545   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8546   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8547   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8548   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8550   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8551   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8552   { -1, -1 }\r
8553 };\r
8554 \r
8555 Enables icsEnables[] = {\r
8556   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8560   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8561   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8562   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8568   { -1, -1 }\r
8569 };\r
8570 \r
8571 #ifdef ZIPPY\r
8572 Enables zippyEnables[] = {\r
8573   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8574   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8575   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8576   { -1, -1 }\r
8577 };\r
8578 #endif\r
8579 \r
8580 Enables ncpEnables[] = {\r
8581   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8583   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8584   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8586   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8587   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8588   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8589   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8590   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8593   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8595   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8596   { -1, -1 }\r
8597 };\r
8598 \r
8599 Enables trainingOnEnables[] = {\r
8600   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8608   { -1, -1 }\r
8609 };\r
8610 \r
8611 Enables trainingOffEnables[] = {\r
8612   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8613   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8614   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8615   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8616   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8617   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8618   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8619   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8620   { -1, -1 }\r
8621 };\r
8622 \r
8623 /* These modify either ncpEnables or gnuEnables */\r
8624 Enables cmailEnables[] = {\r
8625   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8627   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8628   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8629   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8631   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8632   { -1, -1 }\r
8633 };\r
8634 \r
8635 Enables machineThinkingEnables[] = {\r
8636   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8638   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8639   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8640   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8641   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8642   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8643   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8644   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8645   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8646   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8647   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8648   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8651   { -1, -1 }\r
8652 };\r
8653 \r
8654 Enables userThinkingEnables[] = {\r
8655   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8656   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8657   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8658   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8659   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8660   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8661   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8662   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8663   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8664   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8665   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8666   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8667   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8668   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8669   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8670   { -1, -1 }\r
8671 };\r
8672 \r
8673 /*---------------------------------------------------------------------------*\\r
8674  *\r
8675  *  Front-end interface functions exported by XBoard.\r
8676  *  Functions appear in same order as prototypes in frontend.h.\r
8677  * \r
8678 \*---------------------------------------------------------------------------*/\r
8679 VOID\r
8680 ModeHighlight()\r
8681 {\r
8682   static UINT prevChecked = 0;\r
8683   static int prevPausing = 0;\r
8684   UINT nowChecked;\r
8685 \r
8686   if (pausing != prevPausing) {\r
8687     prevPausing = pausing;\r
8688     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8689                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8690     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8691   }\r
8692 \r
8693   switch (gameMode) {\r
8694   case BeginningOfGame:\r
8695     if (appData.icsActive)\r
8696       nowChecked = IDM_IcsClient;\r
8697     else if (appData.noChessProgram)\r
8698       nowChecked = IDM_EditGame;\r
8699     else\r
8700       nowChecked = IDM_MachineBlack;\r
8701     break;\r
8702   case MachinePlaysBlack:\r
8703     nowChecked = IDM_MachineBlack;\r
8704     break;\r
8705   case MachinePlaysWhite:\r
8706     nowChecked = IDM_MachineWhite;\r
8707     break;\r
8708   case TwoMachinesPlay:\r
8709     nowChecked = IDM_TwoMachines;\r
8710     break;\r
8711   case AnalyzeMode:\r
8712     nowChecked = IDM_AnalysisMode;\r
8713     break;\r
8714   case AnalyzeFile:\r
8715     nowChecked = IDM_AnalyzeFile;\r
8716     break;\r
8717   case EditGame:\r
8718     nowChecked = IDM_EditGame;\r
8719     break;\r
8720   case PlayFromGameFile:\r
8721     nowChecked = IDM_LoadGame;\r
8722     break;\r
8723   case EditPosition:\r
8724     nowChecked = IDM_EditPosition;\r
8725     break;\r
8726   case Training:\r
8727     nowChecked = IDM_Training;\r
8728     break;\r
8729   case IcsPlayingWhite:\r
8730   case IcsPlayingBlack:\r
8731   case IcsObserving:\r
8732   case IcsIdle:\r
8733     nowChecked = IDM_IcsClient;\r
8734     break;\r
8735   default:\r
8736   case EndOfGame:\r
8737     nowChecked = 0;\r
8738     break;\r
8739   }\r
8740   if (prevChecked != 0)\r
8741     (void) CheckMenuItem(GetMenu(hwndMain),\r
8742                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8743   if (nowChecked != 0)\r
8744     (void) CheckMenuItem(GetMenu(hwndMain),\r
8745                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8746 \r
8747   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8748     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8749                           MF_BYCOMMAND|MF_ENABLED);\r
8750   } else {\r
8751     (void) EnableMenuItem(GetMenu(hwndMain), \r
8752                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8753   }\r
8754 \r
8755   prevChecked = nowChecked;\r
8756 \r
8757   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8758   if (appData.icsActive) {\r
8759        if (appData.icsEngineAnalyze) {\r
8760                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8761                        MF_BYCOMMAND|MF_CHECKED);\r
8762        } else {\r
8763                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8764                        MF_BYCOMMAND|MF_UNCHECKED);\r
8765        }\r
8766   }\r
8767 }\r
8768 \r
8769 VOID\r
8770 SetICSMode()\r
8771 {\r
8772   HMENU hmenu = GetMenu(hwndMain);\r
8773   SetMenuEnables(hmenu, icsEnables);\r
8774   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8775     MF_BYPOSITION|MF_ENABLED);\r
8776 #ifdef ZIPPY\r
8777   if (appData.zippyPlay) {\r
8778     SetMenuEnables(hmenu, zippyEnables);\r
8779     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8780          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8781           MF_BYCOMMAND|MF_ENABLED);\r
8782   }\r
8783 #endif\r
8784 }\r
8785 \r
8786 VOID\r
8787 SetGNUMode()\r
8788 {\r
8789   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8790 }\r
8791 \r
8792 VOID\r
8793 SetNCPMode()\r
8794 {\r
8795   HMENU hmenu = GetMenu(hwndMain);\r
8796   SetMenuEnables(hmenu, ncpEnables);\r
8797   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8798     MF_BYPOSITION|MF_GRAYED);\r
8799     DrawMenuBar(hwndMain);\r
8800 }\r
8801 \r
8802 VOID\r
8803 SetCmailMode()\r
8804 {\r
8805   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8806 }\r
8807 \r
8808 VOID \r
8809 SetTrainingModeOn()\r
8810 {\r
8811   int i;\r
8812   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8813   for (i = 0; i < N_BUTTONS; i++) {\r
8814     if (buttonDesc[i].hwnd != NULL)\r
8815       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8816   }\r
8817   CommentPopDown();\r
8818 }\r
8819 \r
8820 VOID SetTrainingModeOff()\r
8821 {\r
8822   int i;\r
8823   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8824   for (i = 0; i < N_BUTTONS; i++) {\r
8825     if (buttonDesc[i].hwnd != NULL)\r
8826       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8827   }\r
8828 }\r
8829 \r
8830 \r
8831 VOID\r
8832 SetUserThinkingEnables()\r
8833 {\r
8834   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8835 }\r
8836 \r
8837 VOID\r
8838 SetMachineThinkingEnables()\r
8839 {\r
8840   HMENU hMenu = GetMenu(hwndMain);\r
8841   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8842 \r
8843   SetMenuEnables(hMenu, machineThinkingEnables);\r
8844 \r
8845   if (gameMode == MachinePlaysBlack) {\r
8846     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8847   } else if (gameMode == MachinePlaysWhite) {\r
8848     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8849   } else if (gameMode == TwoMachinesPlay) {\r
8850     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8851   }\r
8852 }\r
8853 \r
8854 \r
8855 VOID\r
8856 DisplayTitle(char *str)\r
8857 {\r
8858   char title[MSG_SIZ], *host;\r
8859   if (str[0] != NULLCHAR) {\r
8860     strcpy(title, str);\r
8861   } else if (appData.icsActive) {\r
8862     if (appData.icsCommPort[0] != NULLCHAR)\r
8863       host = "ICS";\r
8864     else \r
8865       host = appData.icsHost;\r
8866     sprintf(title, "%s: %s", szTitle, host);\r
8867   } else if (appData.noChessProgram) {\r
8868     strcpy(title, szTitle);\r
8869   } else {\r
8870     strcpy(title, szTitle);\r
8871     strcat(title, ": ");\r
8872     strcat(title, first.tidy);\r
8873   }\r
8874   SetWindowText(hwndMain, title);\r
8875 }\r
8876 \r
8877 \r
8878 VOID\r
8879 DisplayMessage(char *str1, char *str2)\r
8880 {\r
8881   HDC hdc;\r
8882   HFONT oldFont;\r
8883   int remain = MESSAGE_TEXT_MAX - 1;\r
8884   int len;\r
8885 \r
8886   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8887   messageText[0] = NULLCHAR;\r
8888   if (*str1) {\r
8889     len = strlen(str1);\r
8890     if (len > remain) len = remain;\r
8891     strncpy(messageText, str1, len);\r
8892     messageText[len] = NULLCHAR;\r
8893     remain -= len;\r
8894   }\r
8895   if (*str2 && remain >= 2) {\r
8896     if (*str1) {\r
8897       strcat(messageText, "  ");\r
8898       remain -= 2;\r
8899     }\r
8900     len = strlen(str2);\r
8901     if (len > remain) len = remain;\r
8902     strncat(messageText, str2, len);\r
8903   }\r
8904   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8905 \r
8906   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8907   hdc = GetDC(hwndMain);\r
8908   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8909   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8910              &messageRect, messageText, strlen(messageText), NULL);\r
8911   (void) SelectObject(hdc, oldFont);\r
8912   (void) ReleaseDC(hwndMain, hdc);\r
8913 }\r
8914 \r
8915 VOID\r
8916 DisplayError(char *str, int error)\r
8917 {\r
8918   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8919   int len;\r
8920 \r
8921   if (error == 0) {\r
8922     strcpy(buf, str);\r
8923   } else {\r
8924     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8925                         NULL, error, LANG_NEUTRAL,\r
8926                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8927     if (len > 0) {\r
8928       sprintf(buf, "%s:\n%s", str, buf2);\r
8929     } else {\r
8930       ErrorMap *em = errmap;\r
8931       while (em->err != 0 && em->err != error) em++;\r
8932       if (em->err != 0) {\r
8933         sprintf(buf, "%s:\n%s", str, em->msg);\r
8934       } else {\r
8935         sprintf(buf, "%s:\nError code %d", str, error);\r
8936       }\r
8937     }\r
8938   }\r
8939   \r
8940   ErrorPopUp("Error", buf);\r
8941 }\r
8942 \r
8943 \r
8944 VOID\r
8945 DisplayMoveError(char *str)\r
8946 {\r
8947   fromX = fromY = -1;\r
8948   ClearHighlights();\r
8949   DrawPosition(FALSE, NULL);\r
8950   if (appData.popupMoveErrors) {\r
8951     ErrorPopUp("Error", str);\r
8952   } else {\r
8953     DisplayMessage(str, "");\r
8954     moveErrorMessageUp = TRUE;\r
8955   }\r
8956 }\r
8957 \r
8958 VOID\r
8959 DisplayFatalError(char *str, int error, int exitStatus)\r
8960 {\r
8961   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8962   int len;\r
8963   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8964 \r
8965   if (error != 0) {\r
8966     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8967                         NULL, error, LANG_NEUTRAL,\r
8968                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8969     if (len > 0) {\r
8970       sprintf(buf, "%s:\n%s", str, buf2);\r
8971     } else {\r
8972       ErrorMap *em = errmap;\r
8973       while (em->err != 0 && em->err != error) em++;\r
8974       if (em->err != 0) {\r
8975         sprintf(buf, "%s:\n%s", str, em->msg);\r
8976       } else {\r
8977         sprintf(buf, "%s:\nError code %d", str, error);\r
8978       }\r
8979     }\r
8980     str = buf;\r
8981   }\r
8982   if (appData.debugMode) {\r
8983     fprintf(debugFP, "%s: %s\n", label, str);\r
8984   }\r
8985   if (appData.popupExitMessage) {\r
8986     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8987                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8988   }\r
8989   ExitEvent(exitStatus);\r
8990 }\r
8991 \r
8992 \r
8993 VOID\r
8994 DisplayInformation(char *str)\r
8995 {\r
8996   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8997 }\r
8998 \r
8999 \r
9000 VOID\r
9001 DisplayNote(char *str)\r
9002 {\r
9003   ErrorPopUp("Note", str);\r
9004 }\r
9005 \r
9006 \r
9007 typedef struct {\r
9008   char *title, *question, *replyPrefix;\r
9009   ProcRef pr;\r
9010 } QuestionParams;\r
9011 \r
9012 LRESULT CALLBACK\r
9013 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9014 {\r
9015   static QuestionParams *qp;\r
9016   char reply[MSG_SIZ];\r
9017   int len, err;\r
9018 \r
9019   switch (message) {\r
9020   case WM_INITDIALOG:\r
9021     qp = (QuestionParams *) lParam;\r
9022     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9023     SetWindowText(hDlg, qp->title);\r
9024     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9025     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9026     return FALSE;\r
9027 \r
9028   case WM_COMMAND:\r
9029     switch (LOWORD(wParam)) {\r
9030     case IDOK:\r
9031       strcpy(reply, qp->replyPrefix);\r
9032       if (*reply) strcat(reply, " ");\r
9033       len = strlen(reply);\r
9034       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9035       strcat(reply, "\n");\r
9036       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9037       EndDialog(hDlg, TRUE);\r
9038       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9039       return TRUE;\r
9040     case IDCANCEL:\r
9041       EndDialog(hDlg, FALSE);\r
9042       return TRUE;\r
9043     default:\r
9044       break;\r
9045     }\r
9046     break;\r
9047   }\r
9048   return FALSE;\r
9049 }\r
9050 \r
9051 VOID\r
9052 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9053 {\r
9054     QuestionParams qp;\r
9055     FARPROC lpProc;\r
9056     \r
9057     qp.title = title;\r
9058     qp.question = question;\r
9059     qp.replyPrefix = replyPrefix;\r
9060     qp.pr = pr;\r
9061     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9062     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9063       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9064     FreeProcInstance(lpProc);\r
9065 }\r
9066 \r
9067 /* [AS] Pick FRC position */\r
9068 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9069 {\r
9070     static int * lpIndexFRC;\r
9071     BOOL index_is_ok;\r
9072     char buf[16];\r
9073 \r
9074     switch( message )\r
9075     {\r
9076     case WM_INITDIALOG:\r
9077         lpIndexFRC = (int *) lParam;\r
9078 \r
9079         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9080 \r
9081         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9082         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9083         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9084         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9085 \r
9086         break;\r
9087 \r
9088     case WM_COMMAND:\r
9089         switch( LOWORD(wParam) ) {\r
9090         case IDOK:\r
9091             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9092             EndDialog( hDlg, 0 );\r
9093             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9094             return TRUE;\r
9095         case IDCANCEL:\r
9096             EndDialog( hDlg, 1 );   \r
9097             return TRUE;\r
9098         case IDC_NFG_Edit:\r
9099             if( HIWORD(wParam) == EN_CHANGE ) {\r
9100                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9101 \r
9102                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9103             }\r
9104             return TRUE;\r
9105         case IDC_NFG_Random:\r
9106             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9107             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9108             return TRUE;\r
9109         }\r
9110 \r
9111         break;\r
9112     }\r
9113 \r
9114     return FALSE;\r
9115 }\r
9116 \r
9117 int NewGameFRC()\r
9118 {\r
9119     int result;\r
9120     int index = appData.defaultFrcPosition;\r
9121     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9122 \r
9123     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9124 \r
9125     if( result == 0 ) {\r
9126         appData.defaultFrcPosition = index;\r
9127     }\r
9128 \r
9129     return result;\r
9130 }\r
9131 \r
9132 /* [AS] Game list options */\r
9133 typedef struct {\r
9134     char id;\r
9135     char * name;\r
9136 } GLT_Item;\r
9137 \r
9138 static GLT_Item GLT_ItemInfo[] = {\r
9139     { GLT_EVENT,      "Event" },\r
9140     { GLT_SITE,       "Site" },\r
9141     { GLT_DATE,       "Date" },\r
9142     { GLT_ROUND,      "Round" },\r
9143     { GLT_PLAYERS,    "Players" },\r
9144     { GLT_RESULT,     "Result" },\r
9145     { GLT_WHITE_ELO,  "White Rating" },\r
9146     { GLT_BLACK_ELO,  "Black Rating" },\r
9147     { GLT_TIME_CONTROL,"Time Control" },\r
9148     { GLT_VARIANT,    "Variant" },\r
9149     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9150     { 0, 0 }\r
9151 };\r
9152 \r
9153 const char * GLT_FindItem( char id )\r
9154 {\r
9155     const char * result = 0;\r
9156 \r
9157     GLT_Item * list = GLT_ItemInfo;\r
9158 \r
9159     while( list->id != 0 ) {\r
9160         if( list->id == id ) {\r
9161             result = list->name;\r
9162             break;\r
9163         }\r
9164 \r
9165         list++;\r
9166     }\r
9167 \r
9168     return result;\r
9169 }\r
9170 \r
9171 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9172 {\r
9173     const char * name = GLT_FindItem( id );\r
9174 \r
9175     if( name != 0 ) {\r
9176         if( index >= 0 ) {\r
9177             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9178         }\r
9179         else {\r
9180             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9181         }\r
9182     }\r
9183 }\r
9184 \r
9185 void GLT_TagsToList( HWND hDlg, char * tags )\r
9186 {\r
9187     char * pc = tags;\r
9188 \r
9189     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9190 \r
9191     while( *pc ) {\r
9192         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9193         pc++;\r
9194     }\r
9195 \r
9196     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9197 \r
9198     pc = GLT_ALL_TAGS;\r
9199 \r
9200     while( *pc ) {\r
9201         if( strchr( tags, *pc ) == 0 ) {\r
9202             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9203         }\r
9204         pc++;\r
9205     }\r
9206 \r
9207     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9208 }\r
9209 \r
9210 char GLT_ListItemToTag( HWND hDlg, int index )\r
9211 {\r
9212     char result = '\0';\r
9213     char name[128];\r
9214 \r
9215     GLT_Item * list = GLT_ItemInfo;\r
9216 \r
9217     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9218         while( list->id != 0 ) {\r
9219             if( strcmp( list->name, name ) == 0 ) {\r
9220                 result = list->id;\r
9221                 break;\r
9222             }\r
9223 \r
9224             list++;\r
9225         }\r
9226     }\r
9227 \r
9228     return result;\r
9229 }\r
9230 \r
9231 void GLT_MoveSelection( HWND hDlg, int delta )\r
9232 {\r
9233     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9234     int idx2 = idx1 + delta;\r
9235     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9236 \r
9237     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9238         char buf[128];\r
9239 \r
9240         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9241         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9242         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9243         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9244     }\r
9245 }\r
9246 \r
9247 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9248 {\r
9249     static char glt[64];\r
9250     static char * lpUserGLT;\r
9251 \r
9252     switch( message )\r
9253     {\r
9254     case WM_INITDIALOG:\r
9255         lpUserGLT = (char *) lParam;\r
9256         \r
9257         strcpy( glt, lpUserGLT );\r
9258 \r
9259         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9260 \r
9261         /* Initialize list */\r
9262         GLT_TagsToList( hDlg, glt );\r
9263 \r
9264         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9265 \r
9266         break;\r
9267 \r
9268     case WM_COMMAND:\r
9269         switch( LOWORD(wParam) ) {\r
9270         case IDOK:\r
9271             {\r
9272                 char * pc = lpUserGLT;\r
9273                 int idx = 0;\r
9274 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9275                 char id;\r
9276 \r
9277                 do {\r
9278                     id = GLT_ListItemToTag( hDlg, idx );\r
9279 \r
9280                     *pc++ = id;\r
9281                     idx++;\r
9282                 } while( id != '\0' );\r
9283             }\r
9284             EndDialog( hDlg, 0 );\r
9285             return TRUE;\r
9286         case IDCANCEL:\r
9287             EndDialog( hDlg, 1 );\r
9288             return TRUE;\r
9289 \r
9290         case IDC_GLT_Default:\r
9291             strcpy( glt, GLT_DEFAULT_TAGS );\r
9292             GLT_TagsToList( hDlg, glt );\r
9293             return TRUE;\r
9294 \r
9295         case IDC_GLT_Restore:\r
9296             strcpy( glt, lpUserGLT );\r
9297             GLT_TagsToList( hDlg, glt );\r
9298             return TRUE;\r
9299 \r
9300         case IDC_GLT_Up:\r
9301             GLT_MoveSelection( hDlg, -1 );\r
9302             return TRUE;\r
9303 \r
9304         case IDC_GLT_Down:\r
9305             GLT_MoveSelection( hDlg, +1 );\r
9306             return TRUE;\r
9307         }\r
9308 \r
9309         break;\r
9310     }\r
9311 \r
9312     return FALSE;\r
9313 }\r
9314 \r
9315 int GameListOptions()\r
9316 {\r
9317     char glt[64];\r
9318     int result;\r
9319     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9320 \r
9321     strcpy( glt, appData.gameListTags );\r
9322 \r
9323     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9324 \r
9325     if( result == 0 ) {\r
9326         /* [AS] Memory leak here! */\r
9327         appData.gameListTags = strdup( glt ); \r
9328     }\r
9329 \r
9330     return result;\r
9331 }\r
9332 \r
9333 \r
9334 VOID\r
9335 DisplayIcsInteractionTitle(char *str)\r
9336 {\r
9337   char consoleTitle[MSG_SIZ];\r
9338 \r
9339   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9340   SetWindowText(hwndConsole, consoleTitle);\r
9341 }\r
9342 \r
9343 void\r
9344 DrawPosition(int fullRedraw, Board board)\r
9345 {\r
9346   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9347 }\r
9348 \r
9349 \r
9350 VOID\r
9351 ResetFrontEnd()\r
9352 {\r
9353   fromX = fromY = -1;\r
9354   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9355     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9356     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9357     dragInfo.lastpos = dragInfo.pos;\r
9358     dragInfo.start.x = dragInfo.start.y = -1;\r
9359     dragInfo.from = dragInfo.start;\r
9360     ReleaseCapture();\r
9361     DrawPosition(TRUE, NULL);\r
9362   }\r
9363 }\r
9364 \r
9365 \r
9366 VOID\r
9367 CommentPopUp(char *title, char *str)\r
9368 {\r
9369   HWND hwnd = GetActiveWindow();\r
9370   EitherCommentPopUp(0, title, str, FALSE);\r
9371   SetActiveWindow(hwnd);\r
9372 }\r
9373 \r
9374 VOID\r
9375 CommentPopDown(void)\r
9376 {\r
9377   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9378   if (commentDialog) {\r
9379     ShowWindow(commentDialog, SW_HIDE);\r
9380   }\r
9381   commentDialogUp = FALSE;\r
9382 }\r
9383 \r
9384 VOID\r
9385 EditCommentPopUp(int index, char *title, char *str)\r
9386 {\r
9387   EitherCommentPopUp(index, title, str, TRUE);\r
9388 }\r
9389 \r
9390 \r
9391 VOID\r
9392 RingBell()\r
9393 {\r
9394   MyPlaySound(&sounds[(int)SoundMove]);\r
9395 }\r
9396 \r
9397 VOID PlayIcsWinSound()\r
9398 {\r
9399   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9400 }\r
9401 \r
9402 VOID PlayIcsLossSound()\r
9403 {\r
9404   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9405 }\r
9406 \r
9407 VOID PlayIcsDrawSound()\r
9408 {\r
9409   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9410 }\r
9411 \r
9412 VOID PlayIcsUnfinishedSound()\r
9413 {\r
9414   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9415 }\r
9416 \r
9417 VOID\r
9418 PlayAlarmSound()\r
9419 {\r
9420   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9421 }\r
9422 \r
9423 \r
9424 VOID\r
9425 EchoOn()\r
9426 {\r
9427   HWND hInput;\r
9428   consoleEcho = TRUE;\r
9429   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9430   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9431   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9432 }\r
9433 \r
9434 \r
9435 VOID\r
9436 EchoOff()\r
9437 {\r
9438   CHARFORMAT cf;\r
9439   HWND hInput;\r
9440   consoleEcho = FALSE;\r
9441   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9442   /* This works OK: set text and background both to the same color */\r
9443   cf = consoleCF;\r
9444   cf.crTextColor = COLOR_ECHOOFF;\r
9445   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9446   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9447 }\r
9448 \r
9449 /* No Raw()...? */\r
9450 \r
9451 void Colorize(ColorClass cc, int continuation)\r
9452 {\r
9453   currentColorClass = cc;\r
9454   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9455   consoleCF.crTextColor = textAttribs[cc].color;\r
9456   consoleCF.dwEffects = textAttribs[cc].effects;\r
9457   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9458 }\r
9459 \r
9460 char *\r
9461 UserName()\r
9462 {\r
9463   static char buf[MSG_SIZ];\r
9464   DWORD bufsiz = MSG_SIZ;\r
9465 \r
9466   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9467         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9468   }\r
9469   if (!GetUserName(buf, &bufsiz)) {\r
9470     /*DisplayError("Error getting user name", GetLastError());*/\r
9471     strcpy(buf, "User");\r
9472   }\r
9473   return buf;\r
9474 }\r
9475 \r
9476 char *\r
9477 HostName()\r
9478 {\r
9479   static char buf[MSG_SIZ];\r
9480   DWORD bufsiz = MSG_SIZ;\r
9481 \r
9482   if (!GetComputerName(buf, &bufsiz)) {\r
9483     /*DisplayError("Error getting host name", GetLastError());*/\r
9484     strcpy(buf, "Unknown");\r
9485   }\r
9486   return buf;\r
9487 }\r
9488 \r
9489 \r
9490 int\r
9491 ClockTimerRunning()\r
9492 {\r
9493   return clockTimerEvent != 0;\r
9494 }\r
9495 \r
9496 int\r
9497 StopClockTimer()\r
9498 {\r
9499   if (clockTimerEvent == 0) return FALSE;\r
9500   KillTimer(hwndMain, clockTimerEvent);\r
9501   clockTimerEvent = 0;\r
9502   return TRUE;\r
9503 }\r
9504 \r
9505 void\r
9506 StartClockTimer(long millisec)\r
9507 {\r
9508   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9509                              (UINT) millisec, NULL);\r
9510 }\r
9511 \r
9512 void\r
9513 DisplayWhiteClock(long timeRemaining, int highlight)\r
9514 {\r
9515   HDC hdc;\r
9516   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9517 \r
9518   if(appData.noGUI) return;\r
9519   hdc = GetDC(hwndMain);\r
9520   if (!IsIconic(hwndMain)) {\r
9521     DisplayAClock(hdc, timeRemaining, highlight, \r
9522                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9523   }\r
9524   if (highlight && iconCurrent == iconBlack) {\r
9525     iconCurrent = iconWhite;\r
9526     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9527     if (IsIconic(hwndMain)) {\r
9528       DrawIcon(hdc, 2, 2, iconCurrent);\r
9529     }\r
9530   }\r
9531   (void) ReleaseDC(hwndMain, hdc);\r
9532   if (hwndConsole)\r
9533     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9534 }\r
9535 \r
9536 void\r
9537 DisplayBlackClock(long timeRemaining, int highlight)\r
9538 {\r
9539   HDC hdc;\r
9540   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9541 \r
9542   if(appData.noGUI) return;\r
9543   hdc = GetDC(hwndMain);\r
9544   if (!IsIconic(hwndMain)) {\r
9545     DisplayAClock(hdc, timeRemaining, highlight, \r
9546                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9547   }\r
9548   if (highlight && iconCurrent == iconWhite) {\r
9549     iconCurrent = iconBlack;\r
9550     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9551     if (IsIconic(hwndMain)) {\r
9552       DrawIcon(hdc, 2, 2, iconCurrent);\r
9553     }\r
9554   }\r
9555   (void) ReleaseDC(hwndMain, hdc);\r
9556   if (hwndConsole)\r
9557     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9558 }\r
9559 \r
9560 \r
9561 int\r
9562 LoadGameTimerRunning()\r
9563 {\r
9564   return loadGameTimerEvent != 0;\r
9565 }\r
9566 \r
9567 int\r
9568 StopLoadGameTimer()\r
9569 {\r
9570   if (loadGameTimerEvent == 0) return FALSE;\r
9571   KillTimer(hwndMain, loadGameTimerEvent);\r
9572   loadGameTimerEvent = 0;\r
9573   return TRUE;\r
9574 }\r
9575 \r
9576 void\r
9577 StartLoadGameTimer(long millisec)\r
9578 {\r
9579   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9580                                 (UINT) millisec, NULL);\r
9581 }\r
9582 \r
9583 void\r
9584 AutoSaveGame()\r
9585 {\r
9586   char *defName;\r
9587   FILE *f;\r
9588   char fileTitle[MSG_SIZ];\r
9589 \r
9590   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9591   f = OpenFileDialog(hwndMain, "a", defName,\r
9592                      appData.oldSaveStyle ? "gam" : "pgn",\r
9593                      GAME_FILT, \r
9594                      "Save Game to File", NULL, fileTitle, NULL);\r
9595   if (f != NULL) {\r
9596     SaveGame(f, 0, "");\r
9597     fclose(f);\r
9598   }\r
9599 }\r
9600 \r
9601 \r
9602 void\r
9603 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9604 {\r
9605   if (delayedTimerEvent != 0) {\r
9606     if (appData.debugMode) {\r
9607       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9608     }\r
9609     KillTimer(hwndMain, delayedTimerEvent);\r
9610     delayedTimerEvent = 0;\r
9611     delayedTimerCallback();\r
9612   }\r
9613   delayedTimerCallback = cb;\r
9614   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9615                                 (UINT) millisec, NULL);\r
9616 }\r
9617 \r
9618 DelayedEventCallback\r
9619 GetDelayedEvent()\r
9620 {\r
9621   if (delayedTimerEvent) {\r
9622     return delayedTimerCallback;\r
9623   } else {\r
9624     return NULL;\r
9625   }\r
9626 }\r
9627 \r
9628 void\r
9629 CancelDelayedEvent()\r
9630 {\r
9631   if (delayedTimerEvent) {\r
9632     KillTimer(hwndMain, delayedTimerEvent);\r
9633     delayedTimerEvent = 0;\r
9634   }\r
9635 }\r
9636 \r
9637 DWORD GetWin32Priority(int nice)\r
9638 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9639 /*\r
9640 REALTIME_PRIORITY_CLASS     0x00000100\r
9641 HIGH_PRIORITY_CLASS         0x00000080\r
9642 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9643 NORMAL_PRIORITY_CLASS       0x00000020\r
9644 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9645 IDLE_PRIORITY_CLASS         0x00000040\r
9646 */\r
9647         if (nice < -15) return 0x00000080;\r
9648         if (nice < 0)   return 0x00008000;\r
9649         if (nice == 0)  return 0x00000020;\r
9650         if (nice < 15)  return 0x00004000;\r
9651         return 0x00000040;\r
9652 }\r
9653 \r
9654 /* Start a child process running the given program.\r
9655    The process's standard output can be read from "from", and its\r
9656    standard input can be written to "to".\r
9657    Exit with fatal error if anything goes wrong.\r
9658    Returns an opaque pointer that can be used to destroy the process\r
9659    later.\r
9660 */\r
9661 int\r
9662 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9663 {\r
9664 #define BUFSIZE 4096\r
9665 \r
9666   HANDLE hChildStdinRd, hChildStdinWr,\r
9667     hChildStdoutRd, hChildStdoutWr;\r
9668   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9669   SECURITY_ATTRIBUTES saAttr;\r
9670   BOOL fSuccess;\r
9671   PROCESS_INFORMATION piProcInfo;\r
9672   STARTUPINFO siStartInfo;\r
9673   ChildProc *cp;\r
9674   char buf[MSG_SIZ];\r
9675   DWORD err;\r
9676 \r
9677   if (appData.debugMode) {\r
9678     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9679   }\r
9680 \r
9681   *pr = NoProc;\r
9682 \r
9683   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9684   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9685   saAttr.bInheritHandle = TRUE;\r
9686   saAttr.lpSecurityDescriptor = NULL;\r
9687 \r
9688   /*\r
9689    * The steps for redirecting child's STDOUT:\r
9690    *     1. Create anonymous pipe to be STDOUT for child.\r
9691    *     2. Create a noninheritable duplicate of read handle,\r
9692    *         and close the inheritable read handle.\r
9693    */\r
9694 \r
9695   /* Create a pipe for the child's STDOUT. */\r
9696   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9697     return GetLastError();\r
9698   }\r
9699 \r
9700   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9701   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9702                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9703                              FALSE,     /* not inherited */\r
9704                              DUPLICATE_SAME_ACCESS);\r
9705   if (! fSuccess) {\r
9706     return GetLastError();\r
9707   }\r
9708   CloseHandle(hChildStdoutRd);\r
9709 \r
9710   /*\r
9711    * The steps for redirecting child's STDIN:\r
9712    *     1. Create anonymous pipe to be STDIN for child.\r
9713    *     2. Create a noninheritable duplicate of write handle,\r
9714    *         and close the inheritable write handle.\r
9715    */\r
9716 \r
9717   /* Create a pipe for the child's STDIN. */\r
9718   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9719     return GetLastError();\r
9720   }\r
9721 \r
9722   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9723   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9724                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9725                              FALSE,     /* not inherited */\r
9726                              DUPLICATE_SAME_ACCESS);\r
9727   if (! fSuccess) {\r
9728     return GetLastError();\r
9729   }\r
9730   CloseHandle(hChildStdinWr);\r
9731 \r
9732   /* Arrange to (1) look in dir for the child .exe file, and\r
9733    * (2) have dir be the child's working directory.  Interpret\r
9734    * dir relative to the directory WinBoard loaded from. */\r
9735   GetCurrentDirectory(MSG_SIZ, buf);\r
9736   SetCurrentDirectory(installDir);\r
9737   SetCurrentDirectory(dir);\r
9738 \r
9739   /* Now create the child process. */\r
9740 \r
9741   siStartInfo.cb = sizeof(STARTUPINFO);\r
9742   siStartInfo.lpReserved = NULL;\r
9743   siStartInfo.lpDesktop = NULL;\r
9744   siStartInfo.lpTitle = NULL;\r
9745   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9746   siStartInfo.cbReserved2 = 0;\r
9747   siStartInfo.lpReserved2 = NULL;\r
9748   siStartInfo.hStdInput = hChildStdinRd;\r
9749   siStartInfo.hStdOutput = hChildStdoutWr;\r
9750   siStartInfo.hStdError = hChildStdoutWr;\r
9751 \r
9752   fSuccess = CreateProcess(NULL,\r
9753                            cmdLine,        /* command line */\r
9754                            NULL,           /* process security attributes */\r
9755                            NULL,           /* primary thread security attrs */\r
9756                            TRUE,           /* handles are inherited */\r
9757                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9758                            NULL,           /* use parent's environment */\r
9759                            NULL,\r
9760                            &siStartInfo, /* STARTUPINFO pointer */\r
9761                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9762 \r
9763   err = GetLastError();\r
9764   SetCurrentDirectory(buf); /* return to prev directory */\r
9765   if (! fSuccess) {\r
9766     return err;\r
9767   }\r
9768 \r
9769   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9770     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9771     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9772   }\r
9773 \r
9774   /* Close the handles we don't need in the parent */\r
9775   CloseHandle(piProcInfo.hThread);\r
9776   CloseHandle(hChildStdinRd);\r
9777   CloseHandle(hChildStdoutWr);\r
9778 \r
9779   /* Prepare return value */\r
9780   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9781   cp->kind = CPReal;\r
9782   cp->hProcess = piProcInfo.hProcess;\r
9783   cp->pid = piProcInfo.dwProcessId;\r
9784   cp->hFrom = hChildStdoutRdDup;\r
9785   cp->hTo = hChildStdinWrDup;\r
9786 \r
9787   *pr = (void *) cp;\r
9788 \r
9789   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9790      2000 where engines sometimes don't see the initial command(s)\r
9791      from WinBoard and hang.  I don't understand how that can happen,\r
9792      but the Sleep is harmless, so I've put it in.  Others have also\r
9793      reported what may be the same problem, so hopefully this will fix\r
9794      it for them too.  */\r
9795   Sleep(500);\r
9796 \r
9797   return NO_ERROR;\r
9798 }\r
9799 \r
9800 \r
9801 void\r
9802 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9803 {\r
9804   ChildProc *cp; int result;\r
9805 \r
9806   cp = (ChildProc *) pr;\r
9807   if (cp == NULL) return;\r
9808 \r
9809   switch (cp->kind) {\r
9810   case CPReal:\r
9811     /* TerminateProcess is considered harmful, so... */\r
9812     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9813     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9814     /* The following doesn't work because the chess program\r
9815        doesn't "have the same console" as WinBoard.  Maybe\r
9816        we could arrange for this even though neither WinBoard\r
9817        nor the chess program uses a console for stdio? */\r
9818     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9819 \r
9820     /* [AS] Special termination modes for misbehaving programs... */\r
9821     if( signal == 9 ) { \r
9822         result = TerminateProcess( cp->hProcess, 0 );\r
9823 \r
9824         if ( appData.debugMode) {\r
9825             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9826         }\r
9827     }\r
9828     else if( signal == 10 ) {\r
9829         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9830 \r
9831         if( dw != WAIT_OBJECT_0 ) {\r
9832             result = TerminateProcess( cp->hProcess, 0 );\r
9833 \r
9834             if ( appData.debugMode) {\r
9835                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9836             }\r
9837 \r
9838         }\r
9839     }\r
9840 \r
9841     CloseHandle(cp->hProcess);\r
9842     break;\r
9843 \r
9844   case CPComm:\r
9845     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9846     break;\r
9847 \r
9848   case CPSock:\r
9849     closesocket(cp->sock);\r
9850     WSACleanup();\r
9851     break;\r
9852 \r
9853   case CPRcmd:\r
9854     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9855     closesocket(cp->sock);\r
9856     closesocket(cp->sock2);\r
9857     WSACleanup();\r
9858     break;\r
9859   }\r
9860   free(cp);\r
9861 }\r
9862 \r
9863 void\r
9864 InterruptChildProcess(ProcRef pr)\r
9865 {\r
9866   ChildProc *cp;\r
9867 \r
9868   cp = (ChildProc *) pr;\r
9869   if (cp == NULL) return;\r
9870   switch (cp->kind) {\r
9871   case CPReal:\r
9872     /* The following doesn't work because the chess program\r
9873        doesn't "have the same console" as WinBoard.  Maybe\r
9874        we could arrange for this even though neither WinBoard\r
9875        nor the chess program uses a console for stdio */\r
9876     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9877     break;\r
9878 \r
9879   case CPComm:\r
9880   case CPSock:\r
9881     /* Can't interrupt */\r
9882     break;\r
9883 \r
9884   case CPRcmd:\r
9885     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9886     break;\r
9887   }\r
9888 }\r
9889 \r
9890 \r
9891 int\r
9892 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9893 {\r
9894   char cmdLine[MSG_SIZ];\r
9895 \r
9896   if (port[0] == NULLCHAR) {\r
9897     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9898   } else {\r
9899     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9900   }\r
9901   return StartChildProcess(cmdLine, "", pr);\r
9902 }\r
9903 \r
9904 \r
9905 /* Code to open TCP sockets */\r
9906 \r
9907 int\r
9908 OpenTCP(char *host, char *port, ProcRef *pr)\r
9909 {\r
9910   ChildProc *cp;\r
9911   int err;\r
9912   SOCKET s;\r
9913   struct sockaddr_in sa, mysa;\r
9914   struct hostent FAR *hp;\r
9915   unsigned short uport;\r
9916   WORD wVersionRequested;\r
9917   WSADATA wsaData;\r
9918 \r
9919   /* Initialize socket DLL */\r
9920   wVersionRequested = MAKEWORD(1, 1);\r
9921   err = WSAStartup(wVersionRequested, &wsaData);\r
9922   if (err != 0) return err;\r
9923 \r
9924   /* Make socket */\r
9925   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9926     err = WSAGetLastError();\r
9927     WSACleanup();\r
9928     return err;\r
9929   }\r
9930 \r
9931   /* Bind local address using (mostly) don't-care values.\r
9932    */\r
9933   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9934   mysa.sin_family = AF_INET;\r
9935   mysa.sin_addr.s_addr = INADDR_ANY;\r
9936   uport = (unsigned short) 0;\r
9937   mysa.sin_port = htons(uport);\r
9938   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9939       == SOCKET_ERROR) {\r
9940     err = WSAGetLastError();\r
9941     WSACleanup();\r
9942     return err;\r
9943   }\r
9944 \r
9945   /* Resolve remote host name */\r
9946   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9947   if (!(hp = gethostbyname(host))) {\r
9948     unsigned int b0, b1, b2, b3;\r
9949 \r
9950     err = WSAGetLastError();\r
9951 \r
9952     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9953       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9954       hp->h_addrtype = AF_INET;\r
9955       hp->h_length = 4;\r
9956       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9957       hp->h_addr_list[0] = (char *) malloc(4);\r
9958       hp->h_addr_list[0][0] = (char) b0;\r
9959       hp->h_addr_list[0][1] = (char) b1;\r
9960       hp->h_addr_list[0][2] = (char) b2;\r
9961       hp->h_addr_list[0][3] = (char) b3;\r
9962     } else {\r
9963       WSACleanup();\r
9964       return err;\r
9965     }\r
9966   }\r
9967   sa.sin_family = hp->h_addrtype;\r
9968   uport = (unsigned short) atoi(port);\r
9969   sa.sin_port = htons(uport);\r
9970   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9971 \r
9972   /* Make connection */\r
9973   if (connect(s, (struct sockaddr *) &sa,\r
9974               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9975     err = WSAGetLastError();\r
9976     WSACleanup();\r
9977     return err;\r
9978   }\r
9979 \r
9980   /* Prepare return value */\r
9981   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9982   cp->kind = CPSock;\r
9983   cp->sock = s;\r
9984   *pr = (ProcRef *) cp;\r
9985 \r
9986   return NO_ERROR;\r
9987 }\r
9988 \r
9989 int\r
9990 OpenCommPort(char *name, ProcRef *pr)\r
9991 {\r
9992   HANDLE h;\r
9993   COMMTIMEOUTS ct;\r
9994   ChildProc *cp;\r
9995   char fullname[MSG_SIZ];\r
9996 \r
9997   if (*name != '\\')\r
9998     sprintf(fullname, "\\\\.\\%s", name);\r
9999   else\r
10000     strcpy(fullname, name);\r
10001 \r
10002   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10003                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10004   if (h == (HANDLE) -1) {\r
10005     return GetLastError();\r
10006   }\r
10007   hCommPort = h;\r
10008 \r
10009   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10010 \r
10011   /* Accumulate characters until a 100ms pause, then parse */\r
10012   ct.ReadIntervalTimeout = 100;\r
10013   ct.ReadTotalTimeoutMultiplier = 0;\r
10014   ct.ReadTotalTimeoutConstant = 0;\r
10015   ct.WriteTotalTimeoutMultiplier = 0;\r
10016   ct.WriteTotalTimeoutConstant = 0;\r
10017   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10018 \r
10019   /* Prepare return value */\r
10020   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10021   cp->kind = CPComm;\r
10022   cp->hFrom = h;\r
10023   cp->hTo = h;\r
10024   *pr = (ProcRef *) cp;\r
10025 \r
10026   return NO_ERROR;\r
10027 }\r
10028 \r
10029 int\r
10030 OpenLoopback(ProcRef *pr)\r
10031 {\r
10032   DisplayFatalError("Not implemented", 0, 1);\r
10033   return NO_ERROR;\r
10034 }\r
10035 \r
10036 \r
10037 int\r
10038 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10039 {\r
10040   ChildProc *cp;\r
10041   int err;\r
10042   SOCKET s, s2, s3;\r
10043   struct sockaddr_in sa, mysa;\r
10044   struct hostent FAR *hp;\r
10045   unsigned short uport;\r
10046   WORD wVersionRequested;\r
10047   WSADATA wsaData;\r
10048   int fromPort;\r
10049   char stderrPortStr[MSG_SIZ];\r
10050 \r
10051   /* Initialize socket DLL */\r
10052   wVersionRequested = MAKEWORD(1, 1);\r
10053   err = WSAStartup(wVersionRequested, &wsaData);\r
10054   if (err != 0) return err;\r
10055 \r
10056   /* Resolve remote host name */\r
10057   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10058   if (!(hp = gethostbyname(host))) {\r
10059     unsigned int b0, b1, b2, b3;\r
10060 \r
10061     err = WSAGetLastError();\r
10062 \r
10063     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10064       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10065       hp->h_addrtype = AF_INET;\r
10066       hp->h_length = 4;\r
10067       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10068       hp->h_addr_list[0] = (char *) malloc(4);\r
10069       hp->h_addr_list[0][0] = (char) b0;\r
10070       hp->h_addr_list[0][1] = (char) b1;\r
10071       hp->h_addr_list[0][2] = (char) b2;\r
10072       hp->h_addr_list[0][3] = (char) b3;\r
10073     } else {\r
10074       WSACleanup();\r
10075       return err;\r
10076     }\r
10077   }\r
10078   sa.sin_family = hp->h_addrtype;\r
10079   uport = (unsigned short) 514;\r
10080   sa.sin_port = htons(uport);\r
10081   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10082 \r
10083   /* Bind local socket to unused "privileged" port address\r
10084    */\r
10085   s = INVALID_SOCKET;\r
10086   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10087   mysa.sin_family = AF_INET;\r
10088   mysa.sin_addr.s_addr = INADDR_ANY;\r
10089   for (fromPort = 1023;; fromPort--) {\r
10090     if (fromPort < 0) {\r
10091       WSACleanup();\r
10092       return WSAEADDRINUSE;\r
10093     }\r
10094     if (s == INVALID_SOCKET) {\r
10095       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10096         err = WSAGetLastError();\r
10097         WSACleanup();\r
10098         return err;\r
10099       }\r
10100     }\r
10101     uport = (unsigned short) fromPort;\r
10102     mysa.sin_port = htons(uport);\r
10103     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10104         == SOCKET_ERROR) {\r
10105       err = WSAGetLastError();\r
10106       if (err == WSAEADDRINUSE) continue;\r
10107       WSACleanup();\r
10108       return err;\r
10109     }\r
10110     if (connect(s, (struct sockaddr *) &sa,\r
10111       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10112       err = WSAGetLastError();\r
10113       if (err == WSAEADDRINUSE) {\r
10114         closesocket(s);\r
10115         s = -1;\r
10116         continue;\r
10117       }\r
10118       WSACleanup();\r
10119       return err;\r
10120     }\r
10121     break;\r
10122   }\r
10123 \r
10124   /* Bind stderr local socket to unused "privileged" port address\r
10125    */\r
10126   s2 = INVALID_SOCKET;\r
10127   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10128   mysa.sin_family = AF_INET;\r
10129   mysa.sin_addr.s_addr = INADDR_ANY;\r
10130   for (fromPort = 1023;; fromPort--) {\r
10131     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10132     if (fromPort < 0) {\r
10133       (void) closesocket(s);\r
10134       WSACleanup();\r
10135       return WSAEADDRINUSE;\r
10136     }\r
10137     if (s2 == INVALID_SOCKET) {\r
10138       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10139         err = WSAGetLastError();\r
10140         closesocket(s);\r
10141         WSACleanup();\r
10142         return err;\r
10143       }\r
10144     }\r
10145     uport = (unsigned short) fromPort;\r
10146     mysa.sin_port = htons(uport);\r
10147     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10148         == SOCKET_ERROR) {\r
10149       err = WSAGetLastError();\r
10150       if (err == WSAEADDRINUSE) continue;\r
10151       (void) closesocket(s);\r
10152       WSACleanup();\r
10153       return err;\r
10154     }\r
10155     if (listen(s2, 1) == SOCKET_ERROR) {\r
10156       err = WSAGetLastError();\r
10157       if (err == WSAEADDRINUSE) {\r
10158         closesocket(s2);\r
10159         s2 = INVALID_SOCKET;\r
10160         continue;\r
10161       }\r
10162       (void) closesocket(s);\r
10163       (void) closesocket(s2);\r
10164       WSACleanup();\r
10165       return err;\r
10166     }\r
10167     break;\r
10168   }\r
10169   prevStderrPort = fromPort; // remember port used\r
10170   sprintf(stderrPortStr, "%d", fromPort);\r
10171 \r
10172   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10173     err = WSAGetLastError();\r
10174     (void) closesocket(s);\r
10175     (void) closesocket(s2);\r
10176     WSACleanup();\r
10177     return err;\r
10178   }\r
10179 \r
10180   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10181     err = WSAGetLastError();\r
10182     (void) closesocket(s);\r
10183     (void) closesocket(s2);\r
10184     WSACleanup();\r
10185     return err;\r
10186   }\r
10187   if (*user == NULLCHAR) user = UserName();\r
10188   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10189     err = WSAGetLastError();\r
10190     (void) closesocket(s);\r
10191     (void) closesocket(s2);\r
10192     WSACleanup();\r
10193     return err;\r
10194   }\r
10195   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10196     err = WSAGetLastError();\r
10197     (void) closesocket(s);\r
10198     (void) closesocket(s2);\r
10199     WSACleanup();\r
10200     return err;\r
10201   }\r
10202 \r
10203   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10204     err = WSAGetLastError();\r
10205     (void) closesocket(s);\r
10206     (void) closesocket(s2);\r
10207     WSACleanup();\r
10208     return err;\r
10209   }\r
10210   (void) closesocket(s2);  /* Stop listening */\r
10211 \r
10212   /* Prepare return value */\r
10213   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10214   cp->kind = CPRcmd;\r
10215   cp->sock = s;\r
10216   cp->sock2 = s3;\r
10217   *pr = (ProcRef *) cp;\r
10218 \r
10219   return NO_ERROR;\r
10220 }\r
10221 \r
10222 \r
10223 InputSourceRef\r
10224 AddInputSource(ProcRef pr, int lineByLine,\r
10225                InputCallback func, VOIDSTAR closure)\r
10226 {\r
10227   InputSource *is, *is2 = NULL;\r
10228   ChildProc *cp = (ChildProc *) pr;\r
10229 \r
10230   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10231   is->lineByLine = lineByLine;\r
10232   is->func = func;\r
10233   is->closure = closure;\r
10234   is->second = NULL;\r
10235   is->next = is->buf;\r
10236   if (pr == NoProc) {\r
10237     is->kind = CPReal;\r
10238     consoleInputSource = is;\r
10239   } else {\r
10240     is->kind = cp->kind;\r
10241     /* \r
10242         [AS] Try to avoid a race condition if the thread is given control too early:\r
10243         we create all threads suspended so that the is->hThread variable can be\r
10244         safely assigned, then let the threads start with ResumeThread.\r
10245     */\r
10246     switch (cp->kind) {\r
10247     case CPReal:\r
10248       is->hFile = cp->hFrom;\r
10249       cp->hFrom = NULL; /* now owned by InputThread */\r
10250       is->hThread =\r
10251         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10252                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10253       break;\r
10254 \r
10255     case CPComm:\r
10256       is->hFile = cp->hFrom;\r
10257       cp->hFrom = NULL; /* now owned by InputThread */\r
10258       is->hThread =\r
10259         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10260                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10261       break;\r
10262 \r
10263     case CPSock:\r
10264       is->sock = cp->sock;\r
10265       is->hThread =\r
10266         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10267                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10268       break;\r
10269 \r
10270     case CPRcmd:\r
10271       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10272       *is2 = *is;\r
10273       is->sock = cp->sock;\r
10274       is->second = is2;\r
10275       is2->sock = cp->sock2;\r
10276       is2->second = is2;\r
10277       is->hThread =\r
10278         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10279                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10280       is2->hThread =\r
10281         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10282                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10283       break;\r
10284     }\r
10285 \r
10286     if( is->hThread != NULL ) {\r
10287         ResumeThread( is->hThread );\r
10288     }\r
10289 \r
10290     if( is2 != NULL && is2->hThread != NULL ) {\r
10291         ResumeThread( is2->hThread );\r
10292     }\r
10293   }\r
10294 \r
10295   return (InputSourceRef) is;\r
10296 }\r
10297 \r
10298 void\r
10299 RemoveInputSource(InputSourceRef isr)\r
10300 {\r
10301   InputSource *is;\r
10302 \r
10303   is = (InputSource *) isr;\r
10304   is->hThread = NULL;  /* tell thread to stop */\r
10305   CloseHandle(is->hThread);\r
10306   if (is->second != NULL) {\r
10307     is->second->hThread = NULL;\r
10308     CloseHandle(is->second->hThread);\r
10309   }\r
10310 }\r
10311 \r
10312 \r
10313 int\r
10314 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10315 {\r
10316   DWORD dOutCount;\r
10317   int outCount = SOCKET_ERROR;\r
10318   ChildProc *cp = (ChildProc *) pr;\r
10319   static OVERLAPPED ovl;\r
10320 \r
10321   if (pr == NoProc) {\r
10322     ConsoleOutput(message, count, FALSE);\r
10323     return count;\r
10324   } \r
10325 \r
10326   if (ovl.hEvent == NULL) {\r
10327     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10328   }\r
10329   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10330 \r
10331   switch (cp->kind) {\r
10332   case CPSock:\r
10333   case CPRcmd:\r
10334     outCount = send(cp->sock, message, count, 0);\r
10335     if (outCount == SOCKET_ERROR) {\r
10336       *outError = WSAGetLastError();\r
10337     } else {\r
10338       *outError = NO_ERROR;\r
10339     }\r
10340     break;\r
10341 \r
10342   case CPReal:\r
10343     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10344                   &dOutCount, NULL)) {\r
10345       *outError = NO_ERROR;\r
10346       outCount = (int) dOutCount;\r
10347     } else {\r
10348       *outError = GetLastError();\r
10349     }\r
10350     break;\r
10351 \r
10352   case CPComm:\r
10353     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10354                             &dOutCount, &ovl);\r
10355     if (*outError == NO_ERROR) {\r
10356       outCount = (int) dOutCount;\r
10357     }\r
10358     break;\r
10359   }\r
10360   return outCount;\r
10361 }\r
10362 \r
10363 int\r
10364 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10365                        long msdelay)\r
10366 {\r
10367   /* Ignore delay, not implemented for WinBoard */\r
10368   return OutputToProcess(pr, message, count, outError);\r
10369 }\r
10370 \r
10371 \r
10372 void\r
10373 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10374                         char *buf, int count, int error)\r
10375 {\r
10376   DisplayFatalError("Not implemented", 0, 1);\r
10377 }\r
10378 \r
10379 /* see wgamelist.c for Game List functions */\r
10380 /* see wedittags.c for Edit Tags functions */\r
10381 \r
10382 \r
10383 VOID\r
10384 ICSInitScript()\r
10385 {\r
10386   FILE *f;\r
10387   char buf[MSG_SIZ];\r
10388   char *dummy;\r
10389 \r
10390   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10391     f = fopen(buf, "r");\r
10392     if (f != NULL) {\r
10393       ProcessICSInitScript(f);\r
10394       fclose(f);\r
10395     }\r
10396   }\r
10397 }\r
10398 \r
10399 \r
10400 VOID\r
10401 StartAnalysisClock()\r
10402 {\r
10403   if (analysisTimerEvent) return;\r
10404   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10405                                         (UINT) 2000, NULL);\r
10406 }\r
10407 \r
10408 LRESULT CALLBACK\r
10409 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10410 {\r
10411   static HANDLE hwndText;\r
10412   RECT rect;\r
10413   static int sizeX, sizeY;\r
10414   int newSizeX, newSizeY, flags;\r
10415   MINMAXINFO *mmi;\r
10416 \r
10417   switch (message) {\r
10418   case WM_INITDIALOG: /* message: initialize dialog box */\r
10419     /* Initialize the dialog items */\r
10420     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10421     SetWindowText(hDlg, analysisTitle);\r
10422     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10423     /* Size and position the dialog */\r
10424     if (!analysisDialog) {\r
10425       analysisDialog = hDlg;\r
10426       flags = SWP_NOZORDER;\r
10427       GetClientRect(hDlg, &rect);\r
10428       sizeX = rect.right;\r
10429       sizeY = rect.bottom;\r
10430       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10431           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10432         WINDOWPLACEMENT wp;\r
10433         EnsureOnScreen(&analysisX, &analysisY);\r
10434         wp.length = sizeof(WINDOWPLACEMENT);\r
10435         wp.flags = 0;\r
10436         wp.showCmd = SW_SHOW;\r
10437         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10438         wp.rcNormalPosition.left = analysisX;\r
10439         wp.rcNormalPosition.right = analysisX + analysisW;\r
10440         wp.rcNormalPosition.top = analysisY;\r
10441         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10442         SetWindowPlacement(hDlg, &wp);\r
10443 \r
10444         GetClientRect(hDlg, &rect);\r
10445         newSizeX = rect.right;\r
10446         newSizeY = rect.bottom;\r
10447         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10448                               newSizeX, newSizeY);\r
10449         sizeX = newSizeX;\r
10450         sizeY = newSizeY;\r
10451       }\r
10452     }\r
10453     return FALSE;\r
10454 \r
10455   case WM_COMMAND: /* message: received a command */\r
10456     switch (LOWORD(wParam)) {\r
10457     case IDCANCEL:\r
10458       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10459           ExitAnalyzeMode();\r
10460           ModeHighlight();\r
10461           return TRUE;\r
10462       }\r
10463       EditGameEvent();\r
10464       return TRUE;\r
10465     default:\r
10466       break;\r
10467     }\r
10468     break;\r
10469 \r
10470   case WM_SIZE:\r
10471     newSizeX = LOWORD(lParam);\r
10472     newSizeY = HIWORD(lParam);\r
10473     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10474     sizeX = newSizeX;\r
10475     sizeY = newSizeY;\r
10476     break;\r
10477 \r
10478   case WM_GETMINMAXINFO:\r
10479     /* Prevent resizing window too small */\r
10480     mmi = (MINMAXINFO *) lParam;\r
10481     mmi->ptMinTrackSize.x = 100;\r
10482     mmi->ptMinTrackSize.y = 100;\r
10483     break;\r
10484   }\r
10485   return FALSE;\r
10486 }\r
10487 \r
10488 VOID\r
10489 AnalysisPopUp(char* title, char* str)\r
10490 {\r
10491   FARPROC lpProc;\r
10492   char *p, *q;\r
10493 \r
10494   /* [AS] */\r
10495   EngineOutputPopUp();\r
10496   return;\r
10497 \r
10498   if (str == NULL) str = "";\r
10499   p = (char *) malloc(2 * strlen(str) + 2);\r
10500   q = p;\r
10501   while (*str) {\r
10502     if (*str == '\n') *q++ = '\r';\r
10503     *q++ = *str++;\r
10504   }\r
10505   *q = NULLCHAR;\r
10506   if (analysisText != NULL) free(analysisText);\r
10507   analysisText = p;\r
10508 \r
10509   if (analysisDialog) {\r
10510     SetWindowText(analysisDialog, title);\r
10511     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10512     ShowWindow(analysisDialog, SW_SHOW);\r
10513   } else {\r
10514     analysisTitle = title;\r
10515     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10516     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10517                  hwndMain, (DLGPROC)lpProc);\r
10518     FreeProcInstance(lpProc);\r
10519   }\r
10520   analysisDialogUp = TRUE;  \r
10521 }\r
10522 \r
10523 VOID\r
10524 AnalysisPopDown()\r
10525 {\r
10526   if (analysisDialog) {\r
10527     ShowWindow(analysisDialog, SW_HIDE);\r
10528   }\r
10529   analysisDialogUp = FALSE;  \r
10530 }\r
10531 \r
10532 \r
10533 VOID\r
10534 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10535 {\r
10536   highlightInfo.sq[0].x = fromX;\r
10537   highlightInfo.sq[0].y = fromY;\r
10538   highlightInfo.sq[1].x = toX;\r
10539   highlightInfo.sq[1].y = toY;\r
10540 }\r
10541 \r
10542 VOID\r
10543 ClearHighlights()\r
10544 {\r
10545   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10546     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10547 }\r
10548 \r
10549 VOID\r
10550 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10551 {\r
10552   premoveHighlightInfo.sq[0].x = fromX;\r
10553   premoveHighlightInfo.sq[0].y = fromY;\r
10554   premoveHighlightInfo.sq[1].x = toX;\r
10555   premoveHighlightInfo.sq[1].y = toY;\r
10556 }\r
10557 \r
10558 VOID\r
10559 ClearPremoveHighlights()\r
10560 {\r
10561   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10562     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10563 }\r
10564 \r
10565 VOID\r
10566 ShutDownFrontEnd()\r
10567 {\r
10568   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10569   DeleteClipboardTempFiles();\r
10570 }\r
10571 \r
10572 void\r
10573 BoardToTop()\r
10574 {\r
10575     if (IsIconic(hwndMain))\r
10576       ShowWindow(hwndMain, SW_RESTORE);\r
10577 \r
10578     SetActiveWindow(hwndMain);\r
10579 }\r
10580 \r
10581 /*\r
10582  * Prototypes for animation support routines\r
10583  */\r
10584 static void ScreenSquare(int column, int row, POINT * pt);\r
10585 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10586      POINT frames[], int * nFrames);\r
10587 \r
10588 \r
10589 void\r
10590 AnimateAtomicCapture(int toX, int toY, int nFrames)\r
10591 {       // [HGM] atomic: animate blast wave\r
10592         int i;\r
10593 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10594         explodeInfo.x = toX;\r
10595         explodeInfo.y = toY;\r
10596         for(i=0; i<nFrames; i++) {\r
10597             explodeInfo.radius = (i*180)/(nFrames-1);\r
10598             DrawPosition(FALSE, NULL);\r
10599             Sleep(appData.animSpeed);\r
10600         }\r
10601         explodeInfo.radius = 0;\r
10602         DrawPosition(TRUE, NULL);\r
10603 }\r
10604 \r
10605 #define kFactor 4\r
10606 \r
10607 void\r
10608 AnimateMove(board, fromX, fromY, toX, toY)\r
10609      Board board;\r
10610      int fromX;\r
10611      int fromY;\r
10612      int toX;\r
10613      int toY;\r
10614 {\r
10615   ChessSquare piece;\r
10616   POINT start, finish, mid;\r
10617   POINT frames[kFactor * 2 + 1];\r
10618   int nFrames, n;\r
10619 \r
10620   if (!appData.animate) return;\r
10621   if (doingSizing) return;\r
10622   if (fromY < 0 || fromX < 0) return;\r
10623   piece = board[fromY][fromX];\r
10624   if (piece >= EmptySquare) return;\r
10625 \r
10626   ScreenSquare(fromX, fromY, &start);\r
10627   ScreenSquare(toX, toY, &finish);\r
10628 \r
10629   /* All pieces except knights move in straight line */\r
10630   if (piece != WhiteKnight && piece != BlackKnight) {\r
10631     mid.x = start.x + (finish.x - start.x) / 2;\r
10632     mid.y = start.y + (finish.y - start.y) / 2;\r
10633   } else {\r
10634     /* Knight: make diagonal movement then straight */\r
10635     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10636        mid.x = start.x + (finish.x - start.x) / 2;\r
10637        mid.y = finish.y;\r
10638      } else {\r
10639        mid.x = finish.x;\r
10640        mid.y = start.y + (finish.y - start.y) / 2;\r
10641      }\r
10642   }\r
10643   \r
10644   /* Don't use as many frames for very short moves */\r
10645   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10646     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10647   else\r
10648     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10649 \r
10650   animInfo.from.x = fromX;\r
10651   animInfo.from.y = fromY;\r
10652   animInfo.to.x = toX;\r
10653   animInfo.to.y = toY;\r
10654   animInfo.lastpos = start;\r
10655   animInfo.piece = piece;\r
10656   for (n = 0; n < nFrames; n++) {\r
10657     animInfo.pos = frames[n];\r
10658     DrawPosition(FALSE, NULL);\r
10659     animInfo.lastpos = animInfo.pos;\r
10660     Sleep(appData.animSpeed);\r
10661   }\r
10662   animInfo.pos = finish;\r
10663   DrawPosition(FALSE, NULL);\r
10664   animInfo.piece = EmptySquare;\r
10665   if(gameInfo.variant == VariantAtomic && board[toY][toX] != EmptySquare)\r
10666     AnimateAtomicCapture(toX, toY, 2*nFrames);\r
10667 }\r
10668 \r
10669 /*      Convert board position to corner of screen rect and color       */\r
10670 \r
10671 static void\r
10672 ScreenSquare(column, row, pt)\r
10673      int column; int row; POINT * pt;\r
10674 {\r
10675   if (flipView) {\r
10676     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10677     pt->y = lineGap + row * (squareSize + lineGap);\r
10678   } else {\r
10679     pt->x = lineGap + column * (squareSize + lineGap);\r
10680     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10681   }\r
10682 }\r
10683 \r
10684 /*      Generate a series of frame coords from start->mid->finish.\r
10685         The movement rate doubles until the half way point is\r
10686         reached, then halves back down to the final destination,\r
10687         which gives a nice slow in/out effect. The algorithmn\r
10688         may seem to generate too many intermediates for short\r
10689         moves, but remember that the purpose is to attract the\r
10690         viewers attention to the piece about to be moved and\r
10691         then to where it ends up. Too few frames would be less\r
10692         noticeable.                                             */\r
10693 \r
10694 static void\r
10695 Tween(start, mid, finish, factor, frames, nFrames)\r
10696      POINT * start; POINT * mid;\r
10697      POINT * finish; int factor;\r
10698      POINT frames[]; int * nFrames;\r
10699 {\r
10700   int n, fraction = 1, count = 0;\r
10701 \r
10702   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10703   for (n = 0; n < factor; n++)\r
10704     fraction *= 2;\r
10705   for (n = 0; n < factor; n++) {\r
10706     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10707     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10708     count ++;\r
10709     fraction = fraction / 2;\r
10710   }\r
10711   \r
10712   /* Midpoint */\r
10713   frames[count] = *mid;\r
10714   count ++;\r
10715   \r
10716   /* Slow out, stepping 1/2, then 1/4, ... */\r
10717   fraction = 2;\r
10718   for (n = 0; n < factor; n++) {\r
10719     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10720     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10721     count ++;\r
10722     fraction = fraction * 2;\r
10723   }\r
10724   *nFrames = count;\r
10725 }\r
10726 \r
10727 void\r
10728 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10729 {\r
10730 #if 0\r
10731     char buf[256];\r
10732 \r
10733     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10734         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10735 \r
10736     OutputDebugString( buf );\r
10737 #endif\r
10738 \r
10739     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10740 \r
10741     EvalGraphSet( first, last, current, pvInfoList );\r
10742 }\r
10743 \r
10744 void SetProgramStats( FrontEndProgramStats * stats )\r
10745 {\r
10746 #if 0\r
10747     char buf[1024];\r
10748 \r
10749     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10750         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10751 \r
10752     OutputDebugString( buf );\r
10753 #endif\r
10754 \r
10755     EngineOutputUpdate( stats );\r
10756 }\r