115b2c3141e64d7c3082e483172d18569f3ff894
[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, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 Boolean flipClock = FALSE;\r
96 \r
97 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
98 \r
99 typedef struct {\r
100   ChessSquare piece;  \r
101   POINT pos;      /* window coordinates of current pos */\r
102   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
103   POINT from;     /* board coordinates of the piece's orig pos */\r
104   POINT to;       /* board coordinates of the piece's new pos */\r
105 } AnimInfo;\r
106 \r
107 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
108 \r
109 typedef struct {\r
110   POINT start;    /* window coordinates of start pos */\r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114 } DragInfo;\r
115 \r
116 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT sq[2];    /* board coordinates of from, to squares */\r
120 } HighlightInfo;\r
121 \r
122 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
123 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
124 \r
125 /* Window class names */\r
126 char szAppName[] = "WinBoard";\r
127 char szConsoleName[] = "WBConsole";\r
128 \r
129 /* Title bar text */\r
130 char szTitle[] = "WinBoard";\r
131 char szConsoleTitle[] = "ICS Interaction";\r
132 \r
133 char *programName;\r
134 char *settingsFileName;\r
135 BOOLEAN saveSettingsOnExit;\r
136 char installDir[MSG_SIZ];\r
137 \r
138 BoardSize boardSize;\r
139 BOOLEAN chessProgram;\r
140 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
141 static int squareSize, lineGap, minorSize;\r
142 static int winWidth, winHeight;\r
143 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
144 static int logoHeight = 0;\r
145 static char messageText[MESSAGE_TEXT_MAX];\r
146 static int clockTimerEvent = 0;\r
147 static int loadGameTimerEvent = 0;\r
148 static int analysisTimerEvent = 0;\r
149 static DelayedEventCallback delayedTimerCallback;\r
150 static int delayedTimerEvent = 0;\r
151 static int buttonCount = 2;\r
152 char *icsTextMenuString;\r
153 char *icsNames;\r
154 char *firstChessProgramNames;\r
155 char *secondChessProgramNames;\r
156 \r
157 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
158 \r
159 #define PALETTESIZE 256\r
160 \r
161 HINSTANCE hInst;          /* current instance */\r
162 HWND hwndMain = NULL;        /* root window*/\r
163 HWND hwndConsole = NULL;\r
164 BOOLEAN alwaysOnTop = FALSE;\r
165 RECT boardRect;\r
166 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
167   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
168 HPALETTE hPal;\r
169 ColorClass currentColorClass;\r
170 \r
171 HWND hCommPort = NULL;    /* currently open comm port */\r
172 static HWND hwndPause;    /* pause button */\r
173 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
174 static HBRUSH lightSquareBrush, darkSquareBrush,\r
175   blackSquareBrush, /* [HGM] for band between board and holdings */\r
176   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
177 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
178 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
179 static HPEN gridPen = NULL;\r
180 static HPEN highlightPen = NULL;\r
181 static HPEN premovePen = NULL;\r
182 static NPLOGPALETTE pLogPal;\r
183 static BOOL paletteChanged = FALSE;\r
184 static HICON iconWhite, iconBlack, iconCurrent;\r
185 static int doingSizing = FALSE;\r
186 static int lastSizing = 0;\r
187 static int prevStderrPort;\r
188 \r
189 /* [AS] Support for background textures */\r
190 #define BACK_TEXTURE_MODE_DISABLED      0\r
191 #define BACK_TEXTURE_MODE_PLAIN         1\r
192 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
193 \r
194 static HBITMAP liteBackTexture = NULL;\r
195 static HBITMAP darkBackTexture = NULL;\r
196 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
197 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
198 static int backTextureSquareSize = 0;\r
199 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
200 \r
201 #if __GNUC__ && !defined(_winmajor)\r
202 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
203 #else\r
204 #define oldDialog (_winmajor < 4)\r
205 #endif\r
206 \r
207 char *defaultTextAttribs[] = \r
208 {\r
209   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
210   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
211   COLOR_NONE\r
212 };\r
213 \r
214 typedef struct {\r
215   char *name;\r
216   int squareSize;\r
217   int lineGap;\r
218   int smallLayout;\r
219   int tinyLayout;\r
220   int cliWidth, cliHeight;\r
221 } SizeInfo;\r
222 \r
223 SizeInfo sizeInfo[] = \r
224 {\r
225   { "tiny",     21, 0, 1, 1, 0, 0 },\r
226   { "teeny",    25, 1, 1, 1, 0, 0 },\r
227   { "dinky",    29, 1, 1, 1, 0, 0 },\r
228   { "petite",   33, 1, 1, 1, 0, 0 },\r
229   { "slim",     37, 2, 1, 0, 0, 0 },\r
230   { "small",    40, 2, 1, 0, 0, 0 },\r
231   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
232   { "middling", 49, 2, 0, 0, 0, 0 },\r
233   { "average",  54, 2, 0, 0, 0, 0 },\r
234   { "moderate", 58, 3, 0, 0, 0, 0 },\r
235   { "medium",   64, 3, 0, 0, 0, 0 },\r
236   { "bulky",    72, 3, 0, 0, 0, 0 },\r
237   { "large",    80, 3, 0, 0, 0, 0 },\r
238   { "big",      87, 3, 0, 0, 0, 0 },\r
239   { "huge",     95, 3, 0, 0, 0, 0 },\r
240   { "giant",    108, 3, 0, 0, 0, 0 },\r
241   { "colossal", 116, 4, 0, 0, 0, 0 },\r
242   { "titanic",  129, 4, 0, 0, 0, 0 },\r
243   { NULL, 0, 0, 0, 0, 0, 0 }\r
244 };\r
245 \r
246 #define MF(x) {x, {0, }, {0, }, 0}\r
247 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
248 {\r
249   { 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
250   { 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
251   { 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
252   { 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
253   { 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
254   { 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
255   { 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
256   { 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
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266   { 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
267 };\r
268 \r
269 MyFont *font[NUM_SIZES][NUM_FONTS];\r
270 \r
271 typedef struct {\r
272   char *label;\r
273   int id;\r
274   HWND hwnd;\r
275   WNDPROC wndproc;\r
276 } MyButtonDesc;\r
277 \r
278 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
279 #define N_BUTTONS 5\r
280 \r
281 MyButtonDesc buttonDesc[N_BUTTONS] =\r
282 {\r
283   {"<<", IDM_ToStart, NULL, NULL},\r
284   {"<", IDM_Backward, NULL, NULL},\r
285   {"P", IDM_Pause, NULL, NULL},\r
286   {">", IDM_Forward, NULL, NULL},\r
287   {">>", IDM_ToEnd, NULL, NULL},\r
288 };\r
289 \r
290 int tinyLayout = 0, smallLayout = 0;\r
291 #define MENU_BAR_ITEMS 6\r
292 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
293   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
294   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
295 };\r
296 \r
297 \r
298 MySound sounds[(int)NSoundClasses];\r
299 MyTextAttribs textAttribs[(int)NColorClasses];\r
300 \r
301 MyColorizeAttribs colorizeAttribs[] = {\r
302   { (COLORREF)0, 0, "Shout Text" },\r
303   { (COLORREF)0, 0, "SShout/CShout" },\r
304   { (COLORREF)0, 0, "Channel 1 Text" },\r
305   { (COLORREF)0, 0, "Channel Text" },\r
306   { (COLORREF)0, 0, "Kibitz Text" },\r
307   { (COLORREF)0, 0, "Tell Text" },\r
308   { (COLORREF)0, 0, "Challenge Text" },\r
309   { (COLORREF)0, 0, "Request Text" },\r
310   { (COLORREF)0, 0, "Seek Text" },\r
311   { (COLORREF)0, 0, "Normal Text" },\r
312   { (COLORREF)0, 0, "None" }\r
313 };\r
314 \r
315 \r
316 \r
317 static char *commentTitle;\r
318 static char *commentText;\r
319 static int commentIndex;\r
320 static Boolean editComment = FALSE;\r
321 HWND commentDialog = NULL;\r
322 BOOLEAN commentDialogUp = FALSE;\r
323 static int commentX, commentY, commentH, commentW;\r
324 \r
325 static char *analysisTitle;\r
326 static char *analysisText;\r
327 HWND analysisDialog = NULL;\r
328 BOOLEAN analysisDialogUp = FALSE;\r
329 static int analysisX, analysisY, analysisH, analysisW;\r
330 \r
331 char errorTitle[MSG_SIZ];\r
332 char errorMessage[2*MSG_SIZ];\r
333 HWND errorDialog = NULL;\r
334 BOOLEAN moveErrorMessageUp = FALSE;\r
335 BOOLEAN consoleEcho = TRUE;\r
336 CHARFORMAT consoleCF;\r
337 COLORREF consoleBackgroundColor;\r
338 \r
339 char *programVersion;\r
340 \r
341 #define CPReal 1\r
342 #define CPComm 2\r
343 #define CPSock 3\r
344 #define CPRcmd 4\r
345 typedef int CPKind;\r
346 \r
347 typedef struct {\r
348   CPKind kind;\r
349   HANDLE hProcess;\r
350   DWORD pid;\r
351   HANDLE hTo;\r
352   HANDLE hFrom;\r
353   SOCKET sock;\r
354   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
355 } ChildProc;\r
356 \r
357 #define INPUT_SOURCE_BUF_SIZE 4096\r
358 \r
359 typedef struct _InputSource {\r
360   CPKind kind;\r
361   HANDLE hFile;\r
362   SOCKET sock;\r
363   int lineByLine;\r
364   HANDLE hThread;\r
365   DWORD id;\r
366   char buf[INPUT_SOURCE_BUF_SIZE];\r
367   char *next;\r
368   DWORD count;\r
369   int error;\r
370   InputCallback func;\r
371   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
372   VOIDSTAR closure;\r
373 } InputSource;\r
374 \r
375 InputSource *consoleInputSource;\r
376 \r
377 DCB dcb;\r
378 \r
379 /* forward */\r
380 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
381 VOID ConsoleCreate();\r
382 LRESULT CALLBACK\r
383   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
384 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
385 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
386 VOID ParseCommSettings(char *arg, DCB *dcb);\r
387 LRESULT CALLBACK\r
388   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
389 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
390 void ParseIcsTextMenu(char *icsTextMenuString);\r
391 VOID PopUpMoveDialog(char firstchar);\r
392 VOID PopUpNameDialog(char firstchar);\r
393 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
394 \r
395 /* [AS] */\r
396 int NewGameFRC();\r
397 int GameListOptions();\r
398 \r
399 HWND moveHistoryDialog = NULL;\r
400 BOOLEAN moveHistoryDialogUp = FALSE;\r
401 \r
402 WindowPlacement wpMoveHistory;\r
403 \r
404 HWND evalGraphDialog = NULL;\r
405 BOOLEAN evalGraphDialogUp = FALSE;\r
406 \r
407 WindowPlacement wpEvalGraph;\r
408 \r
409 HWND engineOutputDialog = NULL;\r
410 BOOLEAN engineOutputDialogUp = FALSE;\r
411 \r
412 WindowPlacement wpEngineOutput;\r
413 \r
414 VOID MoveHistoryPopUp();\r
415 VOID MoveHistoryPopDown();\r
416 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
417 BOOL MoveHistoryIsUp();\r
418 \r
419 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
420 VOID EvalGraphPopUp();\r
421 VOID EvalGraphPopDown();\r
422 BOOL EvalGraphIsUp();\r
423 \r
424 VOID EngineOutputPopUp();\r
425 VOID EngineOutputPopDown();\r
426 BOOL EngineOutputIsUp();\r
427 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
428 \r
429 VOID GothicPopUp(char *title, VariantClass variant);\r
430 /*\r
431  * Setting "frozen" should disable all user input other than deleting\r
432  * the window.  We do this while engines are initializing themselves.\r
433  */\r
434 static int frozen = 0;\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];\r
436 void FreezeUI()\r
437 {\r
438   HMENU hmenu;\r
439   int i;\r
440 \r
441   if (frozen) return;\r
442   frozen = 1;\r
443   hmenu = GetMenu(hwndMain);\r
444   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
445     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
446   }\r
447   DrawMenuBar(hwndMain);\r
448 }\r
449 \r
450 /* Undo a FreezeUI */\r
451 void ThawUI()\r
452 {\r
453   HMENU hmenu;\r
454   int i;\r
455 \r
456   if (!frozen) return;\r
457   frozen = 0;\r
458   hmenu = GetMenu(hwndMain);\r
459   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
460     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
461   }\r
462   DrawMenuBar(hwndMain);\r
463 }\r
464 \r
465 /*---------------------------------------------------------------------------*\\r
466  *\r
467  * WinMain\r
468  *\r
469 \*---------------------------------------------------------------------------*/\r
470 \r
471 int APIENTRY\r
472 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
473         LPSTR lpCmdLine, int nCmdShow)\r
474 {\r
475   MSG msg;\r
476   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
477 \r
478   debugFP = stderr;\r
479 \r
480   LoadLibrary("RICHED32.DLL");\r
481   consoleCF.cbSize = sizeof(CHARFORMAT);\r
482 \r
483   if (!InitApplication(hInstance)) {\r
484     return (FALSE);\r
485   }\r
486   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
487     return (FALSE);\r
488   }\r
489 \r
490   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
491   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
492   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
493 \r
494   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
495 \r
496   while (GetMessage(&msg, /* message structure */\r
497                     NULL, /* handle of window receiving the message */\r
498                     0,    /* lowest message to examine */\r
499                     0))   /* highest message to examine */\r
500     {\r
501       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
502           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
503           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
504           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
505           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
506           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
507           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
508           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
509           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
510           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
511         TranslateMessage(&msg); /* Translates virtual key codes */\r
512         DispatchMessage(&msg);  /* Dispatches message to window */\r
513       }\r
514     }\r
515 \r
516 \r
517   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
518 }\r
519 \r
520 /*---------------------------------------------------------------------------*\\r
521  *\r
522  * Initialization functions\r
523  *\r
524 \*---------------------------------------------------------------------------*/\r
525 \r
526 BOOL\r
527 InitApplication(HINSTANCE hInstance)\r
528 {\r
529   WNDCLASS wc;\r
530 \r
531   /* Fill in window class structure with parameters that describe the */\r
532   /* main window. */\r
533 \r
534   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
535   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
536   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
537   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
538   wc.hInstance     = hInstance;         /* Owner of this class */\r
539   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
540   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
541   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
542   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
543   wc.lpszClassName = szAppName;                 /* Name to register as */\r
544 \r
545   /* Register the window class and return success/failure code. */\r
546   if (!RegisterClass(&wc)) return FALSE;\r
547 \r
548   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
549   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
550   wc.cbClsExtra    = 0;\r
551   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
552   wc.hInstance     = hInstance;\r
553   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
554   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
555   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
556   wc.lpszMenuName  = NULL;\r
557   wc.lpszClassName = szConsoleName;\r
558 \r
559   if (!RegisterClass(&wc)) return FALSE;\r
560   return TRUE;\r
561 }\r
562 \r
563 \r
564 /* Set by InitInstance, used by EnsureOnScreen */\r
565 int screenHeight, screenWidth;\r
566 \r
567 void\r
568 EnsureOnScreen(int *x, int *y)\r
569 {\r
570   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
571   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
572   if (*x > screenWidth - 32) *x = 0;\r
573   if (*y > screenHeight - 32) *y = 0;\r
574   if (*x < 10) *x = 10;\r
575   if (*y < gap) *y = gap;\r
576 }\r
577 \r
578 BOOL\r
579 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
580 {\r
581   HWND hwnd; /* Main window handle. */\r
582   int ibs;\r
583   WINDOWPLACEMENT wp;\r
584   char *filepart;\r
585 \r
586   hInst = hInstance;    /* Store instance handle in our global variable */\r
587 \r
588   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
589     *filepart = NULLCHAR;\r
590   } else {\r
591     GetCurrentDirectory(MSG_SIZ, installDir);\r
592   }\r
593   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
594   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
595   if (appData.debugMode) {\r
596     debugFP = fopen(appData.nameOfDebugFile, "w");\r
597     setbuf(debugFP, NULL);\r
598   }\r
599 \r
600   InitBackEnd1();\r
601 \r
602 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
603 //  InitEngineUCI( installDir, &second );\r
604 \r
605   /* Create a main window for this application instance. */\r
606   hwnd = CreateWindow(szAppName, szTitle,\r
607                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
608                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
609                       NULL, NULL, hInstance, NULL);\r
610   hwndMain = hwnd;\r
611 \r
612   /* If window could not be created, return "failure" */\r
613   if (!hwnd) {\r
614     return (FALSE);\r
615   }\r
616 \r
617   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
618   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
619       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
620 \r
621       if (first.programLogo == NULL && appData.debugMode) {\r
622           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
623       }\r
624   } else if(appData.autoLogo) {\r
625       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
626         char buf[MSG_SIZ];\r
627         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
628         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
629       }\r
630   }\r
631 \r
632   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
633       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
634 \r
635       if (second.programLogo == NULL && appData.debugMode) {\r
636           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
637       }\r
638   } else if(appData.autoLogo) {\r
639       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
640         char buf[MSG_SIZ];\r
641         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
642         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
643       }\r
644   }\r
645 \r
646   iconWhite = LoadIcon(hInstance, "icon_white");\r
647   iconBlack = LoadIcon(hInstance, "icon_black");\r
648   iconCurrent = iconWhite;\r
649   InitDrawingColors();\r
650   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
651   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
652   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
653     /* Compute window size for each board size, and use the largest\r
654        size that fits on this screen as the default. */\r
655     InitDrawingSizes((BoardSize)ibs, 0);\r
656     if (boardSize == (BoardSize)-1 &&\r
657         winHeight <= screenHeight\r
658            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
659         && winWidth <= screenWidth) {\r
660       boardSize = (BoardSize)ibs;\r
661     }\r
662   }\r
663 \r
664   InitDrawingSizes(boardSize, 0);\r
665   InitMenuChecks();\r
666   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
667 \r
668   /* [AS] Load textures if specified */\r
669   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
670   \r
671   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
672       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
673       liteBackTextureMode = appData.liteBackTextureMode;\r
674 \r
675       if (liteBackTexture == NULL && appData.debugMode) {\r
676           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
677       }\r
678   }\r
679   \r
680   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
681       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
682       darkBackTextureMode = appData.darkBackTextureMode;\r
683 \r
684       if (darkBackTexture == NULL && appData.debugMode) {\r
685           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
686       }\r
687   }\r
688 \r
689   mysrandom( (unsigned) time(NULL) );\r
690 \r
691   /* [AS] Restore layout */\r
692   if( wpMoveHistory.visible ) {\r
693       MoveHistoryPopUp();\r
694   }\r
695 \r
696   if( wpEvalGraph.visible ) {\r
697       EvalGraphPopUp();\r
698   }\r
699 \r
700   if( wpEngineOutput.visible ) {\r
701       EngineOutputPopUp();\r
702   }\r
703 \r
704   InitBackEnd2();\r
705 \r
706   /* Make the window visible; update its client area; and return "success" */\r
707   EnsureOnScreen(&boardX, &boardY);\r
708   wp.length = sizeof(WINDOWPLACEMENT);\r
709   wp.flags = 0;\r
710   wp.showCmd = nCmdShow;\r
711   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
712   wp.rcNormalPosition.left = boardX;\r
713   wp.rcNormalPosition.right = boardX + winWidth;\r
714   wp.rcNormalPosition.top = boardY;\r
715   wp.rcNormalPosition.bottom = boardY + winHeight;\r
716   SetWindowPlacement(hwndMain, &wp);\r
717 \r
718   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
719                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
720 \r
721 #if 0\r
722   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
723   if( gameInfo.variant != VariantFischeRandom ) {\r
724       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
725   }\r
726 #endif\r
727   if (hwndConsole) {\r
728 #if AOT_CONSOLE\r
729     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
730                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
731 #endif\r
732     ShowWindow(hwndConsole, nCmdShow);\r
733   }\r
734   UpdateWindow(hwnd);\r
735 \r
736   return TRUE;\r
737 \r
738 }\r
739 \r
740 \r
741 typedef enum {\r
742   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
743   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
744   ArgSettingsFilename\r
745 } ArgType;\r
746 \r
747 typedef struct {\r
748   char *argName;\r
749   ArgType argType;\r
750   /***\r
751   union {\r
752     String *pString;       // ArgString\r
753     int *pInt;             // ArgInt\r
754     float *pFloat;         // ArgFloat\r
755     Boolean *pBoolean;     // ArgBoolean\r
756     COLORREF *pColor;      // ArgColor\r
757     ColorClass cc;         // ArgAttribs\r
758     String *pFilename;     // ArgFilename\r
759     BoardSize *pBoardSize; // ArgBoardSize\r
760     int whichFont;         // ArgFont\r
761     DCB *pDCB;             // ArgCommSettings\r
762     String *pFilename;     // ArgSettingsFilename\r
763   } argLoc;\r
764   ***/\r
765   LPVOID argLoc;\r
766   BOOL save;\r
767 } ArgDescriptor;\r
768 \r
769 int junk;\r
770 ArgDescriptor argDescriptors[] = {\r
771   /* positional arguments */\r
772   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
773   { "", ArgNone, NULL },\r
774   /* keyword arguments */\r
775   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
776   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
777   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
778   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
779   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
780   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
781   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
782   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
783   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
784   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
785   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
786   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
787   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
788   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
789   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
790   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
791   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
792   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
793     FALSE },\r
794   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
795     FALSE },\r
796   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
797     FALSE },\r
798   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
799   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
800     FALSE },\r
801   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
802   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
803   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
804   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
805   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
806   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
807   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
808   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
809   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
810   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
811   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
812   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
813   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
814   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
815   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
816   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
817   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
818   /*!!bitmapDirectory?*/\r
819   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
820   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
821   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
822   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
823   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
824   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
825   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
826   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
827   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
828   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
829   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
830   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
831   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
832   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
833   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
834   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
835   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
836   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
837   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
838   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
839   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
840   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
841   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
842   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
843   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
844   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
845   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
846   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
847   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
848   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
849   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
850   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
851   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
852   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
853   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
854   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
855   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
856   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
857   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
858   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
859   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
860   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
861   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
862   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
863   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
864   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
865   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
866   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
867   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
868   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
869   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
870   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
871   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
872   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
873   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
874   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
875   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
876   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
877   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
878   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
879   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
880   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
881   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
882   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
883   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
884   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
885   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
886   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
887   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
888   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
889   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
890   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
891   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
892   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
893   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
894   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
895   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
896   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
897   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
898   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
899   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
900   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
901   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
902   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
903   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
904   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
905   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
906   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
907   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
908   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
909   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
910   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
911   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
912   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
913     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
914   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
915   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
916   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
917   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
918   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
919   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
920   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
921   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
922     TRUE }, /* must come after all fonts */\r
923   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
924   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
925     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
926   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
927   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
928   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
929   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
930   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
931   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
932   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
933   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
934   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
935   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
936   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
937   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
938   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
939   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
940   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
941   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
942   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
943   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
944   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
945   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
946   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
947   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
948   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
949   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
950   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
951   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
952   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
953   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
954 #if 0\r
955   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
956   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
957 #endif\r
958   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
959   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
960   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
961   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
962   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
963   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
964   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
965   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
966   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
967   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
968   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
969   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
970   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
971   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
972   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
973   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
974   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
975   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
976   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
977   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
978   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
979   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
980   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
981   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
982   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
983   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
984   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
985   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
986   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
987   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
988   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
989   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
990   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
991   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
992   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
993   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
994   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
995   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
996   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
997   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
998   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
999   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1000   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1001   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1002   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1003   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1004   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1005   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1006   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1007   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1008   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1009   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1010   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1011   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1012   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1013   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1014   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1015   { "highlightLastMove", ArgBoolean,\r
1016     (LPVOID) &appData.highlightLastMove, TRUE },\r
1017   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1018   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1019   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1020   { "highlightDragging", ArgBoolean,\r
1021     (LPVOID) &appData.highlightDragging, TRUE },\r
1022   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1023   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1024   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1025   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1026   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1027   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1028   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1029   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1030   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1031   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1032   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1033   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1034   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1035   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1036   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1037   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1038   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1039   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1040   { "soundShout", ArgFilename,\r
1041     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1042   { "soundSShout", ArgFilename,\r
1043     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1044   { "soundChannel1", ArgFilename,\r
1045     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1046   { "soundChannel", ArgFilename,\r
1047     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1048   { "soundKibitz", ArgFilename,\r
1049     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1050   { "soundTell", ArgFilename,\r
1051     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1052   { "soundChallenge", ArgFilename,\r
1053     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1054   { "soundRequest", ArgFilename,\r
1055     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1056   { "soundSeek", ArgFilename,\r
1057     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1058   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1059   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1060   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1061   { "soundIcsLoss", ArgFilename, \r
1062     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1063   { "soundIcsDraw", ArgFilename, \r
1064     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1065   { "soundIcsUnfinished", ArgFilename, \r
1066     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1067   { "soundIcsAlarm", ArgFilename, \r
1068     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1069   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1070   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1071   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1072   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1073   { "reuseChessPrograms", ArgBoolean,\r
1074     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1075   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1076   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1077   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1078   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1079   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1080   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1081   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1082   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1083   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1084   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1085   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1086   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1087   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1088   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1089   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1090   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1091   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1092   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1093   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1094   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1095   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1096   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1097   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1098   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1099   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1100   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1101   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1102   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1103   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1104   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1105   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1106   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1107   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1108   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1109   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1110   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1111   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1112     TRUE },\r
1113   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1114     TRUE },\r
1115   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1116   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1117   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1118   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1119   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1120   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1121   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1122   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1123   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1124   /* [AS] New features */\r
1125   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1126   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1127   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1128   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1129   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1130   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1131   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1132   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1133   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1134   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1135   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1136   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1137   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1138   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1139   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1140   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1141   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1142   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1143   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1144   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1145   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1146   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1147   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1148   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1149   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1150   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1151   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1152   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1153   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1154   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1155   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1156   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1157   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1158   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1159   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1160   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1161   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1162   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1163   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1164   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1165   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1166   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1167   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1168   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1169   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1170   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1171   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1172   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1173   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1174   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1175 \r
1176   /* [AS] Layout stuff */\r
1177   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1178   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1179   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1180   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1181   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1182 \r
1183   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1184   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1185   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1186   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1187   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1188 \r
1189   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1190   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1191   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1192   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1193   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1194 \r
1195   /* [HGM] board-size, adjudication and misc. options */\r
1196   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1197   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1198   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1199   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1200   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1201   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1202   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1203   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1204   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1205   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1206   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1207   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1208   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1209   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1210   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1211   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1212   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1213   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1214   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1215   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1216   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1217   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1218   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1219   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1220   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1221   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1222   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1223   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1224   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1225 \r
1226 #ifdef ZIPPY\r
1227   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1228   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1229   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1230   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1231   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1232   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1233   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1234   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1235   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1236   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1237   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1238   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1239   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1240     FALSE },\r
1241   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1242   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1243   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1244   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1245   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1246   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1247   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1248     FALSE },\r
1249   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1250   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1251   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1252   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1253   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1254   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1255   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1256   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1257   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1258   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1259   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1260   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1261   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1262   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1263   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1264   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1265   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1266   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1267 #endif\r
1268   /* [HGM] options for broadcasting and time odds */\r
1269   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1270   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1271   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1272   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1273   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1274   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1275   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1276   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1277   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1278   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1279   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1280   { NULL, ArgNone, NULL, FALSE }\r
1281 };\r
1282 \r
1283 \r
1284 /* Kludge for indirection files on command line */\r
1285 char* lastIndirectionFilename;\r
1286 ArgDescriptor argDescriptorIndirection =\r
1287 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1288 \r
1289 \r
1290 VOID\r
1291 ExitArgError(char *msg, char *badArg)\r
1292 {\r
1293   char buf[MSG_SIZ];\r
1294 \r
1295   sprintf(buf, "%s %s", msg, badArg);\r
1296   DisplayFatalError(buf, 0, 2);\r
1297   exit(2);\r
1298 }\r
1299 \r
1300 /* Command line font name parser.  NULL name means do nothing.\r
1301    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1302    For backward compatibility, syntax without the colon is also\r
1303    accepted, but font names with digits in them won't work in that case.\r
1304 */\r
1305 VOID\r
1306 ParseFontName(char *name, MyFontParams *mfp)\r
1307 {\r
1308   char *p, *q;\r
1309   if (name == NULL) return;\r
1310   p = name;\r
1311   q = strchr(p, ':');\r
1312   if (q) {\r
1313     if (q - p >= sizeof(mfp->faceName))\r
1314       ExitArgError("Font name too long:", name);\r
1315     memcpy(mfp->faceName, p, q - p);\r
1316     mfp->faceName[q - p] = NULLCHAR;\r
1317     p = q + 1;\r
1318   } else {\r
1319     q = mfp->faceName;\r
1320     while (*p && !isdigit(*p)) {\r
1321       *q++ = *p++;\r
1322       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1323         ExitArgError("Font name too long:", name);\r
1324     }\r
1325     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1326     *q = NULLCHAR;\r
1327   }\r
1328   if (!*p) ExitArgError("Font point size missing:", name);\r
1329   mfp->pointSize = (float) atof(p);\r
1330   mfp->bold = (strchr(p, 'b') != NULL);\r
1331   mfp->italic = (strchr(p, 'i') != NULL);\r
1332   mfp->underline = (strchr(p, 'u') != NULL);\r
1333   mfp->strikeout = (strchr(p, 's') != NULL);\r
1334 }\r
1335 \r
1336 /* Color name parser.\r
1337    X version accepts X color names, but this one\r
1338    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1339 COLORREF\r
1340 ParseColorName(char *name)\r
1341 {\r
1342   int red, green, blue, count;\r
1343   char buf[MSG_SIZ];\r
1344 \r
1345   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1346   if (count != 3) {\r
1347     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1348       &red, &green, &blue);\r
1349   }\r
1350   if (count != 3) {\r
1351     sprintf(buf, "Can't parse color name %s", name);\r
1352     DisplayError(buf, 0);\r
1353     return RGB(0, 0, 0);\r
1354   }\r
1355   return PALETTERGB(red, green, blue);\r
1356 }\r
1357 \r
1358 \r
1359 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1360 {\r
1361   char *e = argValue;\r
1362   int eff = 0;\r
1363 \r
1364   while (*e) {\r
1365     if (*e == 'b')      eff |= CFE_BOLD;\r
1366     else if (*e == 'i') eff |= CFE_ITALIC;\r
1367     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1368     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1369     else if (*e == '#' || isdigit(*e)) break;\r
1370     e++;\r
1371   }\r
1372   *effects = eff;\r
1373   *color   = ParseColorName(e);\r
1374 }\r
1375 \r
1376 \r
1377 BoardSize\r
1378 ParseBoardSize(char *name)\r
1379 {\r
1380   BoardSize bs = SizeTiny;\r
1381   while (sizeInfo[bs].name != NULL) {\r
1382     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1383     bs++;\r
1384   }\r
1385   ExitArgError("Unrecognized board size value", name);\r
1386   return bs; /* not reached */\r
1387 }\r
1388 \r
1389 \r
1390 char\r
1391 StringGet(void *getClosure)\r
1392 {\r
1393   char **p = (char **) getClosure;\r
1394   return *((*p)++);\r
1395 }\r
1396 \r
1397 char\r
1398 FileGet(void *getClosure)\r
1399 {\r
1400   int c;\r
1401   FILE* f = (FILE*) getClosure;\r
1402 \r
1403   c = getc(f);\r
1404   if (c == EOF)\r
1405     return NULLCHAR;\r
1406   else\r
1407     return (char) c;\r
1408 }\r
1409 \r
1410 /* Parse settings file named "name". If file found, return the\r
1411    full name in fullname and return TRUE; else return FALSE */\r
1412 BOOLEAN\r
1413 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1414 {\r
1415   char *dummy;\r
1416   FILE *f;\r
1417 \r
1418   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1419     f = fopen(fullname, "r");\r
1420     if (f != NULL) {\r
1421       ParseArgs(FileGet, f);\r
1422       fclose(f);\r
1423       return TRUE;\r
1424     }\r
1425   }\r
1426   return FALSE;\r
1427 }\r
1428 \r
1429 VOID\r
1430 ParseArgs(GetFunc get, void *cl)\r
1431 {\r
1432   char argName[ARG_MAX];\r
1433   char argValue[ARG_MAX];\r
1434   ArgDescriptor *ad;\r
1435   char start;\r
1436   char *q;\r
1437   int i, octval;\r
1438   char ch;\r
1439   int posarg = 0;\r
1440 \r
1441   ch = get(cl);\r
1442   for (;;) {\r
1443     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1444     if (ch == NULLCHAR) break;\r
1445     if (ch == ';') {\r
1446       /* Comment to end of line */\r
1447       ch = get(cl);\r
1448       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1449       continue;\r
1450     } else if (ch == '/' || ch == '-') {\r
1451       /* Switch */\r
1452       q = argName;\r
1453       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1454              ch != '\n' && ch != '\t') {\r
1455         *q++ = ch;\r
1456         ch = get(cl);\r
1457       }\r
1458       *q = NULLCHAR;\r
1459 \r
1460       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1461         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1462 \r
1463       if (ad->argName == NULL)\r
1464         ExitArgError("Unrecognized argument", argName);\r
1465 \r
1466     } else if (ch == '@') {\r
1467       /* Indirection file */\r
1468       ad = &argDescriptorIndirection;\r
1469       ch = get(cl);\r
1470     } else {\r
1471       /* Positional argument */\r
1472       ad = &argDescriptors[posarg++];\r
1473       strcpy(argName, ad->argName);\r
1474     }\r
1475 \r
1476     if (ad->argType == ArgTrue) {\r
1477       *(Boolean *) ad->argLoc = TRUE;\r
1478       continue;\r
1479     }\r
1480     if (ad->argType == ArgFalse) {\r
1481       *(Boolean *) ad->argLoc = FALSE;\r
1482       continue;\r
1483     }\r
1484 \r
1485     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1486     if (ch == NULLCHAR || ch == '\n') {\r
1487       ExitArgError("No value provided for argument", argName);\r
1488     }\r
1489     q = argValue;\r
1490     if (ch == '{') {\r
1491       // Quoting with { }.  No characters have to (or can) be escaped.\r
1492       // Thus the string cannot contain a '}' character.\r
1493       start = ch;\r
1494       ch = get(cl);\r
1495       while (start) {\r
1496         switch (ch) {\r
1497         case NULLCHAR:\r
1498           start = NULLCHAR;\r
1499           break;\r
1500           \r
1501         case '}':\r
1502           ch = get(cl);\r
1503           start = NULLCHAR;\r
1504           break;\r
1505 \r
1506         default:\r
1507           *q++ = ch;\r
1508           ch = get(cl);\r
1509           break;\r
1510         }\r
1511       }   \r
1512     } else if (ch == '\'' || ch == '"') {\r
1513       // Quoting with ' ' or " ", with \ as escape character.\r
1514       // Inconvenient for long strings that may contain Windows filenames.\r
1515       start = ch;\r
1516       ch = get(cl);\r
1517       while (start) {\r
1518         switch (ch) {\r
1519         case NULLCHAR:\r
1520           start = NULLCHAR;\r
1521           break;\r
1522 \r
1523         default:\r
1524         not_special:\r
1525           *q++ = ch;\r
1526           ch = get(cl);\r
1527           break;\r
1528 \r
1529         case '\'':\r
1530         case '\"':\r
1531           if (ch == start) {\r
1532             ch = get(cl);\r
1533             start = NULLCHAR;\r
1534             break;\r
1535           } else {\r
1536             goto not_special;\r
1537           }\r
1538 \r
1539         case '\\':\r
1540           if (ad->argType == ArgFilename\r
1541               || ad->argType == ArgSettingsFilename) {\r
1542               goto not_special;\r
1543           }\r
1544           ch = get(cl);\r
1545           switch (ch) {\r
1546           case NULLCHAR:\r
1547             ExitArgError("Incomplete \\ escape in value for", argName);\r
1548             break;\r
1549           case 'n':\r
1550             *q++ = '\n';\r
1551             ch = get(cl);\r
1552             break;\r
1553           case 'r':\r
1554             *q++ = '\r';\r
1555             ch = get(cl);\r
1556             break;\r
1557           case 't':\r
1558             *q++ = '\t';\r
1559             ch = get(cl);\r
1560             break;\r
1561           case 'b':\r
1562             *q++ = '\b';\r
1563             ch = get(cl);\r
1564             break;\r
1565           case 'f':\r
1566             *q++ = '\f';\r
1567             ch = get(cl);\r
1568             break;\r
1569           default:\r
1570             octval = 0;\r
1571             for (i = 0; i < 3; i++) {\r
1572               if (ch >= '0' && ch <= '7') {\r
1573                 octval = octval*8 + (ch - '0');\r
1574                 ch = get(cl);\r
1575               } else {\r
1576                 break;\r
1577               }\r
1578             }\r
1579             if (i > 0) {\r
1580               *q++ = (char) octval;\r
1581             } else {\r
1582               *q++ = ch;\r
1583               ch = get(cl);\r
1584             }\r
1585             break;\r
1586           }\r
1587           break;\r
1588         }\r
1589       }\r
1590     } else {\r
1591       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1592         *q++ = ch;\r
1593         ch = get(cl);\r
1594       }\r
1595     }\r
1596     *q = NULLCHAR;\r
1597 \r
1598     switch (ad->argType) {\r
1599     case ArgInt:\r
1600       *(int *) ad->argLoc = atoi(argValue);\r
1601       break;\r
1602 \r
1603     case ArgFloat:\r
1604       *(float *) ad->argLoc = (float) atof(argValue);\r
1605       break;\r
1606 \r
1607     case ArgString:\r
1608     case ArgFilename:\r
1609       *(char **) ad->argLoc = strdup(argValue);\r
1610       break;\r
1611 \r
1612     case ArgSettingsFilename:\r
1613       {\r
1614         char fullname[MSG_SIZ];\r
1615         if (ParseSettingsFile(argValue, fullname)) {\r
1616           if (ad->argLoc != NULL) {\r
1617             *(char **) ad->argLoc = strdup(fullname);\r
1618           }\r
1619         } else {\r
1620           if (ad->argLoc != NULL) {\r
1621           } else {\r
1622             ExitArgError("Failed to open indirection file", argValue);\r
1623           }\r
1624         }\r
1625       }\r
1626       break;\r
1627 \r
1628     case ArgBoolean:\r
1629       switch (argValue[0]) {\r
1630       case 't':\r
1631       case 'T':\r
1632         *(Boolean *) ad->argLoc = TRUE;\r
1633         break;\r
1634       case 'f':\r
1635       case 'F':\r
1636         *(Boolean *) ad->argLoc = FALSE;\r
1637         break;\r
1638       default:\r
1639         ExitArgError("Unrecognized boolean argument value", argValue);\r
1640         break;\r
1641       }\r
1642       break;\r
1643 \r
1644     case ArgColor:\r
1645       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1646       break;\r
1647 \r
1648     case ArgAttribs: {\r
1649       ColorClass cc = (ColorClass)ad->argLoc;\r
1650       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1651       }\r
1652       break;\r
1653       \r
1654     case ArgBoardSize:\r
1655       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1656       break;\r
1657 \r
1658     case ArgFont:\r
1659       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1660       break;\r
1661 \r
1662     case ArgCommSettings:\r
1663       ParseCommSettings(argValue, &dcb);\r
1664       break;\r
1665 \r
1666     case ArgNone:\r
1667       ExitArgError("Unrecognized argument", argValue);\r
1668       break;\r
1669     }\r
1670   }\r
1671 }\r
1672 \r
1673 VOID\r
1674 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1675 {\r
1676   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1677   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1678   DeleteDC(hdc);\r
1679   lf->lfWidth = 0;\r
1680   lf->lfEscapement = 0;\r
1681   lf->lfOrientation = 0;\r
1682   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1683   lf->lfItalic = mfp->italic;\r
1684   lf->lfUnderline = mfp->underline;\r
1685   lf->lfStrikeOut = mfp->strikeout;\r
1686   lf->lfCharSet = DEFAULT_CHARSET;\r
1687   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1688   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1689   lf->lfQuality = DEFAULT_QUALITY;\r
1690   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1691   strcpy(lf->lfFaceName, mfp->faceName);\r
1692 }\r
1693 \r
1694 VOID\r
1695 CreateFontInMF(MyFont *mf)\r
1696 {\r
1697   LFfromMFP(&mf->lf, &mf->mfp);\r
1698   if (mf->hf) DeleteObject(mf->hf);\r
1699   mf->hf = CreateFontIndirect(&mf->lf);\r
1700 }\r
1701 \r
1702 VOID\r
1703 SetDefaultTextAttribs()\r
1704 {\r
1705   ColorClass cc;\r
1706   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1707     ParseAttribs(&textAttribs[cc].color, \r
1708                  &textAttribs[cc].effects, \r
1709                  defaultTextAttribs[cc]);\r
1710   }\r
1711 }\r
1712 \r
1713 VOID\r
1714 SetDefaultSounds()\r
1715 {\r
1716   ColorClass cc;\r
1717   SoundClass sc;\r
1718   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1719     textAttribs[cc].sound.name = strdup("");\r
1720     textAttribs[cc].sound.data = NULL;\r
1721   }\r
1722   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1723     sounds[sc].name = strdup("");\r
1724     sounds[sc].data = NULL;\r
1725   }\r
1726   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1727 }\r
1728 \r
1729 VOID\r
1730 LoadAllSounds()\r
1731 {\r
1732   ColorClass cc;\r
1733   SoundClass sc;\r
1734   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1735     MyLoadSound(&textAttribs[cc].sound);\r
1736   }\r
1737   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1738     MyLoadSound(&sounds[sc]);\r
1739   }\r
1740 }\r
1741 \r
1742 VOID\r
1743 InitAppData(LPSTR lpCmdLine)\r
1744 {\r
1745   int i, j;\r
1746   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1747   char *dummy, *p;\r
1748 \r
1749   programName = szAppName;\r
1750 \r
1751   /* Initialize to defaults */\r
1752   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1753   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1754   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1755   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1756   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1757   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1758   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1759   SetDefaultTextAttribs();\r
1760   SetDefaultSounds();\r
1761   appData.movesPerSession = MOVES_PER_SESSION;\r
1762   appData.initString = INIT_STRING;\r
1763   appData.secondInitString = INIT_STRING;\r
1764   appData.firstComputerString = COMPUTER_STRING;\r
1765   appData.secondComputerString = COMPUTER_STRING;\r
1766   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1767   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1768   appData.firstPlaysBlack = FALSE;\r
1769   appData.noChessProgram = FALSE;\r
1770   chessProgram = FALSE;\r
1771   appData.firstHost = FIRST_HOST;\r
1772   appData.secondHost = SECOND_HOST;\r
1773   appData.firstDirectory = FIRST_DIRECTORY;\r
1774   appData.secondDirectory = SECOND_DIRECTORY;\r
1775   appData.bitmapDirectory = "";\r
1776   appData.remoteShell = REMOTE_SHELL;\r
1777   appData.remoteUser = "";\r
1778   appData.timeDelay = TIME_DELAY;\r
1779   appData.timeControl = TIME_CONTROL;\r
1780   appData.timeIncrement = TIME_INCREMENT;\r
1781   appData.icsActive = FALSE;\r
1782   appData.icsHost = "";\r
1783   appData.icsPort = ICS_PORT;\r
1784   appData.icsCommPort = ICS_COMM_PORT;\r
1785   appData.icsLogon = ICS_LOGON;\r
1786   appData.icsHelper = "";\r
1787   appData.useTelnet = FALSE;\r
1788   appData.telnetProgram = TELNET_PROGRAM;\r
1789   appData.gateway = "";\r
1790   appData.loadGameFile = "";\r
1791   appData.loadGameIndex = 0;\r
1792   appData.saveGameFile = "";\r
1793   appData.autoSaveGames = FALSE;\r
1794   appData.loadPositionFile = "";\r
1795   appData.loadPositionIndex = 1;\r
1796   appData.savePositionFile = "";\r
1797   appData.matchMode = FALSE;\r
1798   appData.matchGames = 0;\r
1799   appData.monoMode = FALSE;\r
1800   appData.debugMode = FALSE;\r
1801   appData.clockMode = TRUE;\r
1802   boardSize = (BoardSize) -1; /* determine by screen size */\r
1803   appData.Iconic = FALSE; /*unused*/\r
1804   appData.searchTime = "";\r
1805   appData.searchDepth = 0;\r
1806   appData.showCoords = FALSE;\r
1807   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1808   appData.autoCallFlag = FALSE;\r
1809   appData.flipView = FALSE;\r
1810   appData.autoFlipView = TRUE;\r
1811   appData.cmailGameName = "";\r
1812   appData.alwaysPromoteToQueen = FALSE;\r
1813   appData.oldSaveStyle = FALSE;\r
1814   appData.quietPlay = FALSE;\r
1815   appData.showThinking = FALSE;\r
1816   appData.ponderNextMove = TRUE;\r
1817   appData.periodicUpdates = TRUE;\r
1818   appData.popupExitMessage = TRUE;\r
1819   appData.popupMoveErrors = FALSE;\r
1820   appData.autoObserve = FALSE;\r
1821   appData.autoComment = FALSE;\r
1822   appData.animate = TRUE;\r
1823   appData.animSpeed = 10;\r
1824   appData.animateDragging = TRUE;\r
1825   appData.highlightLastMove = TRUE;\r
1826   appData.getMoveList = TRUE;\r
1827   appData.testLegality = TRUE;\r
1828   appData.premove = TRUE;\r
1829   appData.premoveWhite = FALSE;\r
1830   appData.premoveWhiteText = "";\r
1831   appData.premoveBlack = FALSE;\r
1832   appData.premoveBlackText = "";\r
1833   appData.icsAlarm = TRUE;\r
1834   appData.icsAlarmTime = 5000;\r
1835   appData.autoRaiseBoard = TRUE;\r
1836   appData.localLineEditing = TRUE;\r
1837   appData.colorize = TRUE;\r
1838   appData.reuseFirst = TRUE;\r
1839   appData.reuseSecond = TRUE;\r
1840   appData.blindfold = FALSE;\r
1841   appData.icsEngineAnalyze = FALSE;\r
1842   dcb.DCBlength = sizeof(DCB);\r
1843   dcb.BaudRate = 9600;\r
1844   dcb.fBinary = TRUE;\r
1845   dcb.fParity = FALSE;\r
1846   dcb.fOutxCtsFlow = FALSE;\r
1847   dcb.fOutxDsrFlow = FALSE;\r
1848   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1849   dcb.fDsrSensitivity = FALSE;\r
1850   dcb.fTXContinueOnXoff = TRUE;\r
1851   dcb.fOutX = FALSE;\r
1852   dcb.fInX = FALSE;\r
1853   dcb.fNull = FALSE;\r
1854   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1855   dcb.fAbortOnError = FALSE;\r
1856   /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */\r
1857   #if (defined(_MSC_VER) && _MSC_VER <= 1200) \r
1858         //dcb.wReserved = 0;\r
1859   #else\r
1860     dcb.wReserved = 0;\r
1861   #endif\r
1862   dcb.ByteSize = 7;\r
1863   dcb.Parity = SPACEPARITY;\r
1864   dcb.StopBits = ONESTOPBIT;\r
1865   settingsFileName = SETTINGS_FILE;\r
1866   saveSettingsOnExit = TRUE;\r
1867   boardX = CW_USEDEFAULT;\r
1868   boardY = CW_USEDEFAULT;\r
1869   consoleX = CW_USEDEFAULT; \r
1870   consoleY = CW_USEDEFAULT; \r
1871   consoleW = CW_USEDEFAULT;\r
1872   consoleH = CW_USEDEFAULT;\r
1873   analysisX = CW_USEDEFAULT; \r
1874   analysisY = CW_USEDEFAULT; \r
1875   analysisW = CW_USEDEFAULT;\r
1876   analysisH = CW_USEDEFAULT;\r
1877   commentX = CW_USEDEFAULT; \r
1878   commentY = CW_USEDEFAULT; \r
1879   commentW = CW_USEDEFAULT;\r
1880   commentH = CW_USEDEFAULT;\r
1881   editTagsX = CW_USEDEFAULT; \r
1882   editTagsY = CW_USEDEFAULT; \r
1883   editTagsW = CW_USEDEFAULT;\r
1884   editTagsH = CW_USEDEFAULT;\r
1885   gameListX = CW_USEDEFAULT; \r
1886   gameListY = CW_USEDEFAULT; \r
1887   gameListW = CW_USEDEFAULT;\r
1888   gameListH = CW_USEDEFAULT;\r
1889   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1890   icsNames = ICS_NAMES;\r
1891   firstChessProgramNames = FCP_NAMES;\r
1892   secondChessProgramNames = SCP_NAMES;\r
1893   appData.initialMode = "";\r
1894   appData.variant = "normal";\r
1895   appData.firstProtocolVersion = PROTOVER;\r
1896   appData.secondProtocolVersion = PROTOVER;\r
1897   appData.showButtonBar = TRUE;\r
1898 \r
1899    /* [AS] New properties (see comments in header file) */\r
1900   appData.firstScoreIsAbsolute = FALSE;\r
1901   appData.secondScoreIsAbsolute = FALSE;\r
1902   appData.saveExtendedInfoInPGN = FALSE;\r
1903   appData.hideThinkingFromHuman = FALSE;\r
1904   appData.liteBackTextureFile = "";\r
1905   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1906   appData.darkBackTextureFile = "";\r
1907   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1908   appData.renderPiecesWithFont = "";\r
1909   appData.fontToPieceTable = "";\r
1910   appData.fontBackColorWhite = 0;\r
1911   appData.fontForeColorWhite = 0;\r
1912   appData.fontBackColorBlack = 0;\r
1913   appData.fontForeColorBlack = 0;\r
1914   appData.fontPieceSize = 80;\r
1915   appData.overrideLineGap = 1;\r
1916   appData.adjudicateLossThreshold = 0;\r
1917   appData.delayBeforeQuit = 0;\r
1918   appData.delayAfterQuit = 0;\r
1919   appData.nameOfDebugFile = "winboard.debug";\r
1920   appData.pgnEventHeader = "Computer Chess Game";\r
1921   appData.defaultFrcPosition = -1;\r
1922   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1923   appData.saveOutOfBookInfo = TRUE;\r
1924   appData.showEvalInMoveHistory = TRUE;\r
1925   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1926   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1927   appData.highlightMoveWithArrow = FALSE;\r
1928   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1929   appData.useStickyWindows = TRUE;\r
1930   appData.adjudicateDrawMoves = 0;\r
1931   appData.autoDisplayComment = TRUE;\r
1932   appData.autoDisplayTags = TRUE;\r
1933   appData.firstIsUCI = FALSE;\r
1934   appData.secondIsUCI = FALSE;\r
1935   appData.firstHasOwnBookUCI = TRUE;\r
1936   appData.secondHasOwnBookUCI = TRUE;\r
1937   appData.polyglotDir = "";\r
1938   appData.usePolyglotBook = FALSE;\r
1939   appData.polyglotBook = "";\r
1940   appData.defaultHashSize = 64;\r
1941   appData.defaultCacheSizeEGTB = 4;\r
1942   appData.defaultPathEGTB = "c:\\egtb";\r
1943   appData.firstOptions = "";\r
1944   appData.secondOptions = "";\r
1945 \r
1946   InitWindowPlacement( &wpMoveHistory );\r
1947   InitWindowPlacement( &wpEvalGraph );\r
1948   InitWindowPlacement( &wpEngineOutput );\r
1949 \r
1950   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1951   appData.NrFiles      = -1;\r
1952   appData.NrRanks      = -1;\r
1953   appData.holdingsSize = -1;\r
1954   appData.testClaims   = FALSE;\r
1955   appData.checkMates   = FALSE;\r
1956   appData.materialDraws= FALSE;\r
1957   appData.trivialDraws = FALSE;\r
1958   appData.ruleMoves    = 51;\r
1959   appData.drawRepeats  = 6;\r
1960   appData.matchPause   = 10000;\r
1961   appData.alphaRank    = FALSE;\r
1962   appData.allWhite     = FALSE;\r
1963   appData.upsideDown   = FALSE;\r
1964   appData.serverPause  = 15;\r
1965   appData.serverMovesName   = NULL;\r
1966   appData.suppressLoadMoves = FALSE;\r
1967   appData.firstTimeOdds  = 1;\r
1968   appData.secondTimeOdds = 1;\r
1969   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1970   appData.secondAccumulateTC = 1;\r
1971   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1972   appData.secondNPS = -1;\r
1973   appData.engineComments = 1;\r
1974   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1975   appData.egtFormats = "";\r
1976 \r
1977 #ifdef ZIPPY\r
1978   appData.zippyTalk = ZIPPY_TALK;\r
1979   appData.zippyPlay = ZIPPY_PLAY;\r
1980   appData.zippyLines = ZIPPY_LINES;\r
1981   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1982   appData.zippyPassword = ZIPPY_PASSWORD;\r
1983   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1984   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1985   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1986   appData.zippyUseI = ZIPPY_USE_I;\r
1987   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1988   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1989   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1990   appData.zippyGameStart = ZIPPY_GAME_START;\r
1991   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1992   appData.zippyAbort = ZIPPY_ABORT;\r
1993   appData.zippyVariants = ZIPPY_VARIANTS;\r
1994   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1995   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1996 #endif\r
1997 \r
1998   /* Point font array elements to structures and\r
1999      parse default font names */\r
2000   for (i=0; i<NUM_FONTS; i++) {\r
2001     for (j=0; j<NUM_SIZES; j++) {\r
2002       font[j][i] = &fontRec[j][i];\r
2003       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2004     }\r
2005   }\r
2006   \r
2007   /* Parse default settings file if any */\r
2008   if (ParseSettingsFile(settingsFileName, buf)) {\r
2009     settingsFileName = strdup(buf);\r
2010   }\r
2011 \r
2012   /* Parse command line */\r
2013   ParseArgs(StringGet, &lpCmdLine);\r
2014 \r
2015   /* [HGM] make sure board size is acceptable */\r
2016   if(appData.NrFiles > BOARD_SIZE ||\r
2017      appData.NrRanks > BOARD_SIZE   )\r
2018       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2019 \r
2020   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2021    * with options from the command line, we now make an even higher priority\r
2022    * overrule by WB options attached to the engine command line. This so that\r
2023    * tournament managers can use WB options (such as /timeOdds) that follow\r
2024    * the engines.\r
2025    */\r
2026   if(appData.firstChessProgram != NULL) {\r
2027       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2028       static char *f = "first";\r
2029       char buf[MSG_SIZ], *q = buf;\r
2030       if(p != NULL) { // engine command line contains WinBoard options\r
2031           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2032           ParseArgs(StringGet, &q);\r
2033           p[-1] = 0; // cut them offengine command line\r
2034       }\r
2035   }\r
2036   // now do same for second chess program\r
2037   if(appData.secondChessProgram != NULL) {\r
2038       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2039       static char *s = "second";\r
2040       char buf[MSG_SIZ], *q = buf;\r
2041       if(p != NULL) { // engine command line contains WinBoard options\r
2042           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2043           ParseArgs(StringGet, &q);\r
2044           p[-1] = 0; // cut them offengine command line\r
2045       }\r
2046   }\r
2047 \r
2048 \r
2049   /* Propagate options that affect others */\r
2050   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2051   if (appData.icsActive || appData.noChessProgram) {\r
2052      chessProgram = FALSE;  /* not local chess program mode */\r
2053   }\r
2054 \r
2055   /* Open startup dialog if needed */\r
2056   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2057       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2058       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2059                         *appData.secondChessProgram == NULLCHAR))) {\r
2060     FARPROC lpProc;\r
2061     \r
2062     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2063     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2064     FreeProcInstance(lpProc);\r
2065   }\r
2066 \r
2067   /* Make sure save files land in the right (?) directory */\r
2068   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2069     appData.saveGameFile = strdup(buf);\r
2070   }\r
2071   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2072     appData.savePositionFile = strdup(buf);\r
2073   }\r
2074 \r
2075   /* Finish initialization for fonts and sounds */\r
2076   for (i=0; i<NUM_FONTS; i++) {\r
2077     for (j=0; j<NUM_SIZES; j++) {\r
2078       CreateFontInMF(font[j][i]);\r
2079     }\r
2080   }\r
2081   /* xboard, and older WinBoards, controlled the move sound with the\r
2082      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2083      always turn the option on (so that the backend will call us),\r
2084      then let the user turn the sound off by setting it to silence if\r
2085      desired.  To accommodate old winboard.ini files saved by old\r
2086      versions of WinBoard, we also turn off the sound if the option\r
2087      was initially set to false. */\r
2088   if (!appData.ringBellAfterMoves) {\r
2089     sounds[(int)SoundMove].name = strdup("");\r
2090     appData.ringBellAfterMoves = TRUE;\r
2091   }\r
2092   GetCurrentDirectory(MSG_SIZ, currDir);\r
2093   SetCurrentDirectory(installDir);\r
2094   LoadAllSounds();\r
2095   SetCurrentDirectory(currDir);\r
2096 \r
2097   p = icsTextMenuString;\r
2098   if (p[0] == '@') {\r
2099     FILE* f = fopen(p + 1, "r");\r
2100     if (f == NULL) {\r
2101       DisplayFatalError(p + 1, errno, 2);\r
2102       return;\r
2103     }\r
2104     i = fread(buf, 1, sizeof(buf)-1, f);\r
2105     fclose(f);\r
2106     buf[i] = NULLCHAR;\r
2107     p = buf;\r
2108   }\r
2109   ParseIcsTextMenu(strdup(p));\r
2110 }\r
2111 \r
2112 \r
2113 VOID\r
2114 InitMenuChecks()\r
2115 {\r
2116   HMENU hmenu = GetMenu(hwndMain);\r
2117 \r
2118   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2119                         MF_BYCOMMAND|((appData.icsActive &&\r
2120                                        *appData.icsCommPort != NULLCHAR) ?\r
2121                                       MF_ENABLED : MF_GRAYED));\r
2122   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2123                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2124                                      MF_CHECKED : MF_UNCHECKED));\r
2125 }\r
2126 \r
2127 \r
2128 VOID\r
2129 SaveSettings(char* name)\r
2130 {\r
2131   FILE *f;\r
2132   ArgDescriptor *ad;\r
2133   WINDOWPLACEMENT wp;\r
2134   char dir[MSG_SIZ];\r
2135 \r
2136   if (!hwndMain) return;\r
2137 \r
2138   GetCurrentDirectory(MSG_SIZ, dir);\r
2139   SetCurrentDirectory(installDir);\r
2140   f = fopen(name, "w");\r
2141   SetCurrentDirectory(dir);\r
2142   if (f == NULL) {\r
2143     DisplayError(name, errno);\r
2144     return;\r
2145   }\r
2146   fprintf(f, ";\n");\r
2147   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2148   fprintf(f, ";\n");\r
2149   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2150   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2151   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2152   fprintf(f, ";\n");\r
2153 \r
2154   wp.length = sizeof(WINDOWPLACEMENT);\r
2155   GetWindowPlacement(hwndMain, &wp);\r
2156   boardX = wp.rcNormalPosition.left;\r
2157   boardY = wp.rcNormalPosition.top;\r
2158 \r
2159   if (hwndConsole) {\r
2160     GetWindowPlacement(hwndConsole, &wp);\r
2161     consoleX = wp.rcNormalPosition.left;\r
2162     consoleY = wp.rcNormalPosition.top;\r
2163     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2164     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2165   }\r
2166 \r
2167   if (analysisDialog) {\r
2168     GetWindowPlacement(analysisDialog, &wp);\r
2169     analysisX = wp.rcNormalPosition.left;\r
2170     analysisY = wp.rcNormalPosition.top;\r
2171     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2172     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2173   }\r
2174 \r
2175   if (commentDialog) {\r
2176     GetWindowPlacement(commentDialog, &wp);\r
2177     commentX = wp.rcNormalPosition.left;\r
2178     commentY = wp.rcNormalPosition.top;\r
2179     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2180     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2181   }\r
2182 \r
2183   if (editTagsDialog) {\r
2184     GetWindowPlacement(editTagsDialog, &wp);\r
2185     editTagsX = wp.rcNormalPosition.left;\r
2186     editTagsY = wp.rcNormalPosition.top;\r
2187     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2188     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2189   }\r
2190 \r
2191   if (gameListDialog) {\r
2192     GetWindowPlacement(gameListDialog, &wp);\r
2193     gameListX = wp.rcNormalPosition.left;\r
2194     gameListY = wp.rcNormalPosition.top;\r
2195     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2196     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2197   }\r
2198 \r
2199   /* [AS] Move history */\r
2200   wpMoveHistory.visible = MoveHistoryIsUp();\r
2201   \r
2202   if( moveHistoryDialog ) {\r
2203     GetWindowPlacement(moveHistoryDialog, &wp);\r
2204     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2205     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2206     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2207     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2208   }\r
2209 \r
2210   /* [AS] Eval graph */\r
2211   wpEvalGraph.visible = EvalGraphIsUp();\r
2212 \r
2213   if( evalGraphDialog ) {\r
2214     GetWindowPlacement(evalGraphDialog, &wp);\r
2215     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2216     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2217     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2218     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2219   }\r
2220 \r
2221   /* [AS] Engine output */\r
2222   wpEngineOutput.visible = EngineOutputIsUp();\r
2223 \r
2224   if( engineOutputDialog ) {\r
2225     GetWindowPlacement(engineOutputDialog, &wp);\r
2226     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2227     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2228     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2229     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2230   }\r
2231 \r
2232   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2233     if (!ad->save) continue;\r
2234     switch (ad->argType) {\r
2235     case ArgString:\r
2236       {\r
2237         char *p = *(char **)ad->argLoc;\r
2238         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2239           /* Quote multiline values or \-containing values\r
2240              with { } if possible */\r
2241           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2242         } else {\r
2243           /* Else quote with " " */\r
2244           fprintf(f, "/%s=\"", ad->argName);\r
2245           while (*p) {\r
2246             if (*p == '\n') fprintf(f, "\n");\r
2247             else if (*p == '\r') fprintf(f, "\\r");\r
2248             else if (*p == '\t') fprintf(f, "\\t");\r
2249             else if (*p == '\b') fprintf(f, "\\b");\r
2250             else if (*p == '\f') fprintf(f, "\\f");\r
2251             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2252             else if (*p == '\"') fprintf(f, "\\\"");\r
2253             else if (*p == '\\') fprintf(f, "\\\\");\r
2254             else putc(*p, f);\r
2255             p++;\r
2256           }\r
2257           fprintf(f, "\"\n");\r
2258         }\r
2259       }\r
2260       break;\r
2261     case ArgInt:\r
2262       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2263       break;\r
2264     case ArgFloat:\r
2265       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2266       break;\r
2267     case ArgBoolean:\r
2268       fprintf(f, "/%s=%s\n", ad->argName, \r
2269         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2270       break;\r
2271     case ArgTrue:\r
2272       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2273       break;\r
2274     case ArgFalse:\r
2275       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2276       break;\r
2277     case ArgColor:\r
2278       {\r
2279         COLORREF color = *(COLORREF *)ad->argLoc;\r
2280         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2281           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2282       }\r
2283       break;\r
2284     case ArgAttribs:\r
2285       {\r
2286         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2287         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2288           (ta->effects & CFE_BOLD) ? "b" : "",\r
2289           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2290           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2291           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2292           (ta->effects) ? " " : "",\r
2293           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2294       }\r
2295       break;\r
2296     case ArgFilename:\r
2297       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2298         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2299       } else {\r
2300         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2301       }\r
2302       break;\r
2303     case ArgBoardSize:\r
2304       fprintf(f, "/%s=%s\n", ad->argName,\r
2305               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2306       break;\r
2307     case ArgFont:\r
2308       {\r
2309         int bs;\r
2310         for (bs=0; bs<NUM_SIZES; bs++) {\r
2311           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2312           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2313           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2314             ad->argName, mfp->faceName, mfp->pointSize,\r
2315             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2316             mfp->bold ? "b" : "",\r
2317             mfp->italic ? "i" : "",\r
2318             mfp->underline ? "u" : "",\r
2319             mfp->strikeout ? "s" : "");\r
2320         }\r
2321       }\r
2322       break;\r
2323     case ArgCommSettings:\r
2324       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2325     }\r
2326   }\r
2327   fclose(f);\r
2328 }\r
2329 \r
2330 \r
2331 \r
2332 /*---------------------------------------------------------------------------*\\r
2333  *\r
2334  * GDI board drawing routines\r
2335  *\r
2336 \*---------------------------------------------------------------------------*/\r
2337 \r
2338 /* [AS] Draw square using background texture */\r
2339 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2340 {\r
2341     XFORM   x;\r
2342 \r
2343     if( mode == 0 ) {\r
2344         return; /* Should never happen! */\r
2345     }\r
2346 \r
2347     SetGraphicsMode( dst, GM_ADVANCED );\r
2348 \r
2349     switch( mode ) {\r
2350     case 1:\r
2351         /* Identity */\r
2352         break;\r
2353     case 2:\r
2354         /* X reflection */\r
2355         x.eM11 = -1.0;\r
2356         x.eM12 = 0;\r
2357         x.eM21 = 0;\r
2358         x.eM22 = 1.0;\r
2359         x.eDx = (FLOAT) dw + dx - 1;\r
2360         x.eDy = 0;\r
2361         dx = 0;\r
2362         SetWorldTransform( dst, &x );\r
2363         break;\r
2364     case 3:\r
2365         /* Y reflection */\r
2366         x.eM11 = 1.0;\r
2367         x.eM12 = 0;\r
2368         x.eM21 = 0;\r
2369         x.eM22 = -1.0;\r
2370         x.eDx = 0;\r
2371         x.eDy = (FLOAT) dh + dy - 1;\r
2372         dy = 0;\r
2373         SetWorldTransform( dst, &x );\r
2374         break;\r
2375     case 4:\r
2376         /* X/Y flip */\r
2377         x.eM11 = 0;\r
2378         x.eM12 = 1.0;\r
2379         x.eM21 = 1.0;\r
2380         x.eM22 = 0;\r
2381         x.eDx = (FLOAT) dx;\r
2382         x.eDy = (FLOAT) dy;\r
2383         dx = 0;\r
2384         dy = 0;\r
2385         SetWorldTransform( dst, &x );\r
2386         break;\r
2387     }\r
2388 \r
2389     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2390 \r
2391     x.eM11 = 1.0;\r
2392     x.eM12 = 0;\r
2393     x.eM21 = 0;\r
2394     x.eM22 = 1.0;\r
2395     x.eDx = 0;\r
2396     x.eDy = 0;\r
2397     SetWorldTransform( dst, &x );\r
2398 \r
2399     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2400 }\r
2401 \r
2402 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2403 enum {\r
2404     PM_WP = (int) WhitePawn, \r
2405     PM_WN = (int) WhiteKnight, \r
2406     PM_WB = (int) WhiteBishop, \r
2407     PM_WR = (int) WhiteRook, \r
2408     PM_WQ = (int) WhiteQueen, \r
2409     PM_WF = (int) WhiteFerz, \r
2410     PM_WW = (int) WhiteWazir, \r
2411     PM_WE = (int) WhiteAlfil, \r
2412     PM_WM = (int) WhiteMan, \r
2413     PM_WO = (int) WhiteCannon, \r
2414     PM_WU = (int) WhiteUnicorn, \r
2415     PM_WH = (int) WhiteNightrider, \r
2416     PM_WA = (int) WhiteAngel, \r
2417     PM_WC = (int) WhiteMarshall, \r
2418     PM_WAB = (int) WhiteCardinal, \r
2419     PM_WD = (int) WhiteDragon, \r
2420     PM_WL = (int) WhiteLance, \r
2421     PM_WS = (int) WhiteCobra, \r
2422     PM_WV = (int) WhiteFalcon, \r
2423     PM_WSG = (int) WhiteSilver, \r
2424     PM_WG = (int) WhiteGrasshopper, \r
2425     PM_WK = (int) WhiteKing,\r
2426     PM_BP = (int) BlackPawn, \r
2427     PM_BN = (int) BlackKnight, \r
2428     PM_BB = (int) BlackBishop, \r
2429     PM_BR = (int) BlackRook, \r
2430     PM_BQ = (int) BlackQueen, \r
2431     PM_BF = (int) BlackFerz, \r
2432     PM_BW = (int) BlackWazir, \r
2433     PM_BE = (int) BlackAlfil, \r
2434     PM_BM = (int) BlackMan,\r
2435     PM_BO = (int) BlackCannon, \r
2436     PM_BU = (int) BlackUnicorn, \r
2437     PM_BH = (int) BlackNightrider, \r
2438     PM_BA = (int) BlackAngel, \r
2439     PM_BC = (int) BlackMarshall, \r
2440     PM_BG = (int) BlackGrasshopper, \r
2441     PM_BAB = (int) BlackCardinal,\r
2442     PM_BD = (int) BlackDragon,\r
2443     PM_BL = (int) BlackLance,\r
2444     PM_BS = (int) BlackCobra,\r
2445     PM_BV = (int) BlackFalcon,\r
2446     PM_BSG = (int) BlackSilver,\r
2447     PM_BK = (int) BlackKing\r
2448 };\r
2449 \r
2450 static HFONT hPieceFont = NULL;\r
2451 static HBITMAP hPieceMask[(int) EmptySquare];\r
2452 static HBITMAP hPieceFace[(int) EmptySquare];\r
2453 static int fontBitmapSquareSize = 0;\r
2454 static char pieceToFontChar[(int) EmptySquare] =\r
2455                               { 'p', 'n', 'b', 'r', 'q', \r
2456                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2457                       'k', 'o', 'm', 'v', 't', 'w', \r
2458                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2459                                                               'l' };\r
2460 \r
2461 extern BOOL SetCharTable( char *table, const char * map );\r
2462 /* [HGM] moved to backend.c */\r
2463 \r
2464 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2465 {\r
2466     HBRUSH hbrush;\r
2467     BYTE r1 = GetRValue( color );\r
2468     BYTE g1 = GetGValue( color );\r
2469     BYTE b1 = GetBValue( color );\r
2470     BYTE r2 = r1 / 2;\r
2471     BYTE g2 = g1 / 2;\r
2472     BYTE b2 = b1 / 2;\r
2473     RECT rc;\r
2474 \r
2475     /* Create a uniform background first */\r
2476     hbrush = CreateSolidBrush( color );\r
2477     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2478     FillRect( hdc, &rc, hbrush );\r
2479     DeleteObject( hbrush );\r
2480     \r
2481     if( mode == 1 ) {\r
2482         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2483         int steps = squareSize / 2;\r
2484         int i;\r
2485 \r
2486         for( i=0; i<steps; i++ ) {\r
2487             BYTE r = r1 - (r1-r2) * i / steps;\r
2488             BYTE g = g1 - (g1-g2) * i / steps;\r
2489             BYTE b = b1 - (b1-b2) * i / steps;\r
2490 \r
2491             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2492             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2493             FillRect( hdc, &rc, hbrush );\r
2494             DeleteObject(hbrush);\r
2495         }\r
2496     }\r
2497     else if( mode == 2 ) {\r
2498         /* Diagonal gradient, good more or less for every piece */\r
2499         POINT triangle[3];\r
2500         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2501         HBRUSH hbrush_old;\r
2502         int steps = squareSize;\r
2503         int i;\r
2504 \r
2505         triangle[0].x = squareSize - steps;\r
2506         triangle[0].y = squareSize;\r
2507         triangle[1].x = squareSize;\r
2508         triangle[1].y = squareSize;\r
2509         triangle[2].x = squareSize;\r
2510         triangle[2].y = squareSize - steps;\r
2511 \r
2512         for( i=0; i<steps; i++ ) {\r
2513             BYTE r = r1 - (r1-r2) * i / steps;\r
2514             BYTE g = g1 - (g1-g2) * i / steps;\r
2515             BYTE b = b1 - (b1-b2) * i / steps;\r
2516 \r
2517             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2518             hbrush_old = SelectObject( hdc, hbrush );\r
2519             Polygon( hdc, triangle, 3 );\r
2520             SelectObject( hdc, hbrush_old );\r
2521             DeleteObject(hbrush);\r
2522             triangle[0].x++;\r
2523             triangle[2].y++;\r
2524         }\r
2525 \r
2526         SelectObject( hdc, hpen );\r
2527     }\r
2528 }\r
2529 \r
2530 /*\r
2531     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2532     seems to work ok. The main problem here is to find the "inside" of a chess\r
2533     piece: follow the steps as explained below.\r
2534 */\r
2535 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2536 {\r
2537     HBITMAP hbm;\r
2538     HBITMAP hbm_old;\r
2539     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2540     RECT rc;\r
2541     SIZE sz;\r
2542     POINT pt;\r
2543     int backColor = whitePieceColor; \r
2544     int foreColor = blackPieceColor;\r
2545     \r
2546     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2547         backColor = appData.fontBackColorWhite;\r
2548         foreColor = appData.fontForeColorWhite;\r
2549     }\r
2550     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2551         backColor = appData.fontBackColorBlack;\r
2552         foreColor = appData.fontForeColorBlack;\r
2553     }\r
2554 \r
2555     /* Mask */\r
2556     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2557 \r
2558     hbm_old = SelectObject( hdc, hbm );\r
2559 \r
2560     rc.left = 0;\r
2561     rc.top = 0;\r
2562     rc.right = squareSize;\r
2563     rc.bottom = squareSize;\r
2564 \r
2565     /* Step 1: background is now black */\r
2566     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2567 \r
2568     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2569 \r
2570     pt.x = (squareSize - sz.cx) / 2;\r
2571     pt.y = (squareSize - sz.cy) / 2;\r
2572 \r
2573     SetBkMode( hdc, TRANSPARENT );\r
2574     SetTextColor( hdc, chroma );\r
2575     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2576     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2577 \r
2578     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2579     /* Step 3: the area outside the piece is filled with white */\r
2580 //    FloodFill( hdc, 0, 0, chroma );\r
2581     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2582     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2583     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2584     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2585     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2586     /* \r
2587         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2588         but if the start point is not inside the piece we're lost!\r
2589         There should be a better way to do this... if we could create a region or path\r
2590         from the fill operation we would be fine for example.\r
2591     */\r
2592 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2593     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2594 \r
2595     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2596         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2597         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2598 \r
2599         SelectObject( dc2, bm2 );\r
2600         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2601         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2602         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2603         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2604         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2605 \r
2606         DeleteDC( dc2 );\r
2607         DeleteObject( bm2 );\r
2608     }\r
2609 \r
2610     SetTextColor( hdc, 0 );\r
2611     /* \r
2612         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2613         draw the piece again in black for safety.\r
2614     */\r
2615     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2616 \r
2617     SelectObject( hdc, hbm_old );\r
2618 \r
2619     if( hPieceMask[index] != NULL ) {\r
2620         DeleteObject( hPieceMask[index] );\r
2621     }\r
2622 \r
2623     hPieceMask[index] = hbm;\r
2624 \r
2625     /* Face */\r
2626     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2627 \r
2628     SelectObject( hdc, hbm );\r
2629 \r
2630     {\r
2631         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2632         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2633         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2634 \r
2635         SelectObject( dc1, hPieceMask[index] );\r
2636         SelectObject( dc2, bm2 );\r
2637         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2638         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2639         \r
2640         /* \r
2641             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2642             the piece background and deletes (makes transparent) the rest.\r
2643             Thanks to that mask, we are free to paint the background with the greates\r
2644             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2645             We use this, to make gradients and give the pieces a "roundish" look.\r
2646         */\r
2647         SetPieceBackground( hdc, backColor, 2 );\r
2648         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2649 \r
2650         DeleteDC( dc2 );\r
2651         DeleteDC( dc1 );\r
2652         DeleteObject( bm2 );\r
2653     }\r
2654 \r
2655     SetTextColor( hdc, foreColor );\r
2656     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2657 \r
2658     SelectObject( hdc, hbm_old );\r
2659 \r
2660     if( hPieceFace[index] != NULL ) {\r
2661         DeleteObject( hPieceFace[index] );\r
2662     }\r
2663 \r
2664     hPieceFace[index] = hbm;\r
2665 }\r
2666 \r
2667 static int TranslatePieceToFontPiece( int piece )\r
2668 {\r
2669     switch( piece ) {\r
2670     case BlackPawn:\r
2671         return PM_BP;\r
2672     case BlackKnight:\r
2673         return PM_BN;\r
2674     case BlackBishop:\r
2675         return PM_BB;\r
2676     case BlackRook:\r
2677         return PM_BR;\r
2678     case BlackQueen:\r
2679         return PM_BQ;\r
2680     case BlackKing:\r
2681         return PM_BK;\r
2682     case WhitePawn:\r
2683         return PM_WP;\r
2684     case WhiteKnight:\r
2685         return PM_WN;\r
2686     case WhiteBishop:\r
2687         return PM_WB;\r
2688     case WhiteRook:\r
2689         return PM_WR;\r
2690     case WhiteQueen:\r
2691         return PM_WQ;\r
2692     case WhiteKing:\r
2693         return PM_WK;\r
2694 \r
2695     case BlackAngel:\r
2696         return PM_BA;\r
2697     case BlackMarshall:\r
2698         return PM_BC;\r
2699     case BlackFerz:\r
2700         return PM_BF;\r
2701     case BlackNightrider:\r
2702         return PM_BH;\r
2703     case BlackAlfil:\r
2704         return PM_BE;\r
2705     case BlackWazir:\r
2706         return PM_BW;\r
2707     case BlackUnicorn:\r
2708         return PM_BU;\r
2709     case BlackCannon:\r
2710         return PM_BO;\r
2711     case BlackGrasshopper:\r
2712         return PM_BG;\r
2713     case BlackMan:\r
2714         return PM_BM;\r
2715     case BlackSilver:\r
2716         return PM_BSG;\r
2717     case BlackLance:\r
2718         return PM_BL;\r
2719     case BlackFalcon:\r
2720         return PM_BV;\r
2721     case BlackCobra:\r
2722         return PM_BS;\r
2723     case BlackCardinal:\r
2724         return PM_BAB;\r
2725     case BlackDragon:\r
2726         return PM_BD;\r
2727 \r
2728     case WhiteAngel:\r
2729         return PM_WA;\r
2730     case WhiteMarshall:\r
2731         return PM_WC;\r
2732     case WhiteFerz:\r
2733         return PM_WF;\r
2734     case WhiteNightrider:\r
2735         return PM_WH;\r
2736     case WhiteAlfil:\r
2737         return PM_WE;\r
2738     case WhiteWazir:\r
2739         return PM_WW;\r
2740     case WhiteUnicorn:\r
2741         return PM_WU;\r
2742     case WhiteCannon:\r
2743         return PM_WO;\r
2744     case WhiteGrasshopper:\r
2745         return PM_WG;\r
2746     case WhiteMan:\r
2747         return PM_WM;\r
2748     case WhiteSilver:\r
2749         return PM_WSG;\r
2750     case WhiteLance:\r
2751         return PM_WL;\r
2752     case WhiteFalcon:\r
2753         return PM_WV;\r
2754     case WhiteCobra:\r
2755         return PM_WS;\r
2756     case WhiteCardinal:\r
2757         return PM_WAB;\r
2758     case WhiteDragon:\r
2759         return PM_WD;\r
2760     }\r
2761 \r
2762     return 0;\r
2763 }\r
2764 \r
2765 void CreatePiecesFromFont()\r
2766 {\r
2767     LOGFONT lf;\r
2768     HDC hdc_window = NULL;\r
2769     HDC hdc = NULL;\r
2770     HFONT hfont_old;\r
2771     int fontHeight;\r
2772     int i;\r
2773 \r
2774     if( fontBitmapSquareSize < 0 ) {\r
2775         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2776         return;\r
2777     }\r
2778 \r
2779     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2780         fontBitmapSquareSize = -1;\r
2781         return;\r
2782     }\r
2783 \r
2784     if( fontBitmapSquareSize != squareSize ) {\r
2785         hdc_window = GetDC( hwndMain );\r
2786         hdc = CreateCompatibleDC( hdc_window );\r
2787 \r
2788         if( hPieceFont != NULL ) {\r
2789             DeleteObject( hPieceFont );\r
2790         }\r
2791         else {\r
2792             for( i=0; i<=(int)BlackKing; i++ ) {\r
2793                 hPieceMask[i] = NULL;\r
2794                 hPieceFace[i] = NULL;\r
2795             }\r
2796         }\r
2797 \r
2798         fontHeight = 75;\r
2799 \r
2800         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2801             fontHeight = appData.fontPieceSize;\r
2802         }\r
2803 \r
2804         fontHeight = (fontHeight * squareSize) / 100;\r
2805 \r
2806         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2807         lf.lfWidth = 0;\r
2808         lf.lfEscapement = 0;\r
2809         lf.lfOrientation = 0;\r
2810         lf.lfWeight = FW_NORMAL;\r
2811         lf.lfItalic = 0;\r
2812         lf.lfUnderline = 0;\r
2813         lf.lfStrikeOut = 0;\r
2814         lf.lfCharSet = DEFAULT_CHARSET;\r
2815         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2816         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2817         lf.lfQuality = PROOF_QUALITY;\r
2818         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2819         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2820         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2821 \r
2822         hPieceFont = CreateFontIndirect( &lf );\r
2823 \r
2824         if( hPieceFont == NULL ) {\r
2825             fontBitmapSquareSize = -2;\r
2826         }\r
2827         else {\r
2828             /* Setup font-to-piece character table */\r
2829             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2830                 /* No (or wrong) global settings, try to detect the font */\r
2831                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2832                     /* Alpha */\r
2833                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2834                 }\r
2835                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2836                     /* DiagramTT* family */\r
2837                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2838                 }\r
2839                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2840                     /* Fairy symbols */\r
2841                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2842                 }\r
2843                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2844                     /* Good Companion (Some characters get warped as literal :-( */\r
2845                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2846                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2847                     SetCharTable(pieceToFontChar, s);\r
2848                 }\r
2849                 else {\r
2850                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2851                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2852                 }\r
2853             }\r
2854 \r
2855             /* Create bitmaps */\r
2856             hfont_old = SelectObject( hdc, hPieceFont );\r
2857 #if 0\r
2858             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2859             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2860             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2861             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2862             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2863             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2864             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2865             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2866             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2867             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2868             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2869             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2870 \r
2871             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2872             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2873             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2874             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2875             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2876             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2877             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2878             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2879             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2880             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2881             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2882             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2883             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2884             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2885             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2903 #else\r
2904             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2905                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2906                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2907 #endif\r
2908             SelectObject( hdc, hfont_old );\r
2909 \r
2910             fontBitmapSquareSize = squareSize;\r
2911         }\r
2912     }\r
2913 \r
2914     if( hdc != NULL ) {\r
2915         DeleteDC( hdc );\r
2916     }\r
2917 \r
2918     if( hdc_window != NULL ) {\r
2919         ReleaseDC( hwndMain, hdc_window );\r
2920     }\r
2921 }\r
2922 \r
2923 HBITMAP\r
2924 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2925 {\r
2926   char name[128];\r
2927 \r
2928   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2929   if (gameInfo.event &&\r
2930       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2931       strcmp(name, "k80s") == 0) {\r
2932     strcpy(name, "tim");\r
2933   }\r
2934   return LoadBitmap(hinst, name);\r
2935 }\r
2936 \r
2937 \r
2938 /* Insert a color into the program's logical palette\r
2939    structure.  This code assumes the given color is\r
2940    the result of the RGB or PALETTERGB macro, and it\r
2941    knows how those macros work (which is documented).\r
2942 */\r
2943 VOID\r
2944 InsertInPalette(COLORREF color)\r
2945 {\r
2946   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2947 \r
2948   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2949     DisplayFatalError("Too many colors", 0, 1);\r
2950     pLogPal->palNumEntries--;\r
2951     return;\r
2952   }\r
2953 \r
2954   pe->peFlags = (char) 0;\r
2955   pe->peRed = (char) (0xFF & color);\r
2956   pe->peGreen = (char) (0xFF & (color >> 8));\r
2957   pe->peBlue = (char) (0xFF & (color >> 16));\r
2958   return;\r
2959 }\r
2960 \r
2961 \r
2962 VOID\r
2963 InitDrawingColors()\r
2964 {\r
2965   if (pLogPal == NULL) {\r
2966     /* Allocate enough memory for a logical palette with\r
2967      * PALETTESIZE entries and set the size and version fields\r
2968      * of the logical palette structure.\r
2969      */\r
2970     pLogPal = (NPLOGPALETTE)\r
2971       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2972                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2973     pLogPal->palVersion    = 0x300;\r
2974   }\r
2975   pLogPal->palNumEntries = 0;\r
2976 \r
2977   InsertInPalette(lightSquareColor);\r
2978   InsertInPalette(darkSquareColor);\r
2979   InsertInPalette(whitePieceColor);\r
2980   InsertInPalette(blackPieceColor);\r
2981   InsertInPalette(highlightSquareColor);\r
2982   InsertInPalette(premoveHighlightColor);\r
2983 \r
2984   /*  create a logical color palette according the information\r
2985    *  in the LOGPALETTE structure.\r
2986    */\r
2987   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2988 \r
2989   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2990   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2991   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2992   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2993   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2994   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2995 \r
2996   /* [AS] Force rendering of the font-based pieces */\r
2997   if( fontBitmapSquareSize > 0 ) {\r
2998     fontBitmapSquareSize = 0;\r
2999   }\r
3000 }\r
3001 \r
3002 \r
3003 int\r
3004 BoardWidth(int boardSize, int n)\r
3005 { /* [HGM] argument n added to allow different width and height */\r
3006   int lineGap = sizeInfo[boardSize].lineGap;\r
3007 \r
3008   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3009       lineGap = appData.overrideLineGap;\r
3010   }\r
3011 \r
3012   return (n + 1) * lineGap +\r
3013           n * sizeInfo[boardSize].squareSize;\r
3014 }\r
3015 \r
3016 /* Respond to board resize by dragging edge */\r
3017 VOID\r
3018 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3019 {\r
3020   BoardSize newSize = NUM_SIZES - 1;\r
3021   static int recurse = 0;\r
3022   if (IsIconic(hwndMain)) return;\r
3023   if (recurse > 0) return;\r
3024   recurse++;\r
3025   while (newSize > 0) {\r
3026         InitDrawingSizes(newSize, 0);\r
3027         if(newSizeX >= sizeInfo[newSize].cliWidth ||\r
3028            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3029     newSize--;\r
3030   } \r
3031   boardSize = newSize;\r
3032   InitDrawingSizes(boardSize, flags);\r
3033   recurse--;\r
3034 }\r
3035 \r
3036 \r
3037 \r
3038 VOID\r
3039 InitDrawingSizes(BoardSize boardSize, int flags)\r
3040 {\r
3041   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3042   ChessSquare piece;\r
3043   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3044   HDC hdc;\r
3045   SIZE clockSize, messageSize;\r
3046   HFONT oldFont;\r
3047   char buf[MSG_SIZ];\r
3048   char *str;\r
3049   HMENU hmenu = GetMenu(hwndMain);\r
3050   RECT crect, wrect;\r
3051   int offby;\r
3052   LOGBRUSH logbrush;\r
3053 \r
3054   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
3055   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3056 \r
3057   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3058   smallLayout = sizeInfo[boardSize].smallLayout;\r
3059   squareSize = sizeInfo[boardSize].squareSize;\r
3060   lineGap = sizeInfo[boardSize].lineGap;\r
3061   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3062 \r
3063   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3064       lineGap = appData.overrideLineGap;\r
3065   }\r
3066 \r
3067   if (tinyLayout != oldTinyLayout) {\r
3068     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3069     if (tinyLayout) {\r
3070       style &= ~WS_SYSMENU;\r
3071       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3072                  "&Minimize\tCtrl+F4");\r
3073     } else {\r
3074       style |= WS_SYSMENU;\r
3075       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3076     }\r
3077     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3078 \r
3079     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3080       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3081         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3082     }\r
3083     DrawMenuBar(hwndMain);\r
3084   }\r
3085 \r
3086   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3087   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3088 \r
3089   /* Get text area sizes */\r
3090   hdc = GetDC(hwndMain);\r
3091   if (appData.clockMode) {\r
3092     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3093   } else {\r
3094     sprintf(buf, "White");\r
3095   }\r
3096   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3097   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3098   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3099   str = "We only care about the height here";\r
3100   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3101   SelectObject(hdc, oldFont);\r
3102   ReleaseDC(hwndMain, hdc);\r
3103 \r
3104   /* Compute where everything goes */\r
3105   if(first.programLogo || second.programLogo) {\r
3106         /* [HGM] logo: if either logo is on, reserve space for it */\r
3107         logoHeight =  2*clockSize.cy;\r
3108         leftLogoRect.left   = OUTER_MARGIN;\r
3109         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3110         leftLogoRect.top    = OUTER_MARGIN;\r
3111         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3112 \r
3113         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3114         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3115         rightLogoRect.top    = OUTER_MARGIN;\r
3116         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3117 \r
3118 \r
3119     blackRect.left = leftLogoRect.right;\r
3120     blackRect.right = rightLogoRect.left;\r
3121     blackRect.top = OUTER_MARGIN;\r
3122     blackRect.bottom = blackRect.top + clockSize.cy;\r
3123 \r
3124     whiteRect.left = blackRect.left ;\r
3125     whiteRect.right = blackRect.right;\r
3126     whiteRect.top = blackRect.bottom;\r
3127     whiteRect.bottom = leftLogoRect.bottom;\r
3128   } else {\r
3129     whiteRect.left = OUTER_MARGIN;\r
3130     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3131     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3132     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3133 \r
3134     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3135     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3136     blackRect.top = whiteRect.top;\r
3137     blackRect.bottom = whiteRect.bottom;\r
3138   }\r
3139 \r
3140   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3141   if (appData.showButtonBar) {\r
3142     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3143       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3144   } else {\r
3145     messageRect.right = OUTER_MARGIN + boardWidth;\r
3146   }\r
3147   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3148   messageRect.bottom = messageRect.top + messageSize.cy;\r
3149 \r
3150   boardRect.left = OUTER_MARGIN;\r
3151   boardRect.right = boardRect.left + boardWidth;\r
3152   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3153   boardRect.bottom = boardRect.top + boardHeight;\r
3154 \r
3155   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3156   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3157   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3158   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3159     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3160   GetWindowRect(hwndMain, &wrect);\r
3161   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3162                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3163   /* compensate if menu bar wrapped */\r
3164   GetClientRect(hwndMain, &crect);\r
3165   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3166   winHeight += offby;\r
3167   switch (flags) {\r
3168   case WMSZ_TOPLEFT:\r
3169     SetWindowPos(hwndMain, NULL, \r
3170                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3171                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3172     break;\r
3173 \r
3174   case WMSZ_TOPRIGHT:\r
3175   case WMSZ_TOP:\r
3176     SetWindowPos(hwndMain, NULL, \r
3177                  wrect.left, wrect.bottom - winHeight, \r
3178                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3179     break;\r
3180 \r
3181   case WMSZ_BOTTOMLEFT:\r
3182   case WMSZ_LEFT:\r
3183     SetWindowPos(hwndMain, NULL, \r
3184                  wrect.right - winWidth, wrect.top, \r
3185                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3186     break;\r
3187 \r
3188   case WMSZ_BOTTOMRIGHT:\r
3189   case WMSZ_BOTTOM:\r
3190   case WMSZ_RIGHT:\r
3191   default:\r
3192     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3193                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3194     break;\r
3195   }\r
3196 \r
3197   hwndPause = NULL;\r
3198   for (i = 0; i < N_BUTTONS; i++) {\r
3199     if (buttonDesc[i].hwnd != NULL) {\r
3200       DestroyWindow(buttonDesc[i].hwnd);\r
3201       buttonDesc[i].hwnd = NULL;\r
3202     }\r
3203     if (appData.showButtonBar) {\r
3204       buttonDesc[i].hwnd =\r
3205         CreateWindow("BUTTON", buttonDesc[i].label,\r
3206                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3207                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3208                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3209                      (HMENU) buttonDesc[i].id,\r
3210                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3211       if (tinyLayout) {\r
3212         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3213                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3214                     MAKELPARAM(FALSE, 0));\r
3215       }\r
3216       if (buttonDesc[i].id == IDM_Pause)\r
3217         hwndPause = buttonDesc[i].hwnd;\r
3218       buttonDesc[i].wndproc = (WNDPROC)\r
3219         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3220     }\r
3221   }\r
3222   if (gridPen != NULL) DeleteObject(gridPen);\r
3223   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3224   if (premovePen != NULL) DeleteObject(premovePen);\r
3225   if (lineGap != 0) {\r
3226     logbrush.lbStyle = BS_SOLID;\r
3227     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3228     gridPen =\r
3229       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3230                    lineGap, &logbrush, 0, NULL);\r
3231     logbrush.lbColor = highlightSquareColor;\r
3232     highlightPen =\r
3233       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3234                    lineGap, &logbrush, 0, NULL);\r
3235 \r
3236     logbrush.lbColor = premoveHighlightColor; \r
3237     premovePen =\r
3238       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3239                    lineGap, &logbrush, 0, NULL);\r
3240 \r
3241     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3242     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3243       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3244       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3245         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3246       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3247         BOARD_WIDTH * (squareSize + lineGap);\r
3248         lineGap / 2 + (i * (squareSize + lineGap));\r
3249       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3250     }\r
3251     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3252       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3253       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3254         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3255         lineGap / 2 + (i * (squareSize + lineGap));\r
3256       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3257         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3258       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3259     }\r
3260   }\r
3261 \r
3262   /* [HGM] Licensing requirement */\r
3263 #ifdef GOTHIC\r
3264   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3265 #endif\r
3266 #ifdef FALCON\r
3267   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3268 #endif\r
3269   GothicPopUp( "", VariantNormal);\r
3270 \r
3271 \r
3272 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3273   oldBoardSize = boardSize;\r
3274   oldTinyLayout = tinyLayout;\r
3275 \r
3276   /* Load piece bitmaps for this board size */\r
3277   for (i=0; i<=2; i++) {\r
3278     for (piece = WhitePawn;\r
3279          (int) piece < (int) BlackPawn;\r
3280          piece = (ChessSquare) ((int) piece + 1)) {\r
3281       if (pieceBitmap[i][piece] != NULL)\r
3282         DeleteObject(pieceBitmap[i][piece]);\r
3283     }\r
3284   }\r
3285 \r
3286   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3287   // Orthodox Chess pieces\r
3288   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3289   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3290   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3291   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3292   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3293   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3294   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3295   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3296   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3297   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3298   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3299   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3300   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3301   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3302   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3303   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3304     // in Shogi, Hijack the unused Queen for Lance\r
3305     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3306     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3307     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3308   } else {\r
3309     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3310     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3311     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3312   }\r
3313 \r
3314   if(squareSize <= 72 && squareSize >= 33) { \r
3315     /* A & C are available in most sizes now */\r
3316     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3317       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3318       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3319       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3320       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3321       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3322       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3323       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3324       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3325       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3326       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3327       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3328       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3329     } else { // Smirf-like\r
3330       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3331       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3332       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3333     }\r
3334     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3335       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3336       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3337       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3338     } else { // WinBoard standard\r
3339       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3340       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3341       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3342     }\r
3343   }\r
3344 \r
3345 \r
3346   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3347     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3348     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3349     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3350     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3351     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3352     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3353     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3354     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3355     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3356     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3357     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3358     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3359     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3360     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3361     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3362     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3363     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3364     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3365     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3366     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3367     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3368     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3369     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3370     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3371     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3372     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3373     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3374     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3375     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3376     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3377 \r
3378     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3379       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3380       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3381       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3382       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3383       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3384       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3385       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3386       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3387       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3388       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3389       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3390       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3391     } else {\r
3392       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3393       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3394       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3395       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3396       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3397       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3398       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3399       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3400       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3401       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3402       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3403       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3404     }\r
3405 \r
3406   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3407     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3408     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3409     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3410     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3411     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3412     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3413     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3414     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3415     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3416     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3417     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3418     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3419     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3420     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3421   }\r
3422 \r
3423 \r
3424   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3425   /* special Shogi support in this size */\r
3426   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3427       for (piece = WhitePawn;\r
3428            (int) piece < (int) BlackPawn;\r
3429            piece = (ChessSquare) ((int) piece + 1)) {\r
3430         if (pieceBitmap[i][piece] != NULL)\r
3431           DeleteObject(pieceBitmap[i][piece]);\r
3432       }\r
3433     }\r
3434   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3435   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3436   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3437   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3438   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3439   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3440   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3441   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3442   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3443   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3444   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3445   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3446   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3447   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3448   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3449   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3450   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3451   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3452   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3453   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3454   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3455   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3456   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3457   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3458   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3459   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3460   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3461   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3462   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3463   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3464   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3465   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3466   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3467   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3468   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3469   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3470   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3471   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3472   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3473   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3474   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3475   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3476   minorSize = 0;\r
3477   }\r
3478 }\r
3479 \r
3480 HBITMAP\r
3481 PieceBitmap(ChessSquare p, int kind)\r
3482 {\r
3483   if ((int) p >= (int) BlackPawn)\r
3484     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3485 \r
3486   return pieceBitmap[kind][(int) p];\r
3487 }\r
3488 \r
3489 /***************************************************************/\r
3490 \r
3491 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3492 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3493 /*\r
3494 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3495 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3496 */\r
3497 \r
3498 VOID\r
3499 SquareToPos(int row, int column, int * x, int * y)\r
3500 {\r
3501   if (flipView) {\r
3502     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3503     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3504   } else {\r
3505     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3506     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3507   }\r
3508 }\r
3509 \r
3510 VOID\r
3511 DrawCoordsOnDC(HDC hdc)\r
3512 {\r
3513   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
3514   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
3515   char str[2] = { NULLCHAR, NULLCHAR };\r
3516   int oldMode, oldAlign, x, y, start, i;\r
3517   HFONT oldFont;\r
3518   HBRUSH oldBrush;\r
3519 \r
3520   if (!appData.showCoords)\r
3521     return;\r
3522 \r
3523   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3524 \r
3525   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3526   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3527   oldAlign = GetTextAlign(hdc);\r
3528   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3529 \r
3530   y = boardRect.top + lineGap;\r
3531   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3532 \r
3533   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3534   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3535     str[0] = files[start + i];\r
3536     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3537     y += squareSize + lineGap;\r
3538   }\r
3539 \r
3540   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3541 \r
3542   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3543   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3544     str[0] = ranks[start + i];\r
3545     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3546     x += squareSize + lineGap;\r
3547   }    \r
3548 \r
3549   SelectObject(hdc, oldBrush);\r
3550   SetBkMode(hdc, oldMode);\r
3551   SetTextAlign(hdc, oldAlign);\r
3552   SelectObject(hdc, oldFont);\r
3553 }\r
3554 \r
3555 VOID\r
3556 DrawGridOnDC(HDC hdc)\r
3557 {\r
3558   HPEN oldPen;\r
3559  \r
3560   if (lineGap != 0) {\r
3561     oldPen = SelectObject(hdc, gridPen);\r
3562     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3563     SelectObject(hdc, oldPen);\r
3564   }\r
3565 }\r
3566 \r
3567 #define HIGHLIGHT_PEN 0\r
3568 #define PREMOVE_PEN   1\r
3569 \r
3570 VOID\r
3571 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3572 {\r
3573   int x1, y1;\r
3574   HPEN oldPen, hPen;\r
3575   if (lineGap == 0) return;\r
3576   if (flipView) {\r
3577     x1 = boardRect.left +\r
3578       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3579     y1 = boardRect.top +\r
3580       lineGap/2 + y * (squareSize + lineGap);\r
3581   } else {\r
3582     x1 = boardRect.left +\r
3583       lineGap/2 + x * (squareSize + lineGap);\r
3584     y1 = boardRect.top +\r
3585       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3586   }\r
3587   hPen = pen ? premovePen : highlightPen;\r
3588   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3589   MoveToEx(hdc, x1, y1, NULL);\r
3590   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3591   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3592   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3593   LineTo(hdc, x1, y1);\r
3594   SelectObject(hdc, oldPen);\r
3595 }\r
3596 \r
3597 VOID\r
3598 DrawHighlightsOnDC(HDC hdc)\r
3599 {\r
3600   int i;\r
3601   for (i=0; i<2; i++) {\r
3602     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3603       DrawHighlightOnDC(hdc, TRUE,\r
3604                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3605                         HIGHLIGHT_PEN);\r
3606   }\r
3607   for (i=0; i<2; i++) {\r
3608     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3609         premoveHighlightInfo.sq[i].y >= 0) {\r
3610         DrawHighlightOnDC(hdc, TRUE,\r
3611                           premoveHighlightInfo.sq[i].x, \r
3612                           premoveHighlightInfo.sq[i].y,\r
3613                           PREMOVE_PEN);\r
3614     }\r
3615   }\r
3616 }\r
3617 \r
3618 /* Note: sqcolor is used only in monoMode */\r
3619 /* Note that this code is largely duplicated in woptions.c,\r
3620    function DrawSampleSquare, so that needs to be updated too */\r
3621 VOID\r
3622 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3623 {\r
3624   HBITMAP oldBitmap;\r
3625   HBRUSH oldBrush;\r
3626   int tmpSize;\r
3627 \r
3628   if (appData.blindfold) return;\r
3629 \r
3630   /* [AS] Use font-based pieces if needed */\r
3631   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3632     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3633     CreatePiecesFromFont();\r
3634 \r
3635     if( fontBitmapSquareSize == squareSize ) {\r
3636         int index = TranslatePieceToFontPiece(piece);\r
3637 \r
3638         SelectObject( tmphdc, hPieceMask[ index ] );\r
3639 \r
3640         BitBlt( hdc,\r
3641             x, y,\r
3642             squareSize, squareSize,\r
3643             tmphdc,\r
3644             0, 0,\r
3645             SRCAND );\r
3646 \r
3647         SelectObject( tmphdc, hPieceFace[ index ] );\r
3648 \r
3649         BitBlt( hdc,\r
3650             x, y,\r
3651             squareSize, squareSize,\r
3652             tmphdc,\r
3653             0, 0,\r
3654             SRCPAINT );\r
3655 \r
3656         return;\r
3657     }\r
3658   }\r
3659 \r
3660   if (appData.monoMode) {\r
3661     SelectObject(tmphdc, PieceBitmap(piece, \r
3662       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3663     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3664            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3665   } else {\r
3666     tmpSize = squareSize;\r
3667     if(minorSize &&\r
3668         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3669          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3670       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3671       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3672       x += (squareSize - minorSize)>>1;\r
3673       y += squareSize - minorSize - 2;\r
3674       tmpSize = minorSize;\r
3675     }\r
3676     if (color || appData.allWhite ) {\r
3677       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3678       if( color )\r
3679               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3680       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3681       if(appData.upsideDown && color==flipView)\r
3682         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3683       else\r
3684         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3685 #if 0\r
3686       /* Use black piece color for outline of white pieces */\r
3687       /* Not sure this looks really good (though xboard does it).\r
3688          Maybe better to have another selectable color, default black */\r
3689       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3690       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3691       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3692 #else\r
3693       /* Use black for outline of white pieces */\r
3694       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3695       if(appData.upsideDown && color==flipView)\r
3696         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3697       else\r
3698         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3699 #endif\r
3700     } else {\r
3701 #if 0\r
3702       /* Use white piece color for details of black pieces */\r
3703       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3704          WHITE_PIECE ones aren't always the right shape. */\r
3705       /* Not sure this looks really good (though xboard does it).\r
3706          Maybe better to have another selectable color, default medium gray? */\r
3707       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3708       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3709       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3710       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3711       SelectObject(hdc, blackPieceBrush);\r
3712       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3713 #else\r
3714       /* Use square color for details of black pieces */\r
3715       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3716       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3717       if(appData.upsideDown && !flipView)\r
3718         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3719       else\r
3720         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3721 #endif\r
3722     }\r
3723     SelectObject(hdc, oldBrush);\r
3724     SelectObject(tmphdc, oldBitmap);\r
3725   }\r
3726 }\r
3727 \r
3728 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3729 int GetBackTextureMode( int algo )\r
3730 {\r
3731     int result = BACK_TEXTURE_MODE_DISABLED;\r
3732 \r
3733     switch( algo ) \r
3734     {\r
3735         case BACK_TEXTURE_MODE_PLAIN:\r
3736             result = 1; /* Always use identity map */\r
3737             break;\r
3738         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3739             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3740             break;\r
3741     }\r
3742 \r
3743     return result;\r
3744 }\r
3745 \r
3746 /* \r
3747     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3748     to handle redraws cleanly (as random numbers would always be different).\r
3749 */\r
3750 VOID RebuildTextureSquareInfo()\r
3751 {\r
3752     BITMAP bi;\r
3753     int lite_w = 0;\r
3754     int lite_h = 0;\r
3755     int dark_w = 0;\r
3756     int dark_h = 0;\r
3757     int row;\r
3758     int col;\r
3759 \r
3760     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3761 \r
3762     if( liteBackTexture != NULL ) {\r
3763         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3764             lite_w = bi.bmWidth;\r
3765             lite_h = bi.bmHeight;\r
3766         }\r
3767     }\r
3768 \r
3769     if( darkBackTexture != NULL ) {\r
3770         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3771             dark_w = bi.bmWidth;\r
3772             dark_h = bi.bmHeight;\r
3773         }\r
3774     }\r
3775 \r
3776     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3777         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3778             if( (col + row) & 1 ) {\r
3779                 /* Lite square */\r
3780                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3781                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3782                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3783                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3784                 }\r
3785             }\r
3786             else {\r
3787                 /* Dark square */\r
3788                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3789                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3790                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3791                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3792                 }\r
3793             }\r
3794         }\r
3795     }\r
3796 }\r
3797 \r
3798 /* [AS] Arrow highlighting support */\r
3799 \r
3800 static int A_WIDTH = 5; /* Width of arrow body */\r
3801 \r
3802 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3803 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3804 \r
3805 static double Sqr( double x )\r
3806 {\r
3807     return x*x;\r
3808 }\r
3809 \r
3810 static int Round( double x )\r
3811 {\r
3812     return (int) (x + 0.5);\r
3813 }\r
3814 \r
3815 /* Draw an arrow between two points using current settings */\r
3816 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3817 {\r
3818     POINT arrow[7];\r
3819     double dx, dy, j, k, x, y;\r
3820 \r
3821     if( d_x == s_x ) {\r
3822         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3823 \r
3824         arrow[0].x = s_x + A_WIDTH;\r
3825         arrow[0].y = s_y;\r
3826 \r
3827         arrow[1].x = s_x + A_WIDTH;\r
3828         arrow[1].y = d_y - h;\r
3829 \r
3830         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3831         arrow[2].y = d_y - h;\r
3832 \r
3833         arrow[3].x = d_x;\r
3834         arrow[3].y = d_y;\r
3835 \r
3836         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3837         arrow[4].y = d_y - h;\r
3838 \r
3839         arrow[5].x = s_x - A_WIDTH;\r
3840         arrow[5].y = d_y - h;\r
3841 \r
3842         arrow[6].x = s_x - A_WIDTH;\r
3843         arrow[6].y = s_y;\r
3844     }\r
3845     else if( d_y == s_y ) {\r
3846         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3847 \r
3848         arrow[0].x = s_x;\r
3849         arrow[0].y = s_y + A_WIDTH;\r
3850 \r
3851         arrow[1].x = d_x - w;\r
3852         arrow[1].y = s_y + A_WIDTH;\r
3853 \r
3854         arrow[2].x = d_x - w;\r
3855         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3856 \r
3857         arrow[3].x = d_x;\r
3858         arrow[3].y = d_y;\r
3859 \r
3860         arrow[4].x = d_x - w;\r
3861         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3862 \r
3863         arrow[5].x = d_x - w;\r
3864         arrow[5].y = s_y - A_WIDTH;\r
3865 \r
3866         arrow[6].x = s_x;\r
3867         arrow[6].y = s_y - A_WIDTH;\r
3868     }\r
3869     else {\r
3870         /* [AS] Needed a lot of paper for this! :-) */\r
3871         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3872         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3873   \r
3874         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3875 \r
3876         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3877 \r
3878         x = s_x;\r
3879         y = s_y;\r
3880 \r
3881         arrow[0].x = Round(x - j);\r
3882         arrow[0].y = Round(y + j*dx);\r
3883 \r
3884         arrow[1].x = Round(x + j);\r
3885         arrow[1].y = Round(y - j*dx);\r
3886 \r
3887         if( d_x > s_x ) {\r
3888             x = (double) d_x - k;\r
3889             y = (double) d_y - k*dy;\r
3890         }\r
3891         else {\r
3892             x = (double) d_x + k;\r
3893             y = (double) d_y + k*dy;\r
3894         }\r
3895 \r
3896         arrow[2].x = Round(x + j);\r
3897         arrow[2].y = Round(y - j*dx);\r
3898 \r
3899         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3900         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3901 \r
3902         arrow[4].x = d_x;\r
3903         arrow[4].y = d_y;\r
3904 \r
3905         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3906         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3907 \r
3908         arrow[6].x = Round(x - j);\r
3909         arrow[6].y = Round(y + j*dx);\r
3910     }\r
3911 \r
3912     Polygon( hdc, arrow, 7 );\r
3913 }\r
3914 \r
3915 /* [AS] Draw an arrow between two squares */\r
3916 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3917 {\r
3918     int s_x, s_y, d_x, d_y;\r
3919     HPEN hpen;\r
3920     HPEN holdpen;\r
3921     HBRUSH hbrush;\r
3922     HBRUSH holdbrush;\r
3923     LOGBRUSH stLB;\r
3924 \r
3925     if( s_col == d_col && s_row == d_row ) {\r
3926         return;\r
3927     }\r
3928 \r
3929     /* Get source and destination points */\r
3930     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3931     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3932 \r
3933     if( d_y > s_y ) {\r
3934         d_y += squareSize / 4;\r
3935     }\r
3936     else if( d_y < s_y ) {\r
3937         d_y += 3 * squareSize / 4;\r
3938     }\r
3939     else {\r
3940         d_y += squareSize / 2;\r
3941     }\r
3942 \r
3943     if( d_x > s_x ) {\r
3944         d_x += squareSize / 4;\r
3945     }\r
3946     else if( d_x < s_x ) {\r
3947         d_x += 3 * squareSize / 4;\r
3948     }\r
3949     else {\r
3950         d_x += squareSize / 2;\r
3951     }\r
3952 \r
3953     s_x += squareSize / 2;\r
3954     s_y += squareSize / 2;\r
3955 \r
3956     /* Adjust width */\r
3957     A_WIDTH = squareSize / 14;\r
3958 \r
3959     /* Draw */\r
3960     stLB.lbStyle = BS_SOLID;\r
3961     stLB.lbColor = appData.highlightArrowColor;\r
3962     stLB.lbHatch = 0;\r
3963 \r
3964     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3965     holdpen = SelectObject( hdc, hpen );\r
3966     hbrush = CreateBrushIndirect( &stLB );\r
3967     holdbrush = SelectObject( hdc, hbrush );\r
3968 \r
3969     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3970 \r
3971     SelectObject( hdc, holdpen );\r
3972     SelectObject( hdc, holdbrush );\r
3973     DeleteObject( hpen );\r
3974     DeleteObject( hbrush );\r
3975 }\r
3976 \r
3977 BOOL HasHighlightInfo()\r
3978 {\r
3979     BOOL result = FALSE;\r
3980 \r
3981     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3982         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3983     {\r
3984         result = TRUE;\r
3985     }\r
3986 \r
3987     return result;\r
3988 }\r
3989 \r
3990 BOOL IsDrawArrowEnabled()\r
3991 {\r
3992     BOOL result = FALSE;\r
3993 \r
3994     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3995         result = TRUE;\r
3996     }\r
3997 \r
3998     return result;\r
3999 }\r
4000 \r
4001 VOID DrawArrowHighlight( HDC hdc )\r
4002 {\r
4003     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4004         DrawArrowBetweenSquares( hdc,\r
4005             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4006             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4007     }\r
4008 }\r
4009 \r
4010 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4011 {\r
4012     HRGN result = NULL;\r
4013 \r
4014     if( HasHighlightInfo() ) {\r
4015         int x1, y1, x2, y2;\r
4016         int sx, sy, dx, dy;\r
4017 \r
4018         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4019         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4020 \r
4021         sx = MIN( x1, x2 );\r
4022         sy = MIN( y1, y2 );\r
4023         dx = MAX( x1, x2 ) + squareSize;\r
4024         dy = MAX( y1, y2 ) + squareSize;\r
4025 \r
4026         result = CreateRectRgn( sx, sy, dx, dy );\r
4027     }\r
4028 \r
4029     return result;\r
4030 }\r
4031 \r
4032 /*\r
4033     Warning: this function modifies the behavior of several other functions. \r
4034     \r
4035     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4036     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4037     repaint is scattered all over the place, which is not good for features such as\r
4038     "arrow highlighting" that require a full repaint of the board.\r
4039 \r
4040     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4041     user interaction, when speed is not so important) but especially to avoid errors\r
4042     in the displayed graphics.\r
4043 \r
4044     In such patched places, I always try refer to this function so there is a single\r
4045     place to maintain knowledge.\r
4046     \r
4047     To restore the original behavior, just return FALSE unconditionally.\r
4048 */\r
4049 BOOL IsFullRepaintPreferrable()\r
4050 {\r
4051     BOOL result = FALSE;\r
4052 \r
4053     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4054         /* Arrow may appear on the board */\r
4055         result = TRUE;\r
4056     }\r
4057 \r
4058     return result;\r
4059 }\r
4060 \r
4061 /* \r
4062     This function is called by DrawPosition to know whether a full repaint must\r
4063     be forced or not.\r
4064 \r
4065     Only DrawPosition may directly call this function, which makes use of \r
4066     some state information. Other function should call DrawPosition specifying \r
4067     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4068 */\r
4069 BOOL DrawPositionNeedsFullRepaint()\r
4070 {\r
4071     BOOL result = FALSE;\r
4072 \r
4073     /* \r
4074         Probably a slightly better policy would be to trigger a full repaint\r
4075         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4076         but animation is fast enough that it's difficult to notice.\r
4077     */\r
4078     if( animInfo.piece == EmptySquare ) {\r
4079         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4080             result = TRUE;\r
4081         }\r
4082     }\r
4083 \r
4084     return result;\r
4085 }\r
4086 \r
4087 VOID\r
4088 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4089 {\r
4090   int row, column, x, y, square_color, piece_color;\r
4091   ChessSquare piece;\r
4092   HBRUSH oldBrush;\r
4093   HDC texture_hdc = NULL;\r
4094 \r
4095   /* [AS] Initialize background textures if needed */\r
4096   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4097       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4098       if( backTextureSquareSize != squareSize \r
4099        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4100           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4101           backTextureSquareSize = squareSize;\r
4102           RebuildTextureSquareInfo();\r
4103       }\r
4104 \r
4105       texture_hdc = CreateCompatibleDC( hdc );\r
4106   }\r
4107 \r
4108   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4109     for (column = 0; column < BOARD_WIDTH; column++) {\r
4110   \r
4111       SquareToPos(row, column, &x, &y);\r
4112 \r
4113       piece = board[row][column];\r
4114 \r
4115       square_color = ((column + row) % 2) == 1;\r
4116       if( gameInfo.variant == VariantXiangqi ) {\r
4117           square_color = !InPalace(row, column);\r
4118           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4119           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4120       }\r
4121       piece_color = (int) piece < (int) BlackPawn;\r
4122 \r
4123 \r
4124       /* [HGM] holdings file: light square or black */\r
4125       if(column == BOARD_LEFT-2) {\r
4126             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4127                 square_color = 1;\r
4128             else {\r
4129                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4130                 continue;\r
4131             }\r
4132       } else\r
4133       if(column == BOARD_RGHT + 1 ) {\r
4134             if( row < gameInfo.holdingsSize )\r
4135                 square_color = 1;\r
4136             else {\r
4137                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4138                 continue;\r
4139             }\r
4140       }\r
4141       if(column == BOARD_LEFT-1 ) /* left align */\r
4142             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4143       else if( column == BOARD_RGHT) /* right align */\r
4144             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4145       else\r
4146       if (appData.monoMode) {\r
4147         if (piece == EmptySquare) {\r
4148           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4149                  square_color ? WHITENESS : BLACKNESS);\r
4150         } else {\r
4151           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4152         }\r
4153       } \r
4154       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4155           /* [AS] Draw the square using a texture bitmap */\r
4156           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4157           int r = row, c = column; // [HGM] do not flip board in flipView\r
4158           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4159 \r
4160           DrawTile( x, y, \r
4161               squareSize, squareSize, \r
4162               hdc, \r
4163               texture_hdc,\r
4164               backTextureSquareInfo[r][c].mode,\r
4165               backTextureSquareInfo[r][c].x,\r
4166               backTextureSquareInfo[r][c].y );\r
4167 \r
4168           SelectObject( texture_hdc, hbm );\r
4169 \r
4170           if (piece != EmptySquare) {\r
4171               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4172           }\r
4173       }\r
4174       else {\r
4175         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4176 \r
4177         oldBrush = SelectObject(hdc, brush );\r
4178         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4179         SelectObject(hdc, oldBrush);\r
4180         if (piece != EmptySquare)\r
4181           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4182       }\r
4183     }\r
4184   }\r
4185 \r
4186   if( texture_hdc != NULL ) {\r
4187     DeleteDC( texture_hdc );\r
4188   }\r
4189 }\r
4190 \r
4191 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4192 void fputDW(FILE *f, int x)\r
4193 {\r
4194         fputc(x     & 255, f);\r
4195         fputc(x>>8  & 255, f);\r
4196         fputc(x>>16 & 255, f);\r
4197         fputc(x>>24 & 255, f);\r
4198 }\r
4199 \r
4200 #define MAX_CLIPS 200   /* more than enough */\r
4201 \r
4202 VOID\r
4203 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4204 {\r
4205   HBITMAP bufferBitmap;\r
4206   BITMAP bi;\r
4207   RECT Rect;\r
4208   HDC tmphdc;\r
4209   HBITMAP hbm;\r
4210   int w = 100, h = 50;\r
4211 \r
4212   if(cps->programLogo == NULL) return;\r
4213 //  GetClientRect(hwndMain, &Rect);\r
4214 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4215 //                                      Rect.bottom-Rect.top+1);\r
4216   tmphdc = CreateCompatibleDC(hdc);\r
4217   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4218   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4219             w = bi.bmWidth;\r
4220             h = bi.bmHeight;\r
4221   }\r
4222   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4223                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4224   SelectObject(tmphdc, hbm);\r
4225   DeleteDC(tmphdc);\r
4226 }\r
4227 \r
4228 VOID\r
4229 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4230 {\r
4231   static Board lastReq, lastDrawn;\r
4232   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4233   static int lastDrawnFlipView = 0;\r
4234   static int lastReqValid = 0, lastDrawnValid = 0;\r
4235   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4236   HDC tmphdc;\r
4237   HDC hdcmem;\r
4238   HBITMAP bufferBitmap;\r
4239   HBITMAP oldBitmap;\r
4240   RECT Rect;\r
4241   HRGN clips[MAX_CLIPS];\r
4242   ChessSquare dragged_piece = EmptySquare;\r
4243 \r
4244   /* I'm undecided on this - this function figures out whether a full\r
4245    * repaint is necessary on its own, so there's no real reason to have the\r
4246    * caller tell it that.  I think this can safely be set to FALSE - but\r
4247    * if we trust the callers not to request full repaints unnessesarily, then\r
4248    * we could skip some clipping work.  In other words, only request a full\r
4249    * redraw when the majority of pieces have changed positions (ie. flip, \r
4250    * gamestart and similar)  --Hawk\r
4251    */\r
4252   Boolean fullrepaint = repaint;\r
4253 \r
4254   if( DrawPositionNeedsFullRepaint() ) {\r
4255       fullrepaint = TRUE;\r
4256   }\r
4257 \r
4258 #if 0\r
4259   if( fullrepaint ) {\r
4260       static int repaint_count = 0;\r
4261       char buf[128];\r
4262 \r
4263       repaint_count++;\r
4264       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4265       OutputDebugString( buf );\r
4266   }\r
4267 #endif\r
4268 \r
4269   if (board == NULL) {\r
4270     if (!lastReqValid) {\r
4271       return;\r
4272     }\r
4273     board = lastReq;\r
4274   } else {\r
4275     CopyBoard(lastReq, board);\r
4276     lastReqValid = 1;\r
4277   }\r
4278 \r
4279   if (doingSizing) {\r
4280     return;\r
4281   }\r
4282 \r
4283   if (IsIconic(hwndMain)) {\r
4284     return;\r
4285   }\r
4286 \r
4287   if (hdc == NULL) {\r
4288     hdc = GetDC(hwndMain);\r
4289     if (!appData.monoMode) {\r
4290       SelectPalette(hdc, hPal, FALSE);\r
4291       RealizePalette(hdc);\r
4292     }\r
4293     releaseDC = TRUE;\r
4294   } else {\r
4295     releaseDC = FALSE;\r
4296   }\r
4297 \r
4298 #if 0\r
4299   fprintf(debugFP, "*******************************\n"\r
4300                    "repaint = %s\n"\r
4301                    "dragInfo.from (%d,%d)\n"\r
4302                    "dragInfo.start (%d,%d)\n"\r
4303                    "dragInfo.pos (%d,%d)\n"\r
4304                    "dragInfo.lastpos (%d,%d)\n", \r
4305                     repaint ? "TRUE" : "FALSE",\r
4306                     dragInfo.from.x, dragInfo.from.y, \r
4307                     dragInfo.start.x, dragInfo.start.y,\r
4308                     dragInfo.pos.x, dragInfo.pos.y,\r
4309                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4310   fprintf(debugFP, "prev:  ");\r
4311   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4312     for (column = 0; column < BOARD_WIDTH; column++) {\r
4313       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4314     }\r
4315   }\r
4316   fprintf(debugFP, "\n");\r
4317   fprintf(debugFP, "board: ");\r
4318   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4319     for (column = 0; column < BOARD_WIDTH; column++) {\r
4320       fprintf(debugFP, "%d ", board[row][column]);\r
4321     }\r
4322   }\r
4323   fprintf(debugFP, "\n");\r
4324   fflush(debugFP);\r
4325 #endif\r
4326 \r
4327   /* Create some work-DCs */\r
4328   hdcmem = CreateCompatibleDC(hdc);\r
4329   tmphdc = CreateCompatibleDC(hdc);\r
4330 \r
4331   /* If dragging is in progress, we temporarely remove the piece */\r
4332   /* [HGM] or temporarily decrease count if stacked              */\r
4333   /*       !! Moved to before board compare !!                   */\r
4334   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4335     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4336     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4337             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4338         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4339     } else \r
4340     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4341             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4342         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4343     } else \r
4344         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4345   }\r
4346 \r
4347   /* Figure out which squares need updating by comparing the \r
4348    * newest board with the last drawn board and checking if\r
4349    * flipping has changed.\r
4350    */\r
4351   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4352     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4353       for (column = 0; column < BOARD_WIDTH; column++) {\r
4354         if (lastDrawn[row][column] != board[row][column]) {\r
4355           SquareToPos(row, column, &x, &y);\r
4356           clips[num_clips++] =\r
4357             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4358         }\r
4359       }\r
4360     }\r
4361     for (i=0; i<2; i++) {\r
4362       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4363           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4364         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4365             lastDrawnHighlight.sq[i].y >= 0) {\r
4366           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4367                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4368           clips[num_clips++] =\r
4369             CreateRectRgn(x - lineGap, y - lineGap, \r
4370                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4371         }\r
4372         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4373           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4374           clips[num_clips++] =\r
4375             CreateRectRgn(x - lineGap, y - lineGap, \r
4376                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4377         }\r
4378       }\r
4379     }\r
4380     for (i=0; i<2; i++) {\r
4381       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4382           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4383         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4384             lastDrawnPremove.sq[i].y >= 0) {\r
4385           SquareToPos(lastDrawnPremove.sq[i].y,\r
4386                       lastDrawnPremove.sq[i].x, &x, &y);\r
4387           clips[num_clips++] =\r
4388             CreateRectRgn(x - lineGap, y - lineGap, \r
4389                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4390         }\r
4391         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4392             premoveHighlightInfo.sq[i].y >= 0) {\r
4393           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4394                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4395           clips[num_clips++] =\r
4396             CreateRectRgn(x - lineGap, y - lineGap, \r
4397                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4398         }\r
4399       }\r
4400     }\r
4401   } else {\r
4402     fullrepaint = TRUE;\r
4403   }\r
4404 \r
4405   /* Create a buffer bitmap - this is the actual bitmap\r
4406    * being written to.  When all the work is done, we can\r
4407    * copy it to the real DC (the screen).  This avoids\r
4408    * the problems with flickering.\r
4409    */\r
4410   GetClientRect(hwndMain, &Rect);\r
4411   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4412                                         Rect.bottom-Rect.top+1);\r
4413   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4414   if (!appData.monoMode) {\r
4415     SelectPalette(hdcmem, hPal, FALSE);\r
4416   }\r
4417 \r
4418   /* Create clips for dragging */\r
4419   if (!fullrepaint) {\r
4420     if (dragInfo.from.x >= 0) {\r
4421       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4422       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4423     }\r
4424     if (dragInfo.start.x >= 0) {\r
4425       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4426       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4427     }\r
4428     if (dragInfo.pos.x >= 0) {\r
4429       x = dragInfo.pos.x - squareSize / 2;\r
4430       y = dragInfo.pos.y - squareSize / 2;\r
4431       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4432     }\r
4433     if (dragInfo.lastpos.x >= 0) {\r
4434       x = dragInfo.lastpos.x - squareSize / 2;\r
4435       y = dragInfo.lastpos.y - squareSize / 2;\r
4436       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4437     }\r
4438   }\r
4439 \r
4440   /* Are we animating a move?  \r
4441    * If so, \r
4442    *   - remove the piece from the board (temporarely)\r
4443    *   - calculate the clipping region\r
4444    */\r
4445   if (!fullrepaint) {\r
4446     if (animInfo.piece != EmptySquare) {\r
4447       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4448       x = boardRect.left + animInfo.lastpos.x;\r
4449       y = boardRect.top + animInfo.lastpos.y;\r
4450       x2 = boardRect.left + animInfo.pos.x;\r
4451       y2 = boardRect.top + animInfo.pos.y;\r
4452       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4453       /* Slight kludge.  The real problem is that after AnimateMove is\r
4454          done, the position on the screen does not match lastDrawn.\r
4455          This currently causes trouble only on e.p. captures in\r
4456          atomic, where the piece moves to an empty square and then\r
4457          explodes.  The old and new positions both had an empty square\r
4458          at the destination, but animation has drawn a piece there and\r
4459          we have to remember to erase it. */\r
4460       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4461     }\r
4462   }\r
4463 \r
4464   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4465   if (num_clips == 0)\r
4466     fullrepaint = TRUE;\r
4467 \r
4468   /* Set clipping on the memory DC */\r
4469   if (!fullrepaint) {\r
4470     SelectClipRgn(hdcmem, clips[0]);\r
4471     for (x = 1; x < num_clips; x++) {\r
4472       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4473         abort();  // this should never ever happen!\r
4474     }\r
4475   }\r
4476 \r
4477   /* Do all the drawing to the memory DC */\r
4478   DrawGridOnDC(hdcmem);\r
4479   DrawHighlightsOnDC(hdcmem);\r
4480   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4481 \r
4482   if(logoHeight) {\r
4483         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4484         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4485   }\r
4486 \r
4487   if( appData.highlightMoveWithArrow ) {\r
4488     DrawArrowHighlight(hdcmem);\r
4489   }\r
4490 \r
4491   DrawCoordsOnDC(hdcmem);\r
4492 \r
4493   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4494                  /* to make sure lastDrawn contains what is actually drawn */\r
4495 \r
4496   /* Put the dragged piece back into place and draw it (out of place!) */\r
4497     if (dragged_piece != EmptySquare) {\r
4498     /* [HGM] or restack */\r
4499     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4500                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4501     else\r
4502     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4503                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4504     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4505     x = dragInfo.pos.x - squareSize / 2;\r
4506     y = dragInfo.pos.y - squareSize / 2;\r
4507     DrawPieceOnDC(hdcmem, dragged_piece,\r
4508                   ((int) dragged_piece < (int) BlackPawn), \r
4509                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4510   }   \r
4511   \r
4512   /* Put the animated piece back into place and draw it */\r
4513   if (animInfo.piece != EmptySquare) {\r
4514     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4515     x = boardRect.left + animInfo.pos.x;\r
4516     y = boardRect.top + animInfo.pos.y;\r
4517     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4518                   ((int) animInfo.piece < (int) BlackPawn),\r
4519                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4520   }\r
4521 \r
4522   /* Release the bufferBitmap by selecting in the old bitmap \r
4523    * and delete the memory DC\r
4524    */\r
4525   SelectObject(hdcmem, oldBitmap);\r
4526   DeleteDC(hdcmem);\r
4527 \r
4528   /* Set clipping on the target DC */\r
4529   if (!fullrepaint) {\r
4530     SelectClipRgn(hdc, clips[0]);\r
4531     for (x = 1; x < num_clips; x++) {\r
4532       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4533         abort();   // this should never ever happen!\r
4534     } \r
4535   }\r
4536 \r
4537   /* Copy the new bitmap onto the screen in one go.\r
4538    * This way we avoid any flickering\r
4539    */\r
4540   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4541   BitBlt(hdc, boardRect.left, boardRect.top,\r
4542          boardRect.right - boardRect.left,\r
4543          boardRect.bottom - boardRect.top,\r
4544          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4545   if(saveDiagFlag) { \r
4546     BITMAP b; int i, j, m, w, wb, fac=0; char pData[1000000]; \r
4547     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4548 \r
4549     GetObject(bufferBitmap, sizeof(b), &b);\r
4550     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4551         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4552         bih.biWidth = b.bmWidth;\r
4553         bih.biHeight = b.bmHeight;\r
4554         bih.biPlanes = 1;\r
4555         bih.biBitCount = b.bmBitsPixel;\r
4556         bih.biCompression = 0;\r
4557         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4558         bih.biXPelsPerMeter = 0;\r
4559         bih.biYPelsPerMeter = 0;\r
4560         bih.biClrUsed = 0;\r
4561         bih.biClrImportant = 0;\r
4562 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4563 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4564         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4565 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4566 \r
4567 #if 1\r
4568         wb = b.bmWidthBytes;\r
4569         // count colors\r
4570         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4571                 int k = ((int*) pData)[i];\r
4572                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4573                 if(j >= 16) break;\r
4574                 color[j] = k;\r
4575                 if(j >= nrColors) nrColors = j+1;\r
4576         }\r
4577         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4578                 INT p = 0;\r
4579                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4580                     for(w=0; w<(wb>>2); w+=2) {\r
4581                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4582                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4583                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4584                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4585                         pData[p++] = m | j<<4;\r
4586                     }\r
4587                     while(p&3) pData[p++] = 0;\r
4588                 }\r
4589                 fac = 3;\r
4590                 wb = (wb+31>>5)<<2;\r
4591         }\r
4592         // write BITMAPFILEHEADER\r
4593         fprintf(diagFile, "BM");\r
4594         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4595         fputDW(diagFile, 0);\r
4596         fputDW(diagFile, 0x36 + (fac?64:0));\r
4597         // write BITMAPINFOHEADER\r
4598         fputDW(diagFile, 40);\r
4599         fputDW(diagFile, b.bmWidth);\r
4600         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4601         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4602         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4603         fputDW(diagFile, 0);\r
4604         fputDW(diagFile, 0);\r
4605         fputDW(diagFile, 0);\r
4606         fputDW(diagFile, 0);\r
4607         fputDW(diagFile, 0);\r
4608         fputDW(diagFile, 0);\r
4609         // write color table\r
4610         if(fac)\r
4611         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4612         // write bitmap data\r
4613         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4614                 fputc(pData[i], diagFile);\r
4615 #endif\r
4616      }\r
4617   }\r
4618 \r
4619   SelectObject(tmphdc, oldBitmap);\r
4620 \r
4621   /* Massive cleanup */\r
4622   for (x = 0; x < num_clips; x++)\r
4623     DeleteObject(clips[x]);\r
4624 \r
4625   DeleteDC(tmphdc);\r
4626   DeleteObject(bufferBitmap);\r
4627 \r
4628   if (releaseDC) \r
4629     ReleaseDC(hwndMain, hdc);\r
4630   \r
4631   if (lastDrawnFlipView != flipView) {\r
4632     if (flipView)\r
4633       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4634     else\r
4635       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4636   }\r
4637 \r
4638 /*  CopyBoard(lastDrawn, board);*/\r
4639   lastDrawnHighlight = highlightInfo;\r
4640   lastDrawnPremove   = premoveHighlightInfo;\r
4641   lastDrawnFlipView = flipView;\r
4642   lastDrawnValid = 1;\r
4643 }\r
4644 \r
4645 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4646 int\r
4647 SaveDiagram(f)\r
4648      FILE *f;\r
4649 {\r
4650     time_t tm;\r
4651     char *fen;\r
4652 \r
4653     saveDiagFlag = 1; diagFile = f;\r
4654     HDCDrawPosition(NULL, TRUE, NULL);\r
4655 \r
4656     saveDiagFlag = 0;\r
4657 \r
4658 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4659     \r
4660     fclose(f);\r
4661     return TRUE;\r
4662 }\r
4663 \r
4664 \r
4665 /*---------------------------------------------------------------------------*\\r
4666 | CLIENT PAINT PROCEDURE\r
4667 |   This is the main event-handler for the WM_PAINT message.\r
4668 |\r
4669 \*---------------------------------------------------------------------------*/\r
4670 VOID\r
4671 PaintProc(HWND hwnd)\r
4672 {\r
4673   HDC         hdc;\r
4674   PAINTSTRUCT ps;\r
4675   HFONT       oldFont;\r
4676 \r
4677   if(hdc = BeginPaint(hwnd, &ps)) {\r
4678     if (IsIconic(hwnd)) {\r
4679       DrawIcon(hdc, 2, 2, iconCurrent);\r
4680     } else {\r
4681       if (!appData.monoMode) {\r
4682         SelectPalette(hdc, hPal, FALSE);\r
4683         RealizePalette(hdc);\r
4684       }\r
4685       HDCDrawPosition(hdc, 1, NULL);\r
4686       oldFont =\r
4687         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4688       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4689                  ETO_CLIPPED|ETO_OPAQUE,\r
4690                  &messageRect, messageText, strlen(messageText), NULL);\r
4691       SelectObject(hdc, oldFont);\r
4692       DisplayBothClocks();\r
4693     }\r
4694     EndPaint(hwnd,&ps);\r
4695   }\r
4696 \r
4697   return;\r
4698 }\r
4699 \r
4700 \r
4701 /*\r
4702  * If the user selects on a border boundary, return -1; if off the board,\r
4703  *   return -2.  Otherwise map the event coordinate to the square.\r
4704  * The offset boardRect.left or boardRect.top must already have been\r
4705  *   subtracted from x.\r
4706  */\r
4707 int\r
4708 EventToSquare(int x)\r
4709 {\r
4710   if (x <= 0)\r
4711     return -2;\r
4712   if (x < lineGap)\r
4713     return -1;\r
4714   x -= lineGap;\r
4715   if ((x % (squareSize + lineGap)) >= squareSize)\r
4716     return -1;\r
4717   x /= (squareSize + lineGap);\r
4718   if (x >= BOARD_SIZE)\r
4719     return -2;\r
4720   return x;\r
4721 }\r
4722 \r
4723 typedef struct {\r
4724   char piece;\r
4725   int command;\r
4726   char* name;\r
4727 } DropEnable;\r
4728 \r
4729 DropEnable dropEnables[] = {\r
4730   { 'P', DP_Pawn, "Pawn" },\r
4731   { 'N', DP_Knight, "Knight" },\r
4732   { 'B', DP_Bishop, "Bishop" },\r
4733   { 'R', DP_Rook, "Rook" },\r
4734   { 'Q', DP_Queen, "Queen" },\r
4735 };\r
4736 \r
4737 VOID\r
4738 SetupDropMenu(HMENU hmenu)\r
4739 {\r
4740   int i, count, enable;\r
4741   char *p;\r
4742   extern char white_holding[], black_holding[];\r
4743   char item[MSG_SIZ];\r
4744 \r
4745   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4746     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4747                dropEnables[i].piece);\r
4748     count = 0;\r
4749     while (p && *p++ == dropEnables[i].piece) count++;\r
4750     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4751     enable = count > 0 || !appData.testLegality\r
4752       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4753                       && !appData.icsActive);\r
4754     ModifyMenu(hmenu, dropEnables[i].command,\r
4755                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4756                dropEnables[i].command, item);\r
4757   }\r
4758 }\r
4759 \r
4760 static int fromX = -1, fromY = -1, toX, toY;\r
4761 \r
4762 /* Event handler for mouse messages */\r
4763 VOID\r
4764 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4765 {\r
4766   int x, y;\r
4767   POINT pt;\r
4768   static int recursive = 0;\r
4769   HMENU hmenu;\r
4770   BOOLEAN needsRedraw = FALSE;\r
4771   BOOLEAN saveAnimate;\r
4772   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4773   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4774   ChessMove moveType;\r
4775 \r
4776   if (recursive) {\r
4777     if (message == WM_MBUTTONUP) {\r
4778       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4779          to the middle button: we simulate pressing the left button too!\r
4780          */\r
4781       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4782       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4783     }\r
4784     return;\r
4785   }\r
4786   recursive++;\r
4787   \r
4788   pt.x = LOWORD(lParam);\r
4789   pt.y = HIWORD(lParam);\r
4790   x = EventToSquare(pt.x - boardRect.left);\r
4791   y = EventToSquare(pt.y - boardRect.top);\r
4792   if (!flipView && y >= 0) {\r
4793     y = BOARD_HEIGHT - 1 - y;\r
4794   }\r
4795   if (flipView && x >= 0) {\r
4796     x = BOARD_WIDTH - 1 - x;\r
4797   }\r
4798 \r
4799   switch (message) {\r
4800   case WM_LBUTTONDOWN:\r
4801     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4802         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4803         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4804         if(gameInfo.holdingsWidth && \r
4805                 (WhiteOnMove(currentMove) \r
4806                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4807                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4808             // click in right holdings, for determining promotion piece\r
4809             ChessSquare p = boards[currentMove][y][x];\r
4810             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4811             if(p != EmptySquare) {\r
4812                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4813                 fromX = fromY = -1;\r
4814                 break;\r
4815             }\r
4816         }\r
4817         DrawPosition(FALSE, boards[currentMove]);\r
4818         break;\r
4819     }\r
4820     ErrorPopDown();\r
4821     sameAgain = FALSE;\r
4822     if (y == -2) {\r
4823       /* Downclick vertically off board; check if on clock */\r
4824       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4825         if (gameMode == EditPosition) {\r
4826           SetWhiteToPlayEvent();\r
4827         } else if (gameMode == IcsPlayingBlack ||\r
4828                    gameMode == MachinePlaysWhite) {\r
4829           CallFlagEvent();\r
4830         } else if (gameMode == EditGame) {\r
4831           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4832         }\r
4833       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4834         if (gameMode == EditPosition) {\r
4835           SetBlackToPlayEvent();\r
4836         } else if (gameMode == IcsPlayingWhite ||\r
4837                    gameMode == MachinePlaysBlack) {\r
4838           CallFlagEvent();\r
4839         } else if (gameMode == EditGame) {\r
4840           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4841         }\r
4842       }\r
4843       if (!appData.highlightLastMove) {\r
4844         ClearHighlights();\r
4845         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4846       }\r
4847       fromX = fromY = -1;\r
4848       dragInfo.start.x = dragInfo.start.y = -1;\r
4849       dragInfo.from = dragInfo.start;\r
4850       break;\r
4851     } else if (x < 0 || y < 0\r
4852       /* [HGM] block clicks between board and holdings */\r
4853               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4854               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4855               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4856         /* EditPosition, empty square, or different color piece;\r
4857            click-click move is possible */\r
4858                                ) {\r
4859       break;\r
4860     } else if (fromX == x && fromY == y) {\r
4861       /* Downclick on same square again */\r
4862       ClearHighlights();\r
4863       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4864       sameAgain = TRUE;  \r
4865     } else if (fromX != -1 &&\r
4866                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4867                                                                         ) {\r
4868       /* Downclick on different square. */\r
4869       /* [HGM] if on holdings file, should count as new first click ! */\r
4870       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4871         toX = x;\r
4872         toY = y;\r
4873         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4874            to make sure move is legal before showing promotion popup */\r
4875         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4876         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4877                 fromX = fromY = -1; \r
4878                 ClearHighlights();\r
4879                 DrawPosition(FALSE, boards[currentMove]);\r
4880                 break; \r
4881         } else \r
4882         if(moveType != ImpossibleMove) {\r
4883           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4884           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4885              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4886               appData.alwaysPromoteToQueen) {\r
4887                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4888                   if (!appData.highlightLastMove) {\r
4889                       ClearHighlights();\r
4890                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4891                   }\r
4892           } else\r
4893           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4894                   SetHighlights(fromX, fromY, toX, toY);\r
4895                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4896                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4897                      If promotion to Q is legal, all are legal! */\r
4898                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4899                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4900                     // kludge to temporarily execute move on display, wthout promotng yet\r
4901                     promotionChoice = TRUE;\r
4902                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4903                     boards[currentMove][toY][toX] = p;\r
4904                     DrawPosition(FALSE, boards[currentMove]);\r
4905                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4906                     boards[currentMove][toY][toX] = q;\r
4907                   } else\r
4908                   PromotionPopup(hwnd);\r
4909           } else {       /* not a promotion */\r
4910              if (appData.animate || appData.highlightLastMove) {\r
4911                  SetHighlights(fromX, fromY, toX, toY);\r
4912              } else {\r
4913                  ClearHighlights();\r
4914              }\r
4915              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4916              fromX = fromY = -1;\r
4917              if (appData.animate && !appData.highlightLastMove) {\r
4918                   ClearHighlights();\r
4919                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4920              }\r
4921           }\r
4922           break;\r
4923         }\r
4924         if (gotPremove) {\r
4925             /* [HGM] it seemed that braces were missing here */\r
4926             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4927             fromX = fromY = -1;\r
4928             break;\r
4929         }\r
4930       }\r
4931       ClearHighlights();\r
4932       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4933     }\r
4934     /* First downclick, or restart on a square with same color piece */\r
4935     if (!frozen && OKToStartUserMove(x, y)) {\r
4936       fromX = x;\r
4937       fromY = y;\r
4938       dragInfo.lastpos = pt;\r
4939       dragInfo.from.x = fromX;\r
4940       dragInfo.from.y = fromY;\r
4941       dragInfo.start = dragInfo.from;\r
4942       SetCapture(hwndMain);\r
4943     } else {\r
4944       fromX = fromY = -1;\r
4945       dragInfo.start.x = dragInfo.start.y = -1;\r
4946       dragInfo.from = dragInfo.start;\r
4947       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4948     }\r
4949     break;\r
4950 \r
4951   case WM_LBUTTONUP:\r
4952     ReleaseCapture();\r
4953     if (fromX == -1) break;\r
4954     if (x == fromX && y == fromY) {\r
4955       dragInfo.from.x = dragInfo.from.y = -1;\r
4956       /* Upclick on same square */\r
4957       if (sameAgain) {\r
4958         /* Clicked same square twice: abort click-click move */\r
4959         fromX = fromY = -1;\r
4960         gotPremove = 0;\r
4961         ClearPremoveHighlights();\r
4962       } else {\r
4963         /* First square clicked: start click-click move */\r
4964         SetHighlights(fromX, fromY, -1, -1);\r
4965       }\r
4966       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4967     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4968       /* Errant click; ignore */\r
4969       break;\r
4970     } else {\r
4971       /* Finish drag move. */\r
4972     if (appData.debugMode) {\r
4973         fprintf(debugFP, "release\n");\r
4974     }\r
4975       dragInfo.from.x = dragInfo.from.y = -1;\r
4976       toX = x;\r
4977       toY = y;\r
4978       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4979       appData.animate = appData.animate && !appData.animateDragging;\r
4980       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4981       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4982                 fromX = fromY = -1; \r
4983                 ClearHighlights();\r
4984                 DrawPosition(FALSE, boards[currentMove]);\r
4985                 break; \r
4986       } else \r
4987       if(moveType != ImpossibleMove) {\r
4988           /* [HGM] use move type to determine if move is promotion.\r
4989              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4990           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4991              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4992               appData.alwaysPromoteToQueen) \r
4993                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4994           else \r
4995           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4996                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4997                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4998                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4999                     // kludge to temporarily execute move on display, wthout promotng yet\r
5000                     promotionChoice = TRUE;\r
5001                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5002                     boards[currentMove][toY][toX] = p;\r
5003                     DrawPosition(FALSE, boards[currentMove]);\r
5004                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5005                     boards[currentMove][toY][toX] = q;\r
5006                     break;\r
5007                   } else\r
5008                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5009         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5010       }\r
5011       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5012       appData.animate = saveAnimate;\r
5013       fromX = fromY = -1;\r
5014       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5015         ClearHighlights();\r
5016       }\r
5017       if (appData.animate || appData.animateDragging ||\r
5018           appData.highlightDragging || gotPremove) {\r
5019         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5020       }\r
5021     }\r
5022     dragInfo.start.x = dragInfo.start.y = -1; \r
5023     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5024     break;\r
5025 \r
5026   case WM_MOUSEMOVE:\r
5027     if ((appData.animateDragging || appData.highlightDragging)\r
5028         && (wParam & MK_LBUTTON)\r
5029         && dragInfo.from.x >= 0) \r
5030     {\r
5031       BOOL full_repaint = FALSE;\r
5032 \r
5033       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5034       if (appData.animateDragging) {\r
5035         dragInfo.pos = pt;\r
5036       }\r
5037       if (appData.highlightDragging) {\r
5038         SetHighlights(fromX, fromY, x, y);\r
5039         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5040             full_repaint = TRUE;\r
5041         }\r
5042       }\r
5043       \r
5044       DrawPosition( full_repaint, NULL);\r
5045       \r
5046       dragInfo.lastpos = dragInfo.pos;\r
5047     }\r
5048     break;\r
5049 \r
5050   case WM_MOUSEWHEEL: // [DM]\r
5051        /* Mouse Wheel is being rolled forward\r
5052         * Play moves forward\r
5053         */\r
5054        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) ForwardEvent();\r
5055        /* Mouse Wheel is being rolled backward\r
5056         * Play moves backward\r
5057         */\r
5058        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) BackwardEvent();\r
5059        break;\r
5060 \r
5061   case WM_MBUTTONDOWN:\r
5062   case WM_RBUTTONDOWN:\r
5063     ErrorPopDown();\r
5064     ReleaseCapture();\r
5065     fromX = fromY = -1;\r
5066     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5067     dragInfo.start.x = dragInfo.start.y = -1;\r
5068     dragInfo.from = dragInfo.start;\r
5069     dragInfo.lastpos = dragInfo.pos;\r
5070     if (appData.highlightDragging) {\r
5071       ClearHighlights();\r
5072     }\r
5073     if(y == -2) {\r
5074       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5075       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5076           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5077       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5078           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5079       }\r
5080     }\r
5081     DrawPosition(TRUE, NULL);\r
5082 \r
5083     switch (gameMode) {\r
5084     case EditPosition:\r
5085     case IcsExamining:\r
5086       if (x < 0 || y < 0) break;\r
5087       fromX = x;\r
5088       fromY = y;\r
5089       if (message == WM_MBUTTONDOWN) {\r
5090         buttonCount = 3;  /* even if system didn't think so */\r
5091         if (wParam & MK_SHIFT) \r
5092           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5093         else\r
5094           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5095       } else { /* message == WM_RBUTTONDOWN */\r
5096 #if 0\r
5097         if (buttonCount == 3) {\r
5098           if (wParam & MK_SHIFT) \r
5099             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5100           else\r
5101             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5102         } else {\r
5103           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5104         }\r
5105 #else\r
5106         /* Just have one menu, on the right button.  Windows users don't\r
5107            think to try the middle one, and sometimes other software steals\r
5108            it, or it doesn't really exist. */\r
5109         if(gameInfo.variant != VariantShogi)\r
5110             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5111         else\r
5112             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5113 #endif\r
5114       }\r
5115       break;\r
5116     case IcsPlayingWhite:\r
5117     case IcsPlayingBlack:\r
5118     case EditGame:\r
5119     case MachinePlaysWhite:\r
5120     case MachinePlaysBlack:\r
5121       if (appData.testLegality &&\r
5122           gameInfo.variant != VariantBughouse &&\r
5123           gameInfo.variant != VariantCrazyhouse) break;\r
5124       if (x < 0 || y < 0) break;\r
5125       fromX = x;\r
5126       fromY = y;\r
5127       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5128       SetupDropMenu(hmenu);\r
5129       MenuPopup(hwnd, pt, hmenu, -1);\r
5130       break;\r
5131     default:\r
5132       break;\r
5133     }\r
5134     break;\r
5135   }\r
5136 \r
5137   recursive--;\r
5138 }\r
5139 \r
5140 /* Preprocess messages for buttons in main window */\r
5141 LRESULT CALLBACK\r
5142 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5143 {\r
5144   int id = GetWindowLong(hwnd, GWL_ID);\r
5145   int i, dir;\r
5146 \r
5147   for (i=0; i<N_BUTTONS; i++) {\r
5148     if (buttonDesc[i].id == id) break;\r
5149   }\r
5150   if (i == N_BUTTONS) return 0;\r
5151   switch (message) {\r
5152   case WM_KEYDOWN:\r
5153     switch (wParam) {\r
5154     case VK_LEFT:\r
5155     case VK_RIGHT:\r
5156       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5157       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5158       return TRUE;\r
5159     }\r
5160     break;\r
5161   case WM_CHAR:\r
5162     switch (wParam) {\r
5163     case '\r':\r
5164       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5165       return TRUE;\r
5166     case '\t':\r
5167       if (appData.icsActive) {\r
5168         if (GetKeyState(VK_SHIFT) < 0) {\r
5169           /* shifted */\r
5170           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5171           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5172           SetFocus(h);\r
5173         } else {\r
5174           /* unshifted */\r
5175           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5176           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5177           SetFocus(h);\r
5178         }\r
5179         return TRUE;\r
5180       }\r
5181       break;\r
5182     default:\r
5183       if (appData.icsActive) {\r
5184         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5185         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5186         SetFocus(h);\r
5187         SendMessage(h, WM_CHAR, wParam, lParam);\r
5188         return TRUE;\r
5189       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5190         PopUpMoveDialog((char)wParam);\r
5191       }\r
5192       break;\r
5193     }\r
5194     break;\r
5195   }\r
5196   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5197 }\r
5198 \r
5199 /* Process messages for Promotion dialog box */\r
5200 LRESULT CALLBACK\r
5201 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5202 {\r
5203   char promoChar;\r
5204 \r
5205   switch (message) {\r
5206   case WM_INITDIALOG: /* message: initialize dialog box */\r
5207     /* Center the dialog over the application window */\r
5208     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5209     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5210       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5211        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5212                SW_SHOW : SW_HIDE);\r
5213     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5214     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5215        (PieceToChar(WhiteAngel) >= 'A' &&\r
5216         PieceToChar(WhiteAngel) != '~' ||\r
5217         PieceToChar(BlackAngel) >= 'A' &&\r
5218         PieceToChar(BlackAngel) != '~'   ) ?\r
5219                SW_SHOW : SW_HIDE);\r
5220     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5221        (PieceToChar(WhiteMarshall) >= 'A' &&\r
5222         PieceToChar(WhiteMarshall) != '~' ||\r
5223         PieceToChar(BlackMarshall) >= 'A' &&\r
5224         PieceToChar(BlackMarshall) != '~'   ) ?\r
5225                SW_SHOW : SW_HIDE);\r
5226     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5227     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5228        gameInfo.variant != VariantShogi ?\r
5229                SW_SHOW : SW_HIDE);\r
5230     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5231        gameInfo.variant != VariantShogi ?\r
5232                SW_SHOW : SW_HIDE);\r
5233     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5234        gameInfo.variant == VariantShogi ?\r
5235                SW_SHOW : SW_HIDE);\r
5236     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5237        gameInfo.variant == VariantShogi ?\r
5238                SW_SHOW : SW_HIDE);\r
5239     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5240        gameInfo.variant == VariantSuper ?\r
5241                SW_SHOW : SW_HIDE);\r
5242     return TRUE;\r
5243 \r
5244   case WM_COMMAND: /* message: received a command */\r
5245     switch (LOWORD(wParam)) {\r
5246     case IDCANCEL:\r
5247       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5248       ClearHighlights();\r
5249       DrawPosition(FALSE, NULL);\r
5250       return TRUE;\r
5251     case PB_King:\r
5252       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5253       break;\r
5254     case PB_Queen:\r
5255       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5256       break;\r
5257     case PB_Rook:\r
5258       promoChar = PieceToChar(BlackRook);\r
5259       break;\r
5260     case PB_Bishop:\r
5261       promoChar = PieceToChar(BlackBishop);\r
5262       break;\r
5263     case PB_Chancellor:\r
5264       promoChar = PieceToChar(BlackMarshall);\r
5265       break;\r
5266     case PB_Archbishop:\r
5267       promoChar = PieceToChar(BlackAngel);\r
5268       break;\r
5269     case PB_Knight:\r
5270       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5271       break;\r
5272     default:\r
5273       return FALSE;\r
5274     }\r
5275     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5276     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5277        only show the popup when we are already sure the move is valid or\r
5278        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5279        will figure out it is a promotion from the promoChar. */\r
5280     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5281     if (!appData.highlightLastMove) {\r
5282       ClearHighlights();\r
5283       DrawPosition(FALSE, NULL);\r
5284     }\r
5285     return TRUE;\r
5286   }\r
5287   return FALSE;\r
5288 }\r
5289 \r
5290 /* Pop up promotion dialog */\r
5291 VOID\r
5292 PromotionPopup(HWND hwnd)\r
5293 {\r
5294   FARPROC lpProc;\r
5295 \r
5296   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5297   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5298     hwnd, (DLGPROC)lpProc);\r
5299   FreeProcInstance(lpProc);\r
5300 }\r
5301 \r
5302 /* Toggle ShowThinking */\r
5303 VOID\r
5304 ToggleShowThinking()\r
5305 {\r
5306   appData.showThinking = !appData.showThinking;\r
5307   ShowThinkingEvent();\r
5308 }\r
5309 \r
5310 VOID\r
5311 LoadGameDialog(HWND hwnd, char* title)\r
5312 {\r
5313   UINT number = 0;\r
5314   FILE *f;\r
5315   char fileTitle[MSG_SIZ];\r
5316   f = OpenFileDialog(hwnd, "rb", "",\r
5317                      appData.oldSaveStyle ? "gam" : "pgn",\r
5318                      GAME_FILT,\r
5319                      title, &number, fileTitle, NULL);\r
5320   if (f != NULL) {\r
5321     cmailMsgLoaded = FALSE;\r
5322     if (number == 0) {\r
5323       int error = GameListBuild(f);\r
5324       if (error) {\r
5325         DisplayError("Cannot build game list", error);\r
5326       } else if (!ListEmpty(&gameList) &&\r
5327                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5328         GameListPopUp(f, fileTitle);\r
5329         return;\r
5330       }\r
5331       GameListDestroy();\r
5332       number = 1;\r
5333     }\r
5334     LoadGame(f, number, fileTitle, FALSE);\r
5335   }\r
5336 }\r
5337 \r
5338 VOID\r
5339 ChangedConsoleFont()\r
5340 {\r
5341   CHARFORMAT cfmt;\r
5342   CHARRANGE tmpsel, sel;\r
5343   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5344   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5345   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5346   PARAFORMAT paraf;\r
5347 \r
5348   cfmt.cbSize = sizeof(CHARFORMAT);\r
5349   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5350   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5351   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5352    * size.  This was undocumented in the version of MSVC++ that I had\r
5353    * when I wrote the code, but is apparently documented now.\r
5354    */\r
5355   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5356   cfmt.bCharSet = f->lf.lfCharSet;\r
5357   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5358   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5359   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5360   /* Why are the following seemingly needed too? */\r
5361   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5362   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5363   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5364   tmpsel.cpMin = 0;\r
5365   tmpsel.cpMax = -1; /*999999?*/\r
5366   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5367   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5368   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5369    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5370    */\r
5371   paraf.cbSize = sizeof(paraf);\r
5372   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5373   paraf.dxStartIndent = 0;\r
5374   paraf.dxOffset = WRAP_INDENT;\r
5375   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5376   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5377 }\r
5378 \r
5379 /*---------------------------------------------------------------------------*\\r
5380  *\r
5381  * Window Proc for main window\r
5382  *\r
5383 \*---------------------------------------------------------------------------*/\r
5384 \r
5385 /* Process messages for main window, etc. */\r
5386 LRESULT CALLBACK\r
5387 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5388 {\r
5389   FARPROC lpProc;\r
5390   int wmId, wmEvent;\r
5391   char *defName;\r
5392   FILE *f;\r
5393   UINT number;\r
5394   char fileTitle[MSG_SIZ];\r
5395   char buf[MSG_SIZ];\r
5396   static SnapData sd;\r
5397 \r
5398   switch (message) {\r
5399 \r
5400   case WM_PAINT: /* message: repaint portion of window */\r
5401     PaintProc(hwnd);\r
5402     break;\r
5403 \r
5404   case WM_ERASEBKGND:\r
5405     if (IsIconic(hwnd)) {\r
5406       /* Cheat; change the message */\r
5407       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5408     } else {\r
5409       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5410     }\r
5411     break;\r
5412 \r
5413   case WM_LBUTTONDOWN:\r
5414   case WM_MBUTTONDOWN:\r
5415   case WM_RBUTTONDOWN:\r
5416   case WM_LBUTTONUP:\r
5417   case WM_MBUTTONUP:\r
5418   case WM_RBUTTONUP:\r
5419   case WM_MOUSEMOVE:\r
5420   case WM_MOUSEWHEEL:\r
5421     MouseEvent(hwnd, message, wParam, lParam);\r
5422     break;\r
5423 \r
5424   case WM_CHAR:\r
5425     \r
5426     if (appData.icsActive) {\r
5427       if (wParam == '\t') {\r
5428         if (GetKeyState(VK_SHIFT) < 0) {\r
5429           /* shifted */\r
5430           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5431           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5432           SetFocus(h);\r
5433         } else {\r
5434           /* unshifted */\r
5435           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5436           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5437           SetFocus(h);\r
5438         }\r
5439       } else {\r
5440         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5441         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5442         SetFocus(h);\r
5443         SendMessage(h, message, wParam, lParam);\r
5444       }\r
5445     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5446       PopUpMoveDialog((char)wParam);\r
5447     }\r
5448     break;\r
5449 \r
5450   case WM_PALETTECHANGED:\r
5451     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5452       int nnew;\r
5453       HDC hdc = GetDC(hwndMain);\r
5454       SelectPalette(hdc, hPal, TRUE);\r
5455       nnew = RealizePalette(hdc);\r
5456       if (nnew > 0) {\r
5457         paletteChanged = TRUE;\r
5458 #if 0\r
5459         UpdateColors(hdc);\r
5460 #else\r
5461         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5462 #endif\r
5463       }\r
5464       ReleaseDC(hwnd, hdc);\r
5465     }\r
5466     break;\r
5467 \r
5468   case WM_QUERYNEWPALETTE:\r
5469     if (!appData.monoMode /*&& paletteChanged*/) {\r
5470       int nnew;\r
5471       HDC hdc = GetDC(hwndMain);\r
5472       paletteChanged = FALSE;\r
5473       SelectPalette(hdc, hPal, FALSE);\r
5474       nnew = RealizePalette(hdc);\r
5475       if (nnew > 0) {\r
5476         InvalidateRect(hwnd, &boardRect, FALSE);\r
5477       }\r
5478       ReleaseDC(hwnd, hdc);\r
5479       return TRUE;\r
5480     }\r
5481     return FALSE;\r
5482 \r
5483   case WM_COMMAND: /* message: command from application menu */\r
5484     wmId    = LOWORD(wParam);\r
5485     wmEvent = HIWORD(wParam);\r
5486 \r
5487     switch (wmId) {\r
5488     case IDM_NewGame:\r
5489       ResetGameEvent();\r
5490       AnalysisPopDown();\r
5491       break;\r
5492 \r
5493     case IDM_NewGameFRC:\r
5494       if( NewGameFRC() == 0 ) {\r
5495         ResetGameEvent();\r
5496         AnalysisPopDown();\r
5497       }\r
5498       break;\r
5499 \r
5500     case IDM_NewVariant:\r
5501       NewVariantPopup(hwnd);\r
5502       break;\r
5503 \r
5504     case IDM_LoadGame:\r
5505       LoadGameDialog(hwnd, "Load Game from File");\r
5506       break;\r
5507 \r
5508     case IDM_LoadNextGame:\r
5509       ReloadGame(1);\r
5510       break;\r
5511 \r
5512     case IDM_LoadPrevGame:\r
5513       ReloadGame(-1);\r
5514       break;\r
5515 \r
5516     case IDM_ReloadGame:\r
5517       ReloadGame(0);\r
5518       break;\r
5519 \r
5520     case IDM_LoadPosition:\r
5521       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5522         Reset(FALSE, TRUE);\r
5523       }\r
5524       number = 1;\r
5525       f = OpenFileDialog(hwnd, "rb", "",\r
5526                          appData.oldSaveStyle ? "pos" : "fen",\r
5527                          POSITION_FILT,\r
5528                          "Load Position from File", &number, fileTitle, NULL);\r
5529       if (f != NULL) {\r
5530         LoadPosition(f, number, fileTitle);\r
5531       }\r
5532       break;\r
5533 \r
5534     case IDM_LoadNextPosition:\r
5535       ReloadPosition(1);\r
5536       break;\r
5537 \r
5538     case IDM_LoadPrevPosition:\r
5539       ReloadPosition(-1);\r
5540       break;\r
5541 \r
5542     case IDM_ReloadPosition:\r
5543       ReloadPosition(0);\r
5544       break;\r
5545 \r
5546     case IDM_SaveGame:\r
5547       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5548       f = OpenFileDialog(hwnd, "a", defName,\r
5549                          appData.oldSaveStyle ? "gam" : "pgn",\r
5550                          GAME_FILT,\r
5551                          "Save Game to File", NULL, fileTitle, NULL);\r
5552       if (f != NULL) {\r
5553         SaveGame(f, 0, "");\r
5554       }\r
5555       break;\r
5556 \r
5557     case IDM_SavePosition:\r
5558       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5559       f = OpenFileDialog(hwnd, "a", defName,\r
5560                          appData.oldSaveStyle ? "pos" : "fen",\r
5561                          POSITION_FILT,\r
5562                          "Save Position to File", NULL, fileTitle, NULL);\r
5563       if (f != NULL) {\r
5564         SavePosition(f, 0, "");\r
5565       }\r
5566       break;\r
5567 \r
5568     case IDM_SaveDiagram:\r
5569       defName = "diagram";\r
5570       f = OpenFileDialog(hwnd, "wb", defName,\r
5571                          "bmp",\r
5572                          DIAGRAM_FILT,\r
5573                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5574       if (f != NULL) {\r
5575         SaveDiagram(f);\r
5576       }\r
5577       break;\r
5578 \r
5579     case IDM_CopyGame:\r
5580       CopyGameToClipboard();\r
5581       break;\r
5582 \r
5583     case IDM_PasteGame:\r
5584       PasteGameFromClipboard();\r
5585       break;\r
5586 \r
5587     case IDM_CopyGameListToClipboard:\r
5588       CopyGameListToClipboard();\r
5589       break;\r
5590 \r
5591     /* [AS] Autodetect FEN or PGN data */\r
5592     case IDM_PasteAny:\r
5593       PasteGameOrFENFromClipboard();\r
5594       break;\r
5595 \r
5596     /* [AS] Move history */\r
5597     case IDM_ShowMoveHistory:\r
5598         if( MoveHistoryIsUp() ) {\r
5599             MoveHistoryPopDown();\r
5600         }\r
5601         else {\r
5602             MoveHistoryPopUp();\r
5603         }\r
5604         break;\r
5605 \r
5606     /* [AS] Eval graph */\r
5607     case IDM_ShowEvalGraph:\r
5608         if( EvalGraphIsUp() ) {\r
5609             EvalGraphPopDown();\r
5610         }\r
5611         else {\r
5612             EvalGraphPopUp();\r
5613         }\r
5614         break;\r
5615 \r
5616     /* [AS] Engine output */\r
5617     case IDM_ShowEngineOutput:\r
5618         if( EngineOutputIsUp() ) {\r
5619             EngineOutputPopDown();\r
5620         }\r
5621         else {\r
5622             EngineOutputPopUp();\r
5623         }\r
5624         break;\r
5625 \r
5626     /* [AS] User adjudication */\r
5627     case IDM_UserAdjudication_White:\r
5628         UserAdjudicationEvent( +1 );\r
5629         break;\r
5630 \r
5631     case IDM_UserAdjudication_Black:\r
5632         UserAdjudicationEvent( -1 );\r
5633         break;\r
5634 \r
5635     case IDM_UserAdjudication_Draw:\r
5636         UserAdjudicationEvent( 0 );\r
5637         break;\r
5638 \r
5639     /* [AS] Game list options dialog */\r
5640     case IDM_GameListOptions:\r
5641       GameListOptions();\r
5642       break;\r
5643 \r
5644     case IDM_CopyPosition:\r
5645       CopyFENToClipboard();\r
5646       break;\r
5647 \r
5648     case IDM_PastePosition:\r
5649       PasteFENFromClipboard();\r
5650       break;\r
5651 \r
5652     case IDM_MailMove:\r
5653       MailMoveEvent();\r
5654       break;\r
5655 \r
5656     case IDM_ReloadCMailMsg:\r
5657       Reset(TRUE, TRUE);\r
5658       ReloadCmailMsgEvent(FALSE);\r
5659       break;\r
5660 \r
5661     case IDM_Minimize:\r
5662       ShowWindow(hwnd, SW_MINIMIZE);\r
5663       break;\r
5664 \r
5665     case IDM_Exit:\r
5666       ExitEvent(0);\r
5667       break;\r
5668 \r
5669     case IDM_MachineWhite:\r
5670       MachineWhiteEvent();\r
5671       /*\r
5672        * refresh the tags dialog only if it's visible\r
5673        */\r
5674       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5675           char *tags;\r
5676           tags = PGNTags(&gameInfo);\r
5677           TagsPopUp(tags, CmailMsg());\r
5678           free(tags);\r
5679       }\r
5680       break;\r
5681 \r
5682     case IDM_MachineBlack:\r
5683       MachineBlackEvent();\r
5684       /*\r
5685        * refresh the tags dialog only if it's visible\r
5686        */\r
5687       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5688           char *tags;\r
5689           tags = PGNTags(&gameInfo);\r
5690           TagsPopUp(tags, CmailMsg());\r
5691           free(tags);\r
5692       }\r
5693       break;\r
5694 \r
5695     case IDM_TwoMachines:\r
5696       TwoMachinesEvent();\r
5697       /*\r
5698        * refresh the tags dialog only if it's visible\r
5699        */\r
5700       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5701           char *tags;\r
5702           tags = PGNTags(&gameInfo);\r
5703           TagsPopUp(tags, CmailMsg());\r
5704           free(tags);\r
5705       }\r
5706       break;\r
5707 \r
5708     case IDM_AnalysisMode:\r
5709       if (!first.analysisSupport) {\r
5710         sprintf(buf, "%s does not support analysis", first.tidy);\r
5711         DisplayError(buf, 0);\r
5712       } else {\r
5713         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5714         if (appData.icsActive) {\r
5715                if (gameMode != IcsObserving) {\r
5716                        sprintf(buf, "You are not observing a game");\r
5717                        DisplayError(buf, 0);\r
5718                        /* secure check */\r
5719                        if (appData.icsEngineAnalyze) {\r
5720                                if (appData.debugMode) \r
5721                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5722                                ExitAnalyzeMode();\r
5723                                ModeHighlight();\r
5724                                break;\r
5725                        }\r
5726                        break;\r
5727                } else {\r
5728                        /* if enable, user want disable icsEngineAnalyze */\r
5729                        if (appData.icsEngineAnalyze) {\r
5730                                ExitAnalyzeMode();\r
5731                                ModeHighlight();\r
5732                                break;\r
5733                        }\r
5734                        appData.icsEngineAnalyze = TRUE;\r
5735                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5736                }\r
5737         } \r
5738         if (!appData.showThinking) ToggleShowThinking();\r
5739         AnalyzeModeEvent();\r
5740       }\r
5741       break;\r
5742 \r
5743     case IDM_AnalyzeFile:\r
5744       if (!first.analysisSupport) {\r
5745         char buf[MSG_SIZ];\r
5746         sprintf(buf, "%s does not support analysis", first.tidy);\r
5747         DisplayError(buf, 0);\r
5748       } else {\r
5749         if (!appData.showThinking) ToggleShowThinking();\r
5750         AnalyzeFileEvent();\r
5751         LoadGameDialog(hwnd, "Analyze Game from File");\r
5752         AnalysisPeriodicEvent(1);\r
5753       }\r
5754       break;\r
5755 \r
5756     case IDM_IcsClient:\r
5757       IcsClientEvent();\r
5758       break;\r
5759 \r
5760     case IDM_EditGame:\r
5761       EditGameEvent();\r
5762       break;\r
5763 \r
5764     case IDM_EditPosition:\r
5765       EditPositionEvent();\r
5766       break;\r
5767 \r
5768     case IDM_Training:\r
5769       TrainingEvent();\r
5770       break;\r
5771 \r
5772     case IDM_ShowGameList:\r
5773       ShowGameListProc();\r
5774       break;\r
5775 \r
5776     case IDM_EditTags:\r
5777       EditTagsProc();\r
5778       break;\r
5779 \r
5780     case IDM_EditComment:\r
5781       if (commentDialogUp && editComment) {\r
5782         CommentPopDown();\r
5783       } else {\r
5784         EditCommentEvent();\r
5785       }\r
5786       break;\r
5787 \r
5788     case IDM_Pause:\r
5789       PauseEvent();\r
5790       break;\r
5791 \r
5792     case IDM_Accept:\r
5793       AcceptEvent();\r
5794       break;\r
5795 \r
5796     case IDM_Decline:\r
5797       DeclineEvent();\r
5798       break;\r
5799 \r
5800     case IDM_Rematch:\r
5801       RematchEvent();\r
5802       break;\r
5803 \r
5804     case IDM_CallFlag:\r
5805       CallFlagEvent();\r
5806       break;\r
5807 \r
5808     case IDM_Draw:\r
5809       DrawEvent();\r
5810       break;\r
5811 \r
5812     case IDM_Adjourn:\r
5813       AdjournEvent();\r
5814       break;\r
5815 \r
5816     case IDM_Abort:\r
5817       AbortEvent();\r
5818       break;\r
5819 \r
5820     case IDM_Resign:\r
5821       ResignEvent();\r
5822       break;\r
5823 \r
5824     case IDM_StopObserving:\r
5825       StopObservingEvent();\r
5826       break;\r
5827 \r
5828     case IDM_StopExamining:\r
5829       StopExaminingEvent();\r
5830       break;\r
5831 \r
5832     case IDM_TypeInMove:\r
5833       PopUpMoveDialog('\000');\r
5834       break;\r
5835 \r
5836     case IDM_TypeInName:\r
5837       PopUpNameDialog('\000');\r
5838       break;\r
5839 \r
5840     case IDM_Backward:\r
5841       BackwardEvent();\r
5842       SetFocus(hwndMain);\r
5843       break;\r
5844 \r
5845     case IDM_Forward:\r
5846       ForwardEvent();\r
5847       SetFocus(hwndMain);\r
5848       break;\r
5849 \r
5850     case IDM_ToStart:\r
5851       ToStartEvent();\r
5852       SetFocus(hwndMain);\r
5853       break;\r
5854 \r
5855     case IDM_ToEnd:\r
5856       ToEndEvent();\r
5857       SetFocus(hwndMain);\r
5858       break;\r
5859 \r
5860     case IDM_Revert:\r
5861       RevertEvent();\r
5862       break;\r
5863 \r
5864     case IDM_TruncateGame:\r
5865       TruncateGameEvent();\r
5866       break;\r
5867 \r
5868     case IDM_MoveNow:\r
5869       MoveNowEvent();\r
5870       break;\r
5871 \r
5872     case IDM_RetractMove:\r
5873       RetractMoveEvent();\r
5874       break;\r
5875 \r
5876     case IDM_FlipView:\r
5877       flipView = !flipView;\r
5878       DrawPosition(FALSE, NULL);\r
5879       break;\r
5880 \r
5881     case IDM_FlipClock:\r
5882       flipClock = !flipClock;\r
5883       DisplayBothClocks();\r
5884       break;\r
5885 \r
5886     case IDM_GeneralOptions:\r
5887       GeneralOptionsPopup(hwnd);\r
5888       DrawPosition(TRUE, NULL);\r
5889       break;\r
5890 \r
5891     case IDM_BoardOptions:\r
5892       BoardOptionsPopup(hwnd);\r
5893       break;\r
5894 \r
5895     case IDM_EnginePlayOptions:\r
5896       EnginePlayOptionsPopup(hwnd);\r
5897       break;\r
5898 \r
5899     case IDM_OptionsUCI:\r
5900       UciOptionsPopup(hwnd);\r
5901       break;\r
5902 \r
5903     case IDM_IcsOptions:\r
5904       IcsOptionsPopup(hwnd);\r
5905       break;\r
5906 \r
5907     case IDM_Fonts:\r
5908       FontsOptionsPopup(hwnd);\r
5909       break;\r
5910 \r
5911     case IDM_Sounds:\r
5912       SoundOptionsPopup(hwnd);\r
5913       break;\r
5914 \r
5915     case IDM_CommPort:\r
5916       CommPortOptionsPopup(hwnd);\r
5917       break;\r
5918 \r
5919     case IDM_LoadOptions:\r
5920       LoadOptionsPopup(hwnd);\r
5921       break;\r
5922 \r
5923     case IDM_SaveOptions:\r
5924       SaveOptionsPopup(hwnd);\r
5925       break;\r
5926 \r
5927     case IDM_TimeControl:\r
5928       TimeControlOptionsPopup(hwnd);\r
5929       break;\r
5930 \r
5931     case IDM_SaveSettings:\r
5932       SaveSettings(settingsFileName);\r
5933       break;\r
5934 \r
5935     case IDM_SaveSettingsOnExit:\r
5936       saveSettingsOnExit = !saveSettingsOnExit;\r
5937       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5938                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5939                                          MF_CHECKED : MF_UNCHECKED));\r
5940       break;\r
5941 \r
5942     case IDM_Hint:\r
5943       HintEvent();\r
5944       break;\r
5945 \r
5946     case IDM_Book:\r
5947       BookEvent();\r
5948       break;\r
5949 \r
5950     case IDM_AboutGame:\r
5951       AboutGameEvent();\r
5952       break;\r
5953 \r
5954     case IDM_Debug:\r
5955       appData.debugMode = !appData.debugMode;\r
5956       if (appData.debugMode) {\r
5957         char dir[MSG_SIZ];\r
5958         GetCurrentDirectory(MSG_SIZ, dir);\r
5959         SetCurrentDirectory(installDir);\r
5960         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5961         SetCurrentDirectory(dir);\r
5962         setbuf(debugFP, NULL);\r
5963       } else {\r
5964         fclose(debugFP);\r
5965         debugFP = NULL;\r
5966       }\r
5967       break;\r
5968 \r
5969     case IDM_HELPCONTENTS:\r
5970       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5971         MessageBox (GetFocus(),\r
5972                     "Unable to activate help",\r
5973                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5974       }\r
5975       break;\r
5976 \r
5977     case IDM_HELPSEARCH:\r
5978       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5979         MessageBox (GetFocus(),\r
5980                     "Unable to activate help",\r
5981                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5982       }\r
5983       break;\r
5984 \r
5985     case IDM_HELPHELP:\r
5986       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5987         MessageBox (GetFocus(),\r
5988                     "Unable to activate help",\r
5989                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5990       }\r
5991       break;\r
5992 \r
5993     case IDM_ABOUT:\r
5994       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5995       DialogBox(hInst, \r
5996         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5997         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5998       FreeProcInstance(lpProc);\r
5999       break;\r
6000 \r
6001     case IDM_DirectCommand1:\r
6002       AskQuestionEvent("Direct Command",\r
6003                        "Send to chess program:", "", "1");\r
6004       break;\r
6005     case IDM_DirectCommand2:\r
6006       AskQuestionEvent("Direct Command",\r
6007                        "Send to second chess program:", "", "2");\r
6008       break;\r
6009 \r
6010     case EP_WhitePawn:\r
6011       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6012       fromX = fromY = -1;\r
6013       break;\r
6014 \r
6015     case EP_WhiteKnight:\r
6016       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6017       fromX = fromY = -1;\r
6018       break;\r
6019 \r
6020     case EP_WhiteBishop:\r
6021       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6022       fromX = fromY = -1;\r
6023       break;\r
6024 \r
6025     case EP_WhiteRook:\r
6026       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6027       fromX = fromY = -1;\r
6028       break;\r
6029 \r
6030     case EP_WhiteQueen:\r
6031       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6032       fromX = fromY = -1;\r
6033       break;\r
6034 \r
6035     case EP_WhiteFerz:\r
6036       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6037       fromX = fromY = -1;\r
6038       break;\r
6039 \r
6040     case EP_WhiteWazir:\r
6041       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6042       fromX = fromY = -1;\r
6043       break;\r
6044 \r
6045     case EP_WhiteAlfil:\r
6046       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6047       fromX = fromY = -1;\r
6048       break;\r
6049 \r
6050     case EP_WhiteCannon:\r
6051       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6052       fromX = fromY = -1;\r
6053       break;\r
6054 \r
6055     case EP_WhiteCardinal:\r
6056       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6057       fromX = fromY = -1;\r
6058       break;\r
6059 \r
6060     case EP_WhiteMarshall:\r
6061       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6062       fromX = fromY = -1;\r
6063       break;\r
6064 \r
6065     case EP_WhiteKing:\r
6066       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6067       fromX = fromY = -1;\r
6068       break;\r
6069 \r
6070     case EP_BlackPawn:\r
6071       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6072       fromX = fromY = -1;\r
6073       break;\r
6074 \r
6075     case EP_BlackKnight:\r
6076       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6077       fromX = fromY = -1;\r
6078       break;\r
6079 \r
6080     case EP_BlackBishop:\r
6081       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6082       fromX = fromY = -1;\r
6083       break;\r
6084 \r
6085     case EP_BlackRook:\r
6086       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6087       fromX = fromY = -1;\r
6088       break;\r
6089 \r
6090     case EP_BlackQueen:\r
6091       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6092       fromX = fromY = -1;\r
6093       break;\r
6094 \r
6095     case EP_BlackFerz:\r
6096       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6097       fromX = fromY = -1;\r
6098       break;\r
6099 \r
6100     case EP_BlackWazir:\r
6101       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6102       fromX = fromY = -1;\r
6103       break;\r
6104 \r
6105     case EP_BlackAlfil:\r
6106       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6107       fromX = fromY = -1;\r
6108       break;\r
6109 \r
6110     case EP_BlackCannon:\r
6111       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6112       fromX = fromY = -1;\r
6113       break;\r
6114 \r
6115     case EP_BlackCardinal:\r
6116       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6117       fromX = fromY = -1;\r
6118       break;\r
6119 \r
6120     case EP_BlackMarshall:\r
6121       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6122       fromX = fromY = -1;\r
6123       break;\r
6124 \r
6125     case EP_BlackKing:\r
6126       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6127       fromX = fromY = -1;\r
6128       break;\r
6129 \r
6130     case EP_EmptySquare:\r
6131       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6132       fromX = fromY = -1;\r
6133       break;\r
6134 \r
6135     case EP_ClearBoard:\r
6136       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6137       fromX = fromY = -1;\r
6138       break;\r
6139 \r
6140     case EP_White:\r
6141       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6142       fromX = fromY = -1;\r
6143       break;\r
6144 \r
6145     case EP_Black:\r
6146       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6147       fromX = fromY = -1;\r
6148       break;\r
6149 \r
6150     case EP_Promote:\r
6151       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6152       fromX = fromY = -1;\r
6153       break;\r
6154 \r
6155     case EP_Demote:\r
6156       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6157       fromX = fromY = -1;\r
6158       break;\r
6159 \r
6160     case DP_Pawn:\r
6161       DropMenuEvent(WhitePawn, fromX, fromY);\r
6162       fromX = fromY = -1;\r
6163       break;\r
6164 \r
6165     case DP_Knight:\r
6166       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6167       fromX = fromY = -1;\r
6168       break;\r
6169 \r
6170     case DP_Bishop:\r
6171       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6172       fromX = fromY = -1;\r
6173       break;\r
6174 \r
6175     case DP_Rook:\r
6176       DropMenuEvent(WhiteRook, fromX, fromY);\r
6177       fromX = fromY = -1;\r
6178       break;\r
6179 \r
6180     case DP_Queen:\r
6181       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6182       fromX = fromY = -1;\r
6183       break;\r
6184 \r
6185     default:\r
6186       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6187     }\r
6188     break;\r
6189 \r
6190   case WM_TIMER:\r
6191     switch (wParam) {\r
6192     case CLOCK_TIMER_ID:\r
6193       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6194       clockTimerEvent = 0;\r
6195       DecrementClocks(); /* call into back end */\r
6196       break;\r
6197     case LOAD_GAME_TIMER_ID:\r
6198       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6199       loadGameTimerEvent = 0;\r
6200       AutoPlayGameLoop(); /* call into back end */\r
6201       break;\r
6202     case ANALYSIS_TIMER_ID:\r
6203       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6204                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6205         AnalysisPeriodicEvent(0);\r
6206       } else {\r
6207         KillTimer(hwnd, analysisTimerEvent);\r
6208         analysisTimerEvent = 0;\r
6209       }\r
6210       break;\r
6211     case DELAYED_TIMER_ID:\r
6212       KillTimer(hwnd, delayedTimerEvent);\r
6213       delayedTimerEvent = 0;\r
6214       delayedTimerCallback();\r
6215       break;\r
6216     }\r
6217     break;\r
6218 \r
6219   case WM_USER_Input:\r
6220     InputEvent(hwnd, message, wParam, lParam);\r
6221     break;\r
6222 \r
6223   /* [AS] Also move "attached" child windows */\r
6224   case WM_WINDOWPOSCHANGING:\r
6225     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6226         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6227 \r
6228         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6229             /* Window is moving */\r
6230             RECT rcMain;\r
6231 \r
6232             GetWindowRect( hwnd, &rcMain );\r
6233             \r
6234             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6235             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6236             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6237         }\r
6238     }\r
6239     break;\r
6240 \r
6241   /* [AS] Snapping */\r
6242   case WM_ENTERSIZEMOVE:\r
6243     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6244     if (hwnd == hwndMain) {\r
6245       doingSizing = TRUE;\r
6246       lastSizing = 0;\r
6247     }\r
6248     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6249     break;\r
6250 \r
6251   case WM_SIZING:\r
6252     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6253     if (hwnd == hwndMain) {\r
6254       lastSizing = wParam;\r
6255     }\r
6256     break;\r
6257 \r
6258   case WM_MOVING:\r
6259     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6260       return OnMoving( &sd, hwnd, wParam, lParam );\r
6261 \r
6262   case WM_EXITSIZEMOVE:\r
6263     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6264     if (hwnd == hwndMain) {\r
6265       RECT client;\r
6266       doingSizing = FALSE;\r
6267       InvalidateRect(hwnd, &boardRect, FALSE);\r
6268       GetClientRect(hwnd, &client);\r
6269       ResizeBoard(client.right, client.bottom, lastSizing);\r
6270       lastSizing = 0;\r
6271       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6272     }\r
6273     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6274     break;\r
6275 \r
6276   case WM_DESTROY: /* message: window being destroyed */\r
6277     PostQuitMessage(0);\r
6278     break;\r
6279 \r
6280   case WM_CLOSE:\r
6281     if (hwnd == hwndMain) {\r
6282       ExitEvent(0);\r
6283     }\r
6284     break;\r
6285 \r
6286   default:      /* Passes it on if unprocessed */\r
6287     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6288   }\r
6289   return 0;\r
6290 }\r
6291 \r
6292 /*---------------------------------------------------------------------------*\\r
6293  *\r
6294  * Misc utility routines\r
6295  *\r
6296 \*---------------------------------------------------------------------------*/\r
6297 \r
6298 /*\r
6299  * Decent random number generator, at least not as bad as Windows\r
6300  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6301  */\r
6302 unsigned int randstate;\r
6303 \r
6304 int\r
6305 myrandom(void)\r
6306 {\r
6307   randstate = randstate * 1664525 + 1013904223;\r
6308   return (int) randstate & 0x7fffffff;\r
6309 }\r
6310 \r
6311 void\r
6312 mysrandom(unsigned int seed)\r
6313 {\r
6314   randstate = seed;\r
6315 }\r
6316 \r
6317 \r
6318 /* \r
6319  * returns TRUE if user selects a different color, FALSE otherwise \r
6320  */\r
6321 \r
6322 BOOL\r
6323 ChangeColor(HWND hwnd, COLORREF *which)\r
6324 {\r
6325   static BOOL firstTime = TRUE;\r
6326   static DWORD customColors[16];\r
6327   CHOOSECOLOR cc;\r
6328   COLORREF newcolor;\r
6329   int i;\r
6330   ColorClass ccl;\r
6331 \r
6332   if (firstTime) {\r
6333     /* Make initial colors in use available as custom colors */\r
6334     /* Should we put the compiled-in defaults here instead? */\r
6335     i = 0;\r
6336     customColors[i++] = lightSquareColor & 0xffffff;\r
6337     customColors[i++] = darkSquareColor & 0xffffff;\r
6338     customColors[i++] = whitePieceColor & 0xffffff;\r
6339     customColors[i++] = blackPieceColor & 0xffffff;\r
6340     customColors[i++] = highlightSquareColor & 0xffffff;\r
6341     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6342 \r
6343     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6344       customColors[i++] = textAttribs[ccl].color;\r
6345     }\r
6346     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6347     firstTime = FALSE;\r
6348   }\r
6349 \r
6350   cc.lStructSize = sizeof(cc);\r
6351   cc.hwndOwner = hwnd;\r
6352   cc.hInstance = NULL;\r
6353   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6354   cc.lpCustColors = (LPDWORD) customColors;\r
6355   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6356 \r
6357   if (!ChooseColor(&cc)) return FALSE;\r
6358 \r
6359   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6360   if (newcolor == *which) return FALSE;\r
6361   *which = newcolor;\r
6362   return TRUE;\r
6363 \r
6364   /*\r
6365   InitDrawingColors();\r
6366   InvalidateRect(hwnd, &boardRect, FALSE);\r
6367   */\r
6368 }\r
6369 \r
6370 BOOLEAN\r
6371 MyLoadSound(MySound *ms)\r
6372 {\r
6373   BOOL ok = FALSE;\r
6374   struct stat st;\r
6375   FILE *f;\r
6376 \r
6377   if (ms->data) free(ms->data);\r
6378   ms->data = NULL;\r
6379 \r
6380   switch (ms->name[0]) {\r
6381   case NULLCHAR:\r
6382     /* Silence */\r
6383     ok = TRUE;\r
6384     break;\r
6385   case '$':\r
6386     /* System sound from Control Panel.  Don't preload here. */\r
6387     ok = TRUE;\r
6388     break;\r
6389   case '!':\r
6390     if (ms->name[1] == NULLCHAR) {\r
6391       /* "!" alone = silence */\r
6392       ok = TRUE;\r
6393     } else {\r
6394       /* Builtin wave resource.  Error if not found. */\r
6395       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6396       if (h == NULL) break;\r
6397       ms->data = (void *)LoadResource(hInst, h);\r
6398       if (h == NULL) break;\r
6399       ok = TRUE;\r
6400     }\r
6401     break;\r
6402   default:\r
6403     /* .wav file.  Error if not found. */\r
6404     f = fopen(ms->name, "rb");\r
6405     if (f == NULL) break;\r
6406     if (fstat(fileno(f), &st) < 0) break;\r
6407     ms->data = malloc(st.st_size);\r
6408     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6409     fclose(f);\r
6410     ok = TRUE;\r
6411     break;\r
6412   }\r
6413   if (!ok) {\r
6414     char buf[MSG_SIZ];\r
6415     sprintf(buf, "Error loading sound %s", ms->name);\r
6416     DisplayError(buf, GetLastError());\r
6417   }\r
6418   return ok;\r
6419 }\r
6420 \r
6421 BOOLEAN\r
6422 MyPlaySound(MySound *ms)\r
6423 {\r
6424   BOOLEAN ok = FALSE;\r
6425   switch (ms->name[0]) {\r
6426   case NULLCHAR:\r
6427     /* Silence */\r
6428     ok = TRUE;\r
6429     break;\r
6430   case '$':\r
6431     /* System sound from Control Panel (deprecated feature).\r
6432        "$" alone or an unset sound name gets default beep (still in use). */\r
6433     if (ms->name[1]) {\r
6434       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6435     }\r
6436     if (!ok) ok = MessageBeep(MB_OK);\r
6437     break; \r
6438   case '!':\r
6439     /* Builtin wave resource, or "!" alone for silence */\r
6440     if (ms->name[1]) {\r
6441       if (ms->data == NULL) return FALSE;\r
6442       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6443     } else {\r
6444       ok = TRUE;\r
6445     }\r
6446     break;\r
6447   default:\r
6448     /* .wav file.  Error if not found. */\r
6449     if (ms->data == NULL) return FALSE;\r
6450     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6451     break;\r
6452   }\r
6453   /* Don't print an error: this can happen innocently if the sound driver\r
6454      is busy; for instance, if another instance of WinBoard is playing\r
6455      a sound at about the same time. */\r
6456 #if 0\r
6457   if (!ok) {\r
6458     char buf[MSG_SIZ];\r
6459     sprintf(buf, "Error playing sound %s", ms->name);\r
6460     DisplayError(buf, GetLastError());\r
6461   }\r
6462 #endif\r
6463   return ok;\r
6464 }\r
6465 \r
6466 \r
6467 LRESULT CALLBACK\r
6468 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6469 {\r
6470   BOOL ok;\r
6471   OPENFILENAME *ofn;\r
6472   static UINT *number; /* gross that this is static */\r
6473 \r
6474   switch (message) {\r
6475   case WM_INITDIALOG: /* message: initialize dialog box */\r
6476     /* Center the dialog over the application window */\r
6477     ofn = (OPENFILENAME *) lParam;\r
6478     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6479       number = (UINT *) ofn->lCustData;\r
6480       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6481     } else {\r
6482       number = NULL;\r
6483     }\r
6484     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6485     return FALSE;  /* Allow for further processing */\r
6486 \r
6487   case WM_COMMAND:\r
6488     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6489       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6490     }\r
6491     return FALSE;  /* Allow for further processing */\r
6492   }\r
6493   return FALSE;\r
6494 }\r
6495 \r
6496 UINT APIENTRY\r
6497 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6498 {\r
6499   static UINT *number;\r
6500   OPENFILENAME *ofname;\r
6501   OFNOTIFY *ofnot;\r
6502   switch (uiMsg) {\r
6503   case WM_INITDIALOG:\r
6504     ofname = (OPENFILENAME *)lParam;\r
6505     number = (UINT *)(ofname->lCustData);\r
6506     break;\r
6507   case WM_NOTIFY:\r
6508     ofnot = (OFNOTIFY *)lParam;\r
6509     if (ofnot->hdr.code == CDN_FILEOK) {\r
6510       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6511     }\r
6512     break;\r
6513   }\r
6514   return 0;\r
6515 }\r
6516 \r
6517 \r
6518 FILE *\r
6519 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6520                char *nameFilt, char *dlgTitle, UINT *number,\r
6521                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6522 {\r
6523   OPENFILENAME openFileName;\r
6524   char buf1[MSG_SIZ];\r
6525   FILE *f;\r
6526 \r
6527   if (fileName == NULL) fileName = buf1;\r
6528   if (defName == NULL) {\r
6529     strcpy(fileName, "*.");\r
6530     strcat(fileName, defExt);\r
6531   } else {\r
6532     strcpy(fileName, defName);\r
6533   }\r
6534   if (fileTitle) strcpy(fileTitle, "");\r
6535   if (number) *number = 0;\r
6536 \r
6537   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6538   openFileName.hwndOwner         = hwnd;\r
6539   openFileName.hInstance         = (HANDLE) hInst;\r
6540   openFileName.lpstrFilter       = nameFilt;\r
6541   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6542   openFileName.nMaxCustFilter    = 0L;\r
6543   openFileName.nFilterIndex      = 1L;\r
6544   openFileName.lpstrFile         = fileName;\r
6545   openFileName.nMaxFile          = MSG_SIZ;\r
6546   openFileName.lpstrFileTitle    = fileTitle;\r
6547   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6548   openFileName.lpstrInitialDir   = NULL;\r
6549   openFileName.lpstrTitle        = dlgTitle;\r
6550   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6551     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6552     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6553     | (oldDialog ? 0 : OFN_EXPLORER);\r
6554   openFileName.nFileOffset       = 0;\r
6555   openFileName.nFileExtension    = 0;\r
6556   openFileName.lpstrDefExt       = defExt;\r
6557   openFileName.lCustData         = (LONG) number;\r
6558   openFileName.lpfnHook          = oldDialog ?\r
6559     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6560   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6561 \r
6562   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6563                         GetOpenFileName(&openFileName)) {\r
6564     /* open the file */\r
6565     f = fopen(openFileName.lpstrFile, write);\r
6566     if (f == NULL) {\r
6567       MessageBox(hwnd, "File open failed", NULL,\r
6568                  MB_OK|MB_ICONEXCLAMATION);\r
6569       return NULL;\r
6570     }\r
6571   } else {\r
6572     int err = CommDlgExtendedError();\r
6573     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6574     return FALSE;\r
6575   }\r
6576   return f;\r
6577 }\r
6578 \r
6579 \r
6580 \r
6581 VOID APIENTRY\r
6582 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6583 {\r
6584   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6585 \r
6586   /*\r
6587    * Get the first pop-up menu in the menu template. This is the\r
6588    * menu that TrackPopupMenu displays.\r
6589    */\r
6590   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6591 \r
6592   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6593 \r
6594   /*\r
6595    * TrackPopup uses screen coordinates, so convert the\r
6596    * coordinates of the mouse click to screen coordinates.\r
6597    */\r
6598   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6599 \r
6600   /* Draw and track the floating pop-up menu. */\r
6601   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6602                  pt.x, pt.y, 0, hwnd, NULL);\r
6603 \r
6604   /* Destroy the menu.*/\r
6605   DestroyMenu(hmenu);\r
6606 }\r
6607    \r
6608 typedef struct {\r
6609   HWND hDlg, hText;\r
6610   int sizeX, sizeY, newSizeX, newSizeY;\r
6611   HDWP hdwp;\r
6612 } ResizeEditPlusButtonsClosure;\r
6613 \r
6614 BOOL CALLBACK\r
6615 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6616 {\r
6617   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6618   RECT rect;\r
6619   POINT pt;\r
6620 \r
6621   if (hChild == cl->hText) return TRUE;\r
6622   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6623   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6624   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6625   ScreenToClient(cl->hDlg, &pt);\r
6626   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6627     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6628   return TRUE;\r
6629 }\r
6630 \r
6631 /* Resize a dialog that has a (rich) edit field filling most of\r
6632    the top, with a row of buttons below */\r
6633 VOID\r
6634 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6635 {\r
6636   RECT rectText;\r
6637   int newTextHeight, newTextWidth;\r
6638   ResizeEditPlusButtonsClosure cl;\r
6639   \r
6640   /*if (IsIconic(hDlg)) return;*/\r
6641   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6642   \r
6643   cl.hdwp = BeginDeferWindowPos(8);\r
6644 \r
6645   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6646   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6647   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6648   if (newTextHeight < 0) {\r
6649     newSizeY += -newTextHeight;\r
6650     newTextHeight = 0;\r
6651   }\r
6652   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6653     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6654 \r
6655   cl.hDlg = hDlg;\r
6656   cl.hText = hText;\r
6657   cl.sizeX = sizeX;\r
6658   cl.sizeY = sizeY;\r
6659   cl.newSizeX = newSizeX;\r
6660   cl.newSizeY = newSizeY;\r
6661   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6662 \r
6663   EndDeferWindowPos(cl.hdwp);\r
6664 }\r
6665 \r
6666 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6667 {\r
6668     RECT    rChild, rParent;\r
6669     int     wChild, hChild, wParent, hParent;\r
6670     int     wScreen, hScreen, xNew, yNew;\r
6671     HDC     hdc;\r
6672 \r
6673     /* Get the Height and Width of the child window */\r
6674     GetWindowRect (hwndChild, &rChild);\r
6675     wChild = rChild.right - rChild.left;\r
6676     hChild = rChild.bottom - rChild.top;\r
6677 \r
6678     /* Get the Height and Width of the parent window */\r
6679     GetWindowRect (hwndParent, &rParent);\r
6680     wParent = rParent.right - rParent.left;\r
6681     hParent = rParent.bottom - rParent.top;\r
6682 \r
6683     /* Get the display limits */\r
6684     hdc = GetDC (hwndChild);\r
6685     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6686     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6687     ReleaseDC(hwndChild, hdc);\r
6688 \r
6689     /* Calculate new X position, then adjust for screen */\r
6690     xNew = rParent.left + ((wParent - wChild) /2);\r
6691     if (xNew < 0) {\r
6692         xNew = 0;\r
6693     } else if ((xNew+wChild) > wScreen) {\r
6694         xNew = wScreen - wChild;\r
6695     }\r
6696 \r
6697     /* Calculate new Y position, then adjust for screen */\r
6698     if( mode == 0 ) {\r
6699         yNew = rParent.top  + ((hParent - hChild) /2);\r
6700     }\r
6701     else {\r
6702         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6703     }\r
6704 \r
6705     if (yNew < 0) {\r
6706         yNew = 0;\r
6707     } else if ((yNew+hChild) > hScreen) {\r
6708         yNew = hScreen - hChild;\r
6709     }\r
6710 \r
6711     /* Set it, and return */\r
6712     return SetWindowPos (hwndChild, NULL,\r
6713                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6714 }\r
6715 \r
6716 /* Center one window over another */\r
6717 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6718 {\r
6719     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6720 }\r
6721 \r
6722 /*---------------------------------------------------------------------------*\\r
6723  *\r
6724  * Startup Dialog functions\r
6725  *\r
6726 \*---------------------------------------------------------------------------*/\r
6727 void\r
6728 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6729 {\r
6730   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6731 \r
6732   while (*cd != NULL) {\r
6733     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6734     cd++;\r
6735   }\r
6736 }\r
6737 \r
6738 void\r
6739 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6740 {\r
6741   char buf1[ARG_MAX];\r
6742   int len;\r
6743 \r
6744   if (str[0] == '@') {\r
6745     FILE* f = fopen(str + 1, "r");\r
6746     if (f == NULL) {\r
6747       DisplayFatalError(str + 1, errno, 2);\r
6748       return;\r
6749     }\r
6750     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6751     fclose(f);\r
6752     buf1[len] = NULLCHAR;\r
6753     str = buf1;\r
6754   }\r
6755 \r
6756   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6757 \r
6758   for (;;) {\r
6759     char buf[MSG_SIZ];\r
6760     char *end = strchr(str, '\n');\r
6761     if (end == NULL) return;\r
6762     memcpy(buf, str, end - str);\r
6763     buf[end - str] = NULLCHAR;\r
6764     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6765     str = end + 1;\r
6766   }\r
6767 }\r
6768 \r
6769 void\r
6770 SetStartupDialogEnables(HWND hDlg)\r
6771 {\r
6772   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6773     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6774     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6775   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6776     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6777   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6778     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6779   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6780     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6781   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6782     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6783     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6784     IsDlgButtonChecked(hDlg, OPT_View));\r
6785 }\r
6786 \r
6787 char *\r
6788 QuoteForFilename(char *filename)\r
6789 {\r
6790   int dquote, space;\r
6791   dquote = strchr(filename, '"') != NULL;\r
6792   space = strchr(filename, ' ') != NULL;\r
6793   if (dquote || space) {\r
6794     if (dquote) {\r
6795       return "'";\r
6796     } else {\r
6797       return "\"";\r
6798     }\r
6799   } else {\r
6800     return "";\r
6801   }\r
6802 }\r
6803 \r
6804 VOID\r
6805 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6806 {\r
6807   char buf[MSG_SIZ];\r
6808   char *q;\r
6809 \r
6810   InitComboStringsFromOption(hwndCombo, nthnames);\r
6811   q = QuoteForFilename(nthcp);\r
6812   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6813   if (*nthdir != NULLCHAR) {\r
6814     q = QuoteForFilename(nthdir);\r
6815     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6816   }\r
6817   if (*nthcp == NULLCHAR) {\r
6818     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6819   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6820     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6821     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6822   }\r
6823 }\r
6824 \r
6825 LRESULT CALLBACK\r
6826 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6827 {\r
6828   char buf[MSG_SIZ];\r
6829   HANDLE hwndCombo;\r
6830   char *p;\r
6831 \r
6832   switch (message) {\r
6833   case WM_INITDIALOG:\r
6834     /* Center the dialog */\r
6835     CenterWindow (hDlg, GetDesktopWindow());\r
6836     /* Initialize the dialog items */\r
6837     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6838                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6839                   firstChessProgramNames);\r
6840     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6841                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6842                   secondChessProgramNames);\r
6843     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6844     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6845     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6846     if (*appData.icsHelper != NULLCHAR) {\r
6847       char *q = QuoteForFilename(appData.icsHelper);\r
6848       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6849     }\r
6850     if (*appData.icsHost == NULLCHAR) {\r
6851       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6852       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6853     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6854       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6855       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6856     }\r
6857 \r
6858     if (appData.icsActive) {\r
6859       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6860     }\r
6861     else if (appData.noChessProgram) {\r
6862       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6863     }\r
6864     else {\r
6865       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6866     }\r
6867 \r
6868     SetStartupDialogEnables(hDlg);\r
6869     return TRUE;\r
6870 \r
6871   case WM_COMMAND:\r
6872     switch (LOWORD(wParam)) {\r
6873     case IDOK:\r
6874       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6875         strcpy(buf, "/fcp=");\r
6876         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6877         p = buf;\r
6878         ParseArgs(StringGet, &p);\r
6879         strcpy(buf, "/scp=");\r
6880         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6881         p = buf;\r
6882         ParseArgs(StringGet, &p);\r
6883         appData.noChessProgram = FALSE;\r
6884         appData.icsActive = FALSE;\r
6885       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6886         strcpy(buf, "/ics /icshost=");\r
6887         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6888         p = buf;\r
6889         ParseArgs(StringGet, &p);\r
6890         if (appData.zippyPlay) {\r
6891           strcpy(buf, "/fcp=");\r
6892           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6893           p = buf;\r
6894           ParseArgs(StringGet, &p);\r
6895         }\r
6896       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6897         appData.noChessProgram = TRUE;\r
6898         appData.icsActive = FALSE;\r
6899       } else {\r
6900         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6901                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6902         return TRUE;\r
6903       }\r
6904       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6905         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6906         p = buf;\r
6907         ParseArgs(StringGet, &p);\r
6908       }\r
6909       EndDialog(hDlg, TRUE);\r
6910       return TRUE;\r
6911 \r
6912     case IDCANCEL:\r
6913       ExitEvent(0);\r
6914       return TRUE;\r
6915 \r
6916     case IDM_HELPCONTENTS:\r
6917       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6918         MessageBox (GetFocus(),\r
6919                     "Unable to activate help",\r
6920                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6921       }\r
6922       break;\r
6923 \r
6924     default:\r
6925       SetStartupDialogEnables(hDlg);\r
6926       break;\r
6927     }\r
6928     break;\r
6929   }\r
6930   return FALSE;\r
6931 }\r
6932 \r
6933 /*---------------------------------------------------------------------------*\\r
6934  *\r
6935  * About box dialog functions\r
6936  *\r
6937 \*---------------------------------------------------------------------------*/\r
6938 \r
6939 /* Process messages for "About" dialog box */\r
6940 LRESULT CALLBACK\r
6941 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6942 {\r
6943   switch (message) {\r
6944   case WM_INITDIALOG: /* message: initialize dialog box */\r
6945     /* Center the dialog over the application window */\r
6946     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6947     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6948     return (TRUE);\r
6949 \r
6950   case WM_COMMAND: /* message: received a command */\r
6951     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6952         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6953       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6954       return (TRUE);\r
6955     }\r
6956     break;\r
6957   }\r
6958   return (FALSE);\r
6959 }\r
6960 \r
6961 /*---------------------------------------------------------------------------*\\r
6962  *\r
6963  * Comment Dialog functions\r
6964  *\r
6965 \*---------------------------------------------------------------------------*/\r
6966 \r
6967 LRESULT CALLBACK\r
6968 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6969 {\r
6970   static HANDLE hwndText = NULL;\r
6971   int len, newSizeX, newSizeY, flags;\r
6972   static int sizeX, sizeY;\r
6973   char *str;\r
6974   RECT rect;\r
6975   MINMAXINFO *mmi;\r
6976 \r
6977   switch (message) {\r
6978   case WM_INITDIALOG: /* message: initialize dialog box */\r
6979     /* Initialize the dialog items */\r
6980     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6981     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6982     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6983     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6984     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6985     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6986     SetWindowText(hDlg, commentTitle);\r
6987     if (editComment) {\r
6988       SetFocus(hwndText);\r
6989     } else {\r
6990       SetFocus(GetDlgItem(hDlg, IDOK));\r
6991     }\r
6992     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6993                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6994                 MAKELPARAM(FALSE, 0));\r
6995     /* Size and position the dialog */\r
6996     if (!commentDialog) {\r
6997       commentDialog = hDlg;\r
6998       flags = SWP_NOZORDER;\r
6999       GetClientRect(hDlg, &rect);\r
7000       sizeX = rect.right;\r
7001       sizeY = rect.bottom;\r
7002       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7003           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7004         WINDOWPLACEMENT wp;\r
7005         EnsureOnScreen(&commentX, &commentY);\r
7006         wp.length = sizeof(WINDOWPLACEMENT);\r
7007         wp.flags = 0;\r
7008         wp.showCmd = SW_SHOW;\r
7009         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7010         wp.rcNormalPosition.left = commentX;\r
7011         wp.rcNormalPosition.right = commentX + commentW;\r
7012         wp.rcNormalPosition.top = commentY;\r
7013         wp.rcNormalPosition.bottom = commentY + commentH;\r
7014         SetWindowPlacement(hDlg, &wp);\r
7015 \r
7016         GetClientRect(hDlg, &rect);\r
7017         newSizeX = rect.right;\r
7018         newSizeY = rect.bottom;\r
7019         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7020                               newSizeX, newSizeY);\r
7021         sizeX = newSizeX;\r
7022         sizeY = newSizeY;\r
7023       }\r
7024     }\r
7025     return FALSE;\r
7026 \r
7027   case WM_COMMAND: /* message: received a command */\r
7028     switch (LOWORD(wParam)) {\r
7029     case IDOK:\r
7030       if (editComment) {\r
7031         char *p, *q;\r
7032         /* Read changed options from the dialog box */\r
7033         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7034         len = GetWindowTextLength(hwndText);\r
7035         str = (char *) malloc(len + 1);\r
7036         GetWindowText(hwndText, str, len + 1);\r
7037         p = q = str;\r
7038         while (*q) {\r
7039           if (*q == '\r')\r
7040             q++;\r
7041           else\r
7042             *p++ = *q++;\r
7043         }\r
7044         *p = NULLCHAR;\r
7045         ReplaceComment(commentIndex, str);\r
7046         free(str);\r
7047       }\r
7048       CommentPopDown();\r
7049       return TRUE;\r
7050 \r
7051     case IDCANCEL:\r
7052     case OPT_CancelComment:\r
7053       CommentPopDown();\r
7054       return TRUE;\r
7055 \r
7056     case OPT_ClearComment:\r
7057       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7058       break;\r
7059 \r
7060     case OPT_EditComment:\r
7061       EditCommentEvent();\r
7062       return TRUE;\r
7063 \r
7064     default:\r
7065       break;\r
7066     }\r
7067     break;\r
7068 \r
7069   case WM_SIZE:\r
7070     newSizeX = LOWORD(lParam);\r
7071     newSizeY = HIWORD(lParam);\r
7072     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7073     sizeX = newSizeX;\r
7074     sizeY = newSizeY;\r
7075     break;\r
7076 \r
7077   case WM_GETMINMAXINFO:\r
7078     /* Prevent resizing window too small */\r
7079     mmi = (MINMAXINFO *) lParam;\r
7080     mmi->ptMinTrackSize.x = 100;\r
7081     mmi->ptMinTrackSize.y = 100;\r
7082     break;\r
7083   }\r
7084   return FALSE;\r
7085 }\r
7086 \r
7087 VOID\r
7088 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7089 {\r
7090   FARPROC lpProc;\r
7091   char *p, *q;\r
7092 \r
7093   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7094 \r
7095   if (str == NULL) str = "";\r
7096   p = (char *) malloc(2 * strlen(str) + 2);\r
7097   q = p;\r
7098   while (*str) {\r
7099     if (*str == '\n') *q++ = '\r';\r
7100     *q++ = *str++;\r
7101   }\r
7102   *q = NULLCHAR;\r
7103   if (commentText != NULL) free(commentText);\r
7104 \r
7105   commentIndex = index;\r
7106   commentTitle = title;\r
7107   commentText = p;\r
7108   editComment = edit;\r
7109 \r
7110   if (commentDialog) {\r
7111     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7112     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7113   } else {\r
7114     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7115     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7116                  hwndMain, (DLGPROC)lpProc);\r
7117     FreeProcInstance(lpProc);\r
7118   }\r
7119   commentDialogUp = TRUE;\r
7120 }\r
7121 \r
7122 \r
7123 /*---------------------------------------------------------------------------*\\r
7124  *\r
7125  * Type-in move dialog functions\r
7126  * \r
7127 \*---------------------------------------------------------------------------*/\r
7128 \r
7129 LRESULT CALLBACK\r
7130 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7131 {\r
7132   char move[MSG_SIZ];\r
7133   HWND hInput;\r
7134   ChessMove moveType;\r
7135   int fromX, fromY, toX, toY;\r
7136   char promoChar;\r
7137 \r
7138   switch (message) {\r
7139   case WM_INITDIALOG:\r
7140     move[0] = (char) lParam;\r
7141     move[1] = NULLCHAR;\r
7142     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7143     hInput = GetDlgItem(hDlg, OPT_Move);\r
7144     SetWindowText(hInput, move);\r
7145     SetFocus(hInput);\r
7146     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7147     return FALSE;\r
7148 \r
7149   case WM_COMMAND:\r
7150     switch (LOWORD(wParam)) {\r
7151     case IDOK:\r
7152       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7153         gameMode != Training) {\r
7154         DisplayMoveError("Displayed move is not current");\r
7155       } else {\r
7156         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7157         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7158           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7159           if (gameMode != Training)\r
7160               forwardMostMove = currentMove;\r
7161           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7162         } else {\r
7163           DisplayMoveError("Could not parse move");\r
7164         }\r
7165       }\r
7166       EndDialog(hDlg, TRUE);\r
7167       return TRUE;\r
7168     case IDCANCEL:\r
7169       EndDialog(hDlg, FALSE);\r
7170       return TRUE;\r
7171     default:\r
7172       break;\r
7173     }\r
7174     break;\r
7175   }\r
7176   return FALSE;\r
7177 }\r
7178 \r
7179 VOID\r
7180 PopUpMoveDialog(char firstchar)\r
7181 {\r
7182     FARPROC lpProc;\r
7183     \r
7184     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7185         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7186         gameMode == AnalyzeMode || gameMode == EditGame || \r
7187         gameMode == EditPosition || gameMode == IcsExamining ||\r
7188         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7189         gameMode == Training) {\r
7190       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7191       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7192         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7193       FreeProcInstance(lpProc);\r
7194     }\r
7195 }\r
7196 \r
7197 /*---------------------------------------------------------------------------*\\r
7198  *\r
7199  * Type-in name dialog functions\r
7200  * \r
7201 \*---------------------------------------------------------------------------*/\r
7202 \r
7203 LRESULT CALLBACK\r
7204 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7205 {\r
7206   char move[MSG_SIZ];\r
7207   HWND hInput;\r
7208 \r
7209   switch (message) {\r
7210   case WM_INITDIALOG:\r
7211     move[0] = (char) lParam;\r
7212     move[1] = NULLCHAR;\r
7213     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7214     hInput = GetDlgItem(hDlg, OPT_Name);\r
7215     SetWindowText(hInput, move);\r
7216     SetFocus(hInput);\r
7217     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7218     return FALSE;\r
7219 \r
7220   case WM_COMMAND:\r
7221     switch (LOWORD(wParam)) {\r
7222     case IDOK:\r
7223       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7224       appData.userName = strdup(move);\r
7225 \r
7226       EndDialog(hDlg, TRUE);\r
7227       return TRUE;\r
7228     case IDCANCEL:\r
7229       EndDialog(hDlg, FALSE);\r
7230       return TRUE;\r
7231     default:\r
7232       break;\r
7233     }\r
7234     break;\r
7235   }\r
7236   return FALSE;\r
7237 }\r
7238 \r
7239 VOID\r
7240 PopUpNameDialog(char firstchar)\r
7241 {\r
7242     FARPROC lpProc;\r
7243     \r
7244       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7245       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7246         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7247       FreeProcInstance(lpProc);\r
7248 }\r
7249 \r
7250 /*---------------------------------------------------------------------------*\\r
7251  *\r
7252  *  Error dialogs\r
7253  * \r
7254 \*---------------------------------------------------------------------------*/\r
7255 \r
7256 /* Nonmodal error box */\r
7257 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7258                              WPARAM wParam, LPARAM lParam);\r
7259 \r
7260 VOID\r
7261 ErrorPopUp(char *title, char *content)\r
7262 {\r
7263   FARPROC lpProc;\r
7264   char *p, *q;\r
7265   BOOLEAN modal = hwndMain == NULL;\r
7266 \r
7267   p = content;\r
7268   q = errorMessage;\r
7269   while (*p) {\r
7270     if (*p == '\n') {\r
7271       if (modal) {\r
7272         *q++ = ' ';\r
7273         p++;\r
7274       } else {\r
7275         *q++ = '\r';\r
7276         *q++ = *p++;\r
7277       }\r
7278     } else {\r
7279       *q++ = *p++;\r
7280     }\r
7281   }\r
7282   *q = NULLCHAR;\r
7283   strncpy(errorTitle, title, sizeof(errorTitle));\r
7284   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7285   \r
7286   if (modal) {\r
7287     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7288   } else {\r
7289     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7290     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7291                  hwndMain, (DLGPROC)lpProc);\r
7292     FreeProcInstance(lpProc);\r
7293   }\r
7294 }\r
7295 \r
7296 VOID\r
7297 ErrorPopDown()\r
7298 {\r
7299   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7300   if (errorDialog == NULL) return;\r
7301   DestroyWindow(errorDialog);\r
7302   errorDialog = NULL;\r
7303 }\r
7304 \r
7305 LRESULT CALLBACK\r
7306 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7307 {\r
7308   HANDLE hwndText;\r
7309   RECT rChild;\r
7310 \r
7311   switch (message) {\r
7312   case WM_INITDIALOG:\r
7313     GetWindowRect(hDlg, &rChild);\r
7314 \r
7315     /*\r
7316     SetWindowPos(hDlg, NULL, rChild.left,\r
7317       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7318       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7319     */\r
7320 \r
7321     /* \r
7322         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7323         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7324         and it doesn't work when you resize the dialog.\r
7325         For now, just give it a default position.\r
7326     */\r
7327     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7328 \r
7329     errorDialog = hDlg;\r
7330     SetWindowText(hDlg, errorTitle);\r
7331     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7332     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7333     return FALSE;\r
7334 \r
7335   case WM_COMMAND:\r
7336     switch (LOWORD(wParam)) {\r
7337     case IDOK:\r
7338     case IDCANCEL:\r
7339       if (errorDialog == hDlg) errorDialog = NULL;\r
7340       DestroyWindow(hDlg);\r
7341       return TRUE;\r
7342 \r
7343     default:\r
7344       break;\r
7345     }\r
7346     break;\r
7347   }\r
7348   return FALSE;\r
7349 }\r
7350 \r
7351 #ifdef GOTHIC\r
7352 HWND gothicDialog = NULL;\r
7353 \r
7354 LRESULT CALLBACK\r
7355 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7356 {\r
7357   HANDLE hwndText;\r
7358   RECT rChild;\r
7359   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7360 \r
7361   switch (message) {\r
7362   case WM_INITDIALOG:\r
7363     GetWindowRect(hDlg, &rChild);\r
7364 \r
7365     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7366                                                              SWP_NOZORDER);\r
7367 \r
7368     /* \r
7369         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7370         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7371         and it doesn't work when you resize the dialog.\r
7372         For now, just give it a default position.\r
7373     */\r
7374     gothicDialog = hDlg;\r
7375     SetWindowText(hDlg, errorTitle);\r
7376     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7377     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7378     return FALSE;\r
7379 \r
7380   case WM_COMMAND:\r
7381     switch (LOWORD(wParam)) {\r
7382     case IDOK:\r
7383     case IDCANCEL:\r
7384       if (errorDialog == hDlg) errorDialog = NULL;\r
7385       DestroyWindow(hDlg);\r
7386       return TRUE;\r
7387 \r
7388     default:\r
7389       break;\r
7390     }\r
7391     break;\r
7392   }\r
7393   return FALSE;\r
7394 }\r
7395 \r
7396 VOID\r
7397 GothicPopUp(char *title, VariantClass variant)\r
7398 {\r
7399   FARPROC lpProc;\r
7400   char *p, *q;\r
7401   BOOLEAN modal = hwndMain == NULL;\r
7402   static char *lastTitle;\r
7403 \r
7404   strncpy(errorTitle, title, sizeof(errorTitle));\r
7405   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7406 \r
7407   if(lastTitle != title && gothicDialog != NULL) {\r
7408     DestroyWindow(gothicDialog);\r
7409     gothicDialog = NULL;\r
7410   }\r
7411   if(variant != VariantNormal && gothicDialog == NULL) {\r
7412     title = lastTitle;\r
7413     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7414     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7415                  hwndMain, (DLGPROC)lpProc);\r
7416     FreeProcInstance(lpProc);\r
7417   }\r
7418 }\r
7419 #endif\r
7420 \r
7421 /*---------------------------------------------------------------------------*\\r
7422  *\r
7423  *  Ics Interaction console functions\r
7424  *\r
7425 \*---------------------------------------------------------------------------*/\r
7426 \r
7427 #define HISTORY_SIZE 64\r
7428 static char *history[HISTORY_SIZE];\r
7429 int histIn = 0, histP = 0;\r
7430 \r
7431 VOID\r
7432 SaveInHistory(char *cmd)\r
7433 {\r
7434   if (history[histIn] != NULL) {\r
7435     free(history[histIn]);\r
7436     history[histIn] = NULL;\r
7437   }\r
7438   if (*cmd == NULLCHAR) return;\r
7439   history[histIn] = StrSave(cmd);\r
7440   histIn = (histIn + 1) % HISTORY_SIZE;\r
7441   if (history[histIn] != NULL) {\r
7442     free(history[histIn]);\r
7443     history[histIn] = NULL;\r
7444   }\r
7445   histP = histIn;\r
7446 }\r
7447 \r
7448 char *\r
7449 PrevInHistory(char *cmd)\r
7450 {\r
7451   int newhp;\r
7452   if (histP == histIn) {\r
7453     if (history[histIn] != NULL) free(history[histIn]);\r
7454     history[histIn] = StrSave(cmd);\r
7455   }\r
7456   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7457   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7458   histP = newhp;\r
7459   return history[histP];\r
7460 }\r
7461 \r
7462 char *\r
7463 NextInHistory()\r
7464 {\r
7465   if (histP == histIn) return NULL;\r
7466   histP = (histP + 1) % HISTORY_SIZE;\r
7467   return history[histP];\r
7468 }\r
7469 \r
7470 typedef struct {\r
7471   char *item;\r
7472   char *command;\r
7473   BOOLEAN getname;\r
7474   BOOLEAN immediate;\r
7475 } IcsTextMenuEntry;\r
7476 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7477 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7478 \r
7479 void\r
7480 ParseIcsTextMenu(char *icsTextMenuString)\r
7481 {\r
7482   int flags = 0;\r
7483   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7484   char *p = icsTextMenuString;\r
7485   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7486     free(e->item);\r
7487     e->item = NULL;\r
7488     if (e->command != NULL) {\r
7489       free(e->command);\r
7490       e->command = NULL;\r
7491     }\r
7492     e++;\r
7493   }\r
7494   e = icsTextMenuEntry;\r
7495   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7496     if (*p == ';' || *p == '\n') {\r
7497       e->item = strdup("-");\r
7498       e->command = NULL;\r
7499       p++;\r
7500     } else if (*p == '-') {\r
7501       e->item = strdup("-");\r
7502       e->command = NULL;\r
7503       p++;\r
7504       if (*p) p++;\r
7505     } else {\r
7506       char *q, *r, *s, *t;\r
7507       char c;\r
7508       q = strchr(p, ',');\r
7509       if (q == NULL) break;\r
7510       *q = NULLCHAR;\r
7511       r = strchr(q + 1, ',');\r
7512       if (r == NULL) break;\r
7513       *r = NULLCHAR;\r
7514       s = strchr(r + 1, ',');\r
7515       if (s == NULL) break;\r
7516       *s = NULLCHAR;\r
7517       c = ';';\r
7518       t = strchr(s + 1, c);\r
7519       if (t == NULL) {\r
7520         c = '\n';\r
7521         t = strchr(s + 1, c);\r
7522       }\r
7523       if (t != NULL) *t = NULLCHAR;\r
7524       e->item = strdup(p);\r
7525       e->command = strdup(q + 1);\r
7526       e->getname = *(r + 1) != '0';\r
7527       e->immediate = *(s + 1) != '0';\r
7528       *q = ',';\r
7529       *r = ',';\r
7530       *s = ',';\r
7531       if (t == NULL) break;\r
7532       *t = c;\r
7533       p = t + 1;\r
7534     }\r
7535     e++;\r
7536   } \r
7537 }\r
7538 \r
7539 HMENU\r
7540 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7541 {\r
7542   HMENU hmenu, h;\r
7543   int i = 0;\r
7544   hmenu = LoadMenu(hInst, "TextMenu");\r
7545   h = GetSubMenu(hmenu, 0);\r
7546   while (e->item) {\r
7547     if (strcmp(e->item, "-") == 0) {\r
7548       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7549     } else {\r
7550       if (e->item[0] == '|') {\r
7551         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7552                    IDM_CommandX + i, &e->item[1]);\r
7553       } else {\r
7554         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7555       }\r
7556     }\r
7557     e++;\r
7558     i++;\r
7559   } \r
7560   return hmenu;\r
7561 }\r
7562 \r
7563 WNDPROC consoleTextWindowProc;\r
7564 \r
7565 void\r
7566 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7567 {\r
7568   char buf[MSG_SIZ], name[MSG_SIZ];\r
7569   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7570   CHARRANGE sel;\r
7571 \r
7572   if (!getname) {\r
7573     SetWindowText(hInput, command);\r
7574     if (immediate) {\r
7575       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7576     } else {\r
7577       sel.cpMin = 999999;\r
7578       sel.cpMax = 999999;\r
7579       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7580       SetFocus(hInput);\r
7581     }\r
7582     return;\r
7583   }    \r
7584   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7585   if (sel.cpMin == sel.cpMax) {\r
7586     /* Expand to surrounding word */\r
7587     TEXTRANGE tr;\r
7588     do {\r
7589       tr.chrg.cpMax = sel.cpMin;\r
7590       tr.chrg.cpMin = --sel.cpMin;\r
7591       if (sel.cpMin < 0) break;\r
7592       tr.lpstrText = name;\r
7593       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7594     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7595     sel.cpMin++;\r
7596 \r
7597     do {\r
7598       tr.chrg.cpMin = sel.cpMax;\r
7599       tr.chrg.cpMax = ++sel.cpMax;\r
7600       tr.lpstrText = name;\r
7601       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7602     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7603     sel.cpMax--;\r
7604 \r
7605     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7606       MessageBeep(MB_ICONEXCLAMATION);\r
7607       return;\r
7608     }\r
7609     tr.chrg = sel;\r
7610     tr.lpstrText = name;\r
7611     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7612   } else {\r
7613     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7614       MessageBeep(MB_ICONEXCLAMATION);\r
7615       return;\r
7616     }\r
7617     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7618   }\r
7619   if (immediate) {\r
7620     sprintf(buf, "%s %s", command, name);\r
7621     SetWindowText(hInput, buf);\r
7622     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7623   } else {\r
7624     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7625     SetWindowText(hInput, buf);\r
7626     sel.cpMin = 999999;\r
7627     sel.cpMax = 999999;\r
7628     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7629     SetFocus(hInput);\r
7630   }\r
7631 }\r
7632 \r
7633 LRESULT CALLBACK \r
7634 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7635 {\r
7636   HWND hInput;\r
7637   CHARRANGE sel;\r
7638 \r
7639   switch (message) {\r
7640   case WM_KEYDOWN:\r
7641     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7642     switch (wParam) {\r
7643     case VK_PRIOR:\r
7644       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7645       return 0;\r
7646     case VK_NEXT:\r
7647       sel.cpMin = 999999;\r
7648       sel.cpMax = 999999;\r
7649       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7650       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7651       return 0;\r
7652     }\r
7653     break;\r
7654   case WM_CHAR:\r
7655     if (wParam == '\t') {\r
7656       if (GetKeyState(VK_SHIFT) < 0) {\r
7657         /* shifted */\r
7658         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7659         if (buttonDesc[0].hwnd) {\r
7660           SetFocus(buttonDesc[0].hwnd);\r
7661         } else {\r
7662           SetFocus(hwndMain);\r
7663         }\r
7664       } else {\r
7665         /* unshifted */\r
7666         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7667       }\r
7668     } else {\r
7669       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7670       SetFocus(hInput);\r
7671       SendMessage(hInput, message, wParam, lParam);\r
7672     }\r
7673     return 0;\r
7674   case WM_PASTE:\r
7675     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7676     SetFocus(hInput);\r
7677     return SendMessage(hInput, message, wParam, lParam);\r
7678   case WM_MBUTTONDOWN:\r
7679     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7680   case WM_RBUTTONDOWN:\r
7681     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7682       /* Move selection here if it was empty */\r
7683       POINT pt;\r
7684       pt.x = LOWORD(lParam);\r
7685       pt.y = HIWORD(lParam);\r
7686       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7687       if (sel.cpMin == sel.cpMax) {\r
7688         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7689         sel.cpMax = sel.cpMin;\r
7690         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7691       }\r
7692       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7693     }\r
7694     return 0;\r
7695   case WM_RBUTTONUP:\r
7696     if (GetKeyState(VK_SHIFT) & ~1) {\r
7697       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7698         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7699     } else {\r
7700       POINT pt;\r
7701       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7702       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7703       if (sel.cpMin == sel.cpMax) {\r
7704         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7705         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7706       }\r
7707       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7708         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7709       }\r
7710       pt.x = LOWORD(lParam);\r
7711       pt.y = HIWORD(lParam);\r
7712       MenuPopup(hwnd, pt, hmenu, -1);\r
7713     }\r
7714     return 0;\r
7715   case WM_COMMAND:\r
7716     switch (LOWORD(wParam)) {\r
7717     case IDM_QuickPaste:\r
7718       {\r
7719         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7720         if (sel.cpMin == sel.cpMax) {\r
7721           MessageBeep(MB_ICONEXCLAMATION);\r
7722           return 0;\r
7723         }\r
7724         SendMessage(hwnd, WM_COPY, 0, 0);\r
7725         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7726         SendMessage(hInput, WM_PASTE, 0, 0);\r
7727         SetFocus(hInput);\r
7728         return 0;\r
7729       }\r
7730     case IDM_Cut:\r
7731       SendMessage(hwnd, WM_CUT, 0, 0);\r
7732       return 0;\r
7733     case IDM_Paste:\r
7734       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7735       return 0;\r
7736     case IDM_Copy:\r
7737       SendMessage(hwnd, WM_COPY, 0, 0);\r
7738       return 0;\r
7739     default:\r
7740       {\r
7741         int i = LOWORD(wParam) - IDM_CommandX;\r
7742         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7743             icsTextMenuEntry[i].command != NULL) {\r
7744           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7745                    icsTextMenuEntry[i].getname,\r
7746                    icsTextMenuEntry[i].immediate);\r
7747           return 0;\r
7748         }\r
7749       }\r
7750       break;\r
7751     }\r
7752     break;\r
7753   }\r
7754   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7755 }\r
7756 \r
7757 WNDPROC consoleInputWindowProc;\r
7758 \r
7759 LRESULT CALLBACK\r
7760 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7761 {\r
7762   char buf[MSG_SIZ];\r
7763   char *p;\r
7764   static BOOL sendNextChar = FALSE;\r
7765   static BOOL quoteNextChar = FALSE;\r
7766   InputSource *is = consoleInputSource;\r
7767   CHARFORMAT cf;\r
7768   CHARRANGE sel;\r
7769 \r
7770   switch (message) {\r
7771   case WM_CHAR:\r
7772     if (!appData.localLineEditing || sendNextChar) {\r
7773       is->buf[0] = (CHAR) wParam;\r
7774       is->count = 1;\r
7775       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7776       sendNextChar = FALSE;\r
7777       return 0;\r
7778     }\r
7779     if (quoteNextChar) {\r
7780       buf[0] = (char) wParam;\r
7781       buf[1] = NULLCHAR;\r
7782       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7783       quoteNextChar = FALSE;\r
7784       return 0;\r
7785     }\r
7786     switch (wParam) {\r
7787     case '\r':   /* Enter key */\r
7788       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7789       if (consoleEcho) SaveInHistory(is->buf);\r
7790       is->buf[is->count++] = '\n';\r
7791       is->buf[is->count] = NULLCHAR;\r
7792       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7793       if (consoleEcho) {\r
7794         ConsoleOutput(is->buf, is->count, TRUE);\r
7795       } else if (appData.localLineEditing) {\r
7796         ConsoleOutput("\n", 1, TRUE);\r
7797       }\r
7798       /* fall thru */\r
7799     case '\033': /* Escape key */\r
7800       SetWindowText(hwnd, "");\r
7801       cf.cbSize = sizeof(CHARFORMAT);\r
7802       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7803       if (consoleEcho) {\r
7804         cf.crTextColor = textAttribs[ColorNormal].color;\r
7805       } else {\r
7806         cf.crTextColor = COLOR_ECHOOFF;\r
7807       }\r
7808       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7809       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7810       return 0;\r
7811     case '\t':   /* Tab key */\r
7812       if (GetKeyState(VK_SHIFT) < 0) {\r
7813         /* shifted */\r
7814         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7815       } else {\r
7816         /* unshifted */\r
7817         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7818         if (buttonDesc[0].hwnd) {\r
7819           SetFocus(buttonDesc[0].hwnd);\r
7820         } else {\r
7821           SetFocus(hwndMain);\r
7822         }\r
7823       }\r
7824       return 0;\r
7825     case '\023': /* Ctrl+S */\r
7826       sendNextChar = TRUE;\r
7827       return 0;\r
7828     case '\021': /* Ctrl+Q */\r
7829       quoteNextChar = TRUE;\r
7830       return 0;\r
7831     default:\r
7832       break;\r
7833     }\r
7834     break;\r
7835   case WM_KEYDOWN:\r
7836     switch (wParam) {\r
7837     case VK_UP:\r
7838       GetWindowText(hwnd, buf, MSG_SIZ);\r
7839       p = PrevInHistory(buf);\r
7840       if (p != NULL) {\r
7841         SetWindowText(hwnd, p);\r
7842         sel.cpMin = 999999;\r
7843         sel.cpMax = 999999;\r
7844         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7845         return 0;\r
7846       }\r
7847       break;\r
7848     case VK_DOWN:\r
7849       p = NextInHistory();\r
7850       if (p != NULL) {\r
7851         SetWindowText(hwnd, p);\r
7852         sel.cpMin = 999999;\r
7853         sel.cpMax = 999999;\r
7854         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7855         return 0;\r
7856       }\r
7857       break;\r
7858     case VK_HOME:\r
7859     case VK_END:\r
7860       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7861       /* fall thru */\r
7862     case VK_PRIOR:\r
7863     case VK_NEXT:\r
7864       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7865       return 0;\r
7866     }\r
7867     break;\r
7868   case WM_MBUTTONDOWN:\r
7869     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7870       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7871     break;\r
7872   case WM_RBUTTONUP:\r
7873     if (GetKeyState(VK_SHIFT) & ~1) {\r
7874       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7875         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7876     } else {\r
7877       POINT pt;\r
7878       HMENU hmenu;\r
7879       hmenu = LoadMenu(hInst, "InputMenu");\r
7880       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7881       if (sel.cpMin == sel.cpMax) {\r
7882         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7883         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7884       }\r
7885       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7886         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7887       }\r
7888       pt.x = LOWORD(lParam);\r
7889       pt.y = HIWORD(lParam);\r
7890       MenuPopup(hwnd, pt, hmenu, -1);\r
7891     }\r
7892     return 0;\r
7893   case WM_COMMAND:\r
7894     switch (LOWORD(wParam)) { \r
7895     case IDM_Undo:\r
7896       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7897       return 0;\r
7898     case IDM_SelectAll:\r
7899       sel.cpMin = 0;\r
7900       sel.cpMax = -1; /*999999?*/\r
7901       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7902       return 0;\r
7903     case IDM_Cut:\r
7904       SendMessage(hwnd, WM_CUT, 0, 0);\r
7905       return 0;\r
7906     case IDM_Paste:\r
7907       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7908       return 0;\r
7909     case IDM_Copy:\r
7910       SendMessage(hwnd, WM_COPY, 0, 0);\r
7911       return 0;\r
7912     }\r
7913     break;\r
7914   }\r
7915   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7916 }\r
7917 \r
7918 #define CO_MAX  100000\r
7919 #define CO_TRIM   1000\r
7920 \r
7921 LRESULT CALLBACK\r
7922 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7923 {\r
7924   static SnapData sd;\r
7925   static HWND hText, hInput, hFocus;\r
7926   InputSource *is = consoleInputSource;\r
7927   RECT rect;\r
7928   static int sizeX, sizeY;\r
7929   int newSizeX, newSizeY;\r
7930   MINMAXINFO *mmi;\r
7931 \r
7932   switch (message) {\r
7933   case WM_INITDIALOG: /* message: initialize dialog box */\r
7934     hwndConsole = hDlg;\r
7935     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7936     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7937     SetFocus(hInput);\r
7938     consoleTextWindowProc = (WNDPROC)\r
7939       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7940     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7941     consoleInputWindowProc = (WNDPROC)\r
7942       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7943     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7944     Colorize(ColorNormal, TRUE);\r
7945     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7946     ChangedConsoleFont();\r
7947     GetClientRect(hDlg, &rect);\r
7948     sizeX = rect.right;\r
7949     sizeY = rect.bottom;\r
7950     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7951         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7952       WINDOWPLACEMENT wp;\r
7953       EnsureOnScreen(&consoleX, &consoleY);\r
7954       wp.length = sizeof(WINDOWPLACEMENT);\r
7955       wp.flags = 0;\r
7956       wp.showCmd = SW_SHOW;\r
7957       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7958       wp.rcNormalPosition.left = consoleX;\r
7959       wp.rcNormalPosition.right = consoleX + consoleW;\r
7960       wp.rcNormalPosition.top = consoleY;\r
7961       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7962       SetWindowPlacement(hDlg, &wp);\r
7963     }\r
7964 #if 0 \r
7965    // [HGM] Chessknight's change 2004-07-13\r
7966    else { /* Determine Defaults */\r
7967        WINDOWPLACEMENT wp;\r
7968        consoleX = winWidth + 1;\r
7969        consoleY = boardY;\r
7970        consoleW = screenWidth -  winWidth;\r
7971        consoleH = winHeight;\r
7972        EnsureOnScreen(&consoleX, &consoleY);\r
7973        wp.length = sizeof(WINDOWPLACEMENT);\r
7974        wp.flags = 0;\r
7975        wp.showCmd = SW_SHOW;\r
7976        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7977        wp.rcNormalPosition.left = consoleX;\r
7978        wp.rcNormalPosition.right = consoleX + consoleW;\r
7979        wp.rcNormalPosition.top = consoleY;\r
7980        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7981        SetWindowPlacement(hDlg, &wp);\r
7982     }\r
7983 #endif\r
7984     return FALSE;\r
7985 \r
7986   case WM_SETFOCUS:\r
7987     SetFocus(hInput);\r
7988     return 0;\r
7989 \r
7990   case WM_CLOSE:\r
7991     ExitEvent(0);\r
7992     /* not reached */\r
7993     break;\r
7994 \r
7995   case WM_SIZE:\r
7996     if (IsIconic(hDlg)) break;\r
7997     newSizeX = LOWORD(lParam);\r
7998     newSizeY = HIWORD(lParam);\r
7999     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8000       RECT rectText, rectInput;\r
8001       POINT pt;\r
8002       int newTextHeight, newTextWidth;\r
8003       GetWindowRect(hText, &rectText);\r
8004       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8005       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8006       if (newTextHeight < 0) {\r
8007         newSizeY += -newTextHeight;\r
8008         newTextHeight = 0;\r
8009       }\r
8010       SetWindowPos(hText, NULL, 0, 0,\r
8011         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8012       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8013       pt.x = rectInput.left;\r
8014       pt.y = rectInput.top + newSizeY - sizeY;\r
8015       ScreenToClient(hDlg, &pt);\r
8016       SetWindowPos(hInput, NULL, \r
8017         pt.x, pt.y, /* needs client coords */   \r
8018         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8019         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8020     }\r
8021     sizeX = newSizeX;\r
8022     sizeY = newSizeY;\r
8023     break;\r
8024 \r
8025   case WM_GETMINMAXINFO:\r
8026     /* Prevent resizing window too small */\r
8027     mmi = (MINMAXINFO *) lParam;\r
8028     mmi->ptMinTrackSize.x = 100;\r
8029     mmi->ptMinTrackSize.y = 100;\r
8030     break;\r
8031 \r
8032   /* [AS] Snapping */\r
8033   case WM_ENTERSIZEMOVE:\r
8034     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8035 \r
8036   case WM_SIZING:\r
8037     return OnSizing( &sd, hDlg, wParam, lParam );\r
8038 \r
8039   case WM_MOVING:\r
8040     return OnMoving( &sd, hDlg, wParam, lParam );\r
8041 \r
8042   case WM_EXITSIZEMOVE:\r
8043     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8044   }\r
8045 \r
8046   return DefWindowProc(hDlg, message, wParam, lParam);\r
8047 }\r
8048 \r
8049 \r
8050 VOID\r
8051 ConsoleCreate()\r
8052 {\r
8053   HWND hCons;\r
8054   if (hwndConsole) return;\r
8055   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8056   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8057 }\r
8058 \r
8059 \r
8060 VOID\r
8061 ConsoleOutput(char* data, int length, int forceVisible)\r
8062 {\r
8063   HWND hText;\r
8064   int trim, exlen;\r
8065   char *p, *q;\r
8066   char buf[CO_MAX+1];\r
8067   POINT pEnd;\r
8068   RECT rect;\r
8069   static int delayLF = 0;\r
8070   CHARRANGE savesel, sel;\r
8071 \r
8072   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8073   p = data;\r
8074   q = buf;\r
8075   if (delayLF) {\r
8076     *q++ = '\r';\r
8077     *q++ = '\n';\r
8078     delayLF = 0;\r
8079   }\r
8080   while (length--) {\r
8081     if (*p == '\n') {\r
8082       if (*++p) {\r
8083         *q++ = '\r';\r
8084         *q++ = '\n';\r
8085       } else {\r
8086         delayLF = 1;\r
8087       }\r
8088     } else if (*p == '\007') {\r
8089        MyPlaySound(&sounds[(int)SoundBell]);\r
8090        p++;\r
8091     } else {\r
8092       *q++ = *p++;\r
8093     }\r
8094   }\r
8095   *q = NULLCHAR;\r
8096   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8097   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8098   /* Save current selection */\r
8099   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8100   exlen = GetWindowTextLength(hText);\r
8101   /* Find out whether current end of text is visible */\r
8102   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8103   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8104   /* Trim existing text if it's too long */\r
8105   if (exlen + (q - buf) > CO_MAX) {\r
8106     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8107     sel.cpMin = 0;\r
8108     sel.cpMax = trim;\r
8109     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8110     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8111     exlen -= trim;\r
8112     savesel.cpMin -= trim;\r
8113     savesel.cpMax -= trim;\r
8114     if (exlen < 0) exlen = 0;\r
8115     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8116     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8117   }\r
8118   /* Append the new text */\r
8119   sel.cpMin = exlen;\r
8120   sel.cpMax = exlen;\r
8121   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8122   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8123   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8124   if (forceVisible || exlen == 0 ||\r
8125       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8126        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8127     /* Scroll to make new end of text visible if old end of text\r
8128        was visible or new text is an echo of user typein */\r
8129     sel.cpMin = 9999999;\r
8130     sel.cpMax = 9999999;\r
8131     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8132     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8133     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8134     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8135   }\r
8136   if (savesel.cpMax == exlen || forceVisible) {\r
8137     /* Move insert point to new end of text if it was at the old\r
8138        end of text or if the new text is an echo of user typein */\r
8139     sel.cpMin = 9999999;\r
8140     sel.cpMax = 9999999;\r
8141     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8142   } else {\r
8143     /* Restore previous selection */\r
8144     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8145   }\r
8146   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8147 }\r
8148 \r
8149 /*---------*/\r
8150 \r
8151 \r
8152 void\r
8153 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8154 {\r
8155   char buf[100];\r
8156   char *str;\r
8157   COLORREF oldFg, oldBg;\r
8158   HFONT oldFont;\r
8159   RECT rect;\r
8160 \r
8161   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8162 \r
8163   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8164   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8165   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8166 \r
8167   rect.left = x;\r
8168   rect.right = x + squareSize;\r
8169   rect.top  = y;\r
8170   rect.bottom = y + squareSize;\r
8171   str = buf;\r
8172 \r
8173   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8174                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8175              y, ETO_CLIPPED|ETO_OPAQUE,\r
8176              &rect, str, strlen(str), NULL);\r
8177 \r
8178   (void) SetTextColor(hdc, oldFg);\r
8179   (void) SetBkColor(hdc, oldBg);\r
8180   (void) SelectObject(hdc, oldFont);\r
8181 }\r
8182 \r
8183 void\r
8184 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8185               RECT *rect, char *color, char *flagFell)\r
8186 {\r
8187   char buf[100];\r
8188   char *str;\r
8189   COLORREF oldFg, oldBg;\r
8190   HFONT oldFont;\r
8191 \r
8192   if (appData.clockMode) {\r
8193     if (tinyLayout)\r
8194       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8195     else\r
8196       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8197     str = buf;\r
8198   } else {\r
8199     str = color;\r
8200   }\r
8201 \r
8202   if (highlight) {\r
8203     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8204     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8205   } else {\r
8206     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8207     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8208   }\r
8209   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8210 \r
8211   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8212              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8213              rect, str, strlen(str), NULL);\r
8214 \r
8215   (void) SetTextColor(hdc, oldFg);\r
8216   (void) SetBkColor(hdc, oldBg);\r
8217   (void) SelectObject(hdc, oldFont);\r
8218 }\r
8219 \r
8220 \r
8221 int\r
8222 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8223            OVERLAPPED *ovl)\r
8224 {\r
8225   int ok, err;\r
8226 \r
8227   /* [AS]  */\r
8228   if( count <= 0 ) {\r
8229     if (appData.debugMode) {\r
8230       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8231     }\r
8232 \r
8233     return ERROR_INVALID_USER_BUFFER;\r
8234   }\r
8235 \r
8236   ResetEvent(ovl->hEvent);\r
8237   ovl->Offset = ovl->OffsetHigh = 0;\r
8238   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8239   if (ok) {\r
8240     err = NO_ERROR;\r
8241   } else {\r
8242     err = GetLastError();\r
8243     if (err == ERROR_IO_PENDING) {\r
8244       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8245       if (ok)\r
8246         err = NO_ERROR;\r
8247       else\r
8248         err = GetLastError();\r
8249     }\r
8250   }\r
8251   return err;\r
8252 }\r
8253 \r
8254 int\r
8255 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8256             OVERLAPPED *ovl)\r
8257 {\r
8258   int ok, err;\r
8259 \r
8260   ResetEvent(ovl->hEvent);\r
8261   ovl->Offset = ovl->OffsetHigh = 0;\r
8262   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8263   if (ok) {\r
8264     err = NO_ERROR;\r
8265   } else {\r
8266     err = GetLastError();\r
8267     if (err == ERROR_IO_PENDING) {\r
8268       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8269       if (ok)\r
8270         err = NO_ERROR;\r
8271       else\r
8272         err = GetLastError();\r
8273     }\r
8274   }\r
8275   return err;\r
8276 }\r
8277 \r
8278 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8279 void CheckForInputBufferFull( InputSource * is )\r
8280 {\r
8281     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8282         /* Look for end of line */\r
8283         char * p = is->buf;\r
8284         \r
8285         while( p < is->next && *p != '\n' ) {\r
8286             p++;\r
8287         }\r
8288 \r
8289         if( p >= is->next ) {\r
8290             if (appData.debugMode) {\r
8291                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
8292             }\r
8293 \r
8294             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8295             is->count = (DWORD) -1;\r
8296             is->next = is->buf;\r
8297         }\r
8298     }\r
8299 }\r
8300 \r
8301 DWORD\r
8302 InputThread(LPVOID arg)\r
8303 {\r
8304   InputSource *is;\r
8305   OVERLAPPED ovl;\r
8306 \r
8307   is = (InputSource *) arg;\r
8308   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8309   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8310   while (is->hThread != NULL) {\r
8311     is->error = DoReadFile(is->hFile, is->next,\r
8312                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8313                            &is->count, &ovl);\r
8314     if (is->error == NO_ERROR) {\r
8315       is->next += is->count;\r
8316     } else {\r
8317       if (is->error == ERROR_BROKEN_PIPE) {\r
8318         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8319         is->count = 0;\r
8320       } else {\r
8321         is->count = (DWORD) -1;\r
8322         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8323         break; \r
8324       }\r
8325     }\r
8326 \r
8327     CheckForInputBufferFull( is );\r
8328 \r
8329     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8330 \r
8331     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8332 \r
8333     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8334   }\r
8335 \r
8336   CloseHandle(ovl.hEvent);\r
8337   CloseHandle(is->hFile);\r
8338 \r
8339   if (appData.debugMode) {\r
8340     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
8341   }\r
8342 \r
8343   return 0;\r
8344 }\r
8345 \r
8346 \r
8347 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8348 DWORD\r
8349 NonOvlInputThread(LPVOID arg)\r
8350 {\r
8351   InputSource *is;\r
8352   char *p, *q;\r
8353   int i;\r
8354   char prev;\r
8355 \r
8356   is = (InputSource *) arg;\r
8357   while (is->hThread != NULL) {\r
8358     is->error = ReadFile(is->hFile, is->next,\r
8359                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8360                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8361     if (is->error == NO_ERROR) {\r
8362       /* Change CRLF to LF */\r
8363       if (is->next > is->buf) {\r
8364         p = is->next - 1;\r
8365         i = is->count + 1;\r
8366       } else {\r
8367         p = is->next;\r
8368         i = is->count;\r
8369       }\r
8370       q = p;\r
8371       prev = NULLCHAR;\r
8372       while (i > 0) {\r
8373         if (prev == '\r' && *p == '\n') {\r
8374           *(q-1) = '\n';\r
8375           is->count--;\r
8376         } else { \r
8377           *q++ = *p;\r
8378         }\r
8379         prev = *p++;\r
8380         i--;\r
8381       }\r
8382       *q = NULLCHAR;\r
8383       is->next = q;\r
8384     } else {\r
8385       if (is->error == ERROR_BROKEN_PIPE) {\r
8386         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8387         is->count = 0; \r
8388       } else {\r
8389         is->count = (DWORD) -1;\r
8390       }\r
8391     }\r
8392 \r
8393     CheckForInputBufferFull( is );\r
8394 \r
8395     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8396 \r
8397     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8398 \r
8399     if (is->count < 0) break;  /* Quit on error */\r
8400   }\r
8401   CloseHandle(is->hFile);\r
8402   return 0;\r
8403 }\r
8404 \r
8405 DWORD\r
8406 SocketInputThread(LPVOID arg)\r
8407 {\r
8408   InputSource *is;\r
8409 \r
8410   is = (InputSource *) arg;\r
8411   while (is->hThread != NULL) {\r
8412     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8413     if ((int)is->count == SOCKET_ERROR) {\r
8414       is->count = (DWORD) -1;\r
8415       is->error = WSAGetLastError();\r
8416     } else {\r
8417       is->error = NO_ERROR;\r
8418       is->next += is->count;\r
8419       if (is->count == 0 && is->second == is) {\r
8420         /* End of file on stderr; quit with no message */\r
8421         break;\r
8422       }\r
8423     }\r
8424     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8425 \r
8426     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8427 \r
8428     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8429   }\r
8430   return 0;\r
8431 }\r
8432 \r
8433 VOID\r
8434 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8435 {\r
8436   InputSource *is;\r
8437 \r
8438   is = (InputSource *) lParam;\r
8439   if (is->lineByLine) {\r
8440     /* Feed in lines one by one */\r
8441     char *p = is->buf;\r
8442     char *q = p;\r
8443     while (q < is->next) {\r
8444       if (*q++ == '\n') {\r
8445         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8446         p = q;\r
8447       }\r
8448     }\r
8449     \r
8450     /* Move any partial line to the start of the buffer */\r
8451     q = is->buf;\r
8452     while (p < is->next) {\r
8453       *q++ = *p++;\r
8454     }\r
8455     is->next = q;\r
8456 \r
8457     if (is->error != NO_ERROR || is->count == 0) {\r
8458       /* Notify backend of the error.  Note: If there was a partial\r
8459          line at the end, it is not flushed through. */\r
8460       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8461     }\r
8462   } else {\r
8463     /* Feed in the whole chunk of input at once */\r
8464     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8465     is->next = is->buf;\r
8466   }\r
8467 }\r
8468 \r
8469 /*---------------------------------------------------------------------------*\\r
8470  *\r
8471  *  Menu enables. Used when setting various modes.\r
8472  *\r
8473 \*---------------------------------------------------------------------------*/\r
8474 \r
8475 typedef struct {\r
8476   int item;\r
8477   int flags;\r
8478 } Enables;\r
8479 \r
8480 VOID\r
8481 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8482 {\r
8483   while (enab->item > 0) {\r
8484     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8485     enab++;\r
8486   }\r
8487 }\r
8488 \r
8489 Enables gnuEnables[] = {\r
8490   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8491   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8492   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8493   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8494   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8495   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8496   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8497   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8498   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8499   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8500   { -1, -1 }\r
8501 };\r
8502 \r
8503 Enables icsEnables[] = {\r
8504   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8505   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8506   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8507   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8510   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8511   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8513   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8515   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8516   { -1, -1 }\r
8517 };\r
8518 \r
8519 #ifdef ZIPPY\r
8520 Enables zippyEnables[] = {\r
8521   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8522   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8523   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8524   { -1, -1 }\r
8525 };\r
8526 #endif\r
8527 \r
8528 Enables ncpEnables[] = {\r
8529   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8530   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8531   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8532   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8533   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8534   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8535   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8536   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8537   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8538   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8539   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8540   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8541   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8542   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8543   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8544   { -1, -1 }\r
8545 };\r
8546 \r
8547 Enables trainingOnEnables[] = {\r
8548   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8550   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8551   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8556   { -1, -1 }\r
8557 };\r
8558 \r
8559 Enables trainingOffEnables[] = {\r
8560   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8561   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8562   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8563   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8564   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8565   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8566   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8567   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8568   { -1, -1 }\r
8569 };\r
8570 \r
8571 /* These modify either ncpEnables or gnuEnables */\r
8572 Enables cmailEnables[] = {\r
8573   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8574   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8575   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8576   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8578   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8580   { -1, -1 }\r
8581 };\r
8582 \r
8583 Enables machineThinkingEnables[] = {\r
8584   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8586   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8587   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8588   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8589   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8593   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8595   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8596   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8597   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8598   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8599   { -1, -1 }\r
8600 };\r
8601 \r
8602 Enables userThinkingEnables[] = {\r
8603   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8604   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8605   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8606   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8607   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8608   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8609   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8610   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8611   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8612   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8613   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8614   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8615   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8616   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8617   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8618   { -1, -1 }\r
8619 };\r
8620 \r
8621 /*---------------------------------------------------------------------------*\\r
8622  *\r
8623  *  Front-end interface functions exported by XBoard.\r
8624  *  Functions appear in same order as prototypes in frontend.h.\r
8625  * \r
8626 \*---------------------------------------------------------------------------*/\r
8627 VOID\r
8628 ModeHighlight()\r
8629 {\r
8630   static UINT prevChecked = 0;\r
8631   static int prevPausing = 0;\r
8632   UINT nowChecked;\r
8633 \r
8634   if (pausing != prevPausing) {\r
8635     prevPausing = pausing;\r
8636     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8637                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8638     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8639   }\r
8640 \r
8641   switch (gameMode) {\r
8642   case BeginningOfGame:\r
8643     if (appData.icsActive)\r
8644       nowChecked = IDM_IcsClient;\r
8645     else if (appData.noChessProgram)\r
8646       nowChecked = IDM_EditGame;\r
8647     else\r
8648       nowChecked = IDM_MachineBlack;\r
8649     break;\r
8650   case MachinePlaysBlack:\r
8651     nowChecked = IDM_MachineBlack;\r
8652     break;\r
8653   case MachinePlaysWhite:\r
8654     nowChecked = IDM_MachineWhite;\r
8655     break;\r
8656   case TwoMachinesPlay:\r
8657     nowChecked = IDM_TwoMachines;\r
8658     break;\r
8659   case AnalyzeMode:\r
8660     nowChecked = IDM_AnalysisMode;\r
8661     break;\r
8662   case AnalyzeFile:\r
8663     nowChecked = IDM_AnalyzeFile;\r
8664     break;\r
8665   case EditGame:\r
8666     nowChecked = IDM_EditGame;\r
8667     break;\r
8668   case PlayFromGameFile:\r
8669     nowChecked = IDM_LoadGame;\r
8670     break;\r
8671   case EditPosition:\r
8672     nowChecked = IDM_EditPosition;\r
8673     break;\r
8674   case Training:\r
8675     nowChecked = IDM_Training;\r
8676     break;\r
8677   case IcsPlayingWhite:\r
8678   case IcsPlayingBlack:\r
8679   case IcsObserving:\r
8680   case IcsIdle:\r
8681     nowChecked = IDM_IcsClient;\r
8682     break;\r
8683   default:\r
8684   case EndOfGame:\r
8685     nowChecked = 0;\r
8686     break;\r
8687   }\r
8688   if (prevChecked != 0)\r
8689     (void) CheckMenuItem(GetMenu(hwndMain),\r
8690                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8691   if (nowChecked != 0)\r
8692     (void) CheckMenuItem(GetMenu(hwndMain),\r
8693                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8694 \r
8695   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8696     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8697                           MF_BYCOMMAND|MF_ENABLED);\r
8698   } else {\r
8699     (void) EnableMenuItem(GetMenu(hwndMain), \r
8700                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8701   }\r
8702 \r
8703   prevChecked = nowChecked;\r
8704 \r
8705   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8706   if (appData.icsActive) {\r
8707        if (appData.icsEngineAnalyze) {\r
8708                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8709                        MF_BYCOMMAND|MF_CHECKED);\r
8710        } else {\r
8711                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8712                        MF_BYCOMMAND|MF_UNCHECKED);\r
8713        }\r
8714   }\r
8715 }\r
8716 \r
8717 VOID\r
8718 SetICSMode()\r
8719 {\r
8720   HMENU hmenu = GetMenu(hwndMain);\r
8721   SetMenuEnables(hmenu, icsEnables);\r
8722   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8723     MF_BYPOSITION|MF_ENABLED);\r
8724 #ifdef ZIPPY\r
8725   if (appData.zippyPlay) {\r
8726     SetMenuEnables(hmenu, zippyEnables);\r
8727     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8728          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8729           MF_BYCOMMAND|MF_ENABLED);\r
8730   }\r
8731 #endif\r
8732 }\r
8733 \r
8734 VOID\r
8735 SetGNUMode()\r
8736 {\r
8737   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8738 }\r
8739 \r
8740 VOID\r
8741 SetNCPMode()\r
8742 {\r
8743   HMENU hmenu = GetMenu(hwndMain);\r
8744   SetMenuEnables(hmenu, ncpEnables);\r
8745   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8746     MF_BYPOSITION|MF_GRAYED);\r
8747     DrawMenuBar(hwndMain);\r
8748 }\r
8749 \r
8750 VOID\r
8751 SetCmailMode()\r
8752 {\r
8753   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8754 }\r
8755 \r
8756 VOID \r
8757 SetTrainingModeOn()\r
8758 {\r
8759   int i;\r
8760   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8761   for (i = 0; i < N_BUTTONS; i++) {\r
8762     if (buttonDesc[i].hwnd != NULL)\r
8763       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8764   }\r
8765   CommentPopDown();\r
8766 }\r
8767 \r
8768 VOID SetTrainingModeOff()\r
8769 {\r
8770   int i;\r
8771   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8772   for (i = 0; i < N_BUTTONS; i++) {\r
8773     if (buttonDesc[i].hwnd != NULL)\r
8774       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8775   }\r
8776 }\r
8777 \r
8778 \r
8779 VOID\r
8780 SetUserThinkingEnables()\r
8781 {\r
8782   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8783 }\r
8784 \r
8785 VOID\r
8786 SetMachineThinkingEnables()\r
8787 {\r
8788   HMENU hMenu = GetMenu(hwndMain);\r
8789   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8790 \r
8791   SetMenuEnables(hMenu, machineThinkingEnables);\r
8792 \r
8793   if (gameMode == MachinePlaysBlack) {\r
8794     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8795   } else if (gameMode == MachinePlaysWhite) {\r
8796     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8797   } else if (gameMode == TwoMachinesPlay) {\r
8798     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8799   }\r
8800 }\r
8801 \r
8802 \r
8803 VOID\r
8804 DisplayTitle(char *str)\r
8805 {\r
8806   char title[MSG_SIZ], *host;\r
8807   if (str[0] != NULLCHAR) {\r
8808     strcpy(title, str);\r
8809   } else if (appData.icsActive) {\r
8810     if (appData.icsCommPort[0] != NULLCHAR)\r
8811       host = "ICS";\r
8812     else \r
8813       host = appData.icsHost;\r
8814     sprintf(title, "%s: %s", szTitle, host);\r
8815   } else if (appData.noChessProgram) {\r
8816     strcpy(title, szTitle);\r
8817   } else {\r
8818     strcpy(title, szTitle);\r
8819     strcat(title, ": ");\r
8820     strcat(title, first.tidy);\r
8821   }\r
8822   SetWindowText(hwndMain, title);\r
8823 }\r
8824 \r
8825 \r
8826 VOID\r
8827 DisplayMessage(char *str1, char *str2)\r
8828 {\r
8829   HDC hdc;\r
8830   HFONT oldFont;\r
8831   int remain = MESSAGE_TEXT_MAX - 1;\r
8832   int len;\r
8833 \r
8834   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8835   messageText[0] = NULLCHAR;\r
8836   if (*str1) {\r
8837     len = strlen(str1);\r
8838     if (len > remain) len = remain;\r
8839     strncpy(messageText, str1, len);\r
8840     messageText[len] = NULLCHAR;\r
8841     remain -= len;\r
8842   }\r
8843   if (*str2 && remain >= 2) {\r
8844     if (*str1) {\r
8845       strcat(messageText, "  ");\r
8846       remain -= 2;\r
8847     }\r
8848     len = strlen(str2);\r
8849     if (len > remain) len = remain;\r
8850     strncat(messageText, str2, len);\r
8851   }\r
8852   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8853 \r
8854   if (IsIconic(hwndMain)) return;\r
8855   hdc = GetDC(hwndMain);\r
8856   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8857   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8858              &messageRect, messageText, strlen(messageText), NULL);\r
8859   (void) SelectObject(hdc, oldFont);\r
8860   (void) ReleaseDC(hwndMain, hdc);\r
8861 }\r
8862 \r
8863 VOID\r
8864 DisplayError(char *str, int error)\r
8865 {\r
8866   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8867   int len;\r
8868 \r
8869   if (error == 0) {\r
8870     strcpy(buf, str);\r
8871   } else {\r
8872     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8873                         NULL, error, LANG_NEUTRAL,\r
8874                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8875     if (len > 0) {\r
8876       sprintf(buf, "%s:\n%s", str, buf2);\r
8877     } else {\r
8878       ErrorMap *em = errmap;\r
8879       while (em->err != 0 && em->err != error) em++;\r
8880       if (em->err != 0) {\r
8881         sprintf(buf, "%s:\n%s", str, em->msg);\r
8882       } else {\r
8883         sprintf(buf, "%s:\nError code %d", str, error);\r
8884       }\r
8885     }\r
8886   }\r
8887   \r
8888   ErrorPopUp("Error", buf);\r
8889 }\r
8890 \r
8891 \r
8892 VOID\r
8893 DisplayMoveError(char *str)\r
8894 {\r
8895   fromX = fromY = -1;\r
8896   ClearHighlights();\r
8897   DrawPosition(FALSE, NULL);\r
8898   if (appData.popupMoveErrors) {\r
8899     ErrorPopUp("Error", str);\r
8900   } else {\r
8901     DisplayMessage(str, "");\r
8902     moveErrorMessageUp = TRUE;\r
8903   }\r
8904 }\r
8905 \r
8906 VOID\r
8907 DisplayFatalError(char *str, int error, int exitStatus)\r
8908 {\r
8909   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8910   int len;\r
8911   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8912 \r
8913   if (error != 0) {\r
8914     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8915                         NULL, error, LANG_NEUTRAL,\r
8916                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8917     if (len > 0) {\r
8918       sprintf(buf, "%s:\n%s", str, buf2);\r
8919     } else {\r
8920       ErrorMap *em = errmap;\r
8921       while (em->err != 0 && em->err != error) em++;\r
8922       if (em->err != 0) {\r
8923         sprintf(buf, "%s:\n%s", str, em->msg);\r
8924       } else {\r
8925         sprintf(buf, "%s:\nError code %d", str, error);\r
8926       }\r
8927     }\r
8928     str = buf;\r
8929   }\r
8930   if (appData.debugMode) {\r
8931     fprintf(debugFP, "%s: %s\n", label, str);\r
8932   }\r
8933   if (appData.popupExitMessage) {\r
8934     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8935                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8936   }\r
8937   ExitEvent(exitStatus);\r
8938 }\r
8939 \r
8940 \r
8941 VOID\r
8942 DisplayInformation(char *str)\r
8943 {\r
8944   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8945 }\r
8946 \r
8947 \r
8948 VOID\r
8949 DisplayNote(char *str)\r
8950 {\r
8951   ErrorPopUp("Note", str);\r
8952 }\r
8953 \r
8954 \r
8955 typedef struct {\r
8956   char *title, *question, *replyPrefix;\r
8957   ProcRef pr;\r
8958 } QuestionParams;\r
8959 \r
8960 LRESULT CALLBACK\r
8961 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8962 {\r
8963   static QuestionParams *qp;\r
8964   char reply[MSG_SIZ];\r
8965   int len, err;\r
8966 \r
8967   switch (message) {\r
8968   case WM_INITDIALOG:\r
8969     qp = (QuestionParams *) lParam;\r
8970     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8971     SetWindowText(hDlg, qp->title);\r
8972     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8973     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8974     return FALSE;\r
8975 \r
8976   case WM_COMMAND:\r
8977     switch (LOWORD(wParam)) {\r
8978     case IDOK:\r
8979       strcpy(reply, qp->replyPrefix);\r
8980       if (*reply) strcat(reply, " ");\r
8981       len = strlen(reply);\r
8982       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8983       strcat(reply, "\n");\r
8984       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8985       EndDialog(hDlg, TRUE);\r
8986       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8987       return TRUE;\r
8988     case IDCANCEL:\r
8989       EndDialog(hDlg, FALSE);\r
8990       return TRUE;\r
8991     default:\r
8992       break;\r
8993     }\r
8994     break;\r
8995   }\r
8996   return FALSE;\r
8997 }\r
8998 \r
8999 VOID\r
9000 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9001 {\r
9002     QuestionParams qp;\r
9003     FARPROC lpProc;\r
9004     \r
9005     qp.title = title;\r
9006     qp.question = question;\r
9007     qp.replyPrefix = replyPrefix;\r
9008     qp.pr = pr;\r
9009     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9010     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9011       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9012     FreeProcInstance(lpProc);\r
9013 }\r
9014 \r
9015 /* [AS] Pick FRC position */\r
9016 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9017 {\r
9018     static int * lpIndexFRC;\r
9019     BOOL index_is_ok;\r
9020     char buf[16];\r
9021 \r
9022     switch( message )\r
9023     {\r
9024     case WM_INITDIALOG:\r
9025         lpIndexFRC = (int *) lParam;\r
9026 \r
9027         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9028 \r
9029         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9030         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9031         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9032         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9033 \r
9034         break;\r
9035 \r
9036     case WM_COMMAND:\r
9037         switch( LOWORD(wParam) ) {\r
9038         case IDOK:\r
9039             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9040             EndDialog( hDlg, 0 );\r
9041             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9042             return TRUE;\r
9043         case IDCANCEL:\r
9044             EndDialog( hDlg, 1 );   \r
9045             return TRUE;\r
9046         case IDC_NFG_Edit:\r
9047             if( HIWORD(wParam) == EN_CHANGE ) {\r
9048                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9049 \r
9050                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9051             }\r
9052             return TRUE;\r
9053         case IDC_NFG_Random:\r
9054             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9055             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9056             return TRUE;\r
9057         }\r
9058 \r
9059         break;\r
9060     }\r
9061 \r
9062     return FALSE;\r
9063 }\r
9064 \r
9065 int NewGameFRC()\r
9066 {\r
9067     int result;\r
9068     int index = appData.defaultFrcPosition;\r
9069     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9070 \r
9071     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9072 \r
9073     if( result == 0 ) {\r
9074         appData.defaultFrcPosition = index;\r
9075     }\r
9076 \r
9077     return result;\r
9078 }\r
9079 \r
9080 /* [AS] Game list options */\r
9081 typedef struct {\r
9082     char id;\r
9083     char * name;\r
9084 } GLT_Item;\r
9085 \r
9086 static GLT_Item GLT_ItemInfo[] = {\r
9087     { GLT_EVENT,      "Event" },\r
9088     { GLT_SITE,       "Site" },\r
9089     { GLT_DATE,       "Date" },\r
9090     { GLT_ROUND,      "Round" },\r
9091     { GLT_PLAYERS,    "Players" },\r
9092     { GLT_RESULT,     "Result" },\r
9093     { GLT_WHITE_ELO,  "White Rating" },\r
9094     { GLT_BLACK_ELO,  "Black Rating" },\r
9095     { GLT_TIME_CONTROL,"Time Control" },\r
9096     { GLT_VARIANT,    "Variant" },\r
9097     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9098     { 0, 0 }\r
9099 };\r
9100 \r
9101 const char * GLT_FindItem( char id )\r
9102 {\r
9103     const char * result = 0;\r
9104 \r
9105     GLT_Item * list = GLT_ItemInfo;\r
9106 \r
9107     while( list->id != 0 ) {\r
9108         if( list->id == id ) {\r
9109             result = list->name;\r
9110             break;\r
9111         }\r
9112 \r
9113         list++;\r
9114     }\r
9115 \r
9116     return result;\r
9117 }\r
9118 \r
9119 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9120 {\r
9121     const char * name = GLT_FindItem( id );\r
9122 \r
9123     if( name != 0 ) {\r
9124         if( index >= 0 ) {\r
9125             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9126         }\r
9127         else {\r
9128             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9129         }\r
9130     }\r
9131 }\r
9132 \r
9133 void GLT_TagsToList( HWND hDlg, char * tags )\r
9134 {\r
9135     char * pc = tags;\r
9136 \r
9137     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9138 \r
9139     while( *pc ) {\r
9140         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9141         pc++;\r
9142     }\r
9143 \r
9144     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9145 \r
9146     pc = GLT_ALL_TAGS;\r
9147 \r
9148     while( *pc ) {\r
9149         if( strchr( tags, *pc ) == 0 ) {\r
9150             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9151         }\r
9152         pc++;\r
9153     }\r
9154 \r
9155     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9156 }\r
9157 \r
9158 char GLT_ListItemToTag( HWND hDlg, int index )\r
9159 {\r
9160     char result = '\0';\r
9161     char name[128];\r
9162 \r
9163     GLT_Item * list = GLT_ItemInfo;\r
9164 \r
9165     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9166         while( list->id != 0 ) {\r
9167             if( strcmp( list->name, name ) == 0 ) {\r
9168                 result = list->id;\r
9169                 break;\r
9170             }\r
9171 \r
9172             list++;\r
9173         }\r
9174     }\r
9175 \r
9176     return result;\r
9177 }\r
9178 \r
9179 void GLT_MoveSelection( HWND hDlg, int delta )\r
9180 {\r
9181     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9182     int idx2 = idx1 + delta;\r
9183     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9184 \r
9185     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9186         char buf[128];\r
9187 \r
9188         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9189         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9190         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9191         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9192     }\r
9193 }\r
9194 \r
9195 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9196 {\r
9197     static char glt[64];\r
9198     static char * lpUserGLT;\r
9199 \r
9200     switch( message )\r
9201     {\r
9202     case WM_INITDIALOG:\r
9203         lpUserGLT = (char *) lParam;\r
9204         \r
9205         strcpy( glt, lpUserGLT );\r
9206 \r
9207         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9208 \r
9209         /* Initialize list */\r
9210         GLT_TagsToList( hDlg, glt );\r
9211 \r
9212         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9213 \r
9214         break;\r
9215 \r
9216     case WM_COMMAND:\r
9217         switch( LOWORD(wParam) ) {\r
9218         case IDOK:\r
9219             {\r
9220                 char * pc = lpUserGLT;\r
9221                 int idx = 0;\r
9222                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9223                 char id;\r
9224 \r
9225                 do {\r
9226                     id = GLT_ListItemToTag( hDlg, idx );\r
9227 \r
9228                     *pc++ = id;\r
9229                     idx++;\r
9230                 } while( id != '\0' );\r
9231             }\r
9232             EndDialog( hDlg, 0 );\r
9233             return TRUE;\r
9234         case IDCANCEL:\r
9235             EndDialog( hDlg, 1 );\r
9236             return TRUE;\r
9237 \r
9238         case IDC_GLT_Default:\r
9239             strcpy( glt, GLT_DEFAULT_TAGS );\r
9240             GLT_TagsToList( hDlg, glt );\r
9241             return TRUE;\r
9242 \r
9243         case IDC_GLT_Restore:\r
9244             strcpy( glt, lpUserGLT );\r
9245             GLT_TagsToList( hDlg, glt );\r
9246             return TRUE;\r
9247 \r
9248         case IDC_GLT_Up:\r
9249             GLT_MoveSelection( hDlg, -1 );\r
9250             return TRUE;\r
9251 \r
9252         case IDC_GLT_Down:\r
9253             GLT_MoveSelection( hDlg, +1 );\r
9254             return TRUE;\r
9255         }\r
9256 \r
9257         break;\r
9258     }\r
9259 \r
9260     return FALSE;\r
9261 }\r
9262 \r
9263 int GameListOptions()\r
9264 {\r
9265     char glt[64];\r
9266     int result;\r
9267     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9268 \r
9269     strcpy( glt, appData.gameListTags );\r
9270 \r
9271     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9272 \r
9273     if( result == 0 ) {\r
9274         /* [AS] Memory leak here! */\r
9275         appData.gameListTags = strdup( glt ); \r
9276     }\r
9277 \r
9278     return result;\r
9279 }\r
9280 \r
9281 \r
9282 VOID\r
9283 DisplayIcsInteractionTitle(char *str)\r
9284 {\r
9285   char consoleTitle[MSG_SIZ];\r
9286 \r
9287   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9288   SetWindowText(hwndConsole, consoleTitle);\r
9289 }\r
9290 \r
9291 void\r
9292 DrawPosition(int fullRedraw, Board board)\r
9293 {\r
9294   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9295 }\r
9296 \r
9297 \r
9298 VOID\r
9299 ResetFrontEnd()\r
9300 {\r
9301   fromX = fromY = -1;\r
9302   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9303     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9304     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9305     dragInfo.lastpos = dragInfo.pos;\r
9306     dragInfo.start.x = dragInfo.start.y = -1;\r
9307     dragInfo.from = dragInfo.start;\r
9308     ReleaseCapture();\r
9309     DrawPosition(TRUE, NULL);\r
9310   }\r
9311 }\r
9312 \r
9313 \r
9314 VOID\r
9315 CommentPopUp(char *title, char *str)\r
9316 {\r
9317   HWND hwnd = GetActiveWindow();\r
9318   EitherCommentPopUp(0, title, str, FALSE);\r
9319   SetActiveWindow(hwnd);\r
9320 }\r
9321 \r
9322 VOID\r
9323 CommentPopDown(void)\r
9324 {\r
9325   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9326   if (commentDialog) {\r
9327     ShowWindow(commentDialog, SW_HIDE);\r
9328   }\r
9329   commentDialogUp = FALSE;\r
9330 }\r
9331 \r
9332 VOID\r
9333 EditCommentPopUp(int index, char *title, char *str)\r
9334 {\r
9335   EitherCommentPopUp(index, title, str, TRUE);\r
9336 }\r
9337 \r
9338 \r
9339 VOID\r
9340 RingBell()\r
9341 {\r
9342   MyPlaySound(&sounds[(int)SoundMove]);\r
9343 }\r
9344 \r
9345 VOID PlayIcsWinSound()\r
9346 {\r
9347   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9348 }\r
9349 \r
9350 VOID PlayIcsLossSound()\r
9351 {\r
9352   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9353 }\r
9354 \r
9355 VOID PlayIcsDrawSound()\r
9356 {\r
9357   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9358 }\r
9359 \r
9360 VOID PlayIcsUnfinishedSound()\r
9361 {\r
9362   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9363 }\r
9364 \r
9365 VOID\r
9366 PlayAlarmSound()\r
9367 {\r
9368   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9369 }\r
9370 \r
9371 \r
9372 VOID\r
9373 EchoOn()\r
9374 {\r
9375   HWND hInput;\r
9376   consoleEcho = TRUE;\r
9377   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9378   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9379   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9380 }\r
9381 \r
9382 \r
9383 VOID\r
9384 EchoOff()\r
9385 {\r
9386   CHARFORMAT cf;\r
9387   HWND hInput;\r
9388   consoleEcho = FALSE;\r
9389   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9390   /* This works OK: set text and background both to the same color */\r
9391   cf = consoleCF;\r
9392   cf.crTextColor = COLOR_ECHOOFF;\r
9393   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9394   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9395 }\r
9396 \r
9397 /* No Raw()...? */\r
9398 \r
9399 void Colorize(ColorClass cc, int continuation)\r
9400 {\r
9401   currentColorClass = cc;\r
9402   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9403   consoleCF.crTextColor = textAttribs[cc].color;\r
9404   consoleCF.dwEffects = textAttribs[cc].effects;\r
9405   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9406 }\r
9407 \r
9408 char *\r
9409 UserName()\r
9410 {\r
9411   static char buf[MSG_SIZ];\r
9412   DWORD bufsiz = MSG_SIZ;\r
9413 \r
9414   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9415         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9416   }\r
9417   if (!GetUserName(buf, &bufsiz)) {\r
9418     /*DisplayError("Error getting user name", GetLastError());*/\r
9419     strcpy(buf, "User");\r
9420   }\r
9421   return buf;\r
9422 }\r
9423 \r
9424 char *\r
9425 HostName()\r
9426 {\r
9427   static char buf[MSG_SIZ];\r
9428   DWORD bufsiz = MSG_SIZ;\r
9429 \r
9430   if (!GetComputerName(buf, &bufsiz)) {\r
9431     /*DisplayError("Error getting host name", GetLastError());*/\r
9432     strcpy(buf, "Unknown");\r
9433   }\r
9434   return buf;\r
9435 }\r
9436 \r
9437 \r
9438 int\r
9439 ClockTimerRunning()\r
9440 {\r
9441   return clockTimerEvent != 0;\r
9442 }\r
9443 \r
9444 int\r
9445 StopClockTimer()\r
9446 {\r
9447   if (clockTimerEvent == 0) return FALSE;\r
9448   KillTimer(hwndMain, clockTimerEvent);\r
9449   clockTimerEvent = 0;\r
9450   return TRUE;\r
9451 }\r
9452 \r
9453 void\r
9454 StartClockTimer(long millisec)\r
9455 {\r
9456   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9457                              (UINT) millisec, NULL);\r
9458 }\r
9459 \r
9460 void\r
9461 DisplayWhiteClock(long timeRemaining, int highlight)\r
9462 {\r
9463   HDC hdc;\r
9464   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9465 \r
9466   if(appData.noGUI) return;\r
9467   hdc = GetDC(hwndMain);\r
9468   if (!IsIconic(hwndMain)) {\r
9469     DisplayAClock(hdc, timeRemaining, highlight, \r
9470                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9471   }\r
9472   if (highlight && iconCurrent == iconBlack) {\r
9473     iconCurrent = iconWhite;\r
9474     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9475     if (IsIconic(hwndMain)) {\r
9476       DrawIcon(hdc, 2, 2, iconCurrent);\r
9477     }\r
9478   }\r
9479   (void) ReleaseDC(hwndMain, hdc);\r
9480   if (hwndConsole)\r
9481     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9482 }\r
9483 \r
9484 void\r
9485 DisplayBlackClock(long timeRemaining, int highlight)\r
9486 {\r
9487   HDC hdc;\r
9488   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9489 \r
9490   if(appData.noGUI) return;\r
9491   hdc = GetDC(hwndMain);\r
9492   if (!IsIconic(hwndMain)) {\r
9493     DisplayAClock(hdc, timeRemaining, highlight, \r
9494                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9495   }\r
9496   if (highlight && iconCurrent == iconWhite) {\r
9497     iconCurrent = iconBlack;\r
9498     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9499     if (IsIconic(hwndMain)) {\r
9500       DrawIcon(hdc, 2, 2, iconCurrent);\r
9501     }\r
9502   }\r
9503   (void) ReleaseDC(hwndMain, hdc);\r
9504   if (hwndConsole)\r
9505     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9506 }\r
9507 \r
9508 \r
9509 int\r
9510 LoadGameTimerRunning()\r
9511 {\r
9512   return loadGameTimerEvent != 0;\r
9513 }\r
9514 \r
9515 int\r
9516 StopLoadGameTimer()\r
9517 {\r
9518   if (loadGameTimerEvent == 0) return FALSE;\r
9519   KillTimer(hwndMain, loadGameTimerEvent);\r
9520   loadGameTimerEvent = 0;\r
9521   return TRUE;\r
9522 }\r
9523 \r
9524 void\r
9525 StartLoadGameTimer(long millisec)\r
9526 {\r
9527   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9528                                 (UINT) millisec, NULL);\r
9529 }\r
9530 \r
9531 void\r
9532 AutoSaveGame()\r
9533 {\r
9534   char *defName;\r
9535   FILE *f;\r
9536   char fileTitle[MSG_SIZ];\r
9537 \r
9538   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9539   f = OpenFileDialog(hwndMain, "a", defName,\r
9540                      appData.oldSaveStyle ? "gam" : "pgn",\r
9541                      GAME_FILT, \r
9542                      "Save Game to File", NULL, fileTitle, NULL);\r
9543   if (f != NULL) {\r
9544     SaveGame(f, 0, "");\r
9545     fclose(f);\r
9546   }\r
9547 }\r
9548 \r
9549 \r
9550 void\r
9551 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9552 {\r
9553   if (delayedTimerEvent != 0) {\r
9554     if (appData.debugMode) {\r
9555       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9556     }\r
9557     KillTimer(hwndMain, delayedTimerEvent);\r
9558     delayedTimerEvent = 0;\r
9559     delayedTimerCallback();\r
9560   }\r
9561   delayedTimerCallback = cb;\r
9562   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9563                                 (UINT) millisec, NULL);\r
9564 }\r
9565 \r
9566 DelayedEventCallback\r
9567 GetDelayedEvent()\r
9568 {\r
9569   if (delayedTimerEvent) {\r
9570     return delayedTimerCallback;\r
9571   } else {\r
9572     return NULL;\r
9573   }\r
9574 }\r
9575 \r
9576 void\r
9577 CancelDelayedEvent()\r
9578 {\r
9579   if (delayedTimerEvent) {\r
9580     KillTimer(hwndMain, delayedTimerEvent);\r
9581     delayedTimerEvent = 0;\r
9582   }\r
9583 }\r
9584 \r
9585 DWORD GetWin32Priority(int nice)\r
9586 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9587 /*\r
9588 REALTIME_PRIORITY_CLASS     0x00000100\r
9589 HIGH_PRIORITY_CLASS         0x00000080\r
9590 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9591 NORMAL_PRIORITY_CLASS       0x00000020\r
9592 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9593 IDLE_PRIORITY_CLASS         0x00000040\r
9594 */\r
9595         if (nice < -15) return 0x00000080;\r
9596         if (nice < 0)   return 0x00008000;\r
9597         if (nice == 0)  return 0x00000020;\r
9598         if (nice < 15)  return 0x00004000;\r
9599         return 0x00000040;\r
9600 }\r
9601 \r
9602 /* Start a child process running the given program.\r
9603    The process's standard output can be read from "from", and its\r
9604    standard input can be written to "to".\r
9605    Exit with fatal error if anything goes wrong.\r
9606    Returns an opaque pointer that can be used to destroy the process\r
9607    later.\r
9608 */\r
9609 int\r
9610 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9611 {\r
9612 #define BUFSIZE 4096\r
9613 \r
9614   HANDLE hChildStdinRd, hChildStdinWr,\r
9615     hChildStdoutRd, hChildStdoutWr;\r
9616   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9617   SECURITY_ATTRIBUTES saAttr;\r
9618   BOOL fSuccess;\r
9619   PROCESS_INFORMATION piProcInfo;\r
9620   STARTUPINFO siStartInfo;\r
9621   ChildProc *cp;\r
9622   char buf[MSG_SIZ];\r
9623   DWORD err;\r
9624 \r
9625   if (appData.debugMode) {\r
9626     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9627   }\r
9628 \r
9629   *pr = NoProc;\r
9630 \r
9631   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9632   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9633   saAttr.bInheritHandle = TRUE;\r
9634   saAttr.lpSecurityDescriptor = NULL;\r
9635 \r
9636   /*\r
9637    * The steps for redirecting child's STDOUT:\r
9638    *     1. Create anonymous pipe to be STDOUT for child.\r
9639    *     2. Create a noninheritable duplicate of read handle,\r
9640    *         and close the inheritable read handle.\r
9641    */\r
9642 \r
9643   /* Create a pipe for the child's STDOUT. */\r
9644   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9645     return GetLastError();\r
9646   }\r
9647 \r
9648   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9649   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9650                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9651                              FALSE,     /* not inherited */\r
9652                              DUPLICATE_SAME_ACCESS);\r
9653   if (! fSuccess) {\r
9654     return GetLastError();\r
9655   }\r
9656   CloseHandle(hChildStdoutRd);\r
9657 \r
9658   /*\r
9659    * The steps for redirecting child's STDIN:\r
9660    *     1. Create anonymous pipe to be STDIN for child.\r
9661    *     2. Create a noninheritable duplicate of write handle,\r
9662    *         and close the inheritable write handle.\r
9663    */\r
9664 \r
9665   /* Create a pipe for the child's STDIN. */\r
9666   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9667     return GetLastError();\r
9668   }\r
9669 \r
9670   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9671   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9672                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9673                              FALSE,     /* not inherited */\r
9674                              DUPLICATE_SAME_ACCESS);\r
9675   if (! fSuccess) {\r
9676     return GetLastError();\r
9677   }\r
9678   CloseHandle(hChildStdinWr);\r
9679 \r
9680   /* Arrange to (1) look in dir for the child .exe file, and\r
9681    * (2) have dir be the child's working directory.  Interpret\r
9682    * dir relative to the directory WinBoard loaded from. */\r
9683   GetCurrentDirectory(MSG_SIZ, buf);\r
9684   SetCurrentDirectory(installDir);\r
9685   SetCurrentDirectory(dir);\r
9686 \r
9687   /* Now create the child process. */\r
9688 \r
9689   siStartInfo.cb = sizeof(STARTUPINFO);\r
9690   siStartInfo.lpReserved = NULL;\r
9691   siStartInfo.lpDesktop = NULL;\r
9692   siStartInfo.lpTitle = NULL;\r
9693   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9694   siStartInfo.cbReserved2 = 0;\r
9695   siStartInfo.lpReserved2 = NULL;\r
9696   siStartInfo.hStdInput = hChildStdinRd;\r
9697   siStartInfo.hStdOutput = hChildStdoutWr;\r
9698   siStartInfo.hStdError = hChildStdoutWr;\r
9699 \r
9700   fSuccess = CreateProcess(NULL,\r
9701                            cmdLine,        /* command line */\r
9702                            NULL,           /* process security attributes */\r
9703                            NULL,           /* primary thread security attrs */\r
9704                            TRUE,           /* handles are inherited */\r
9705                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9706                            NULL,           /* use parent's environment */\r
9707                            NULL,\r
9708                            &siStartInfo, /* STARTUPINFO pointer */\r
9709                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9710 \r
9711   err = GetLastError();\r
9712   SetCurrentDirectory(buf); /* return to prev directory */\r
9713   if (! fSuccess) {\r
9714     return err;\r
9715   }\r
9716 \r
9717   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9718     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9719     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9720   }\r
9721 \r
9722   /* Close the handles we don't need in the parent */\r
9723   CloseHandle(piProcInfo.hThread);\r
9724   CloseHandle(hChildStdinRd);\r
9725   CloseHandle(hChildStdoutWr);\r
9726 \r
9727   /* Prepare return value */\r
9728   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9729   cp->kind = CPReal;\r
9730   cp->hProcess = piProcInfo.hProcess;\r
9731   cp->pid = piProcInfo.dwProcessId;\r
9732   cp->hFrom = hChildStdoutRdDup;\r
9733   cp->hTo = hChildStdinWrDup;\r
9734 \r
9735   *pr = (void *) cp;\r
9736 \r
9737   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9738      2000 where engines sometimes don't see the initial command(s)\r
9739      from WinBoard and hang.  I don't understand how that can happen,\r
9740      but the Sleep is harmless, so I've put it in.  Others have also\r
9741      reported what may be the same problem, so hopefully this will fix\r
9742      it for them too.  */\r
9743   Sleep(500);\r
9744 \r
9745   return NO_ERROR;\r
9746 }\r
9747 \r
9748 \r
9749 void\r
9750 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9751 {\r
9752   ChildProc *cp; int result;\r
9753 \r
9754   cp = (ChildProc *) pr;\r
9755   if (cp == NULL) return;\r
9756 \r
9757   switch (cp->kind) {\r
9758   case CPReal:\r
9759     /* TerminateProcess is considered harmful, so... */\r
9760     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9761     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9762     /* The following doesn't work because the chess program\r
9763        doesn't "have the same console" as WinBoard.  Maybe\r
9764        we could arrange for this even though neither WinBoard\r
9765        nor the chess program uses a console for stdio? */\r
9766     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9767 \r
9768     /* [AS] Special termination modes for misbehaving programs... */\r
9769     if( signal == 9 ) { \r
9770         result = TerminateProcess( cp->hProcess, 0 );\r
9771 \r
9772         if ( appData.debugMode) {\r
9773             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9774         }\r
9775     }\r
9776     else if( signal == 10 ) {\r
9777         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9778 \r
9779         if( dw != WAIT_OBJECT_0 ) {\r
9780             result = TerminateProcess( cp->hProcess, 0 );\r
9781 \r
9782             if ( appData.debugMode) {\r
9783                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9784             }\r
9785 \r
9786         }\r
9787     }\r
9788 \r
9789     CloseHandle(cp->hProcess);\r
9790     break;\r
9791 \r
9792   case CPComm:\r
9793     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9794     break;\r
9795 \r
9796   case CPSock:\r
9797     closesocket(cp->sock);\r
9798     WSACleanup();\r
9799     break;\r
9800 \r
9801   case CPRcmd:\r
9802     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9803     closesocket(cp->sock);\r
9804     closesocket(cp->sock2);\r
9805     WSACleanup();\r
9806     break;\r
9807   }\r
9808   free(cp);\r
9809 }\r
9810 \r
9811 void\r
9812 InterruptChildProcess(ProcRef pr)\r
9813 {\r
9814   ChildProc *cp;\r
9815 \r
9816   cp = (ChildProc *) pr;\r
9817   if (cp == NULL) return;\r
9818   switch (cp->kind) {\r
9819   case CPReal:\r
9820     /* The following doesn't work because the chess program\r
9821        doesn't "have the same console" as WinBoard.  Maybe\r
9822        we could arrange for this even though neither WinBoard\r
9823        nor the chess program uses a console for stdio */\r
9824     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9825     break;\r
9826 \r
9827   case CPComm:\r
9828   case CPSock:\r
9829     /* Can't interrupt */\r
9830     break;\r
9831 \r
9832   case CPRcmd:\r
9833     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9834     break;\r
9835   }\r
9836 }\r
9837 \r
9838 \r
9839 int\r
9840 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9841 {\r
9842   char cmdLine[MSG_SIZ];\r
9843 \r
9844   if (port[0] == NULLCHAR) {\r
9845     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9846   } else {\r
9847     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9848   }\r
9849   return StartChildProcess(cmdLine, "", pr);\r
9850 }\r
9851 \r
9852 \r
9853 /* Code to open TCP sockets */\r
9854 \r
9855 int\r
9856 OpenTCP(char *host, char *port, ProcRef *pr)\r
9857 {\r
9858   ChildProc *cp;\r
9859   int err;\r
9860   SOCKET s;\r
9861   struct sockaddr_in sa, mysa;\r
9862   struct hostent FAR *hp;\r
9863   unsigned short uport;\r
9864   WORD wVersionRequested;\r
9865   WSADATA wsaData;\r
9866 \r
9867   /* Initialize socket DLL */\r
9868   wVersionRequested = MAKEWORD(1, 1);\r
9869   err = WSAStartup(wVersionRequested, &wsaData);\r
9870   if (err != 0) return err;\r
9871 \r
9872   /* Make socket */\r
9873   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9874     err = WSAGetLastError();\r
9875     WSACleanup();\r
9876     return err;\r
9877   }\r
9878 \r
9879   /* Bind local address using (mostly) don't-care values.\r
9880    */\r
9881   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9882   mysa.sin_family = AF_INET;\r
9883   mysa.sin_addr.s_addr = INADDR_ANY;\r
9884   uport = (unsigned short) 0;\r
9885   mysa.sin_port = htons(uport);\r
9886   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9887       == SOCKET_ERROR) {\r
9888     err = WSAGetLastError();\r
9889     WSACleanup();\r
9890     return err;\r
9891   }\r
9892 \r
9893   /* Resolve remote host name */\r
9894   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9895   if (!(hp = gethostbyname(host))) {\r
9896     unsigned int b0, b1, b2, b3;\r
9897 \r
9898     err = WSAGetLastError();\r
9899 \r
9900     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9901       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9902       hp->h_addrtype = AF_INET;\r
9903       hp->h_length = 4;\r
9904       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9905       hp->h_addr_list[0] = (char *) malloc(4);\r
9906       hp->h_addr_list[0][0] = (char) b0;\r
9907       hp->h_addr_list[0][1] = (char) b1;\r
9908       hp->h_addr_list[0][2] = (char) b2;\r
9909       hp->h_addr_list[0][3] = (char) b3;\r
9910     } else {\r
9911       WSACleanup();\r
9912       return err;\r
9913     }\r
9914   }\r
9915   sa.sin_family = hp->h_addrtype;\r
9916   uport = (unsigned short) atoi(port);\r
9917   sa.sin_port = htons(uport);\r
9918   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9919 \r
9920   /* Make connection */\r
9921   if (connect(s, (struct sockaddr *) &sa,\r
9922               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9923     err = WSAGetLastError();\r
9924     WSACleanup();\r
9925     return err;\r
9926   }\r
9927 \r
9928   /* Prepare return value */\r
9929   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9930   cp->kind = CPSock;\r
9931   cp->sock = s;\r
9932   *pr = (ProcRef *) cp;\r
9933 \r
9934   return NO_ERROR;\r
9935 }\r
9936 \r
9937 int\r
9938 OpenCommPort(char *name, ProcRef *pr)\r
9939 {\r
9940   HANDLE h;\r
9941   COMMTIMEOUTS ct;\r
9942   ChildProc *cp;\r
9943   char fullname[MSG_SIZ];\r
9944 \r
9945   if (*name != '\\')\r
9946     sprintf(fullname, "\\\\.\\%s", name);\r
9947   else\r
9948     strcpy(fullname, name);\r
9949 \r
9950   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9951                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9952   if (h == (HANDLE) -1) {\r
9953     return GetLastError();\r
9954   }\r
9955   hCommPort = h;\r
9956 \r
9957   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9958 \r
9959   /* Accumulate characters until a 100ms pause, then parse */\r
9960   ct.ReadIntervalTimeout = 100;\r
9961   ct.ReadTotalTimeoutMultiplier = 0;\r
9962   ct.ReadTotalTimeoutConstant = 0;\r
9963   ct.WriteTotalTimeoutMultiplier = 0;\r
9964   ct.WriteTotalTimeoutConstant = 0;\r
9965   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9966 \r
9967   /* Prepare return value */\r
9968   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9969   cp->kind = CPComm;\r
9970   cp->hFrom = h;\r
9971   cp->hTo = h;\r
9972   *pr = (ProcRef *) cp;\r
9973 \r
9974   return NO_ERROR;\r
9975 }\r
9976 \r
9977 int\r
9978 OpenLoopback(ProcRef *pr)\r
9979 {\r
9980   DisplayFatalError("Not implemented", 0, 1);\r
9981   return NO_ERROR;\r
9982 }\r
9983 \r
9984 \r
9985 int\r
9986 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9987 {\r
9988   ChildProc *cp;\r
9989   int err;\r
9990   SOCKET s, s2, s3;\r
9991   struct sockaddr_in sa, mysa;\r
9992   struct hostent FAR *hp;\r
9993   unsigned short uport;\r
9994   WORD wVersionRequested;\r
9995   WSADATA wsaData;\r
9996   int fromPort;\r
9997   char stderrPortStr[MSG_SIZ];\r
9998 \r
9999   /* Initialize socket DLL */\r
10000   wVersionRequested = MAKEWORD(1, 1);\r
10001   err = WSAStartup(wVersionRequested, &wsaData);\r
10002   if (err != 0) return err;\r
10003 \r
10004   /* Resolve remote host name */\r
10005   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10006   if (!(hp = gethostbyname(host))) {\r
10007     unsigned int b0, b1, b2, b3;\r
10008 \r
10009     err = WSAGetLastError();\r
10010 \r
10011     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10012       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10013       hp->h_addrtype = AF_INET;\r
10014       hp->h_length = 4;\r
10015       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10016       hp->h_addr_list[0] = (char *) malloc(4);\r
10017       hp->h_addr_list[0][0] = (char) b0;\r
10018       hp->h_addr_list[0][1] = (char) b1;\r
10019       hp->h_addr_list[0][2] = (char) b2;\r
10020       hp->h_addr_list[0][3] = (char) b3;\r
10021     } else {\r
10022       WSACleanup();\r
10023       return err;\r
10024     }\r
10025   }\r
10026   sa.sin_family = hp->h_addrtype;\r
10027   uport = (unsigned short) 514;\r
10028   sa.sin_port = htons(uport);\r
10029   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10030 \r
10031   /* Bind local socket to unused "privileged" port address\r
10032    */\r
10033   s = INVALID_SOCKET;\r
10034   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10035   mysa.sin_family = AF_INET;\r
10036   mysa.sin_addr.s_addr = INADDR_ANY;\r
10037   for (fromPort = 1023;; fromPort--) {\r
10038     if (fromPort < 0) {\r
10039       WSACleanup();\r
10040       return WSAEADDRINUSE;\r
10041     }\r
10042     if (s == INVALID_SOCKET) {\r
10043       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10044         err = WSAGetLastError();\r
10045         WSACleanup();\r
10046         return err;\r
10047       }\r
10048     }\r
10049     uport = (unsigned short) fromPort;\r
10050     mysa.sin_port = htons(uport);\r
10051     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10052         == SOCKET_ERROR) {\r
10053       err = WSAGetLastError();\r
10054       if (err == WSAEADDRINUSE) continue;\r
10055       WSACleanup();\r
10056       return err;\r
10057     }\r
10058     if (connect(s, (struct sockaddr *) &sa,\r
10059       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10060       err = WSAGetLastError();\r
10061       if (err == WSAEADDRINUSE) {\r
10062         closesocket(s);\r
10063         s = -1;\r
10064         continue;\r
10065       }\r
10066       WSACleanup();\r
10067       return err;\r
10068     }\r
10069     break;\r
10070   }\r
10071 \r
10072   /* Bind stderr local socket to unused "privileged" port address\r
10073    */\r
10074   s2 = INVALID_SOCKET;\r
10075   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10076   mysa.sin_family = AF_INET;\r
10077   mysa.sin_addr.s_addr = INADDR_ANY;\r
10078   for (fromPort = 1023;; fromPort--) {\r
10079     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10080     if (fromPort < 0) {\r
10081       (void) closesocket(s);\r
10082       WSACleanup();\r
10083       return WSAEADDRINUSE;\r
10084     }\r
10085     if (s2 == INVALID_SOCKET) {\r
10086       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10087         err = WSAGetLastError();\r
10088         closesocket(s);\r
10089         WSACleanup();\r
10090         return err;\r
10091       }\r
10092     }\r
10093     uport = (unsigned short) fromPort;\r
10094     mysa.sin_port = htons(uport);\r
10095     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10096         == SOCKET_ERROR) {\r
10097       err = WSAGetLastError();\r
10098       if (err == WSAEADDRINUSE) continue;\r
10099       (void) closesocket(s);\r
10100       WSACleanup();\r
10101       return err;\r
10102     }\r
10103     if (listen(s2, 1) == SOCKET_ERROR) {\r
10104       err = WSAGetLastError();\r
10105       if (err == WSAEADDRINUSE) {\r
10106         closesocket(s2);\r
10107         s2 = INVALID_SOCKET;\r
10108         continue;\r
10109       }\r
10110       (void) closesocket(s);\r
10111       (void) closesocket(s2);\r
10112       WSACleanup();\r
10113       return err;\r
10114     }\r
10115     break;\r
10116   }\r
10117   prevStderrPort = fromPort; // remember port used\r
10118   sprintf(stderrPortStr, "%d", fromPort);\r
10119 \r
10120   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10121     err = WSAGetLastError();\r
10122     (void) closesocket(s);\r
10123     (void) closesocket(s2);\r
10124     WSACleanup();\r
10125     return err;\r
10126   }\r
10127 \r
10128   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10129     err = WSAGetLastError();\r
10130     (void) closesocket(s);\r
10131     (void) closesocket(s2);\r
10132     WSACleanup();\r
10133     return err;\r
10134   }\r
10135   if (*user == NULLCHAR) user = UserName();\r
10136   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10137     err = WSAGetLastError();\r
10138     (void) closesocket(s);\r
10139     (void) closesocket(s2);\r
10140     WSACleanup();\r
10141     return err;\r
10142   }\r
10143   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10144     err = WSAGetLastError();\r
10145     (void) closesocket(s);\r
10146     (void) closesocket(s2);\r
10147     WSACleanup();\r
10148     return err;\r
10149   }\r
10150 \r
10151   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10152     err = WSAGetLastError();\r
10153     (void) closesocket(s);\r
10154     (void) closesocket(s2);\r
10155     WSACleanup();\r
10156     return err;\r
10157   }\r
10158   (void) closesocket(s2);  /* Stop listening */\r
10159 \r
10160   /* Prepare return value */\r
10161   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10162   cp->kind = CPRcmd;\r
10163   cp->sock = s;\r
10164   cp->sock2 = s3;\r
10165   *pr = (ProcRef *) cp;\r
10166 \r
10167   return NO_ERROR;\r
10168 }\r
10169 \r
10170 \r
10171 InputSourceRef\r
10172 AddInputSource(ProcRef pr, int lineByLine,\r
10173                InputCallback func, VOIDSTAR closure)\r
10174 {\r
10175   InputSource *is, *is2 = NULL;\r
10176   ChildProc *cp = (ChildProc *) pr;\r
10177 \r
10178   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10179   is->lineByLine = lineByLine;\r
10180   is->func = func;\r
10181   is->closure = closure;\r
10182   is->second = NULL;\r
10183   is->next = is->buf;\r
10184   if (pr == NoProc) {\r
10185     is->kind = CPReal;\r
10186     consoleInputSource = is;\r
10187   } else {\r
10188     is->kind = cp->kind;\r
10189     /* \r
10190         [AS] Try to avoid a race condition if the thread is given control too early:\r
10191         we create all threads suspended so that the is->hThread variable can be\r
10192         safely assigned, then let the threads start with ResumeThread.\r
10193     */\r
10194     switch (cp->kind) {\r
10195     case CPReal:\r
10196       is->hFile = cp->hFrom;\r
10197       cp->hFrom = NULL; /* now owned by InputThread */\r
10198       is->hThread =\r
10199         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10200                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10201       break;\r
10202 \r
10203     case CPComm:\r
10204       is->hFile = cp->hFrom;\r
10205       cp->hFrom = NULL; /* now owned by InputThread */\r
10206       is->hThread =\r
10207         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10208                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10209       break;\r
10210 \r
10211     case CPSock:\r
10212       is->sock = cp->sock;\r
10213       is->hThread =\r
10214         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10215                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10216       break;\r
10217 \r
10218     case CPRcmd:\r
10219       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10220       *is2 = *is;\r
10221       is->sock = cp->sock;\r
10222       is->second = is2;\r
10223       is2->sock = cp->sock2;\r
10224       is2->second = is2;\r
10225       is->hThread =\r
10226         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10227                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10228       is2->hThread =\r
10229         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10230                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10231       break;\r
10232     }\r
10233 \r
10234     if( is->hThread != NULL ) {\r
10235         ResumeThread( is->hThread );\r
10236     }\r
10237 \r
10238     if( is2 != NULL && is2->hThread != NULL ) {\r
10239         ResumeThread( is2->hThread );\r
10240     }\r
10241   }\r
10242 \r
10243   return (InputSourceRef) is;\r
10244 }\r
10245 \r
10246 void\r
10247 RemoveInputSource(InputSourceRef isr)\r
10248 {\r
10249   InputSource *is;\r
10250 \r
10251   is = (InputSource *) isr;\r
10252   is->hThread = NULL;  /* tell thread to stop */\r
10253   CloseHandle(is->hThread);\r
10254   if (is->second != NULL) {\r
10255     is->second->hThread = NULL;\r
10256     CloseHandle(is->second->hThread);\r
10257   }\r
10258 }\r
10259 \r
10260 \r
10261 int\r
10262 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10263 {\r
10264   DWORD dOutCount;\r
10265   int outCount = SOCKET_ERROR;\r
10266   ChildProc *cp = (ChildProc *) pr;\r
10267   static OVERLAPPED ovl;\r
10268 \r
10269   if (pr == NoProc) {\r
10270     ConsoleOutput(message, count, FALSE);\r
10271     return count;\r
10272   } \r
10273 \r
10274   if (ovl.hEvent == NULL) {\r
10275     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10276   }\r
10277   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10278 \r
10279   switch (cp->kind) {\r
10280   case CPSock:\r
10281   case CPRcmd:\r
10282     outCount = send(cp->sock, message, count, 0);\r
10283     if (outCount == SOCKET_ERROR) {\r
10284       *outError = WSAGetLastError();\r
10285     } else {\r
10286       *outError = NO_ERROR;\r
10287     }\r
10288     break;\r
10289 \r
10290   case CPReal:\r
10291     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10292                   &dOutCount, NULL)) {\r
10293       *outError = NO_ERROR;\r
10294       outCount = (int) dOutCount;\r
10295     } else {\r
10296       *outError = GetLastError();\r
10297     }\r
10298     break;\r
10299 \r
10300   case CPComm:\r
10301     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10302                             &dOutCount, &ovl);\r
10303     if (*outError == NO_ERROR) {\r
10304       outCount = (int) dOutCount;\r
10305     }\r
10306     break;\r
10307   }\r
10308   return outCount;\r
10309 }\r
10310 \r
10311 int\r
10312 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10313                        long msdelay)\r
10314 {\r
10315   /* Ignore delay, not implemented for WinBoard */\r
10316   return OutputToProcess(pr, message, count, outError);\r
10317 }\r
10318 \r
10319 \r
10320 void\r
10321 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10322                         char *buf, int count, int error)\r
10323 {\r
10324   DisplayFatalError("Not implemented", 0, 1);\r
10325 }\r
10326 \r
10327 /* see wgamelist.c for Game List functions */\r
10328 /* see wedittags.c for Edit Tags functions */\r
10329 \r
10330 \r
10331 VOID\r
10332 ICSInitScript()\r
10333 {\r
10334   FILE *f;\r
10335   char buf[MSG_SIZ];\r
10336   char *dummy;\r
10337 \r
10338   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10339     f = fopen(buf, "r");\r
10340     if (f != NULL) {\r
10341       ProcessICSInitScript(f);\r
10342       fclose(f);\r
10343     }\r
10344   }\r
10345 }\r
10346 \r
10347 \r
10348 VOID\r
10349 StartAnalysisClock()\r
10350 {\r
10351   if (analysisTimerEvent) return;\r
10352   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10353                                         (UINT) 2000, NULL);\r
10354 }\r
10355 \r
10356 LRESULT CALLBACK\r
10357 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10358 {\r
10359   static HANDLE hwndText;\r
10360   RECT rect;\r
10361   static int sizeX, sizeY;\r
10362   int newSizeX, newSizeY, flags;\r
10363   MINMAXINFO *mmi;\r
10364 \r
10365   switch (message) {\r
10366   case WM_INITDIALOG: /* message: initialize dialog box */\r
10367     /* Initialize the dialog items */\r
10368     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10369     SetWindowText(hDlg, analysisTitle);\r
10370     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10371     /* Size and position the dialog */\r
10372     if (!analysisDialog) {\r
10373       analysisDialog = hDlg;\r
10374       flags = SWP_NOZORDER;\r
10375       GetClientRect(hDlg, &rect);\r
10376       sizeX = rect.right;\r
10377       sizeY = rect.bottom;\r
10378       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10379           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10380         WINDOWPLACEMENT wp;\r
10381         EnsureOnScreen(&analysisX, &analysisY);\r
10382         wp.length = sizeof(WINDOWPLACEMENT);\r
10383         wp.flags = 0;\r
10384         wp.showCmd = SW_SHOW;\r
10385         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10386         wp.rcNormalPosition.left = analysisX;\r
10387         wp.rcNormalPosition.right = analysisX + analysisW;\r
10388         wp.rcNormalPosition.top = analysisY;\r
10389         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10390         SetWindowPlacement(hDlg, &wp);\r
10391 \r
10392         GetClientRect(hDlg, &rect);\r
10393         newSizeX = rect.right;\r
10394         newSizeY = rect.bottom;\r
10395         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10396                               newSizeX, newSizeY);\r
10397         sizeX = newSizeX;\r
10398         sizeY = newSizeY;\r
10399       }\r
10400     }\r
10401     return FALSE;\r
10402 \r
10403   case WM_COMMAND: /* message: received a command */\r
10404     switch (LOWORD(wParam)) {\r
10405     case IDCANCEL:\r
10406       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10407           ExitAnalyzeMode();\r
10408           ModeHighlight();\r
10409           return TRUE;\r
10410       }\r
10411       EditGameEvent();\r
10412       return TRUE;\r
10413     default:\r
10414       break;\r
10415     }\r
10416     break;\r
10417 \r
10418   case WM_SIZE:\r
10419     newSizeX = LOWORD(lParam);\r
10420     newSizeY = HIWORD(lParam);\r
10421     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10422     sizeX = newSizeX;\r
10423     sizeY = newSizeY;\r
10424     break;\r
10425 \r
10426   case WM_GETMINMAXINFO:\r
10427     /* Prevent resizing window too small */\r
10428     mmi = (MINMAXINFO *) lParam;\r
10429     mmi->ptMinTrackSize.x = 100;\r
10430     mmi->ptMinTrackSize.y = 100;\r
10431     break;\r
10432   }\r
10433   return FALSE;\r
10434 }\r
10435 \r
10436 VOID\r
10437 AnalysisPopUp(char* title, char* str)\r
10438 {\r
10439   FARPROC lpProc;\r
10440   char *p, *q;\r
10441 \r
10442   /* [AS] */\r
10443   EngineOutputPopUp();\r
10444   return;\r
10445 \r
10446   if (str == NULL) str = "";\r
10447   p = (char *) malloc(2 * strlen(str) + 2);\r
10448   q = p;\r
10449   while (*str) {\r
10450     if (*str == '\n') *q++ = '\r';\r
10451     *q++ = *str++;\r
10452   }\r
10453   *q = NULLCHAR;\r
10454   if (analysisText != NULL) free(analysisText);\r
10455   analysisText = p;\r
10456 \r
10457   if (analysisDialog) {\r
10458     SetWindowText(analysisDialog, title);\r
10459     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10460     ShowWindow(analysisDialog, SW_SHOW);\r
10461   } else {\r
10462     analysisTitle = title;\r
10463     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10464     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10465                  hwndMain, (DLGPROC)lpProc);\r
10466     FreeProcInstance(lpProc);\r
10467   }\r
10468   analysisDialogUp = TRUE;  \r
10469 }\r
10470 \r
10471 VOID\r
10472 AnalysisPopDown()\r
10473 {\r
10474   if (analysisDialog) {\r
10475     ShowWindow(analysisDialog, SW_HIDE);\r
10476   }\r
10477   analysisDialogUp = FALSE;  \r
10478 }\r
10479 \r
10480 \r
10481 VOID\r
10482 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10483 {\r
10484   highlightInfo.sq[0].x = fromX;\r
10485   highlightInfo.sq[0].y = fromY;\r
10486   highlightInfo.sq[1].x = toX;\r
10487   highlightInfo.sq[1].y = toY;\r
10488 }\r
10489 \r
10490 VOID\r
10491 ClearHighlights()\r
10492 {\r
10493   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10494     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10495 }\r
10496 \r
10497 VOID\r
10498 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10499 {\r
10500   premoveHighlightInfo.sq[0].x = fromX;\r
10501   premoveHighlightInfo.sq[0].y = fromY;\r
10502   premoveHighlightInfo.sq[1].x = toX;\r
10503   premoveHighlightInfo.sq[1].y = toY;\r
10504 }\r
10505 \r
10506 VOID\r
10507 ClearPremoveHighlights()\r
10508 {\r
10509   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10510     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10511 }\r
10512 \r
10513 VOID\r
10514 ShutDownFrontEnd()\r
10515 {\r
10516   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10517   DeleteClipboardTempFiles();\r
10518 }\r
10519 \r
10520 void\r
10521 BoardToTop()\r
10522 {\r
10523     if (IsIconic(hwndMain))\r
10524       ShowWindow(hwndMain, SW_RESTORE);\r
10525 \r
10526     SetActiveWindow(hwndMain);\r
10527 }\r
10528 \r
10529 /*\r
10530  * Prototypes for animation support routines\r
10531  */\r
10532 static void ScreenSquare(int column, int row, POINT * pt);\r
10533 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10534      POINT frames[], int * nFrames);\r
10535 \r
10536 \r
10537 #define kFactor 4\r
10538 \r
10539 void\r
10540 AnimateMove(board, fromX, fromY, toX, toY)\r
10541      Board board;\r
10542      int fromX;\r
10543      int fromY;\r
10544      int toX;\r
10545      int toY;\r
10546 {\r
10547   ChessSquare piece;\r
10548   POINT start, finish, mid;\r
10549   POINT frames[kFactor * 2 + 1];\r
10550   int nFrames, n;\r
10551 \r
10552   if (!appData.animate) return;\r
10553   if (doingSizing) return;\r
10554   if (fromY < 0 || fromX < 0) return;\r
10555   piece = board[fromY][fromX];\r
10556   if (piece >= EmptySquare) return;\r
10557 \r
10558   ScreenSquare(fromX, fromY, &start);\r
10559   ScreenSquare(toX, toY, &finish);\r
10560 \r
10561   /* All pieces except knights move in straight line */\r
10562   if (piece != WhiteKnight && piece != BlackKnight) {\r
10563     mid.x = start.x + (finish.x - start.x) / 2;\r
10564     mid.y = start.y + (finish.y - start.y) / 2;\r
10565   } else {\r
10566     /* Knight: make diagonal movement then straight */\r
10567     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10568        mid.x = start.x + (finish.x - start.x) / 2;\r
10569        mid.y = finish.y;\r
10570      } else {\r
10571        mid.x = finish.x;\r
10572        mid.y = start.y + (finish.y - start.y) / 2;\r
10573      }\r
10574   }\r
10575   \r
10576   /* Don't use as many frames for very short moves */\r
10577   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10578     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10579   else\r
10580     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10581 \r
10582   animInfo.from.x = fromX;\r
10583   animInfo.from.y = fromY;\r
10584   animInfo.to.x = toX;\r
10585   animInfo.to.y = toY;\r
10586   animInfo.lastpos = start;\r
10587   animInfo.piece = piece;\r
10588   for (n = 0; n < nFrames; n++) {\r
10589     animInfo.pos = frames[n];\r
10590     DrawPosition(FALSE, NULL);\r
10591     animInfo.lastpos = animInfo.pos;\r
10592     Sleep(appData.animSpeed);\r
10593   }\r
10594   animInfo.pos = finish;\r
10595   DrawPosition(FALSE, NULL);\r
10596   animInfo.piece = EmptySquare;\r
10597 }\r
10598 \r
10599 /*      Convert board position to corner of screen rect and color       */\r
10600 \r
10601 static void\r
10602 ScreenSquare(column, row, pt)\r
10603      int column; int row; POINT * pt;\r
10604 {\r
10605   if (flipView) {\r
10606     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10607     pt->y = lineGap + row * (squareSize + lineGap);\r
10608   } else {\r
10609     pt->x = lineGap + column * (squareSize + lineGap);\r
10610     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10611   }\r
10612 }\r
10613 \r
10614 /*      Generate a series of frame coords from start->mid->finish.\r
10615         The movement rate doubles until the half way point is\r
10616         reached, then halves back down to the final destination,\r
10617         which gives a nice slow in/out effect. The algorithmn\r
10618         may seem to generate too many intermediates for short\r
10619         moves, but remember that the purpose is to attract the\r
10620         viewers attention to the piece about to be moved and\r
10621         then to where it ends up. Too few frames would be less\r
10622         noticeable.                                             */\r
10623 \r
10624 static void\r
10625 Tween(start, mid, finish, factor, frames, nFrames)\r
10626      POINT * start; POINT * mid;\r
10627      POINT * finish; int factor;\r
10628      POINT frames[]; int * nFrames;\r
10629 {\r
10630   int n, fraction = 1, count = 0;\r
10631 \r
10632   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10633   for (n = 0; n < factor; n++)\r
10634     fraction *= 2;\r
10635   for (n = 0; n < factor; n++) {\r
10636     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10637     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10638     count ++;\r
10639     fraction = fraction / 2;\r
10640   }\r
10641   \r
10642   /* Midpoint */\r
10643   frames[count] = *mid;\r
10644   count ++;\r
10645   \r
10646   /* Slow out, stepping 1/2, then 1/4, ... */\r
10647   fraction = 2;\r
10648   for (n = 0; n < factor; n++) {\r
10649     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10650     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10651     count ++;\r
10652     fraction = fraction * 2;\r
10653   }\r
10654   *nFrames = count;\r
10655 }\r
10656 \r
10657 void\r
10658 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10659 {\r
10660 #if 0\r
10661     char buf[256];\r
10662 \r
10663     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10664         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10665 \r
10666     OutputDebugString( buf );\r
10667 #endif\r
10668 \r
10669     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10670 \r
10671     EvalGraphSet( first, last, current, pvInfoList );\r
10672 }\r
10673 \r
10674 void SetProgramStats( FrontEndProgramStats * stats )\r
10675 {\r
10676 #if 0\r
10677     char buf[1024];\r
10678 \r
10679     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10680         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10681 \r
10682     OutputDebugString( buf );\r
10683 #endif\r
10684 \r
10685     EngineOutputUpdate( stats );\r
10686 }\r