changes by H.G. Muller; version 4.3.4
[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 \r
96 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
97 \r
98 typedef struct {\r
99   ChessSquare piece;  \r
100   POINT pos;      /* window coordinates of current pos */\r
101   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
102   POINT from;     /* board coordinates of the piece's orig pos */\r
103   POINT to;       /* board coordinates of the piece's new pos */\r
104 } AnimInfo;\r
105 \r
106 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
107 \r
108 typedef struct {\r
109   POINT start;    /* window coordinates of start pos */\r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113 } DragInfo;\r
114 \r
115 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
116 \r
117 typedef struct {\r
118   POINT sq[2];    /* board coordinates of from, to squares */\r
119 } HighlightInfo;\r
120 \r
121 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
122 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
123 \r
124 /* Window class names */\r
125 char szAppName[] = "WinBoard";\r
126 char szConsoleName[] = "WBConsole";\r
127 \r
128 /* Title bar text */\r
129 char szTitle[] = "WinBoard";\r
130 char szConsoleTitle[] = "ICS Interaction";\r
131 \r
132 char *programName;\r
133 char *settingsFileName;\r
134 BOOLEAN saveSettingsOnExit;\r
135 char installDir[MSG_SIZ];\r
136 \r
137 BoardSize boardSize;\r
138 BOOLEAN chessProgram;\r
139 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
140 static int squareSize, lineGap;\r
141 static int winWidth, winHeight;\r
142 static RECT messageRect, whiteRect, blackRect;\r
143 static char messageText[MESSAGE_TEXT_MAX];\r
144 static int clockTimerEvent = 0;\r
145 static int loadGameTimerEvent = 0;\r
146 static int analysisTimerEvent = 0;\r
147 static DelayedEventCallback delayedTimerCallback;\r
148 static int delayedTimerEvent = 0;\r
149 static int buttonCount = 2;\r
150 char *icsTextMenuString;\r
151 char *icsNames;\r
152 char *firstChessProgramNames;\r
153 char *secondChessProgramNames;\r
154 \r
155 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
156 \r
157 #define PALETTESIZE 256\r
158 \r
159 HINSTANCE hInst;          /* current instance */\r
160 HWND hwndMain = NULL;        /* root window*/\r
161 HWND hwndConsole = NULL;\r
162 BOOLEAN alwaysOnTop = FALSE;\r
163 RECT boardRect;\r
164 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
165   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
166 HPALETTE hPal;\r
167 ColorClass currentColorClass;\r
168 \r
169 HWND hCommPort = NULL;    /* currently open comm port */\r
170 static HWND hwndPause;    /* pause button */\r
171 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
172 static HBRUSH lightSquareBrush, darkSquareBrush,\r
173   blackSquareBrush, /* [HGM] for band between board and holdings */\r
174   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
175 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
176 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
177 static HPEN gridPen = NULL;\r
178 static HPEN highlightPen = NULL;\r
179 static HPEN premovePen = NULL;\r
180 static NPLOGPALETTE pLogPal;\r
181 static BOOL paletteChanged = FALSE;\r
182 static HICON iconWhite, iconBlack, iconCurrent;\r
183 static int doingSizing = FALSE;\r
184 static int lastSizing = 0;\r
185 static int prevStderrPort;\r
186 \r
187 /* [AS] Support for background textures */\r
188 #define BACK_TEXTURE_MODE_DISABLED      0\r
189 #define BACK_TEXTURE_MODE_PLAIN         1\r
190 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
191 \r
192 static HBITMAP liteBackTexture = NULL;\r
193 static HBITMAP darkBackTexture = NULL;\r
194 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
195 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
196 static int backTextureSquareSize = 0;\r
197 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
198 \r
199 #if __GNUC__ && !defined(_winmajor)\r
200 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
201 #else\r
202 #define oldDialog (_winmajor < 4)\r
203 #endif\r
204 \r
205 char *defaultTextAttribs[] = \r
206 {\r
207   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
208   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
209   COLOR_NONE\r
210 };\r
211 \r
212 typedef struct {\r
213   char *name;\r
214   int squareSize;\r
215   int lineGap;\r
216   int smallLayout;\r
217   int tinyLayout;\r
218   int cliWidth, cliHeight;\r
219 } SizeInfo;\r
220 \r
221 SizeInfo sizeInfo[] = \r
222 {\r
223   { "tiny",     21, 0, 1, 1, 0, 0 },\r
224   { "teeny",    25, 1, 1, 1, 0, 0 },\r
225   { "dinky",    29, 1, 1, 1, 0, 0 },\r
226   { "petite",   33, 1, 1, 1, 0, 0 },\r
227   { "slim",     37, 2, 1, 0, 0, 0 },\r
228   { "small",    40, 2, 1, 0, 0, 0 },\r
229   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
230   { "middling", 49, 2, 0, 0, 0, 0 },\r
231   { "average",  54, 2, 0, 0, 0, 0 },\r
232   { "moderate", 58, 3, 0, 0, 0, 0 },\r
233   { "medium",   64, 3, 0, 0, 0, 0 },\r
234   { "bulky",    72, 3, 0, 0, 0, 0 },\r
235   { "large",    80, 3, 0, 0, 0, 0 },\r
236   { "big",      87, 3, 0, 0, 0, 0 },\r
237   { "huge",     95, 3, 0, 0, 0, 0 },\r
238   { "giant",    108, 3, 0, 0, 0, 0 },\r
239   { "colossal", 116, 4, 0, 0, 0, 0 },\r
240   { "titanic",  129, 4, 0, 0, 0, 0 },\r
241   { NULL, 0, 0, 0, 0, 0, 0 }\r
242 };\r
243 \r
244 #define MF(x) {x, {0, }, {0, }, 0}\r
245 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
246 {\r
247   { 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
248   { 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
249   { 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
250   { 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
251   { 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
252   { 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
253   { 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
254   { 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
255   { 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
256   { 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
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265 };\r
266 \r
267 MyFont *font[NUM_SIZES][NUM_FONTS];\r
268 \r
269 typedef struct {\r
270   char *label;\r
271   int id;\r
272   HWND hwnd;\r
273   WNDPROC wndproc;\r
274 } MyButtonDesc;\r
275 \r
276 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
277 #define N_BUTTONS 5\r
278 \r
279 MyButtonDesc buttonDesc[N_BUTTONS] =\r
280 {\r
281   {"<<", IDM_ToStart, NULL, NULL},\r
282   {"<", IDM_Backward, NULL, NULL},\r
283   {"P", IDM_Pause, NULL, NULL},\r
284   {">", IDM_Forward, NULL, NULL},\r
285   {">>", IDM_ToEnd, NULL, NULL},\r
286 };\r
287 \r
288 int tinyLayout = 0, smallLayout = 0;\r
289 #define MENU_BAR_ITEMS 6\r
290 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
291   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
292   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
293 };\r
294 \r
295 \r
296 MySound sounds[(int)NSoundClasses];\r
297 MyTextAttribs textAttribs[(int)NColorClasses];\r
298 \r
299 MyColorizeAttribs colorizeAttribs[] = {\r
300   { (COLORREF)0, 0, "Shout Text" },\r
301   { (COLORREF)0, 0, "SShout/CShout" },\r
302   { (COLORREF)0, 0, "Channel 1 Text" },\r
303   { (COLORREF)0, 0, "Channel Text" },\r
304   { (COLORREF)0, 0, "Kibitz Text" },\r
305   { (COLORREF)0, 0, "Tell Text" },\r
306   { (COLORREF)0, 0, "Challenge Text" },\r
307   { (COLORREF)0, 0, "Request Text" },\r
308   { (COLORREF)0, 0, "Seek Text" },\r
309   { (COLORREF)0, 0, "Normal Text" },\r
310   { (COLORREF)0, 0, "None" }\r
311 };\r
312 \r
313 \r
314 \r
315 static char *commentTitle;\r
316 static char *commentText;\r
317 static int commentIndex;\r
318 static Boolean editComment = FALSE;\r
319 HWND commentDialog = NULL;\r
320 BOOLEAN commentDialogUp = FALSE;\r
321 static int commentX, commentY, commentH, commentW;\r
322 \r
323 static char *analysisTitle;\r
324 static char *analysisText;\r
325 HWND analysisDialog = NULL;\r
326 BOOLEAN analysisDialogUp = FALSE;\r
327 static int analysisX, analysisY, analysisH, analysisW;\r
328 \r
329 char errorTitle[MSG_SIZ];\r
330 char errorMessage[2*MSG_SIZ];\r
331 HWND errorDialog = NULL;\r
332 BOOLEAN moveErrorMessageUp = FALSE;\r
333 BOOLEAN consoleEcho = TRUE;\r
334 CHARFORMAT consoleCF;\r
335 COLORREF consoleBackgroundColor;\r
336 \r
337 char *programVersion;\r
338 \r
339 #define CPReal 1\r
340 #define CPComm 2\r
341 #define CPSock 3\r
342 #define CPRcmd 4\r
343 typedef int CPKind;\r
344 \r
345 typedef struct {\r
346   CPKind kind;\r
347   HANDLE hProcess;\r
348   DWORD pid;\r
349   HANDLE hTo;\r
350   HANDLE hFrom;\r
351   SOCKET sock;\r
352   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
353 } ChildProc;\r
354 \r
355 #define INPUT_SOURCE_BUF_SIZE 4096\r
356 \r
357 typedef struct _InputSource {\r
358   CPKind kind;\r
359   HANDLE hFile;\r
360   SOCKET sock;\r
361   int lineByLine;\r
362   HANDLE hThread;\r
363   DWORD id;\r
364   char buf[INPUT_SOURCE_BUF_SIZE];\r
365   char *next;\r
366   DWORD count;\r
367   int error;\r
368   InputCallback func;\r
369   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
370   VOIDSTAR closure;\r
371 } InputSource;\r
372 \r
373 InputSource *consoleInputSource;\r
374 \r
375 DCB dcb;\r
376 \r
377 /* forward */\r
378 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
379 VOID ConsoleCreate();\r
380 LRESULT CALLBACK\r
381   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
382 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
383 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
384 VOID ParseCommSettings(char *arg, DCB *dcb);\r
385 LRESULT CALLBACK\r
386   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
387 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
388 void ParseIcsTextMenu(char *icsTextMenuString);\r
389 VOID PopUpMoveDialog(char firstchar);\r
390 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
391 \r
392 /* [AS] */\r
393 int NewGameFRC();\r
394 int GameListOptions();\r
395 \r
396 HWND moveHistoryDialog = NULL;\r
397 BOOLEAN moveHistoryDialogUp = FALSE;\r
398 \r
399 WindowPlacement wpMoveHistory;\r
400 \r
401 HWND evalGraphDialog = NULL;\r
402 BOOLEAN evalGraphDialogUp = FALSE;\r
403 \r
404 WindowPlacement wpEvalGraph;\r
405 \r
406 HWND engineOutputDialog = NULL;\r
407 BOOLEAN engineOutputDialogUp = FALSE;\r
408 \r
409 WindowPlacement wpEngineOutput;\r
410 \r
411 VOID MoveHistoryPopUp();\r
412 VOID MoveHistoryPopDown();\r
413 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
414 BOOL MoveHistoryIsUp();\r
415 \r
416 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
417 VOID EvalGraphPopUp();\r
418 VOID EvalGraphPopDown();\r
419 BOOL EvalGraphIsUp();\r
420 \r
421 VOID EngineOutputPopUp();\r
422 VOID EngineOutputPopDown();\r
423 BOOL EngineOutputIsUp();\r
424 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
425 \r
426 VOID GothicPopUp(char *title);\r
427 /*\r
428  * Setting "frozen" should disable all user input other than deleting\r
429  * the window.  We do this while engines are initializing themselves.\r
430  */\r
431 static int frozen = 0;\r
432 static int oldMenuItemState[MENU_BAR_ITEMS];\r
433 void FreezeUI()\r
434 {\r
435   HMENU hmenu;\r
436   int i;\r
437 \r
438   if (frozen) return;\r
439   frozen = 1;\r
440   hmenu = GetMenu(hwndMain);\r
441   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
442     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
443   }\r
444   DrawMenuBar(hwndMain);\r
445 }\r
446 \r
447 /* Undo a FreezeUI */\r
448 void ThawUI()\r
449 {\r
450   HMENU hmenu;\r
451   int i;\r
452 \r
453   if (!frozen) return;\r
454   frozen = 0;\r
455   hmenu = GetMenu(hwndMain);\r
456   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
457     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
458   }\r
459   DrawMenuBar(hwndMain);\r
460 }\r
461 \r
462 /*---------------------------------------------------------------------------*\\r
463  *\r
464  * WinMain\r
465  *\r
466 \*---------------------------------------------------------------------------*/\r
467 \r
468 int APIENTRY\r
469 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
470         LPSTR lpCmdLine, int nCmdShow)\r
471 {\r
472   MSG msg;\r
473   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
474 \r
475   debugFP = stderr;\r
476 \r
477   LoadLibrary("RICHED32.DLL");\r
478   consoleCF.cbSize = sizeof(CHARFORMAT);\r
479 \r
480   if (!InitApplication(hInstance)) {\r
481     return (FALSE);\r
482   }\r
483   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
484     return (FALSE);\r
485   }\r
486 \r
487   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
488   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
489   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
490 \r
491   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
492 \r
493   while (GetMessage(&msg, /* message structure */\r
494                     NULL, /* handle of window receiving the message */\r
495                     0,    /* lowest message to examine */\r
496                     0))   /* highest message to examine */\r
497     {\r
498       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
499           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
500           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
501           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
502           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
503           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
504           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
505           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
506           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
507           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
508         TranslateMessage(&msg); /* Translates virtual key codes */\r
509         DispatchMessage(&msg);  /* Dispatches message to window */\r
510       }\r
511     }\r
512 \r
513 \r
514   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
515 }\r
516 \r
517 /*---------------------------------------------------------------------------*\\r
518  *\r
519  * Initialization functions\r
520  *\r
521 \*---------------------------------------------------------------------------*/\r
522 \r
523 BOOL\r
524 InitApplication(HINSTANCE hInstance)\r
525 {\r
526   WNDCLASS wc;\r
527 \r
528   /* Fill in window class structure with parameters that describe the */\r
529   /* main window. */\r
530 \r
531   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
532   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
533   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
534   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
535   wc.hInstance     = hInstance;         /* Owner of this class */\r
536   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
537   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
538   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
539   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
540   wc.lpszClassName = szAppName;                 /* Name to register as */\r
541 \r
542   /* Register the window class and return success/failure code. */\r
543   if (!RegisterClass(&wc)) return FALSE;\r
544 \r
545   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
546   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
547   wc.cbClsExtra    = 0;\r
548   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
549   wc.hInstance     = hInstance;\r
550   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
551   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
552   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
553   wc.lpszMenuName  = NULL;\r
554   wc.lpszClassName = szConsoleName;\r
555 \r
556   if (!RegisterClass(&wc)) return FALSE;\r
557   return TRUE;\r
558 }\r
559 \r
560 \r
561 /* Set by InitInstance, used by EnsureOnScreen */\r
562 int screenHeight, screenWidth;\r
563 \r
564 void\r
565 EnsureOnScreen(int *x, int *y)\r
566 {\r
567   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
568   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
569   if (*x > screenWidth - 32) *x = 0;\r
570   if (*y > screenHeight - 32) *y = 0;\r
571   if (*x < 10) *x = 10;\r
572   if (*y < gap) *y = gap;\r
573 }\r
574 \r
575 BOOL\r
576 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
577 {\r
578   HWND hwnd; /* Main window handle. */\r
579   int ibs;\r
580   WINDOWPLACEMENT wp;\r
581   char *filepart;\r
582 \r
583   hInst = hInstance;    /* Store instance handle in our global variable */\r
584 \r
585   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
586     *filepart = NULLCHAR;\r
587   } else {\r
588     GetCurrentDirectory(MSG_SIZ, installDir);\r
589   }\r
590   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
591   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
592   if (appData.debugMode) {\r
593     debugFP = fopen(appData.nameOfDebugFile, "w");\r
594     setbuf(debugFP, NULL);\r
595   }\r
596 \r
597   InitBackEnd1();\r
598 \r
599   InitEngineUCI( installDir, &first );\r
600   InitEngineUCI( installDir, &second );\r
601 \r
602   /* Create a main window for this application instance. */\r
603   hwnd = CreateWindow(szAppName, szTitle,\r
604                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
605                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
606                       NULL, NULL, hInstance, NULL);\r
607   hwndMain = hwnd;\r
608 \r
609   /* If window could not be created, return "failure" */\r
610   if (!hwnd) {\r
611     return (FALSE);\r
612   }\r
613 \r
614   iconWhite = LoadIcon(hInstance, "icon_white");\r
615   iconBlack = LoadIcon(hInstance, "icon_black");\r
616   iconCurrent = iconWhite;\r
617   InitDrawingColors();\r
618   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
619   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
620   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
621     /* Compute window size for each board size, and use the largest\r
622        size that fits on this screen as the default. */\r
623     InitDrawingSizes((BoardSize)ibs, 0);\r
624     if (boardSize == (BoardSize)-1 &&\r
625         winHeight <= screenHeight\r
626            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
627         && winWidth <= screenWidth) {\r
628       boardSize = (BoardSize)ibs;\r
629     }\r
630   }\r
631   InitDrawingSizes(boardSize, 0);\r
632   InitMenuChecks();\r
633   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
634 \r
635   /* [AS] Load textures if specified */\r
636   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
637   \r
638   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
639       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
640       liteBackTextureMode = appData.liteBackTextureMode;\r
641 \r
642       if (liteBackTexture == NULL && appData.debugMode) {\r
643           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
644       }\r
645   }\r
646   \r
647   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
648       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
649       darkBackTextureMode = appData.darkBackTextureMode;\r
650 \r
651       if (darkBackTexture == NULL && appData.debugMode) {\r
652           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
653       }\r
654   }\r
655 \r
656   mysrandom( (unsigned) time(NULL) );\r
657 \r
658   /* Make a console window if needed */\r
659   if (appData.icsActive) {\r
660     ConsoleCreate();\r
661   }\r
662 \r
663   /* [AS] Restore layout */\r
664   if( wpMoveHistory.visible ) {\r
665       MoveHistoryPopUp();\r
666   }\r
667 \r
668   if( wpEvalGraph.visible ) {\r
669       EvalGraphPopUp();\r
670   }\r
671 \r
672   if( wpEngineOutput.visible ) {\r
673       EngineOutputPopUp();\r
674   }\r
675 \r
676   InitBackEnd2();\r
677 \r
678   /* Make the window visible; update its client area; and return "success" */\r
679   EnsureOnScreen(&boardX, &boardY);\r
680   wp.length = sizeof(WINDOWPLACEMENT);\r
681   wp.flags = 0;\r
682   wp.showCmd = nCmdShow;\r
683   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
684   wp.rcNormalPosition.left = boardX;\r
685   wp.rcNormalPosition.right = boardX + winWidth;\r
686   wp.rcNormalPosition.top = boardY;\r
687   wp.rcNormalPosition.bottom = boardY + winHeight;\r
688   SetWindowPlacement(hwndMain, &wp);\r
689 \r
690   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
691                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
692 \r
693   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
694   if( gameInfo.variant != VariantFischeRandom ) {\r
695       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
696   }\r
697 #ifdef FAIRY\r
698 #ifdef GOTHIC\r
699   /* [HGM] Gothic licensing requirement */\r
700   if(gameInfo.variant == VariantGothic)\r
701       GothicPopUp(GOTHIC);\r
702 #endif // GOTHIC\r
703 #endif // FAIRY\r
704   if (hwndConsole) {\r
705 #if AOT_CONSOLE\r
706     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
707                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
708 #endif\r
709     ShowWindow(hwndConsole, nCmdShow);\r
710   }\r
711   UpdateWindow(hwnd);\r
712 \r
713   return TRUE;\r
714 \r
715 }\r
716 \r
717 \r
718 typedef enum {\r
719   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
720   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
721   ArgSettingsFilename\r
722 } ArgType;\r
723 \r
724 typedef struct {\r
725   char *argName;\r
726   ArgType argType;\r
727   /***\r
728   union {\r
729     String *pString;       // ArgString\r
730     int *pInt;             // ArgInt\r
731     float *pFloat;         // ArgFloat\r
732     Boolean *pBoolean;     // ArgBoolean\r
733     COLORREF *pColor;      // ArgColor\r
734     ColorClass cc;         // ArgAttribs\r
735     String *pFilename;     // ArgFilename\r
736     BoardSize *pBoardSize; // ArgBoardSize\r
737     int whichFont;         // ArgFont\r
738     DCB *pDCB;             // ArgCommSettings\r
739     String *pFilename;     // ArgSettingsFilename\r
740   } argLoc;\r
741   ***/\r
742   LPVOID argLoc;\r
743   BOOL save;\r
744 } ArgDescriptor;\r
745 \r
746 int junk;\r
747 ArgDescriptor argDescriptors[] = {\r
748   /* positional arguments */\r
749   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
750   { "", ArgNone, NULL },\r
751   /* keyword arguments */\r
752   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
753   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
754   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
755   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
756   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
757   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
758   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
759   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
760   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
761   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
762   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
763   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
764   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
765   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
766   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
767   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
768   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
769   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
770     FALSE },\r
771   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
772     FALSE },\r
773   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
774     FALSE },\r
775   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
776   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
777     FALSE },\r
778   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
779   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
780   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
781   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
782   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
783   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
784   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
785   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
786   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
787   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
788   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
789   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
790   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
791   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
792   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
793   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
794   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
795   /*!!bitmapDirectory?*/\r
796   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
797   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
798   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
799   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
800   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
801   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
802   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
803   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
804   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
805   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
806   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
807   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
808   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
809   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
810   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
811   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
812   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
813   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
814   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
815   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
816   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
817   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
818   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
819   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
820   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
821   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
822   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
823   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
824   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
825   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
826   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
827   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
828   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
829   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
830   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
831   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
832   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
833   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
834   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
835   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
836   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
837   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
838   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
839   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
840   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
841   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
842   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
843   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
844   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
845   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
846   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
847   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
848   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
849   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
850   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
851   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
852   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
853   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
854   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
855   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
856   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
857   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
858   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
859   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
860   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
861   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
862   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
863   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
864   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
865   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
866   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
867   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
868   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
869   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
870   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
871   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
872   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
873   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
874   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
875   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
876   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
877   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
878   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
879   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
880   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
881   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
882   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
883   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
884   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
885   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
886   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
887   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
888   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
889   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
890     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
891   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
892   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
893   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
894   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
895   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
896   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
897   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
898   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
899     TRUE }, /* must come after all fonts */\r
900   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
901   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
902     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
903   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
904   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
905   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
906   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
907   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
908   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
909   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
910   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
911   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
912   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
913   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
914   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
915   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
916   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
917   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
918   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
919   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
920   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
921   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
922   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
923   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
924   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
925   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
926   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
927   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
928   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
929   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
930   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
931 #if 0\r
932   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
933   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
934 #endif\r
935   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
936   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
937   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
938   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
939   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
940   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
941   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
942   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
943   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
944   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
945   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
946   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
947   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
948   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
949   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
950   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
951   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
952   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
953   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
954   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
955   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
956   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
957   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
958   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
959   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
960   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
961   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
962   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
963   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
964   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
965   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
966   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
967   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
968   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
969   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
970   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
971   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
972   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
973   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
974   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
975   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
976   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
977   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
978   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
979   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
980   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
981   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
982   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
983   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
984   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
985   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
986   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
987   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
988   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
989   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
990   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
991   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
992   { "highlightLastMove", ArgBoolean,\r
993     (LPVOID) &appData.highlightLastMove, TRUE },\r
994   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
995   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
996   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
997   { "highlightDragging", ArgBoolean,\r
998     (LPVOID) &appData.highlightDragging, TRUE },\r
999   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1000   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1001   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1002   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1003   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1004   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1005   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1006   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1007   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1008   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1009   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1010   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1011   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1012   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1013   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1014   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1015   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1016   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1017   { "soundShout", ArgFilename,\r
1018     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1019   { "soundSShout", ArgFilename,\r
1020     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1021   { "soundChannel1", ArgFilename,\r
1022     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1023   { "soundChannel", ArgFilename,\r
1024     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1025   { "soundKibitz", ArgFilename,\r
1026     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1027   { "soundTell", ArgFilename,\r
1028     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1029   { "soundChallenge", ArgFilename,\r
1030     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1031   { "soundRequest", ArgFilename,\r
1032     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1033   { "soundSeek", ArgFilename,\r
1034     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1035   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1036   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1037   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1038   { "soundIcsLoss", ArgFilename, \r
1039     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1040   { "soundIcsDraw", ArgFilename, \r
1041     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1042   { "soundIcsUnfinished", ArgFilename, \r
1043     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1044   { "soundIcsAlarm", ArgFilename, \r
1045     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1046   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1047   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1048   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1049   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1050   { "reuseChessPrograms", ArgBoolean,\r
1051     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1052   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1053   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1054   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1055   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1056   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1057   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1058   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1059   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1060   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1061   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1062   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1063   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1064   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1065   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1066   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1067   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1068   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1069   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1070   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1071   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1072   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1073   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1074   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1075   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1076   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1077   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1078   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1079   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1080   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1081   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1082   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1083   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1084   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1085   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1086   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1087   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1088   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1089     TRUE },\r
1090   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1091     TRUE },\r
1092   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1093   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1094   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1095   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1096   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1097   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1098   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1099   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1100   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1101   /* [AS] New features */\r
1102   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1103   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1104   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1105   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1106   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1107   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1108   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1109   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1110   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1111   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1112   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1113   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1114   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1115   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1116   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1117   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1118   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1119   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1120   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1121   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1122   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1123   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1124   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1125   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1126   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1127   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1128   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1129   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1130   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1131   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1132   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1133   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1134   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1135   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1136   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1137   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1138   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1139   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1140   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1141   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1142   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1143   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1144   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1145   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1146   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1147   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1148   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1149   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1150 \r
1151   /* [AS] Layout stuff */\r
1152   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1153   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1154   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1155   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1156   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1157 \r
1158   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1159   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1160   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1161   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1162   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1163 \r
1164   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1165   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1166   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1167   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1168   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1169 \r
1170   /* [HGM] board-size, adjudication and misc. options */\r
1171   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1172   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1173   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1174   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, FALSE },\r
1175   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, TRUE },\r
1176   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1177   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1178   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1179   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1180   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1181   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1182 \r
1183 #ifdef ZIPPY\r
1184   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1185   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1186   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1187   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1188   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1189   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1190   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1191   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1192   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1193   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1194   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1195   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1196   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1197     FALSE },\r
1198   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1199   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1200   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1201   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1202   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1203   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1204   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1205     FALSE },\r
1206   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1207   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1208   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1209   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1210   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1211   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1212   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1213   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1214   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1215   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1216   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1217   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1218   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1219   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1220   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1221   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1222   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1223   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1224 #endif\r
1225   { NULL, ArgNone, NULL, FALSE }\r
1226 };\r
1227 \r
1228 \r
1229 /* Kludge for indirection files on command line */\r
1230 char* lastIndirectionFilename;\r
1231 ArgDescriptor argDescriptorIndirection =\r
1232 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1233 \r
1234 \r
1235 VOID\r
1236 ExitArgError(char *msg, char *badArg)\r
1237 {\r
1238   char buf[MSG_SIZ];\r
1239 \r
1240   sprintf(buf, "%s %s", msg, badArg);\r
1241   DisplayFatalError(buf, 0, 2);\r
1242   exit(2);\r
1243 }\r
1244 \r
1245 /* Command line font name parser.  NULL name means do nothing.\r
1246    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1247    For backward compatibility, syntax without the colon is also\r
1248    accepted, but font names with digits in them won't work in that case.\r
1249 */\r
1250 VOID\r
1251 ParseFontName(char *name, MyFontParams *mfp)\r
1252 {\r
1253   char *p, *q;\r
1254   if (name == NULL) return;\r
1255   p = name;\r
1256   q = strchr(p, ':');\r
1257   if (q) {\r
1258     if (q - p >= sizeof(mfp->faceName))\r
1259       ExitArgError("Font name too long:", name);\r
1260     memcpy(mfp->faceName, p, q - p);\r
1261     mfp->faceName[q - p] = NULLCHAR;\r
1262     p = q + 1;\r
1263   } else {\r
1264     q = mfp->faceName;\r
1265     while (*p && !isdigit(*p)) {\r
1266       *q++ = *p++;\r
1267       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1268         ExitArgError("Font name too long:", name);\r
1269     }\r
1270     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1271     *q = NULLCHAR;\r
1272   }\r
1273   if (!*p) ExitArgError("Font point size missing:", name);\r
1274   mfp->pointSize = (float) atof(p);\r
1275   mfp->bold = (strchr(p, 'b') != NULL);\r
1276   mfp->italic = (strchr(p, 'i') != NULL);\r
1277   mfp->underline = (strchr(p, 'u') != NULL);\r
1278   mfp->strikeout = (strchr(p, 's') != NULL);\r
1279 }\r
1280 \r
1281 /* Color name parser.\r
1282    X version accepts X color names, but this one\r
1283    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1284 COLORREF\r
1285 ParseColorName(char *name)\r
1286 {\r
1287   int red, green, blue, count;\r
1288   char buf[MSG_SIZ];\r
1289 \r
1290   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1291   if (count != 3) {\r
1292     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1293       &red, &green, &blue);\r
1294   }\r
1295   if (count != 3) {\r
1296     sprintf(buf, "Can't parse color name %s", name);\r
1297     DisplayError(buf, 0);\r
1298     return RGB(0, 0, 0);\r
1299   }\r
1300   return PALETTERGB(red, green, blue);\r
1301 }\r
1302 \r
1303 \r
1304 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1305 {\r
1306   char *e = argValue;\r
1307   int eff = 0;\r
1308 \r
1309   while (*e) {\r
1310     if (*e == 'b')      eff |= CFE_BOLD;\r
1311     else if (*e == 'i') eff |= CFE_ITALIC;\r
1312     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1313     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1314     else if (*e == '#' || isdigit(*e)) break;\r
1315     e++;\r
1316   }\r
1317   *effects = eff;\r
1318   *color   = ParseColorName(e);\r
1319 }\r
1320 \r
1321 \r
1322 BoardSize\r
1323 ParseBoardSize(char *name)\r
1324 {\r
1325   BoardSize bs = SizeTiny;\r
1326   while (sizeInfo[bs].name != NULL) {\r
1327     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1328     bs++;\r
1329   }\r
1330   ExitArgError("Unrecognized board size value", name);\r
1331   return bs; /* not reached */\r
1332 }\r
1333 \r
1334 \r
1335 char\r
1336 StringGet(void *getClosure)\r
1337 {\r
1338   char **p = (char **) getClosure;\r
1339   return *((*p)++);\r
1340 }\r
1341 \r
1342 char\r
1343 FileGet(void *getClosure)\r
1344 {\r
1345   int c;\r
1346   FILE* f = (FILE*) getClosure;\r
1347 \r
1348   c = getc(f);\r
1349   if (c == EOF)\r
1350     return NULLCHAR;\r
1351   else\r
1352     return (char) c;\r
1353 }\r
1354 \r
1355 /* Parse settings file named "name". If file found, return the\r
1356    full name in fullname and return TRUE; else return FALSE */\r
1357 BOOLEAN\r
1358 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1359 {\r
1360   char *dummy;\r
1361   FILE *f;\r
1362 \r
1363   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1364     f = fopen(fullname, "r");\r
1365     if (f != NULL) {\r
1366       ParseArgs(FileGet, f);\r
1367       fclose(f);\r
1368       return TRUE;\r
1369     }\r
1370   }\r
1371   return FALSE;\r
1372 }\r
1373 \r
1374 VOID\r
1375 ParseArgs(GetFunc get, void *cl)\r
1376 {\r
1377   char argName[ARG_MAX];\r
1378   char argValue[ARG_MAX];\r
1379   ArgDescriptor *ad;\r
1380   char start;\r
1381   char *q;\r
1382   int i, octval;\r
1383   char ch;\r
1384   int posarg = 0;\r
1385 \r
1386   ch = get(cl);\r
1387   for (;;) {\r
1388     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1389     if (ch == NULLCHAR) break;\r
1390     if (ch == ';') {\r
1391       /* Comment to end of line */\r
1392       ch = get(cl);\r
1393       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1394       continue;\r
1395     } else if (ch == '/' || ch == '-') {\r
1396       /* Switch */\r
1397       q = argName;\r
1398       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1399              ch != '\n' && ch != '\t') {\r
1400         *q++ = ch;\r
1401         ch = get(cl);\r
1402       }\r
1403       *q = NULLCHAR;\r
1404 \r
1405       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1406         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1407 \r
1408       if (ad->argName == NULL)\r
1409         ExitArgError("Unrecognized argument", argName);\r
1410 \r
1411     } else if (ch == '@') {\r
1412       /* Indirection file */\r
1413       ad = &argDescriptorIndirection;\r
1414       ch = get(cl);\r
1415     } else {\r
1416       /* Positional argument */\r
1417       ad = &argDescriptors[posarg++];\r
1418       strcpy(argName, ad->argName);\r
1419     }\r
1420 \r
1421     if (ad->argType == ArgTrue) {\r
1422       *(Boolean *) ad->argLoc = TRUE;\r
1423       continue;\r
1424     }\r
1425     if (ad->argType == ArgFalse) {\r
1426       *(Boolean *) ad->argLoc = FALSE;\r
1427       continue;\r
1428     }\r
1429 \r
1430     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1431     if (ch == NULLCHAR || ch == '\n') {\r
1432       ExitArgError("No value provided for argument", argName);\r
1433     }\r
1434     q = argValue;\r
1435     if (ch == '{') {\r
1436       // Quoting with { }.  No characters have to (or can) be escaped.\r
1437       // Thus the string cannot contain a '}' character.\r
1438       start = ch;\r
1439       ch = get(cl);\r
1440       while (start) {\r
1441         switch (ch) {\r
1442         case NULLCHAR:\r
1443           start = NULLCHAR;\r
1444           break;\r
1445           \r
1446         case '}':\r
1447           ch = get(cl);\r
1448           start = NULLCHAR;\r
1449           break;\r
1450 \r
1451         default:\r
1452           *q++ = ch;\r
1453           ch = get(cl);\r
1454           break;\r
1455         }\r
1456       }   \r
1457     } else if (ch == '\'' || ch == '"') {\r
1458       // Quoting with ' ' or " ", with \ as escape character.\r
1459       // Inconvenient for long strings that may contain Windows filenames.\r
1460       start = ch;\r
1461       ch = get(cl);\r
1462       while (start) {\r
1463         switch (ch) {\r
1464         case NULLCHAR:\r
1465           start = NULLCHAR;\r
1466           break;\r
1467 \r
1468         default:\r
1469         not_special:\r
1470           *q++ = ch;\r
1471           ch = get(cl);\r
1472           break;\r
1473 \r
1474         case '\'':\r
1475         case '\"':\r
1476           if (ch == start) {\r
1477             ch = get(cl);\r
1478             start = NULLCHAR;\r
1479             break;\r
1480           } else {\r
1481             goto not_special;\r
1482           }\r
1483 \r
1484         case '\\':\r
1485           if (ad->argType == ArgFilename\r
1486               || ad->argType == ArgSettingsFilename) {\r
1487               goto not_special;\r
1488           }\r
1489           ch = get(cl);\r
1490           switch (ch) {\r
1491           case NULLCHAR:\r
1492             ExitArgError("Incomplete \\ escape in value for", argName);\r
1493             break;\r
1494           case 'n':\r
1495             *q++ = '\n';\r
1496             ch = get(cl);\r
1497             break;\r
1498           case 'r':\r
1499             *q++ = '\r';\r
1500             ch = get(cl);\r
1501             break;\r
1502           case 't':\r
1503             *q++ = '\t';\r
1504             ch = get(cl);\r
1505             break;\r
1506           case 'b':\r
1507             *q++ = '\b';\r
1508             ch = get(cl);\r
1509             break;\r
1510           case 'f':\r
1511             *q++ = '\f';\r
1512             ch = get(cl);\r
1513             break;\r
1514           default:\r
1515             octval = 0;\r
1516             for (i = 0; i < 3; i++) {\r
1517               if (ch >= '0' && ch <= '7') {\r
1518                 octval = octval*8 + (ch - '0');\r
1519                 ch = get(cl);\r
1520               } else {\r
1521                 break;\r
1522               }\r
1523             }\r
1524             if (i > 0) {\r
1525               *q++ = (char) octval;\r
1526             } else {\r
1527               *q++ = ch;\r
1528               ch = get(cl);\r
1529             }\r
1530             break;\r
1531           }\r
1532           break;\r
1533         }\r
1534       }\r
1535     } else {\r
1536       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1537         *q++ = ch;\r
1538         ch = get(cl);\r
1539       }\r
1540     }\r
1541     *q = NULLCHAR;\r
1542 \r
1543     switch (ad->argType) {\r
1544     case ArgInt:\r
1545       *(int *) ad->argLoc = atoi(argValue);\r
1546       break;\r
1547 \r
1548     case ArgFloat:\r
1549       *(float *) ad->argLoc = (float) atof(argValue);\r
1550       break;\r
1551 \r
1552     case ArgString:\r
1553     case ArgFilename:\r
1554       *(char **) ad->argLoc = strdup(argValue);\r
1555       break;\r
1556 \r
1557     case ArgSettingsFilename:\r
1558       {\r
1559         char fullname[MSG_SIZ];\r
1560         if (ParseSettingsFile(argValue, fullname)) {\r
1561           if (ad->argLoc != NULL) {\r
1562             *(char **) ad->argLoc = strdup(fullname);\r
1563           }\r
1564         } else {\r
1565           if (ad->argLoc != NULL) {\r
1566           } else {\r
1567             ExitArgError("Failed to open indirection file", argValue);\r
1568           }\r
1569         }\r
1570       }\r
1571       break;\r
1572 \r
1573     case ArgBoolean:\r
1574       switch (argValue[0]) {\r
1575       case 't':\r
1576       case 'T':\r
1577         *(Boolean *) ad->argLoc = TRUE;\r
1578         break;\r
1579       case 'f':\r
1580       case 'F':\r
1581         *(Boolean *) ad->argLoc = FALSE;\r
1582         break;\r
1583       default:\r
1584         ExitArgError("Unrecognized boolean argument value", argValue);\r
1585         break;\r
1586       }\r
1587       break;\r
1588 \r
1589     case ArgColor:\r
1590       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1591       break;\r
1592 \r
1593     case ArgAttribs: {\r
1594       ColorClass cc = (ColorClass)ad->argLoc;\r
1595       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1596       }\r
1597       break;\r
1598       \r
1599     case ArgBoardSize:\r
1600       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1601       break;\r
1602 \r
1603     case ArgFont:\r
1604       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1605       break;\r
1606 \r
1607     case ArgCommSettings:\r
1608       ParseCommSettings(argValue, &dcb);\r
1609       break;\r
1610 \r
1611     case ArgNone:\r
1612       ExitArgError("Unrecognized argument", argValue);\r
1613       break;\r
1614     }\r
1615   }\r
1616 }\r
1617 \r
1618 VOID\r
1619 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1620 {\r
1621   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1622   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1623   DeleteDC(hdc);\r
1624   lf->lfWidth = 0;\r
1625   lf->lfEscapement = 0;\r
1626   lf->lfOrientation = 0;\r
1627   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1628   lf->lfItalic = mfp->italic;\r
1629   lf->lfUnderline = mfp->underline;\r
1630   lf->lfStrikeOut = mfp->strikeout;\r
1631   lf->lfCharSet = DEFAULT_CHARSET;\r
1632   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1633   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1634   lf->lfQuality = DEFAULT_QUALITY;\r
1635   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1636   strcpy(lf->lfFaceName, mfp->faceName);\r
1637 }\r
1638 \r
1639 VOID\r
1640 CreateFontInMF(MyFont *mf)\r
1641 {\r
1642   LFfromMFP(&mf->lf, &mf->mfp);\r
1643   if (mf->hf) DeleteObject(mf->hf);\r
1644   mf->hf = CreateFontIndirect(&mf->lf);\r
1645 }\r
1646 \r
1647 VOID\r
1648 SetDefaultTextAttribs()\r
1649 {\r
1650   ColorClass cc;\r
1651   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1652     ParseAttribs(&textAttribs[cc].color, \r
1653                  &textAttribs[cc].effects, \r
1654                  defaultTextAttribs[cc]);\r
1655   }\r
1656 }\r
1657 \r
1658 VOID\r
1659 SetDefaultSounds()\r
1660 {\r
1661   ColorClass cc;\r
1662   SoundClass sc;\r
1663   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1664     textAttribs[cc].sound.name = strdup("");\r
1665     textAttribs[cc].sound.data = NULL;\r
1666   }\r
1667   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1668     sounds[sc].name = strdup("");\r
1669     sounds[sc].data = NULL;\r
1670   }\r
1671   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1672 }\r
1673 \r
1674 VOID\r
1675 LoadAllSounds()\r
1676 {\r
1677   ColorClass cc;\r
1678   SoundClass sc;\r
1679   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1680     MyLoadSound(&textAttribs[cc].sound);\r
1681   }\r
1682   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1683     MyLoadSound(&sounds[sc]);\r
1684   }\r
1685 }\r
1686 \r
1687 VOID\r
1688 InitAppData(LPSTR lpCmdLine)\r
1689 {\r
1690   int i, j;\r
1691   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1692   char *dummy, *p;\r
1693 \r
1694   programName = szAppName;\r
1695 \r
1696   /* Initialize to defaults */\r
1697   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1698   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1699   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1700   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1701   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1702   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1703   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1704   SetDefaultTextAttribs();\r
1705   SetDefaultSounds();\r
1706   appData.movesPerSession = MOVES_PER_SESSION;\r
1707   appData.initString = INIT_STRING;\r
1708   appData.secondInitString = INIT_STRING;\r
1709   appData.firstComputerString = COMPUTER_STRING;\r
1710   appData.secondComputerString = COMPUTER_STRING;\r
1711   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1712   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1713   appData.firstPlaysBlack = FALSE;\r
1714   appData.noChessProgram = FALSE;\r
1715   chessProgram = FALSE;\r
1716   appData.firstHost = FIRST_HOST;\r
1717   appData.secondHost = SECOND_HOST;\r
1718   appData.firstDirectory = FIRST_DIRECTORY;\r
1719   appData.secondDirectory = SECOND_DIRECTORY;\r
1720   appData.bitmapDirectory = "";\r
1721   appData.remoteShell = REMOTE_SHELL;\r
1722   appData.remoteUser = "";\r
1723   appData.timeDelay = TIME_DELAY;\r
1724   appData.timeControl = TIME_CONTROL;\r
1725   appData.timeIncrement = TIME_INCREMENT;\r
1726   appData.icsActive = FALSE;\r
1727   appData.icsHost = "";\r
1728   appData.icsPort = ICS_PORT;\r
1729   appData.icsCommPort = ICS_COMM_PORT;\r
1730   appData.icsLogon = ICS_LOGON;\r
1731   appData.icsHelper = "";\r
1732   appData.useTelnet = FALSE;\r
1733   appData.telnetProgram = TELNET_PROGRAM;\r
1734   appData.gateway = "";\r
1735   appData.loadGameFile = "";\r
1736   appData.loadGameIndex = 0;\r
1737   appData.saveGameFile = "";\r
1738   appData.autoSaveGames = FALSE;\r
1739   appData.loadPositionFile = "";\r
1740   appData.loadPositionIndex = 1;\r
1741   appData.savePositionFile = "";\r
1742   appData.matchMode = FALSE;\r
1743   appData.matchGames = 0;\r
1744   appData.monoMode = FALSE;\r
1745   appData.debugMode = FALSE;\r
1746   appData.clockMode = TRUE;\r
1747   boardSize = (BoardSize) -1; /* determine by screen size */\r
1748   appData.Iconic = FALSE; /*unused*/\r
1749   appData.searchTime = "";\r
1750   appData.searchDepth = 0;\r
1751   appData.showCoords = FALSE;\r
1752   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1753   appData.autoCallFlag = FALSE;\r
1754   appData.flipView = FALSE;\r
1755   appData.autoFlipView = TRUE;\r
1756   appData.cmailGameName = "";\r
1757   appData.alwaysPromoteToQueen = FALSE;\r
1758   appData.oldSaveStyle = FALSE;\r
1759   appData.quietPlay = FALSE;\r
1760   appData.showThinking = FALSE;\r
1761   appData.ponderNextMove = TRUE;\r
1762   appData.periodicUpdates = TRUE;\r
1763   appData.popupExitMessage = TRUE;\r
1764   appData.popupMoveErrors = FALSE;\r
1765   appData.autoObserve = FALSE;\r
1766   appData.autoComment = FALSE;\r
1767   appData.animate = TRUE;\r
1768   appData.animSpeed = 10;\r
1769   appData.animateDragging = TRUE;\r
1770   appData.highlightLastMove = TRUE;\r
1771   appData.getMoveList = TRUE;\r
1772   appData.testLegality = TRUE;\r
1773   appData.premove = TRUE;\r
1774   appData.premoveWhite = FALSE;\r
1775   appData.premoveWhiteText = "";\r
1776   appData.premoveBlack = FALSE;\r
1777   appData.premoveBlackText = "";\r
1778   appData.icsAlarm = TRUE;\r
1779   appData.icsAlarmTime = 5000;\r
1780   appData.autoRaiseBoard = TRUE;\r
1781   appData.localLineEditing = TRUE;\r
1782   appData.colorize = TRUE;\r
1783   appData.reuseFirst = TRUE;\r
1784   appData.reuseSecond = TRUE;\r
1785   appData.blindfold = FALSE;\r
1786   dcb.DCBlength = sizeof(DCB);\r
1787   dcb.BaudRate = 9600;\r
1788   dcb.fBinary = TRUE;\r
1789   dcb.fParity = FALSE;\r
1790   dcb.fOutxCtsFlow = FALSE;\r
1791   dcb.fOutxDsrFlow = FALSE;\r
1792   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1793   dcb.fDsrSensitivity = FALSE;\r
1794   dcb.fTXContinueOnXoff = TRUE;\r
1795   dcb.fOutX = FALSE;\r
1796   dcb.fInX = FALSE;\r
1797   dcb.fNull = FALSE;\r
1798   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1799   dcb.fAbortOnError = FALSE;\r
1800   dcb.wReserved = 0;\r
1801   dcb.ByteSize = 7;\r
1802   dcb.Parity = SPACEPARITY;\r
1803   dcb.StopBits = ONESTOPBIT;\r
1804   settingsFileName = SETTINGS_FILE;\r
1805   saveSettingsOnExit = TRUE;\r
1806   boardX = CW_USEDEFAULT;\r
1807   boardY = CW_USEDEFAULT;\r
1808   consoleX = CW_USEDEFAULT; \r
1809   consoleY = CW_USEDEFAULT; \r
1810   consoleW = CW_USEDEFAULT;\r
1811   consoleH = CW_USEDEFAULT;\r
1812   analysisX = CW_USEDEFAULT; \r
1813   analysisY = CW_USEDEFAULT; \r
1814   analysisW = CW_USEDEFAULT;\r
1815   analysisH = CW_USEDEFAULT;\r
1816   commentX = CW_USEDEFAULT; \r
1817   commentY = CW_USEDEFAULT; \r
1818   commentW = CW_USEDEFAULT;\r
1819   commentH = CW_USEDEFAULT;\r
1820   editTagsX = CW_USEDEFAULT; \r
1821   editTagsY = CW_USEDEFAULT; \r
1822   editTagsW = CW_USEDEFAULT;\r
1823   editTagsH = CW_USEDEFAULT;\r
1824   gameListX = CW_USEDEFAULT; \r
1825   gameListY = CW_USEDEFAULT; \r
1826   gameListW = CW_USEDEFAULT;\r
1827   gameListH = CW_USEDEFAULT;\r
1828   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1829   icsNames = ICS_NAMES;\r
1830   firstChessProgramNames = FCP_NAMES;\r
1831   secondChessProgramNames = SCP_NAMES;\r
1832   appData.initialMode = "";\r
1833   appData.variant = "normal";\r
1834   appData.firstProtocolVersion = PROTOVER;\r
1835   appData.secondProtocolVersion = PROTOVER;\r
1836   appData.showButtonBar = TRUE;\r
1837 \r
1838    /* [AS] New properties (see comments in header file) */\r
1839   appData.firstScoreIsAbsolute = FALSE;\r
1840   appData.secondScoreIsAbsolute = FALSE;\r
1841   appData.saveExtendedInfoInPGN = FALSE;\r
1842   appData.hideThinkingFromHuman = FALSE;\r
1843   appData.liteBackTextureFile = "";\r
1844   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1845   appData.darkBackTextureFile = "";\r
1846   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1847   appData.renderPiecesWithFont = "";\r
1848   appData.fontToPieceTable = "";\r
1849   appData.fontBackColorWhite = 0;\r
1850   appData.fontForeColorWhite = 0;\r
1851   appData.fontBackColorBlack = 0;\r
1852   appData.fontForeColorBlack = 0;\r
1853   appData.fontPieceSize = 80;\r
1854   appData.overrideLineGap = 1;\r
1855   appData.adjudicateLossThreshold = 0;\r
1856   appData.delayBeforeQuit = 0;\r
1857   appData.delayAfterQuit = 0;\r
1858   appData.nameOfDebugFile = "winboard.debug";\r
1859   appData.pgnEventHeader = "Computer Chess Game";\r
1860   appData.defaultFrcPosition = -1;\r
1861   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1862   appData.saveOutOfBookInfo = TRUE;\r
1863   appData.showEvalInMoveHistory = TRUE;\r
1864   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1865   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1866   appData.highlightMoveWithArrow = FALSE;\r
1867   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1868   appData.useStickyWindows = TRUE;\r
1869   appData.adjudicateDrawMoves = 0;\r
1870   appData.autoDisplayComment = TRUE;\r
1871   appData.autoDisplayTags = TRUE;\r
1872   appData.firstIsUCI = FALSE;\r
1873   appData.secondIsUCI = FALSE;\r
1874   appData.firstHasOwnBookUCI = TRUE;\r
1875   appData.secondHasOwnBookUCI = TRUE;\r
1876   appData.polyglotDir = "";\r
1877   appData.usePolyglotBook = FALSE;\r
1878   appData.polyglotBook = "";\r
1879   appData.defaultHashSize = 64;\r
1880   appData.defaultCacheSizeEGTB = 4;\r
1881   appData.defaultPathEGTB = "c:\\egtb";\r
1882 \r
1883   InitWindowPlacement( &wpMoveHistory );\r
1884   InitWindowPlacement( &wpEvalGraph );\r
1885   InitWindowPlacement( &wpEngineOutput );\r
1886 \r
1887   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1888   appData.NrFiles      = -1;\r
1889   appData.NrRanks      = -1;\r
1890   appData.holdingsSize = -1;\r
1891   appData.testClaims   = FALSE;\r
1892   appData.checkMates   = FALSE;\r
1893   appData.materialDraws= FALSE;\r
1894   appData.trivialDraws = FALSE;\r
1895   appData.ruleMoves    = 51;\r
1896   appData.drawRepeats  = 6;\r
1897   appData.matchPause   = 10000;\r
1898   appData.alphaRank    = FALSE;\r
1899 \r
1900 #ifdef ZIPPY\r
1901   appData.zippyTalk = ZIPPY_TALK;\r
1902   appData.zippyPlay = ZIPPY_PLAY;\r
1903   appData.zippyLines = ZIPPY_LINES;\r
1904   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1905   appData.zippyPassword = ZIPPY_PASSWORD;\r
1906   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1907   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1908   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1909   appData.zippyUseI = ZIPPY_USE_I;\r
1910   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1911   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1912   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1913   appData.zippyGameStart = ZIPPY_GAME_START;\r
1914   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1915   appData.zippyAbort = ZIPPY_ABORT;\r
1916   appData.zippyVariants = ZIPPY_VARIANTS;\r
1917   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1918   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1919 #endif\r
1920 \r
1921   /* Point font array elements to structures and\r
1922      parse default font names */\r
1923   for (i=0; i<NUM_FONTS; i++) {\r
1924     for (j=0; j<NUM_SIZES; j++) {\r
1925       font[j][i] = &fontRec[j][i];\r
1926       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1927     }\r
1928   }\r
1929   \r
1930   /* Parse default settings file if any */\r
1931   if (ParseSettingsFile(settingsFileName, buf)) {\r
1932     settingsFileName = strdup(buf);\r
1933   }\r
1934 \r
1935   /* Parse command line */\r
1936   ParseArgs(StringGet, &lpCmdLine);\r
1937 \r
1938   /* [HGM] make sure board size is acceptable */\r
1939   if(appData.NrFiles > BOARD_SIZE ||\r
1940      appData.NrRanks > BOARD_SIZE   )\r
1941       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
1942 \r
1943   /* Propagate options that affect others */\r
1944   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1945   if (appData.icsActive || appData.noChessProgram) {\r
1946      chessProgram = FALSE;  /* not local chess program mode */\r
1947   }\r
1948 \r
1949   /* Open startup dialog if needed */\r
1950   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1951       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1952       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1953                         *appData.secondChessProgram == NULLCHAR))) {\r
1954     FARPROC lpProc;\r
1955     \r
1956     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1957     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1958     FreeProcInstance(lpProc);\r
1959   }\r
1960 \r
1961   /* Make sure save files land in the right (?) directory */\r
1962   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
1963     appData.saveGameFile = strdup(buf);\r
1964   }\r
1965   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1966     appData.savePositionFile = strdup(buf);\r
1967   }\r
1968 \r
1969   /* Finish initialization for fonts and sounds */\r
1970   for (i=0; i<NUM_FONTS; i++) {\r
1971     for (j=0; j<NUM_SIZES; j++) {\r
1972       CreateFontInMF(font[j][i]);\r
1973     }\r
1974   }\r
1975   /* xboard, and older WinBoards, controlled the move sound with the\r
1976      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1977      always turn the option on (so that the backend will call us),\r
1978      then let the user turn the sound off by setting it to silence if\r
1979      desired.  To accommodate old winboard.ini files saved by old\r
1980      versions of WinBoard, we also turn off the sound if the option\r
1981      was initially set to false. */\r
1982   if (!appData.ringBellAfterMoves) {\r
1983     sounds[(int)SoundMove].name = strdup("");\r
1984     appData.ringBellAfterMoves = TRUE;\r
1985   }\r
1986   GetCurrentDirectory(MSG_SIZ, currDir);\r
1987   SetCurrentDirectory(installDir);\r
1988   LoadAllSounds();\r
1989   SetCurrentDirectory(currDir);\r
1990 \r
1991   p = icsTextMenuString;\r
1992   if (p[0] == '@') {\r
1993     FILE* f = fopen(p + 1, "r");\r
1994     if (f == NULL) {\r
1995       DisplayFatalError(p + 1, errno, 2);\r
1996       return;\r
1997     }\r
1998     i = fread(buf, 1, sizeof(buf)-1, f);\r
1999     fclose(f);\r
2000     buf[i] = NULLCHAR;\r
2001     p = buf;\r
2002   }\r
2003   ParseIcsTextMenu(strdup(p));\r
2004 }\r
2005 \r
2006 \r
2007 VOID\r
2008 InitMenuChecks()\r
2009 {\r
2010   HMENU hmenu = GetMenu(hwndMain);\r
2011 \r
2012   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2013                         MF_BYCOMMAND|((appData.icsActive &&\r
2014                                        *appData.icsCommPort != NULLCHAR) ?\r
2015                                       MF_ENABLED : MF_GRAYED));\r
2016   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2017                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2018                                      MF_CHECKED : MF_UNCHECKED));\r
2019 }\r
2020 \r
2021 \r
2022 VOID\r
2023 SaveSettings(char* name)\r
2024 {\r
2025   FILE *f;\r
2026   ArgDescriptor *ad;\r
2027   WINDOWPLACEMENT wp;\r
2028   char dir[MSG_SIZ];\r
2029 \r
2030   if (!hwndMain) return;\r
2031 \r
2032   GetCurrentDirectory(MSG_SIZ, dir);\r
2033   SetCurrentDirectory(installDir);\r
2034   f = fopen(name, "w");\r
2035   SetCurrentDirectory(dir);\r
2036   if (f == NULL) {\r
2037     DisplayError(name, errno);\r
2038     return;\r
2039   }\r
2040   fprintf(f, ";\n");\r
2041   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2042   fprintf(f, ";\n");\r
2043   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2044   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2045   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2046   fprintf(f, ";\n");\r
2047 \r
2048   wp.length = sizeof(WINDOWPLACEMENT);\r
2049   GetWindowPlacement(hwndMain, &wp);\r
2050   boardX = wp.rcNormalPosition.left;\r
2051   boardY = wp.rcNormalPosition.top;\r
2052 \r
2053   if (hwndConsole) {\r
2054     GetWindowPlacement(hwndConsole, &wp);\r
2055     consoleX = wp.rcNormalPosition.left;\r
2056     consoleY = wp.rcNormalPosition.top;\r
2057     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2058     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2059   }\r
2060 \r
2061   if (analysisDialog) {\r
2062     GetWindowPlacement(analysisDialog, &wp);\r
2063     analysisX = wp.rcNormalPosition.left;\r
2064     analysisY = wp.rcNormalPosition.top;\r
2065     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2066     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2067   }\r
2068 \r
2069   if (commentDialog) {\r
2070     GetWindowPlacement(commentDialog, &wp);\r
2071     commentX = wp.rcNormalPosition.left;\r
2072     commentY = wp.rcNormalPosition.top;\r
2073     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2074     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2075   }\r
2076 \r
2077   if (editTagsDialog) {\r
2078     GetWindowPlacement(editTagsDialog, &wp);\r
2079     editTagsX = wp.rcNormalPosition.left;\r
2080     editTagsY = wp.rcNormalPosition.top;\r
2081     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2082     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2083   }\r
2084 \r
2085   if (gameListDialog) {\r
2086     GetWindowPlacement(gameListDialog, &wp);\r
2087     gameListX = wp.rcNormalPosition.left;\r
2088     gameListY = wp.rcNormalPosition.top;\r
2089     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2090     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2091   }\r
2092 \r
2093   /* [AS] Move history */\r
2094   wpMoveHistory.visible = MoveHistoryIsUp();\r
2095   \r
2096   if( moveHistoryDialog ) {\r
2097     GetWindowPlacement(moveHistoryDialog, &wp);\r
2098     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2099     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2100     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2101     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2102   }\r
2103 \r
2104   /* [AS] Eval graph */\r
2105   wpEvalGraph.visible = EvalGraphIsUp();\r
2106 \r
2107   if( evalGraphDialog ) {\r
2108     GetWindowPlacement(evalGraphDialog, &wp);\r
2109     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2110     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2111     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2112     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2113   }\r
2114 \r
2115   /* [AS] Engine output */\r
2116   wpEngineOutput.visible = EngineOutputIsUp();\r
2117 \r
2118   if( engineOutputDialog ) {\r
2119     GetWindowPlacement(engineOutputDialog, &wp);\r
2120     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2121     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2122     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2123     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2124   }\r
2125 \r
2126   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2127     if (!ad->save) continue;\r
2128     switch (ad->argType) {\r
2129     case ArgString:\r
2130       {\r
2131         char *p = *(char **)ad->argLoc;\r
2132         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2133           /* Quote multiline values or \-containing values\r
2134              with { } if possible */\r
2135           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2136         } else {\r
2137           /* Else quote with " " */\r
2138           fprintf(f, "/%s=\"", ad->argName);\r
2139           while (*p) {\r
2140             if (*p == '\n') fprintf(f, "\n");\r
2141             else if (*p == '\r') fprintf(f, "\\r");\r
2142             else if (*p == '\t') fprintf(f, "\\t");\r
2143             else if (*p == '\b') fprintf(f, "\\b");\r
2144             else if (*p == '\f') fprintf(f, "\\f");\r
2145             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2146             else if (*p == '\"') fprintf(f, "\\\"");\r
2147             else if (*p == '\\') fprintf(f, "\\\\");\r
2148             else putc(*p, f);\r
2149             p++;\r
2150           }\r
2151           fprintf(f, "\"\n");\r
2152         }\r
2153       }\r
2154       break;\r
2155     case ArgInt:\r
2156       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2157       break;\r
2158     case ArgFloat:\r
2159       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2160       break;\r
2161     case ArgBoolean:\r
2162       fprintf(f, "/%s=%s\n", ad->argName, \r
2163         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2164       break;\r
2165     case ArgTrue:\r
2166       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2167       break;\r
2168     case ArgFalse:\r
2169       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2170       break;\r
2171     case ArgColor:\r
2172       {\r
2173         COLORREF color = *(COLORREF *)ad->argLoc;\r
2174         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2175           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2176       }\r
2177       break;\r
2178     case ArgAttribs:\r
2179       {\r
2180         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2181         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2182           (ta->effects & CFE_BOLD) ? "b" : "",\r
2183           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2184           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2185           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2186           (ta->effects) ? " " : "",\r
2187           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2188       }\r
2189       break;\r
2190     case ArgFilename:\r
2191       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2192         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2193       } else {\r
2194         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2195       }\r
2196       break;\r
2197     case ArgBoardSize:\r
2198       fprintf(f, "/%s=%s\n", ad->argName,\r
2199               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2200       break;\r
2201     case ArgFont:\r
2202       {\r
2203         int bs;\r
2204         for (bs=0; bs<NUM_SIZES; bs++) {\r
2205           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2206           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2207           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2208             ad->argName, mfp->faceName, mfp->pointSize,\r
2209             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2210             mfp->bold ? "b" : "",\r
2211             mfp->italic ? "i" : "",\r
2212             mfp->underline ? "u" : "",\r
2213             mfp->strikeout ? "s" : "");\r
2214         }\r
2215       }\r
2216       break;\r
2217     case ArgCommSettings:\r
2218       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2219     }\r
2220   }\r
2221   fclose(f);\r
2222 }\r
2223 \r
2224 \r
2225 \r
2226 /*---------------------------------------------------------------------------*\\r
2227  *\r
2228  * GDI board drawing routines\r
2229  *\r
2230 \*---------------------------------------------------------------------------*/\r
2231 \r
2232 /* [AS] Draw square using background texture */\r
2233 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2234 {\r
2235     XFORM   x;\r
2236 \r
2237     if( mode == 0 ) {\r
2238         return; /* Should never happen! */\r
2239     }\r
2240 \r
2241     SetGraphicsMode( dst, GM_ADVANCED );\r
2242 \r
2243     switch( mode ) {\r
2244     case 1:\r
2245         /* Identity */\r
2246         break;\r
2247     case 2:\r
2248         /* X reflection */\r
2249         x.eM11 = -1.0;\r
2250         x.eM12 = 0;\r
2251         x.eM21 = 0;\r
2252         x.eM22 = 1.0;\r
2253         x.eDx = (FLOAT) dw + dx - 1;\r
2254         x.eDy = 0;\r
2255         dx = 0;\r
2256         SetWorldTransform( dst, &x );\r
2257         break;\r
2258     case 3:\r
2259         /* Y reflection */\r
2260         x.eM11 = 1.0;\r
2261         x.eM12 = 0;\r
2262         x.eM21 = 0;\r
2263         x.eM22 = -1.0;\r
2264         x.eDx = 0;\r
2265         x.eDy = (FLOAT) dh + dy - 1;\r
2266         dy = 0;\r
2267         SetWorldTransform( dst, &x );\r
2268         break;\r
2269     case 4:\r
2270         /* X/Y flip */\r
2271         x.eM11 = 0;\r
2272         x.eM12 = 1.0;\r
2273         x.eM21 = 1.0;\r
2274         x.eM22 = 0;\r
2275         x.eDx = (FLOAT) dx;\r
2276         x.eDy = (FLOAT) dy;\r
2277         dx = 0;\r
2278         dy = 0;\r
2279         SetWorldTransform( dst, &x );\r
2280         break;\r
2281     }\r
2282 \r
2283     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2284 \r
2285     x.eM11 = 1.0;\r
2286     x.eM12 = 0;\r
2287     x.eM21 = 0;\r
2288     x.eM22 = 1.0;\r
2289     x.eDx = 0;\r
2290     x.eDy = 0;\r
2291     SetWorldTransform( dst, &x );\r
2292 \r
2293     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2294 }\r
2295 \r
2296 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2297 enum {\r
2298     PM_WP = (int) WhitePawn, \r
2299     PM_WN = (int) WhiteKnight, \r
2300     PM_WB = (int) WhiteBishop, \r
2301     PM_WR = (int) WhiteRook, \r
2302     PM_WQ = (int) WhiteQueen, \r
2303 #ifdef FAIRY\r
2304     PM_WF = (int) WhiteFerz, \r
2305     PM_WW = (int) WhiteWazir, \r
2306     PM_WE = (int) WhiteAlfil, \r
2307     PM_WH = (int) WhiteNightrider, \r
2308     PM_WA = (int) WhiteCardinal, \r
2309     PM_WC = (int) WhiteMarshall, \r
2310     PM_WG = (int) WhiteGrasshopper, \r
2311     PM_WO = (int) WhiteCannon, \r
2312     PM_WM = (int) WhiteMan, \r
2313     PM_WU = (int) WhiteUnicorn, \r
2314 #endif\r
2315     PM_WK = (int) WhiteKing,\r
2316     PM_BP = (int) BlackPawn, \r
2317     PM_BN = (int) BlackKnight, \r
2318     PM_BB = (int) BlackBishop, \r
2319     PM_BR = (int) BlackRook, \r
2320     PM_BQ = (int) BlackQueen, \r
2321 #ifdef FAIRY\r
2322     PM_BF = (int) BlackFerz, \r
2323     PM_BW = (int) BlackWazir, \r
2324     PM_BE = (int) BlackAlfil, \r
2325     PM_BH = (int) BlackNightrider, \r
2326     PM_BA = (int) BlackCardinal, \r
2327     PM_BC = (int) BlackMarshall, \r
2328     PM_BG = (int) BlackGrasshopper, \r
2329     PM_BO = (int) BlackCannon, \r
2330     PM_BM = (int) BlackMan,\r
2331     PM_BU = (int) BlackUnicorn, \r
2332 #endif\r
2333     PM_BK = (int) BlackKing\r
2334 };\r
2335 \r
2336 static HFONT hPieceFont = NULL;\r
2337 static HBITMAP hPieceMask[(int) EmptySquare];\r
2338 static HBITMAP hPieceFace[(int) EmptySquare];\r
2339 static int fontBitmapSquareSize = 0;\r
2340 static char pieceToFontChar[(int) EmptySquare] =\r
2341                               { 'p', 'n', 'b', 'r', 'q', \r
2342 #ifdef FAIRY\r
2343                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2344 #endif\r
2345                       'k', 'o', 'm', 'v', 't', 'w', \r
2346 #ifdef FAIRY\r
2347                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2348 #endif\r
2349                                                               'l' };\r
2350 \r
2351 static BOOL SetPieceToFontCharTable( const char * map )\r
2352 {\r
2353     BOOL result = FALSE; int NrPieces;\r
2354 \r
2355     if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare \r
2356                     && NrPieces >= 12 && !(NrPieces&1)) {\r
2357         int i; /* [HGM] Accept even length from 12 to 32 */\r
2358 \r
2359         for( i=0; i<(int) EmptySquare; i++ ) pieceToFontChar[i] = 0;\r
2360         for( i=0; i<NrPieces/2-1; i++ ) {\r
2361             pieceToFontChar[i] = map[i];\r
2362             pieceToFontChar[i + (int)BlackPawn - (int) WhitePawn] = map[i+NrPieces/2];\r
2363         }\r
2364         pieceToFontChar[(int) WhiteKing]  = map[NrPieces/2-1];\r
2365         pieceToFontChar[(int) BlackKing]  = map[NrPieces-1];\r
2366 \r
2367         result = TRUE;\r
2368     }\r
2369 \r
2370     return result;\r
2371 }\r
2372 \r
2373 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2374 {\r
2375     HBRUSH hbrush;\r
2376     BYTE r1 = GetRValue( color );\r
2377     BYTE g1 = GetGValue( color );\r
2378     BYTE b1 = GetBValue( color );\r
2379     BYTE r2 = r1 / 2;\r
2380     BYTE g2 = g1 / 2;\r
2381     BYTE b2 = b1 / 2;\r
2382     RECT rc;\r
2383 \r
2384     /* Create a uniform background first */\r
2385     hbrush = CreateSolidBrush( color );\r
2386     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2387     FillRect( hdc, &rc, hbrush );\r
2388     DeleteObject( hbrush );\r
2389     \r
2390     if( mode == 1 ) {\r
2391         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2392         int steps = squareSize / 2;\r
2393         int i;\r
2394 \r
2395         for( i=0; i<steps; i++ ) {\r
2396             BYTE r = r1 - (r1-r2) * i / steps;\r
2397             BYTE g = g1 - (g1-g2) * i / steps;\r
2398             BYTE b = b1 - (b1-b2) * i / steps;\r
2399 \r
2400             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2401             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2402             FillRect( hdc, &rc, hbrush );\r
2403             DeleteObject(hbrush);\r
2404         }\r
2405     }\r
2406     else if( mode == 2 ) {\r
2407         /* Diagonal gradient, good more or less for every piece */\r
2408         POINT triangle[3];\r
2409         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2410         HBRUSH hbrush_old;\r
2411         int steps = squareSize;\r
2412         int i;\r
2413 \r
2414         triangle[0].x = squareSize - steps;\r
2415         triangle[0].y = squareSize;\r
2416         triangle[1].x = squareSize;\r
2417         triangle[1].y = squareSize;\r
2418         triangle[2].x = squareSize;\r
2419         triangle[2].y = squareSize - steps;\r
2420 \r
2421         for( i=0; i<steps; i++ ) {\r
2422             BYTE r = r1 - (r1-r2) * i / steps;\r
2423             BYTE g = g1 - (g1-g2) * i / steps;\r
2424             BYTE b = b1 - (b1-b2) * i / steps;\r
2425 \r
2426             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2427             hbrush_old = SelectObject( hdc, hbrush );\r
2428             Polygon( hdc, triangle, 3 );\r
2429             SelectObject( hdc, hbrush_old );\r
2430             DeleteObject(hbrush);\r
2431             triangle[0].x++;\r
2432             triangle[2].y++;\r
2433         }\r
2434 \r
2435         SelectObject( hdc, hpen );\r
2436     }\r
2437 }\r
2438 \r
2439 /*\r
2440     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2441     seems to work ok. The main problem here is to find the "inside" of a chess\r
2442     piece: follow the steps as explained below.\r
2443 */\r
2444 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2445 {\r
2446     HBITMAP hbm;\r
2447     HBITMAP hbm_old;\r
2448     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2449     RECT rc;\r
2450     SIZE sz;\r
2451     POINT pt;\r
2452     int backColor = whitePieceColor; \r
2453     int foreColor = blackPieceColor;\r
2454     int shapeIndex = index < 6 ? index+6 : index;\r
2455     \r
2456     if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2457         backColor = appData.fontBackColorWhite;\r
2458         foreColor = appData.fontForeColorWhite;\r
2459     }\r
2460     else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2461         backColor = appData.fontBackColorBlack;\r
2462         foreColor = appData.fontForeColorBlack;\r
2463     }\r
2464 \r
2465     /* Mask */\r
2466     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2467 \r
2468     hbm_old = SelectObject( hdc, hbm );\r
2469 \r
2470     rc.left = 0;\r
2471     rc.top = 0;\r
2472     rc.right = squareSize;\r
2473     rc.bottom = squareSize;\r
2474 \r
2475     /* Step 1: background is now black */\r
2476     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2477 \r
2478     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2479 \r
2480     pt.x = (squareSize - sz.cx) / 2;\r
2481     pt.y = (squareSize - sz.cy) / 2;\r
2482 \r
2483     SetBkMode( hdc, TRANSPARENT );\r
2484     SetTextColor( hdc, chroma );\r
2485     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2486     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2487 \r
2488     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2489     /* Step 3: the area outside the piece is filled with white */\r
2490     FloodFill( hdc, 0, 0, chroma );\r
2491     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2492     /* \r
2493         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2494         but if the start point is not inside the piece we're lost!\r
2495         There should be a better way to do this... if we could create a region or path\r
2496         from the fill operation we would be fine for example.\r
2497     */\r
2498     FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2499 \r
2500     SetTextColor( hdc, 0 );\r
2501     /* \r
2502         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2503         draw the piece again in black for safety.\r
2504     */\r
2505     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2506 \r
2507     SelectObject( hdc, hbm_old );\r
2508 \r
2509     if( hPieceMask[index] != NULL ) {\r
2510         DeleteObject( hPieceMask[index] );\r
2511     }\r
2512 \r
2513     hPieceMask[index] = hbm;\r
2514 \r
2515     /* Face */\r
2516     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2517 \r
2518     SelectObject( hdc, hbm );\r
2519 \r
2520     {\r
2521         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2522         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2523         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2524 \r
2525         SelectObject( dc1, hPieceMask[index] );\r
2526         SelectObject( dc2, bm2 );\r
2527         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2528         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2529         \r
2530         /* \r
2531             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2532             the piece background and deletes (makes transparent) the rest.\r
2533             Thanks to that mask, we are free to paint the background with the greates\r
2534             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2535             We use this, to make gradients and give the pieces a "roundish" look.\r
2536         */\r
2537         SetPieceBackground( hdc, backColor, 2 );\r
2538         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2539 \r
2540         DeleteDC( dc2 );\r
2541         DeleteDC( dc1 );\r
2542         DeleteObject( bm2 );\r
2543     }\r
2544 \r
2545     SetTextColor( hdc, foreColor );\r
2546     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2547 \r
2548     SelectObject( hdc, hbm_old );\r
2549 \r
2550     if( hPieceFace[index] != NULL ) {\r
2551         DeleteObject( hPieceFace[index] );\r
2552     }\r
2553 \r
2554     hPieceFace[index] = hbm;\r
2555 }\r
2556 \r
2557 static int TranslatePieceToFontPiece( int piece )\r
2558 {\r
2559     switch( piece ) {\r
2560     case BlackPawn:\r
2561         return PM_BP;\r
2562     case BlackKnight:\r
2563         return PM_BN;\r
2564     case BlackBishop:\r
2565         return PM_BB;\r
2566     case BlackRook:\r
2567         return PM_BR;\r
2568     case BlackQueen:\r
2569         return PM_BQ;\r
2570     case BlackKing:\r
2571         return PM_BK;\r
2572     case WhitePawn:\r
2573         return PM_WP;\r
2574     case WhiteKnight:\r
2575         return PM_WN;\r
2576     case WhiteBishop:\r
2577         return PM_WB;\r
2578     case WhiteRook:\r
2579         return PM_WR;\r
2580     case WhiteQueen:\r
2581         return PM_WQ;\r
2582     case WhiteKing:\r
2583         return PM_WK;\r
2584 #ifdef FAIRY\r
2585     case BlackCardinal:\r
2586         return PM_BA;\r
2587     case BlackMarshall:\r
2588         return PM_BC;\r
2589     case BlackFerz:\r
2590         return PM_BF;\r
2591     case BlackNightrider:\r
2592         return PM_BH;\r
2593     case BlackAlfil:\r
2594         return PM_BE;\r
2595     case BlackWazir:\r
2596         return PM_BW;\r
2597     case BlackUnicorn:\r
2598         return PM_BU;\r
2599     case BlackCannon:\r
2600         return PM_BO;\r
2601     case BlackGrasshopper:\r
2602         return PM_BG;\r
2603     case BlackMan:\r
2604         return PM_BM;\r
2605     case WhiteCardinal:\r
2606         return PM_WA;\r
2607     case WhiteMarshall:\r
2608         return PM_WC;\r
2609     case WhiteFerz:\r
2610         return PM_WF;\r
2611     case WhiteNightrider:\r
2612         return PM_WH;\r
2613     case WhiteAlfil:\r
2614         return PM_WE;\r
2615     case WhiteWazir:\r
2616         return PM_WW;\r
2617     case WhiteUnicorn:\r
2618         return PM_WU;\r
2619     case WhiteCannon:\r
2620         return PM_WO;\r
2621     case WhiteGrasshopper:\r
2622         return PM_WG;\r
2623     case WhiteMan:\r
2624         return PM_WM;\r
2625 #endif\r
2626     }\r
2627 \r
2628     return 0;\r
2629 }\r
2630 \r
2631 void CreatePiecesFromFont()\r
2632 {\r
2633     LOGFONT lf;\r
2634     HDC hdc_window = NULL;\r
2635     HDC hdc = NULL;\r
2636     HFONT hfont_old;\r
2637     int fontHeight;\r
2638     int i;\r
2639 \r
2640     if( fontBitmapSquareSize < 0 ) {\r
2641         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2642         return;\r
2643     }\r
2644 \r
2645     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2646         fontBitmapSquareSize = -1;\r
2647         return;\r
2648     }\r
2649 \r
2650     if( fontBitmapSquareSize != squareSize ) {\r
2651         hdc_window = GetDC( hwndMain );\r
2652         hdc = CreateCompatibleDC( hdc_window );\r
2653 \r
2654         if( hPieceFont != NULL ) {\r
2655             DeleteObject( hPieceFont );\r
2656         }\r
2657         else {\r
2658             for( i=0; i<12; i++ ) {\r
2659                 hPieceMask[i] = NULL;\r
2660                 hPieceFace[i] = NULL;\r
2661             }\r
2662         }\r
2663 \r
2664         fontHeight = 75;\r
2665 \r
2666         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2667             fontHeight = appData.fontPieceSize;\r
2668         }\r
2669 \r
2670         fontHeight = (fontHeight * squareSize) / 100;\r
2671 \r
2672         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2673         lf.lfWidth = 0;\r
2674         lf.lfEscapement = 0;\r
2675         lf.lfOrientation = 0;\r
2676         lf.lfWeight = FW_NORMAL;\r
2677         lf.lfItalic = 0;\r
2678         lf.lfUnderline = 0;\r
2679         lf.lfStrikeOut = 0;\r
2680         lf.lfCharSet = DEFAULT_CHARSET;\r
2681         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2682         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2683         lf.lfQuality = PROOF_QUALITY;\r
2684         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2685         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2686         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2687 \r
2688         hPieceFont = CreateFontIndirect( &lf );\r
2689 \r
2690         if( hPieceFont == NULL ) {\r
2691             fontBitmapSquareSize = -2;\r
2692         }\r
2693         else {\r
2694             /* Setup font-to-piece character table */\r
2695             if( ! SetPieceToFontCharTable(appData.fontToPieceTable) ) {\r
2696                 /* No (or wrong) global settings, try to detect the font */\r
2697                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2698                     /* Alpha */\r
2699                     SetPieceToFontCharTable("phbrqkojntwl");\r
2700                 }\r
2701                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2702                     /* DiagramTT* family */\r
2703                     SetPieceToFontCharTable("PNLRQKpnlrqk");\r
2704                 }\r
2705 #ifdef FAIRY\r
2706                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2707                     /* Fairy symbols */\r
2708                         SetPieceToFontCharTable("PNBRACFHEWUOGMQKpnbracfewuogmqk");\r
2709                 }\r
2710 #endif\r
2711                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2712                     /* Good Companion (Some characters get warped as literal :-( */\r
2713                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2714                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2715                     SetPieceToFontCharTable(s);\r
2716                 }\r
2717                 else {\r
2718                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2719                     SetPieceToFontCharTable("pnbrqkomvtwl");\r
2720                 }\r
2721             }\r
2722 \r
2723             /* Create bitmaps */\r
2724             hfont_old = SelectObject( hdc, hPieceFont );\r
2725 \r
2726             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2727             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2728             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2729             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2730             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2731             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2732             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2733             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2734             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2735             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2736             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2737             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2738 #ifdef FAIRY\r
2739             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2740             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2741             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2742             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2743             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2744             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2745             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2746             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2747             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2748             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2749             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2750             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2751             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2752             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2753             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2754             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2755             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2756             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2757             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2758             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2759 #endif\r
2760 \r
2761             SelectObject( hdc, hfont_old );\r
2762 \r
2763             fontBitmapSquareSize = squareSize;\r
2764         }\r
2765     }\r
2766 \r
2767     if( hdc != NULL ) {\r
2768         DeleteDC( hdc );\r
2769     }\r
2770 \r
2771     if( hdc_window != NULL ) {\r
2772         ReleaseDC( hwndMain, hdc_window );\r
2773     }\r
2774 }\r
2775 \r
2776 HBITMAP\r
2777 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2778 {\r
2779   char name[128];\r
2780 \r
2781   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2782   if (gameInfo.event &&\r
2783       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2784       strcmp(name, "k80s") == 0) {\r
2785     strcpy(name, "tim");\r
2786   }\r
2787   return LoadBitmap(hinst, name);\r
2788 }\r
2789 \r
2790 \r
2791 /* Insert a color into the program's logical palette\r
2792    structure.  This code assumes the given color is\r
2793    the result of the RGB or PALETTERGB macro, and it\r
2794    knows how those macros work (which is documented).\r
2795 */\r
2796 VOID\r
2797 InsertInPalette(COLORREF color)\r
2798 {\r
2799   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2800 \r
2801   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2802     DisplayFatalError("Too many colors", 0, 1);\r
2803     pLogPal->palNumEntries--;\r
2804     return;\r
2805   }\r
2806 \r
2807   pe->peFlags = (char) 0;\r
2808   pe->peRed = (char) (0xFF & color);\r
2809   pe->peGreen = (char) (0xFF & (color >> 8));\r
2810   pe->peBlue = (char) (0xFF & (color >> 16));\r
2811   return;\r
2812 }\r
2813 \r
2814 \r
2815 VOID\r
2816 InitDrawingColors()\r
2817 {\r
2818   if (pLogPal == NULL) {\r
2819     /* Allocate enough memory for a logical palette with\r
2820      * PALETTESIZE entries and set the size and version fields\r
2821      * of the logical palette structure.\r
2822      */\r
2823     pLogPal = (NPLOGPALETTE)\r
2824       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2825                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2826     pLogPal->palVersion    = 0x300;\r
2827   }\r
2828   pLogPal->palNumEntries = 0;\r
2829 \r
2830   InsertInPalette(lightSquareColor);\r
2831   InsertInPalette(darkSquareColor);\r
2832   InsertInPalette(whitePieceColor);\r
2833   InsertInPalette(blackPieceColor);\r
2834   InsertInPalette(highlightSquareColor);\r
2835   InsertInPalette(premoveHighlightColor);\r
2836 \r
2837   /*  create a logical color palette according the information\r
2838    *  in the LOGPALETTE structure.\r
2839    */\r
2840   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2841 \r
2842   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2843   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2844   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2845   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2846   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2847   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2848 \r
2849   /* [AS] Force rendering of the font-based pieces */\r
2850   if( fontBitmapSquareSize > 0 ) {\r
2851     fontBitmapSquareSize = 0;\r
2852   }\r
2853 }\r
2854 \r
2855 \r
2856 int\r
2857 BoardWidth(int boardSize, int n)\r
2858 { /* [HGM] argument n added to allow different width and height */\r
2859   int lineGap = sizeInfo[boardSize].lineGap;\r
2860 \r
2861   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2862       lineGap = appData.overrideLineGap;\r
2863   }\r
2864 \r
2865   return (n + 1) * lineGap +\r
2866           n * sizeInfo[boardSize].squareSize;\r
2867 }\r
2868 \r
2869 /* Respond to board resize by dragging edge */\r
2870 VOID\r
2871 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2872 {\r
2873   BoardSize newSize = NUM_SIZES - 1;\r
2874   static int recurse = 0;\r
2875   if (IsIconic(hwndMain)) return;\r
2876   if (recurse > 0) return;\r
2877   recurse++;\r
2878   while (newSize > 0 &&\r
2879          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2880           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2881     newSize--;\r
2882   } \r
2883   boardSize = newSize;\r
2884   InitDrawingSizes(boardSize, flags);\r
2885   recurse--;\r
2886 }\r
2887 \r
2888 \r
2889 \r
2890 VOID\r
2891 InitDrawingSizes(BoardSize boardSize, int flags)\r
2892 {\r
2893   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2894   ChessSquare piece;\r
2895   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2896   HDC hdc;\r
2897   SIZE clockSize, messageSize;\r
2898   HFONT oldFont;\r
2899   char buf[MSG_SIZ];\r
2900   char *str;\r
2901   HMENU hmenu = GetMenu(hwndMain);\r
2902   RECT crect, wrect;\r
2903   int offby;\r
2904   LOGBRUSH logbrush;\r
2905 \r
2906   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
2907   if(boardSize == (BoardSize)(-1) ) boardSize = oldBoardSize;\r
2908 \r
2909   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2910   smallLayout = sizeInfo[boardSize].smallLayout;\r
2911   squareSize = sizeInfo[boardSize].squareSize;\r
2912   lineGap = sizeInfo[boardSize].lineGap;\r
2913 \r
2914   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2915       lineGap = appData.overrideLineGap;\r
2916   }\r
2917 \r
2918   if (tinyLayout != oldTinyLayout) {\r
2919     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2920     if (tinyLayout) {\r
2921       style &= ~WS_SYSMENU;\r
2922       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2923                  "&Minimize\tCtrl+F4");\r
2924     } else {\r
2925       style |= WS_SYSMENU;\r
2926       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2927     }\r
2928     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2929 \r
2930     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2931       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2932         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2933     }\r
2934     DrawMenuBar(hwndMain);\r
2935   }\r
2936 \r
2937   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2938   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2939 \r
2940   /* Get text area sizes */\r
2941   hdc = GetDC(hwndMain);\r
2942   if (appData.clockMode) {\r
2943     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2944   } else {\r
2945     sprintf(buf, "White");\r
2946   }\r
2947   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2948   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2949   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2950   str = "We only care about the height here";\r
2951   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2952   SelectObject(hdc, oldFont);\r
2953   ReleaseDC(hwndMain, hdc);\r
2954 \r
2955   /* Compute where everything goes */\r
2956   whiteRect.left = OUTER_MARGIN;\r
2957   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2958   whiteRect.top = OUTER_MARGIN;\r
2959   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2960 \r
2961   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2962   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2963   blackRect.top = whiteRect.top;\r
2964   blackRect.bottom = whiteRect.bottom;\r
2965 \r
2966   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2967   if (appData.showButtonBar) {\r
2968     messageRect.right = blackRect.right\r
2969       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2970   } else {\r
2971     messageRect.right = blackRect.right;\r
2972   }\r
2973   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2974   messageRect.bottom = messageRect.top + messageSize.cy;\r
2975 \r
2976   boardRect.left = whiteRect.left;\r
2977   boardRect.right = boardRect.left + boardWidth;\r
2978   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2979   boardRect.bottom = boardRect.top + boardHeight;\r
2980 \r
2981   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2982   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2983   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2984   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2985     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2986   GetWindowRect(hwndMain, &wrect);\r
2987   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2988                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2989   /* compensate if menu bar wrapped */\r
2990   GetClientRect(hwndMain, &crect);\r
2991   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2992   winHeight += offby;\r
2993   switch (flags) {\r
2994   case WMSZ_TOPLEFT:\r
2995     SetWindowPos(hwndMain, NULL, \r
2996                  wrect.right - winWidth, wrect.bottom - winHeight, \r
2997                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2998     break;\r
2999 \r
3000   case WMSZ_TOPRIGHT:\r
3001   case WMSZ_TOP:\r
3002     SetWindowPos(hwndMain, NULL, \r
3003                  wrect.left, wrect.bottom - winHeight, \r
3004                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3005     break;\r
3006 \r
3007   case WMSZ_BOTTOMLEFT:\r
3008   case WMSZ_LEFT:\r
3009     SetWindowPos(hwndMain, NULL, \r
3010                  wrect.right - winWidth, wrect.top, \r
3011                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3012     break;\r
3013 \r
3014   case WMSZ_BOTTOMRIGHT:\r
3015   case WMSZ_BOTTOM:\r
3016   case WMSZ_RIGHT:\r
3017   default:\r
3018     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3019                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3020     break;\r
3021   }\r
3022 \r
3023   hwndPause = NULL;\r
3024   for (i = 0; i < N_BUTTONS; i++) {\r
3025     if (buttonDesc[i].hwnd != NULL) {\r
3026       DestroyWindow(buttonDesc[i].hwnd);\r
3027       buttonDesc[i].hwnd = NULL;\r
3028     }\r
3029     if (appData.showButtonBar) {\r
3030       buttonDesc[i].hwnd =\r
3031         CreateWindow("BUTTON", buttonDesc[i].label,\r
3032                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3033                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3034                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3035                      (HMENU) buttonDesc[i].id,\r
3036                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3037       if (tinyLayout) {\r
3038         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3039                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3040                     MAKELPARAM(FALSE, 0));\r
3041       }\r
3042       if (buttonDesc[i].id == IDM_Pause)\r
3043         hwndPause = buttonDesc[i].hwnd;\r
3044       buttonDesc[i].wndproc = (WNDPROC)\r
3045         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3046     }\r
3047   }\r
3048   if (gridPen != NULL) DeleteObject(gridPen);\r
3049   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3050   if (premovePen != NULL) DeleteObject(premovePen);\r
3051   if (lineGap != 0) {\r
3052     logbrush.lbStyle = BS_SOLID;\r
3053     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3054     gridPen =\r
3055       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3056                    lineGap, &logbrush, 0, NULL);\r
3057     logbrush.lbColor = highlightSquareColor;\r
3058     highlightPen =\r
3059       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3060                    lineGap, &logbrush, 0, NULL);\r
3061 \r
3062     logbrush.lbColor = premoveHighlightColor; \r
3063     premovePen =\r
3064       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3065                    lineGap, &logbrush, 0, NULL);\r
3066 \r
3067     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3068     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3069       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3070       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3071         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3072       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3073         BOARD_WIDTH * (squareSize + lineGap);\r
3074         lineGap / 2 + (i * (squareSize + lineGap));\r
3075       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3076     }\r
3077     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3078       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3079       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3080         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3081         lineGap / 2 + (i * (squareSize + lineGap));\r
3082       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3083         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3084       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3085     }\r
3086   }\r
3087 \r
3088   if (boardSize == oldBoardSize) return;\r
3089   oldBoardSize = boardSize;\r
3090   oldTinyLayout = tinyLayout;\r
3091 \r
3092   /* Load piece bitmaps for this board size */\r
3093   for (i=0; i<=2; i++) {\r
3094     for (piece = WhitePawn;\r
3095          (int) piece < (int) BlackPawn;\r
3096          piece = (ChessSquare) ((int) piece + 1)) {\r
3097       if (pieceBitmap[i][piece] != NULL)\r
3098         DeleteObject(pieceBitmap[i][piece]);\r
3099     }\r
3100   }\r
3101 \r
3102   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3103   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3104   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3105   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3106   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3107   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3108   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3109   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3110   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3111   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3112   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3113   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3114   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3115   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3116   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3117   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3118   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3119   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3120 #ifdef FAIRY\r
3121   if(squareSize==72 || squareSize==49) { /* experiment with some home-made bitmaps */\r
3122   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3123   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3124   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3125   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3126   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3127   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3128   if(!strcmp(appData.variant, "shogi")) { /* promoted Gold represemtations */\r
3129   pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3130   pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3131   pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3132   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3133   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3134   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3135   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3136   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3137   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3138   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3139   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3140   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3141   } else {\r
3142   pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3143   pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3144   pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3145   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3146   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3147   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3148   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3149   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3150   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3151   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3152   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3153   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3154   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3155   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3156   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3157   }\r
3158   pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3159   pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3160   pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3161   pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3162   pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3163   pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3164   if(strcmp(appData.variant, "crazyhouse") && strcmp(appData.variant, "shogi")) {\r
3165   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3166   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3167   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3168   } else {\r
3169   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3170   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3171   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3172   }\r
3173   if(!strcmp(appData.variant, "xiangqi") || !strcmp(appData.variant, "shogi")) {\r
3174       for(i=0; i<2; i++)\r
3175       if (pieceBitmap[i][WhiteQueen] != NULL)\r
3176         DeleteObject(pieceBitmap[i][WhiteQueen]);\r
3177   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3178   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3179   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3180   }\r
3181   }\r
3182 #endif\r
3183 \r
3184 }\r
3185 \r
3186 HBITMAP\r
3187 PieceBitmap(ChessSquare p, int kind)\r
3188 {\r
3189   if ((int) p >= (int) BlackPawn)\r
3190     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3191 \r
3192   return pieceBitmap[kind][(int) p];\r
3193 }\r
3194 \r
3195 /***************************************************************/\r
3196 \r
3197 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3198 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3199 /*\r
3200 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3201 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3202 */\r
3203 \r
3204 VOID\r
3205 SquareToPos(int row, int column, int * x, int * y)\r
3206 {\r
3207   if (flipView) {\r
3208     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3209     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3210   } else {\r
3211     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3212     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3213   }\r
3214 }\r
3215 \r
3216 VOID\r
3217 DrawCoordsOnDC(HDC hdc)\r
3218 {\r
3219   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
3220   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
3221   char str[2] = { NULLCHAR, NULLCHAR };\r
3222   int oldMode, oldAlign, x, y, start, i;\r
3223   HFONT oldFont;\r
3224   HBRUSH oldBrush;\r
3225 \r
3226   if (!appData.showCoords)\r
3227     return;\r
3228 \r
3229   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3230 \r
3231   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3232   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3233   oldAlign = GetTextAlign(hdc);\r
3234   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3235 \r
3236   y = boardRect.top + lineGap;\r
3237   x = boardRect.left + lineGap;\r
3238 \r
3239   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3240   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3241     str[0] = files[start + i];\r
3242     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3243     y += squareSize + lineGap;\r
3244   }\r
3245 \r
3246   start = flipView ? 12-BOARD_WIDTH : 12;\r
3247 \r
3248   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3249   for (i = 0; i < BOARD_WIDTH; i++) {\r
3250     str[0] = ranks[start + i];\r
3251     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3252     x += squareSize + lineGap;\r
3253   }    \r
3254 \r
3255   SelectObject(hdc, oldBrush);\r
3256   SetBkMode(hdc, oldMode);\r
3257   SetTextAlign(hdc, oldAlign);\r
3258   SelectObject(hdc, oldFont);\r
3259 }\r
3260 \r
3261 VOID\r
3262 DrawGridOnDC(HDC hdc)\r
3263 {\r
3264   HPEN oldPen;\r
3265  \r
3266   if (lineGap != 0) {\r
3267     oldPen = SelectObject(hdc, gridPen);\r
3268     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3269     SelectObject(hdc, oldPen);\r
3270   }\r
3271 }\r
3272 \r
3273 #define HIGHLIGHT_PEN 0\r
3274 #define PREMOVE_PEN   1\r
3275 \r
3276 VOID\r
3277 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3278 {\r
3279   int x1, y1;\r
3280   HPEN oldPen, hPen;\r
3281   if (lineGap == 0) return;\r
3282   if (flipView) {\r
3283     x1 = boardRect.left +\r
3284       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3285     y1 = boardRect.top +\r
3286       lineGap/2 + y * (squareSize + lineGap);\r
3287   } else {\r
3288     x1 = boardRect.left +\r
3289       lineGap/2 + x * (squareSize + lineGap);\r
3290     y1 = boardRect.top +\r
3291       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3292   }\r
3293   hPen = pen ? premovePen : highlightPen;\r
3294   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3295   MoveToEx(hdc, x1, y1, NULL);\r
3296   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3297   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3298   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3299   LineTo(hdc, x1, y1);\r
3300   SelectObject(hdc, oldPen);\r
3301 }\r
3302 \r
3303 VOID\r
3304 DrawHighlightsOnDC(HDC hdc)\r
3305 {\r
3306   int i;\r
3307   for (i=0; i<2; i++) {\r
3308     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3309       DrawHighlightOnDC(hdc, TRUE,\r
3310                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3311                         HIGHLIGHT_PEN);\r
3312   }\r
3313   for (i=0; i<2; i++) {\r
3314     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3315         premoveHighlightInfo.sq[i].y >= 0) {\r
3316         DrawHighlightOnDC(hdc, TRUE,\r
3317                           premoveHighlightInfo.sq[i].x, \r
3318                           premoveHighlightInfo.sq[i].y,\r
3319                           PREMOVE_PEN);\r
3320     }\r
3321   }\r
3322 }\r
3323 \r
3324 /* Note: sqcolor is used only in monoMode */\r
3325 /* Note that this code is largely duplicated in woptions.c,\r
3326    function DrawSampleSquare, so that needs to be updated too */\r
3327 VOID\r
3328 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3329 {\r
3330   HBITMAP oldBitmap;\r
3331   HBRUSH oldBrush;\r
3332 \r
3333   if (appData.blindfold) return;\r
3334 \r
3335   /* [AS] Use font-based pieces if needed */\r
3336   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3337     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3338     CreatePiecesFromFont();\r
3339 \r
3340     if( fontBitmapSquareSize == squareSize ) {\r
3341         int index = TranslatePieceToFontPiece( piece );\r
3342 \r
3343         SelectObject( tmphdc, hPieceMask[ index ] );\r
3344 \r
3345         BitBlt( hdc,\r
3346             x, y,\r
3347             squareSize, squareSize,\r
3348             tmphdc,\r
3349             0, 0,\r
3350             SRCAND );\r
3351 \r
3352         SelectObject( tmphdc, hPieceFace[ index ] );\r
3353 \r
3354         BitBlt( hdc,\r
3355             x, y,\r
3356             squareSize, squareSize,\r
3357             tmphdc,\r
3358             0, 0,\r
3359             SRCPAINT );\r
3360 \r
3361         return;\r
3362     }\r
3363   }\r
3364 \r
3365   if (appData.monoMode) {\r
3366     SelectObject(tmphdc, PieceBitmap(piece, \r
3367       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3368     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3369            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3370   } else {\r
3371     if (color) {\r
3372       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3373       oldBrush = SelectObject(hdc, whitePieceBrush);\r
3374       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3375 #if 0\r
3376       /* Use black piece color for outline of white pieces */\r
3377       /* Not sure this looks really good (though xboard does it).\r
3378          Maybe better to have another selectable color, default black */\r
3379       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3380       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3381       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3382 #else\r
3383       /* Use black for outline of white pieces */\r
3384       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3385       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);\r
3386 #endif\r
3387     } else {\r
3388 #if 0\r
3389       /* Use white piece color for details of black pieces */\r
3390       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3391          WHITE_PIECE ones aren't always the right shape. */\r
3392       /* Not sure this looks really good (though xboard does it).\r
3393          Maybe better to have another selectable color, default medium gray? */\r
3394       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3395       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3396       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3397       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3398       SelectObject(hdc, blackPieceBrush);\r
3399       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3400 #else\r
3401       /* Use square color for details of black pieces */\r
3402       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3403       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3404       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3405 #endif\r
3406     }\r
3407     SelectObject(hdc, oldBrush);\r
3408     SelectObject(tmphdc, oldBitmap);\r
3409   }\r
3410 }\r
3411 \r
3412 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3413 int GetBackTextureMode( int algo )\r
3414 {\r
3415     int result = BACK_TEXTURE_MODE_DISABLED;\r
3416 \r
3417     switch( algo ) \r
3418     {\r
3419         case BACK_TEXTURE_MODE_PLAIN:\r
3420             result = 1; /* Always use identity map */\r
3421             break;\r
3422         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3423             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3424             break;\r
3425     }\r
3426 \r
3427     return result;\r
3428 }\r
3429 \r
3430 /* \r
3431     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3432     to handle redraws cleanly (as random numbers would always be different).\r
3433 */\r
3434 VOID RebuildTextureSquareInfo()\r
3435 {\r
3436     BITMAP bi;\r
3437     int lite_w = 0;\r
3438     int lite_h = 0;\r
3439     int dark_w = 0;\r
3440     int dark_h = 0;\r
3441     int row;\r
3442     int col;\r
3443 \r
3444     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3445 \r
3446     if( liteBackTexture != NULL ) {\r
3447         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3448             lite_w = bi.bmWidth;\r
3449             lite_h = bi.bmHeight;\r
3450         }\r
3451     }\r
3452 \r
3453     if( darkBackTexture != NULL ) {\r
3454         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3455             dark_w = bi.bmWidth;\r
3456             dark_h = bi.bmHeight;\r
3457         }\r
3458     }\r
3459 \r
3460     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3461         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3462             if( (col + row) & 1 ) {\r
3463                 /* Lite square */\r
3464                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3465                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3466                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3467                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3468                 }\r
3469             }\r
3470             else {\r
3471                 /* Dark square */\r
3472                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3473                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3474                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3475                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3476                 }\r
3477             }\r
3478         }\r
3479     }\r
3480 }\r
3481 \r
3482 /* [AS] Arrow highlighting support */\r
3483 \r
3484 static int A_WIDTH = 5; /* Width of arrow body */\r
3485 \r
3486 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3487 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3488 \r
3489 static double Sqr( double x )\r
3490 {\r
3491     return x*x;\r
3492 }\r
3493 \r
3494 static int Round( double x )\r
3495 {\r
3496     return (int) (x + 0.5);\r
3497 }\r
3498 \r
3499 /* Draw an arrow between two points using current settings */\r
3500 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3501 {\r
3502     POINT arrow[7];\r
3503     double dx, dy, j, k, x, y;\r
3504 \r
3505     if( d_x == s_x ) {\r
3506         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3507 \r
3508         arrow[0].x = s_x + A_WIDTH;\r
3509         arrow[0].y = s_y;\r
3510 \r
3511         arrow[1].x = s_x + A_WIDTH;\r
3512         arrow[1].y = d_y - h;\r
3513 \r
3514         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3515         arrow[2].y = d_y - h;\r
3516 \r
3517         arrow[3].x = d_x;\r
3518         arrow[3].y = d_y;\r
3519 \r
3520         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3521         arrow[4].y = d_y - h;\r
3522 \r
3523         arrow[5].x = s_x - A_WIDTH;\r
3524         arrow[5].y = d_y - h;\r
3525 \r
3526         arrow[6].x = s_x - A_WIDTH;\r
3527         arrow[6].y = s_y;\r
3528     }\r
3529     else if( d_y == s_y ) {\r
3530         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3531 \r
3532         arrow[0].x = s_x;\r
3533         arrow[0].y = s_y + A_WIDTH;\r
3534 \r
3535         arrow[1].x = d_x - w;\r
3536         arrow[1].y = s_y + A_WIDTH;\r
3537 \r
3538         arrow[2].x = d_x - w;\r
3539         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3540 \r
3541         arrow[3].x = d_x;\r
3542         arrow[3].y = d_y;\r
3543 \r
3544         arrow[4].x = d_x - w;\r
3545         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3546 \r
3547         arrow[5].x = d_x - w;\r
3548         arrow[5].y = s_y - A_WIDTH;\r
3549 \r
3550         arrow[6].x = s_x;\r
3551         arrow[6].y = s_y - A_WIDTH;\r
3552     }\r
3553     else {\r
3554         /* [AS] Needed a lot of paper for this! :-) */\r
3555         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3556         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3557   \r
3558         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3559 \r
3560         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3561 \r
3562         x = s_x;\r
3563         y = s_y;\r
3564 \r
3565         arrow[0].x = Round(x - j);\r
3566         arrow[0].y = Round(y + j*dx);\r
3567 \r
3568         arrow[1].x = Round(x + j);\r
3569         arrow[1].y = Round(y - j*dx);\r
3570 \r
3571         if( d_x > s_x ) {\r
3572             x = (double) d_x - k;\r
3573             y = (double) d_y - k*dy;\r
3574         }\r
3575         else {\r
3576             x = (double) d_x + k;\r
3577             y = (double) d_y + k*dy;\r
3578         }\r
3579 \r
3580         arrow[2].x = Round(x + j);\r
3581         arrow[2].y = Round(y - j*dx);\r
3582 \r
3583         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3584         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3585 \r
3586         arrow[4].x = d_x;\r
3587         arrow[4].y = d_y;\r
3588 \r
3589         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3590         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3591 \r
3592         arrow[6].x = Round(x - j);\r
3593         arrow[6].y = Round(y + j*dx);\r
3594     }\r
3595 \r
3596     Polygon( hdc, arrow, 7 );\r
3597 }\r
3598 \r
3599 /* [AS] Draw an arrow between two squares */\r
3600 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3601 {\r
3602     int s_x, s_y, d_x, d_y;\r
3603     HPEN hpen;\r
3604     HPEN holdpen;\r
3605     HBRUSH hbrush;\r
3606     HBRUSH holdbrush;\r
3607     LOGBRUSH stLB;\r
3608 \r
3609     if( s_col == d_col && s_row == d_row ) {\r
3610         return;\r
3611     }\r
3612 \r
3613     /* Get source and destination points */\r
3614     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3615     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3616 \r
3617     if( d_y > s_y ) {\r
3618         d_y += squareSize / 4;\r
3619     }\r
3620     else if( d_y < s_y ) {\r
3621         d_y += 3 * squareSize / 4;\r
3622     }\r
3623     else {\r
3624         d_y += squareSize / 2;\r
3625     }\r
3626 \r
3627     if( d_x > s_x ) {\r
3628         d_x += squareSize / 4;\r
3629     }\r
3630     else if( d_x < s_x ) {\r
3631         d_x += 3 * squareSize / 4;\r
3632     }\r
3633     else {\r
3634         d_x += squareSize / 2;\r
3635     }\r
3636 \r
3637     s_x += squareSize / 2;\r
3638     s_y += squareSize / 2;\r
3639 \r
3640     /* Adjust width */\r
3641     A_WIDTH = squareSize / 14;\r
3642 \r
3643     /* Draw */\r
3644     stLB.lbStyle = BS_SOLID;\r
3645     stLB.lbColor = appData.highlightArrowColor;\r
3646     stLB.lbHatch = 0;\r
3647 \r
3648     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3649     holdpen = SelectObject( hdc, hpen );\r
3650     hbrush = CreateBrushIndirect( &stLB );\r
3651     holdbrush = SelectObject( hdc, hbrush );\r
3652 \r
3653     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3654 \r
3655     SelectObject( hdc, holdpen );\r
3656     SelectObject( hdc, holdbrush );\r
3657     DeleteObject( hpen );\r
3658     DeleteObject( hbrush );\r
3659 }\r
3660 \r
3661 BOOL HasHighlightInfo()\r
3662 {\r
3663     BOOL result = FALSE;\r
3664 \r
3665     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3666         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3667     {\r
3668         result = TRUE;\r
3669     }\r
3670 \r
3671     return result;\r
3672 }\r
3673 \r
3674 BOOL IsDrawArrowEnabled()\r
3675 {\r
3676     BOOL result = FALSE;\r
3677 \r
3678     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3679         result = TRUE;\r
3680     }\r
3681 \r
3682     return result;\r
3683 }\r
3684 \r
3685 VOID DrawArrowHighlight( HDC hdc )\r
3686 {\r
3687     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3688         DrawArrowBetweenSquares( hdc,\r
3689             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3690             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3691     }\r
3692 }\r
3693 \r
3694 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3695 {\r
3696     HRGN result = NULL;\r
3697 \r
3698     if( HasHighlightInfo() ) {\r
3699         int x1, y1, x2, y2;\r
3700         int sx, sy, dx, dy;\r
3701 \r
3702         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3703         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3704 \r
3705         sx = MIN( x1, x2 );\r
3706         sy = MIN( y1, y2 );\r
3707         dx = MAX( x1, x2 ) + squareSize;\r
3708         dy = MAX( y1, y2 ) + squareSize;\r
3709 \r
3710         result = CreateRectRgn( sx, sy, dx, dy );\r
3711     }\r
3712 \r
3713     return result;\r
3714 }\r
3715 \r
3716 /*\r
3717     Warning: this function modifies the behavior of several other functions. \r
3718     \r
3719     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3720     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3721     repaint is scattered all over the place, which is not good for features such as\r
3722     "arrow highlighting" that require a full repaint of the board.\r
3723 \r
3724     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3725     user interaction, when speed is not so important) but especially to avoid errors\r
3726     in the displayed graphics.\r
3727 \r
3728     In such patched places, I always try refer to this function so there is a single\r
3729     place to maintain knowledge.\r
3730     \r
3731     To restore the original behavior, just return FALSE unconditionally.\r
3732 */\r
3733 BOOL IsFullRepaintPreferrable()\r
3734 {\r
3735     BOOL result = FALSE;\r
3736 \r
3737     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3738         /* Arrow may appear on the board */\r
3739         result = TRUE;\r
3740     }\r
3741 \r
3742     return result;\r
3743 }\r
3744 \r
3745 /* \r
3746     This function is called by DrawPosition to know whether a full repaint must\r
3747     be forced or not.\r
3748 \r
3749     Only DrawPosition may directly call this function, which makes use of \r
3750     some state information. Other function should call DrawPosition specifying \r
3751     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3752 */\r
3753 BOOL DrawPositionNeedsFullRepaint()\r
3754 {\r
3755     BOOL result = FALSE;\r
3756 \r
3757     /* \r
3758         Probably a slightly better policy would be to trigger a full repaint\r
3759         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3760         but animation is fast enough that it's difficult to notice.\r
3761     */\r
3762     if( animInfo.piece == EmptySquare ) {\r
3763         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3764             result = TRUE;\r
3765         }\r
3766     }\r
3767 \r
3768     return result;\r
3769 }\r
3770 \r
3771 VOID\r
3772 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3773 {\r
3774   int row, column, x, y, square_color, piece_color;\r
3775   ChessSquare piece;\r
3776   HBRUSH oldBrush;\r
3777   HDC texture_hdc = NULL;\r
3778 \r
3779   /* [AS] Initialize background textures if needed */\r
3780   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3781       if( backTextureSquareSize != squareSize ) {\r
3782           backTextureSquareSize = squareSize;\r
3783           RebuildTextureSquareInfo();\r
3784       }\r
3785 \r
3786       texture_hdc = CreateCompatibleDC( hdc );\r
3787   }\r
3788 \r
3789   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3790     for (column = 0; column < BOARD_WIDTH; column++) {\r
3791   \r
3792       SquareToPos(row, column, &x, &y);\r
3793 \r
3794       piece = board[row][column];\r
3795 \r
3796       square_color = ((column + row) % 2) == 1;\r
3797       if(!strcmp(appData.variant, "xiangqi") ) {\r
3798           square_color = !InPalace(row, column);\r
3799           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3800           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3801       }\r
3802       piece_color = (int) piece < (int) BlackPawn;\r
3803 \r
3804 \r
3805 #ifdef FAIRY\r
3806       /* [HGM] holdings file: light square or black */\r
3807       if(column == BOARD_LEFT-2) {\r
3808             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3809                 square_color = 1;\r
3810             else {\r
3811                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3812                 continue;\r
3813             }\r
3814       } else\r
3815       if(column == BOARD_RGHT + 1 ) {\r
3816             if( row < gameInfo.holdingsSize )\r
3817                 square_color = 1;\r
3818             else {\r
3819                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3820                 continue;\r
3821             }\r
3822       }\r
3823       if(column == BOARD_LEFT-1 ) /* left align */\r
3824             DisplayHoldingsCount(hdc, x, y, 0, (int) board[row][column]);\r
3825       else if( column == BOARD_RGHT) /* right align */\r
3826             DisplayHoldingsCount(hdc, x, y, 1, (int) board[row][column]);\r
3827       else\r
3828 #endif\r
3829       if (appData.monoMode) {\r
3830         if (piece == EmptySquare) {\r
3831           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3832                  square_color ? WHITENESS : BLACKNESS);\r
3833         } else {\r
3834           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3835         }\r
3836       } \r
3837       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3838           /* [AS] Draw the square using a texture bitmap */\r
3839           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3840 \r
3841           DrawTile( x, y, \r
3842               squareSize, squareSize, \r
3843               hdc, \r
3844               texture_hdc,\r
3845               backTextureSquareInfo[row][column].mode,\r
3846               backTextureSquareInfo[row][column].x,\r
3847               backTextureSquareInfo[row][column].y );\r
3848 \r
3849           SelectObject( texture_hdc, hbm );\r
3850 \r
3851           if (piece != EmptySquare) {\r
3852               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3853           }\r
3854       }\r
3855       else {\r
3856         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3857 \r
3858         oldBrush = SelectObject(hdc, brush );\r
3859         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3860         SelectObject(hdc, oldBrush);\r
3861         if (piece != EmptySquare)\r
3862           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3863       }\r
3864     }\r
3865   }\r
3866 \r
3867   if( texture_hdc != NULL ) {\r
3868     DeleteDC( texture_hdc );\r
3869   }\r
3870 }\r
3871 \r
3872 #define MAX_CLIPS 200   /* more than enough */\r
3873 \r
3874 VOID\r
3875 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3876 {\r
3877   static Board lastReq, lastDrawn;\r
3878   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3879   static int lastDrawnFlipView = 0;\r
3880   static int lastReqValid = 0, lastDrawnValid = 0;\r
3881   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3882   HDC tmphdc;\r
3883   HDC hdcmem;\r
3884   HBITMAP bufferBitmap;\r
3885   HBITMAP oldBitmap;\r
3886   RECT Rect;\r
3887   HRGN clips[MAX_CLIPS];\r
3888   ChessSquare dragged_piece = EmptySquare;\r
3889 \r
3890   /* I'm undecided on this - this function figures out whether a full\r
3891    * repaint is necessary on its own, so there's no real reason to have the\r
3892    * caller tell it that.  I think this can safely be set to FALSE - but\r
3893    * if we trust the callers not to request full repaints unnessesarily, then\r
3894    * we could skip some clipping work.  In other words, only request a full\r
3895    * redraw when the majority of pieces have changed positions (ie. flip, \r
3896    * gamestart and similar)  --Hawk\r
3897    */\r
3898   Boolean fullrepaint = repaint;\r
3899 \r
3900   if( DrawPositionNeedsFullRepaint() ) {\r
3901       fullrepaint = TRUE;\r
3902   }\r
3903 \r
3904 #if 0\r
3905   if( fullrepaint ) {\r
3906       static int repaint_count = 0;\r
3907       char buf[128];\r
3908 \r
3909       repaint_count++;\r
3910       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
3911       OutputDebugString( buf );\r
3912   }\r
3913 #endif\r
3914 \r
3915   if (board == NULL) {\r
3916     if (!lastReqValid) {\r
3917       return;\r
3918     }\r
3919     board = lastReq;\r
3920   } else {\r
3921     CopyBoard(lastReq, board);\r
3922     lastReqValid = 1;\r
3923   }\r
3924 \r
3925   if (doingSizing) {\r
3926     return;\r
3927   }\r
3928 \r
3929   if (IsIconic(hwndMain)) {\r
3930     return;\r
3931   }\r
3932 \r
3933   if (hdc == NULL) {\r
3934     hdc = GetDC(hwndMain);\r
3935     if (!appData.monoMode) {\r
3936       SelectPalette(hdc, hPal, FALSE);\r
3937       RealizePalette(hdc);\r
3938     }\r
3939     releaseDC = TRUE;\r
3940   } else {\r
3941     releaseDC = FALSE;\r
3942   }\r
3943 \r
3944 #if 0\r
3945   fprintf(debugFP, "*******************************\n"\r
3946                    "repaint = %s\n"\r
3947                    "dragInfo.from (%d,%d)\n"\r
3948                    "dragInfo.start (%d,%d)\n"\r
3949                    "dragInfo.pos (%d,%d)\n"\r
3950                    "dragInfo.lastpos (%d,%d)\n", \r
3951                     repaint ? "TRUE" : "FALSE",\r
3952                     dragInfo.from.x, dragInfo.from.y, \r
3953                     dragInfo.start.x, dragInfo.start.y,\r
3954                     dragInfo.pos.x, dragInfo.pos.y,\r
3955                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
3956   fprintf(debugFP, "prev:  ");\r
3957   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3958     for (column = 0; column < BOARD_WIDTH; column++) {\r
3959       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
3960     }\r
3961   }\r
3962   fprintf(debugFP, "\n");\r
3963   fprintf(debugFP, "board: ");\r
3964   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3965     for (column = 0; column < BOARD_WIDTH; column++) {\r
3966       fprintf(debugFP, "%d ", board[row][column]);\r
3967     }\r
3968   }\r
3969   fprintf(debugFP, "\n");\r
3970   fflush(debugFP);\r
3971 #endif\r
3972 \r
3973   /* Create some work-DCs */\r
3974   hdcmem = CreateCompatibleDC(hdc);\r
3975   tmphdc = CreateCompatibleDC(hdc);\r
3976 \r
3977   /* If dragging is in progress, we temporarely remove the piece */\r
3978   /* [HGM] or temporarily decrease count if stacked              */\r
3979   /*       !! Moved to before board compare !!                   */\r
3980   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3981     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3982     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3983             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3984         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3985     } else \r
3986     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3987             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
3988         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3989     } else \r
3990         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3991   }\r
3992 \r
3993   /* Figure out which squares need updating by comparing the \r
3994    * newest board with the last drawn board and checking if\r
3995    * flipping has changed.\r
3996    */\r
3997   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3998     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3999       for (column = 0; column < BOARD_WIDTH; column++) {\r
4000         if (lastDrawn[row][column] != board[row][column]) {\r
4001           SquareToPos(row, column, &x, &y);\r
4002           clips[num_clips++] =\r
4003             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4004         }\r
4005       }\r
4006     }\r
4007     for (i=0; i<2; i++) {\r
4008       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4009           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4010         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4011             lastDrawnHighlight.sq[i].y >= 0) {\r
4012           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4013                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4014           clips[num_clips++] =\r
4015             CreateRectRgn(x - lineGap, y - lineGap, \r
4016                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4017         }\r
4018         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4019           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4020           clips[num_clips++] =\r
4021             CreateRectRgn(x - lineGap, y - lineGap, \r
4022                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4023         }\r
4024       }\r
4025     }\r
4026     for (i=0; i<2; i++) {\r
4027       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4028           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4029         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4030             lastDrawnPremove.sq[i].y >= 0) {\r
4031           SquareToPos(lastDrawnPremove.sq[i].y,\r
4032                       lastDrawnPremove.sq[i].x, &x, &y);\r
4033           clips[num_clips++] =\r
4034             CreateRectRgn(x - lineGap, y - lineGap, \r
4035                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4036         }\r
4037         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4038             premoveHighlightInfo.sq[i].y >= 0) {\r
4039           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4040                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4041           clips[num_clips++] =\r
4042             CreateRectRgn(x - lineGap, y - lineGap, \r
4043                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4044         }\r
4045       }\r
4046     }\r
4047   } else {\r
4048     fullrepaint = TRUE;\r
4049   }\r
4050 \r
4051   /* Create a buffer bitmap - this is the actual bitmap\r
4052    * being written to.  When all the work is done, we can\r
4053    * copy it to the real DC (the screen).  This avoids\r
4054    * the problems with flickering.\r
4055    */\r
4056   GetClientRect(hwndMain, &Rect);\r
4057   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4058                                         Rect.bottom-Rect.top+1);\r
4059   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4060   if (!appData.monoMode) {\r
4061     SelectPalette(hdcmem, hPal, FALSE);\r
4062   }\r
4063 \r
4064   /* Create clips for dragging */\r
4065   if (!fullrepaint) {\r
4066     if (dragInfo.from.x >= 0) {\r
4067       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4068       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4069     }\r
4070     if (dragInfo.start.x >= 0) {\r
4071       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4072       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4073     }\r
4074     if (dragInfo.pos.x >= 0) {\r
4075       x = dragInfo.pos.x - squareSize / 2;\r
4076       y = dragInfo.pos.y - squareSize / 2;\r
4077       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4078     }\r
4079     if (dragInfo.lastpos.x >= 0) {\r
4080       x = dragInfo.lastpos.x - squareSize / 2;\r
4081       y = dragInfo.lastpos.y - squareSize / 2;\r
4082       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4083     }\r
4084   }\r
4085 \r
4086   /* Are we animating a move?  \r
4087    * If so, \r
4088    *   - remove the piece from the board (temporarely)\r
4089    *   - calculate the clipping region\r
4090    */\r
4091   if (!fullrepaint) {\r
4092     if (animInfo.piece != EmptySquare) {\r
4093       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4094       x = boardRect.left + animInfo.lastpos.x;\r
4095       y = boardRect.top + animInfo.lastpos.y;\r
4096       x2 = boardRect.left + animInfo.pos.x;\r
4097       y2 = boardRect.top + animInfo.pos.y;\r
4098       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4099       /* Slight kludge.  The real problem is that after AnimateMove is\r
4100          done, the position on the screen does not match lastDrawn.\r
4101          This currently causes trouble only on e.p. captures in\r
4102          atomic, where the piece moves to an empty square and then\r
4103          explodes.  The old and new positions both had an empty square\r
4104          at the destination, but animation has drawn a piece there and\r
4105          we have to remember to erase it. */\r
4106       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4107     }\r
4108   }\r
4109 \r
4110   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4111   if (num_clips == 0)\r
4112     fullrepaint = TRUE;\r
4113 \r
4114   /* Set clipping on the memory DC */\r
4115   if (!fullrepaint) {\r
4116     SelectClipRgn(hdcmem, clips[0]);\r
4117     for (x = 1; x < num_clips; x++) {\r
4118       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4119         abort();  // this should never ever happen!\r
4120     }\r
4121   }\r
4122 \r
4123   /* Do all the drawing to the memory DC */\r
4124   DrawGridOnDC(hdcmem);\r
4125   DrawHighlightsOnDC(hdcmem);\r
4126   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4127 \r
4128   if( appData.highlightMoveWithArrow ) {\r
4129     DrawArrowHighlight(hdcmem);\r
4130   }\r
4131 \r
4132   DrawCoordsOnDC(hdcmem);\r
4133 \r
4134   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4135                  /* to make sure lastDrawn contains what is actually drawn */\r
4136 \r
4137   /* Put the dragged piece back into place and draw it (out of place!) */\r
4138     if (dragged_piece != EmptySquare) {\r
4139     /* [HGM] or restack */\r
4140     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4141                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4142     else\r
4143     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4144                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4145     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4146     x = dragInfo.pos.x - squareSize / 2;\r
4147     y = dragInfo.pos.y - squareSize / 2;\r
4148     DrawPieceOnDC(hdcmem, dragged_piece,\r
4149                   ((int) dragged_piece < (int) BlackPawn), \r
4150                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4151   }   \r
4152   \r
4153   /* Put the animated piece back into place and draw it */\r
4154   if (animInfo.piece != EmptySquare) {\r
4155     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4156     x = boardRect.left + animInfo.pos.x;\r
4157     y = boardRect.top + animInfo.pos.y;\r
4158     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4159                   ((int) animInfo.piece < (int) BlackPawn),\r
4160                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4161   }\r
4162 \r
4163   /* Release the bufferBitmap by selecting in the old bitmap \r
4164    * and delete the memory DC\r
4165    */\r
4166   SelectObject(hdcmem, oldBitmap);\r
4167   DeleteDC(hdcmem);\r
4168 \r
4169   /* Set clipping on the target DC */\r
4170   if (!fullrepaint) {\r
4171     SelectClipRgn(hdc, clips[0]);\r
4172     for (x = 1; x < num_clips; x++) {\r
4173       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4174         abort();   // this should never ever happen!\r
4175     } \r
4176   }\r
4177 \r
4178   /* Copy the new bitmap onto the screen in one go.\r
4179    * This way we avoid any flickering\r
4180    */\r
4181   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4182   BitBlt(hdc, boardRect.left, boardRect.top,\r
4183          boardRect.right - boardRect.left,\r
4184          boardRect.bottom - boardRect.top,\r
4185          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4186   SelectObject(tmphdc, oldBitmap);\r
4187 \r
4188   /* Massive cleanup */\r
4189   for (x = 0; x < num_clips; x++)\r
4190     DeleteObject(clips[x]);\r
4191 \r
4192   DeleteDC(tmphdc);\r
4193   DeleteObject(bufferBitmap);\r
4194 \r
4195   if (releaseDC) \r
4196     ReleaseDC(hwndMain, hdc);\r
4197   \r
4198   if (lastDrawnFlipView != flipView) {\r
4199     if (flipView)\r
4200       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4201     else\r
4202       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4203   }\r
4204 \r
4205 /*  CopyBoard(lastDrawn, board);*/\r
4206   lastDrawnHighlight = highlightInfo;\r
4207   lastDrawnPremove   = premoveHighlightInfo;\r
4208   lastDrawnFlipView = flipView;\r
4209   lastDrawnValid = 1;\r
4210 }\r
4211 \r
4212 \r
4213 /*---------------------------------------------------------------------------*\\r
4214 | CLIENT PAINT PROCEDURE\r
4215 |   This is the main event-handler for the WM_PAINT message.\r
4216 |\r
4217 \*---------------------------------------------------------------------------*/\r
4218 VOID\r
4219 PaintProc(HWND hwnd)\r
4220 {\r
4221   HDC         hdc;\r
4222   PAINTSTRUCT ps;\r
4223   HFONT       oldFont;\r
4224 \r
4225   if(hdc = BeginPaint(hwnd, &ps)) {\r
4226     if (IsIconic(hwnd)) {\r
4227       DrawIcon(hdc, 2, 2, iconCurrent);\r
4228     } else {\r
4229       if (!appData.monoMode) {\r
4230         SelectPalette(hdc, hPal, FALSE);\r
4231         RealizePalette(hdc);\r
4232       }\r
4233       HDCDrawPosition(hdc, 1, NULL);\r
4234       oldFont =\r
4235         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4236       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4237                  ETO_CLIPPED|ETO_OPAQUE,\r
4238                  &messageRect, messageText, strlen(messageText), NULL);\r
4239       SelectObject(hdc, oldFont);\r
4240       DisplayBothClocks();\r
4241     }\r
4242     EndPaint(hwnd,&ps);\r
4243   }\r
4244 \r
4245   return;\r
4246 }\r
4247 \r
4248 \r
4249 /*\r
4250  * If the user selects on a border boundary, return -1; if off the board,\r
4251  *   return -2.  Otherwise map the event coordinate to the square.\r
4252  * The offset boardRect.left or boardRect.top must already have been\r
4253  *   subtracted from x.\r
4254  */\r
4255 int\r
4256 EventToSquare(int x)\r
4257 {\r
4258   if (x <= 0)\r
4259     return -2;\r
4260   if (x < lineGap)\r
4261     return -1;\r
4262   x -= lineGap;\r
4263   if ((x % (squareSize + lineGap)) >= squareSize)\r
4264     return -1;\r
4265   x /= (squareSize + lineGap);\r
4266   if (x >= BOARD_SIZE)\r
4267     return -2;\r
4268   return x;\r
4269 }\r
4270 \r
4271 typedef struct {\r
4272   char piece;\r
4273   int command;\r
4274   char* name;\r
4275 } DropEnable;\r
4276 \r
4277 DropEnable dropEnables[] = {\r
4278   { 'P', DP_Pawn, "Pawn" },\r
4279   { 'N', DP_Knight, "Knight" },\r
4280   { 'B', DP_Bishop, "Bishop" },\r
4281   { 'R', DP_Rook, "Rook" },\r
4282   { 'Q', DP_Queen, "Queen" },\r
4283 };\r
4284 \r
4285 VOID\r
4286 SetupDropMenu(HMENU hmenu)\r
4287 {\r
4288   int i, count, enable;\r
4289   char *p;\r
4290   extern char white_holding[], black_holding[];\r
4291   char item[MSG_SIZ];\r
4292 \r
4293   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4294     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4295                dropEnables[i].piece);\r
4296     count = 0;\r
4297     while (p && *p++ == dropEnables[i].piece) count++;\r
4298     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4299     enable = count > 0 || !appData.testLegality\r
4300       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4301                       && !appData.icsActive);\r
4302     ModifyMenu(hmenu, dropEnables[i].command,\r
4303                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4304                dropEnables[i].command, item);\r
4305   }\r
4306 }\r
4307 \r
4308 static int fromX = -1, fromY = -1, toX, toY;\r
4309 \r
4310 /* Event handler for mouse messages */\r
4311 VOID\r
4312 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4313 {\r
4314   int x, y;\r
4315   POINT pt;\r
4316   static int recursive = 0;\r
4317   HMENU hmenu;\r
4318   BOOLEAN needsRedraw = FALSE;\r
4319   BOOLEAN saveAnimate;\r
4320   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4321   static BOOLEAN sameAgain = FALSE;\r
4322   ChessMove moveType;\r
4323 \r
4324   if (recursive) {\r
4325     if (message == WM_MBUTTONUP) {\r
4326       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4327          to the middle button: we simulate pressing the left button too!\r
4328          */\r
4329       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4330       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4331     }\r
4332     return;\r
4333   }\r
4334   recursive++;\r
4335   \r
4336   pt.x = LOWORD(lParam);\r
4337   pt.y = HIWORD(lParam);\r
4338   x = EventToSquare(pt.x - boardRect.left);\r
4339   y = EventToSquare(pt.y - boardRect.top);\r
4340   if (!flipView && y >= 0) {\r
4341     y = BOARD_HEIGHT - 1 - y;\r
4342   }\r
4343   if (flipView && x >= 0) {\r
4344     x = BOARD_WIDTH - 1 - x;\r
4345   }\r
4346 \r
4347   switch (message) {\r
4348   case WM_LBUTTONDOWN:\r
4349     ErrorPopDown();\r
4350     sameAgain = FALSE;\r
4351     if (y == -2) {\r
4352       /* Downclick vertically off board; check if on clock */\r
4353       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4354         if (gameMode == EditPosition) {\r
4355           SetWhiteToPlayEvent();\r
4356         } else if (gameMode == IcsPlayingBlack ||\r
4357                    gameMode == MachinePlaysWhite) {\r
4358           CallFlagEvent();\r
4359         }\r
4360       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4361         if (gameMode == EditPosition) {\r
4362           SetBlackToPlayEvent();\r
4363         } else if (gameMode == IcsPlayingWhite ||\r
4364                    gameMode == MachinePlaysBlack) {\r
4365           CallFlagEvent();\r
4366         }\r
4367       }\r
4368       if (!appData.highlightLastMove) {\r
4369         ClearHighlights();\r
4370         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4371       }\r
4372       fromX = fromY = -1;\r
4373       dragInfo.start.x = dragInfo.start.y = -1;\r
4374       dragInfo.from = dragInfo.start;\r
4375       break;\r
4376     } else if (x < 0 || y < 0\r
4377       /* [HGM] block clicks between board and holdings */\r
4378               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4379               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4380               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4381                                ) {\r
4382       break;\r
4383     } else if (fromX == x && fromY == y) {\r
4384       /* Downclick on same square again */\r
4385       ClearHighlights();\r
4386       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4387       sameAgain = TRUE;  \r
4388     } else if (fromX != -1 &&\r
4389                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4390                                                                         ) {\r
4391       /* Downclick on different square. */\r
4392       /* [HGM] if on holdings file, should count as new first click ! */\r
4393        \r
4394       ChessSquare pdown, pup;\r
4395       pdown = boards[currentMove][fromY][fromX];\r
4396       pup = boards[currentMove][y][x];\r
4397       if (gameMode == EditPosition || /* [HGM] max piece > King! */\r
4398           !((WhitePawn <= pdown && pdown < BlackPawn &&\r
4399              WhitePawn <= pup && pup < BlackPawn) ||\r
4400             (BlackPawn <= pdown && pdown < EmptySquare &&\r
4401              BlackPawn <= pup && pup < EmptySquare))) {\r
4402         /* EditPosition, empty square, or different color piece;\r
4403            click-click move is possible */\r
4404         toX = x;\r
4405         toY = y;\r
4406         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4407         if(moveType != ImpossibleMove) {\r
4408           if (IsPromotion(fromX, fromY, toX, toY)) {\r
4409              /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4410                 to make sure move is legal before showing promotion popup */\r
4411              if (appData.alwaysPromoteToQueen) {\r
4412                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4413                   if (!appData.highlightLastMove) {\r
4414                       ClearHighlights();\r
4415                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4416                   }\r
4417              } else {\r
4418                   SetHighlights(fromX, fromY, toX, toY);\r
4419                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4420                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4421                      If promotion to Q is legal, all are legal! */\r
4422                   PromotionPopup(hwnd);\r
4423              }\r
4424           } else {       /* not a promotion */\r
4425              if (appData.animate || appData.highlightLastMove) {\r
4426                  SetHighlights(fromX, fromY, toX, toY);\r
4427              } else {\r
4428                  ClearHighlights();\r
4429              }\r
4430              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4431              if (appData.animate && !appData.highlightLastMove) {\r
4432                   ClearHighlights();\r
4433                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4434              }\r
4435           }\r
4436         }\r
4437         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4438         fromX = fromY = -1;\r
4439         break;\r
4440       }\r
4441       ClearHighlights();\r
4442       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4443     }\r
4444     /* First downclick, or restart on a square with same color piece */\r
4445     if (!frozen && OKToStartUserMove(x, y)) {\r
4446       fromX = x;\r
4447       fromY = y;\r
4448       dragInfo.lastpos = pt;\r
4449       dragInfo.from.x = fromX;\r
4450       dragInfo.from.y = fromY;\r
4451       dragInfo.start = dragInfo.from;\r
4452       SetCapture(hwndMain);\r
4453     } else {\r
4454       fromX = fromY = -1;\r
4455       dragInfo.start.x = dragInfo.start.y = -1;\r
4456       dragInfo.from = dragInfo.start;\r
4457       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4458     }\r
4459     break;\r
4460 \r
4461   case WM_LBUTTONUP:\r
4462     ReleaseCapture();\r
4463     if (fromX == -1) break;\r
4464     if (x == fromX && y == fromY) {\r
4465       /* Upclick on same square */\r
4466       if (sameAgain) {\r
4467         /* Clicked same square twice: abort click-click move */\r
4468         fromX = fromY = -1;\r
4469         gotPremove = 0;\r
4470         ClearPremoveHighlights();\r
4471       } else {\r
4472         /* First square clicked: start click-click move */\r
4473         SetHighlights(fromX, fromY, -1, -1);\r
4474       }\r
4475       dragInfo.from.x = dragInfo.from.y = -1;\r
4476       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4477     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4478       /* Errant click; ignore */\r
4479       break;\r
4480     } else {\r
4481       /* Finish drag move. */\r
4482     if (appData.debugMode) {\r
4483         fprintf(debugFP, "release\n");\r
4484     }\r
4485       dragInfo.from.x = dragInfo.from.y = -1;\r
4486       toX = x;\r
4487       toY = y;\r
4488       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4489       appData.animate = appData.animate && !appData.animateDragging;\r
4490       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4491       if(moveType != ImpossibleMove) {\r
4492         if (IsPromotion(fromX, fromY, toX, toY)) {\r
4493           if (appData.alwaysPromoteToQueen)\r
4494                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4495           else {\r
4496             DrawPosition(forceFullRepaint || FALSE, NULL);\r
4497             PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
4498           }\r
4499         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4500       }\r
4501       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4502       appData.animate = saveAnimate;\r
4503       fromX = fromY = -1;\r
4504       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4505         ClearHighlights();\r
4506       }\r
4507       if (appData.animate || appData.animateDragging ||\r
4508           appData.highlightDragging || gotPremove) {\r
4509         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4510       }\r
4511     }\r
4512     dragInfo.start.x = dragInfo.start.y = -1; \r
4513     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4514     break;\r
4515 \r
4516   case WM_MOUSEMOVE:\r
4517     if ((appData.animateDragging || appData.highlightDragging)\r
4518         && (wParam & MK_LBUTTON)\r
4519         && dragInfo.from.x >= 0) \r
4520     {\r
4521       BOOL full_repaint = FALSE;\r
4522 \r
4523       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
4524       if (appData.animateDragging) {\r
4525         dragInfo.pos = pt;\r
4526       }\r
4527       if (appData.highlightDragging) {\r
4528         SetHighlights(fromX, fromY, x, y);\r
4529         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4530             full_repaint = TRUE;\r
4531         }\r
4532       }\r
4533       \r
4534       DrawPosition( full_repaint, NULL);\r
4535       \r
4536       dragInfo.lastpos = dragInfo.pos;\r
4537     }\r
4538     break;\r
4539 \r
4540   case WM_MBUTTONDOWN:\r
4541   case WM_RBUTTONDOWN:\r
4542     ErrorPopDown();\r
4543     ReleaseCapture();\r
4544     fromX = fromY = -1;\r
4545     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4546     dragInfo.start.x = dragInfo.start.y = -1;\r
4547     dragInfo.from = dragInfo.start;\r
4548     dragInfo.lastpos = dragInfo.pos;\r
4549     if (appData.highlightDragging) {\r
4550       ClearHighlights();\r
4551     }\r
4552     DrawPosition(TRUE, NULL);\r
4553 \r
4554     switch (gameMode) {\r
4555     case EditPosition:\r
4556     case IcsExamining:\r
4557       if (x < 0 || y < 0) break;\r
4558       fromX = x;\r
4559       fromY = y;\r
4560       if (message == WM_MBUTTONDOWN) {\r
4561         buttonCount = 3;  /* even if system didn't think so */\r
4562         if (wParam & MK_SHIFT) \r
4563           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4564         else\r
4565           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4566       } else { /* message == WM_RBUTTONDOWN */\r
4567 #if 0\r
4568         if (buttonCount == 3) {\r
4569           if (wParam & MK_SHIFT) \r
4570             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4571           else\r
4572             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4573         } else {\r
4574           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4575         }\r
4576 #else\r
4577         /* Just have one menu, on the right button.  Windows users don't\r
4578            think to try the middle one, and sometimes other software steals\r
4579            it, or it doesn't really exist. */\r
4580         MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4581 #endif\r
4582       }\r
4583       break;\r
4584     case IcsPlayingWhite:\r
4585     case IcsPlayingBlack:\r
4586     case EditGame:\r
4587     case MachinePlaysWhite:\r
4588     case MachinePlaysBlack:\r
4589       if (appData.testLegality &&\r
4590           gameInfo.variant != VariantBughouse &&\r
4591           gameInfo.variant != VariantCrazyhouse) break;\r
4592       if (x < 0 || y < 0) break;\r
4593       fromX = x;\r
4594       fromY = y;\r
4595       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4596       SetupDropMenu(hmenu);\r
4597       MenuPopup(hwnd, pt, hmenu, -1);\r
4598       break;\r
4599     default:\r
4600       break;\r
4601     }\r
4602     break;\r
4603   }\r
4604 \r
4605   recursive--;\r
4606 }\r
4607 \r
4608 /* Preprocess messages for buttons in main window */\r
4609 LRESULT CALLBACK\r
4610 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4611 {\r
4612   int id = GetWindowLong(hwnd, GWL_ID);\r
4613   int i, dir;\r
4614 \r
4615   for (i=0; i<N_BUTTONS; i++) {\r
4616     if (buttonDesc[i].id == id) break;\r
4617   }\r
4618   if (i == N_BUTTONS) return 0;\r
4619   switch (message) {\r
4620   case WM_KEYDOWN:\r
4621     switch (wParam) {\r
4622     case VK_LEFT:\r
4623     case VK_RIGHT:\r
4624       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4625       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4626       return TRUE;\r
4627     }\r
4628     break;\r
4629   case WM_CHAR:\r
4630     switch (wParam) {\r
4631     case '\r':\r
4632       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4633       return TRUE;\r
4634     case '\t':\r
4635       if (appData.icsActive) {\r
4636         if (GetKeyState(VK_SHIFT) < 0) {\r
4637           /* shifted */\r
4638           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4639           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4640           SetFocus(h);\r
4641         } else {\r
4642           /* unshifted */\r
4643           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4644           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4645           SetFocus(h);\r
4646         }\r
4647         return TRUE;\r
4648       }\r
4649       break;\r
4650     default:\r
4651       if (appData.icsActive) {\r
4652         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4653         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4654         SetFocus(h);\r
4655         SendMessage(h, WM_CHAR, wParam, lParam);\r
4656         return TRUE;\r
4657       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4658         PopUpMoveDialog((char)wParam);\r
4659       }\r
4660       break;\r
4661     }\r
4662     break;\r
4663   }\r
4664   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4665 }\r
4666 \r
4667 /* Process messages for Promotion dialog box */\r
4668 LRESULT CALLBACK\r
4669 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4670 {\r
4671   char promoChar;\r
4672 \r
4673   switch (message) {\r
4674   case WM_INITDIALOG: /* message: initialize dialog box */\r
4675     /* Center the dialog over the application window */\r
4676     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4677     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4678       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4679        gameInfo.variant == VariantGiveaway) ?\r
4680                SW_SHOW : SW_HIDE);\r
4681 #ifdef FAIRY\r
4682     /* [HGM] Only allow C & A promotions in Capablanca Chess */\r
4683     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4684       (gameInfo.variant == VariantCapablanca || \r
4685        gameInfo.variant == VariantGothic) ?\r
4686                SW_SHOW : SW_HIDE);\r
4687     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4688       (gameInfo.variant == VariantCapablanca || \r
4689        gameInfo.variant == VariantGothic) ?\r
4690                SW_SHOW : SW_HIDE);\r
4691 #endif\r
4692     return TRUE;\r
4693 \r
4694   case WM_COMMAND: /* message: received a command */\r
4695     switch (LOWORD(wParam)) {\r
4696     case IDCANCEL:\r
4697       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4698       ClearHighlights();\r
4699       DrawPosition(FALSE, NULL);\r
4700       return TRUE;\r
4701     case PB_King:\r
4702       promoChar = 'k';\r
4703       break;\r
4704     case PB_Queen:\r
4705       promoChar = 'q';\r
4706       break;\r
4707     case PB_Rook:\r
4708       promoChar = 'r';\r
4709       break;\r
4710     case PB_Bishop:\r
4711       promoChar = 'b';\r
4712       break;\r
4713 #ifdef FAIRY\r
4714     case PB_Chancellor:\r
4715       promoChar = 'c';\r
4716       break;\r
4717     case PB_Archbishop:\r
4718       promoChar = 'a';\r
4719       break;\r
4720 #endif\r
4721     case PB_Knight:\r
4722       promoChar = 'n';\r
4723       break;\r
4724     default:\r
4725       return FALSE;\r
4726     }\r
4727     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4728     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4729        only show the popup when we are already sure the move is valid or\r
4730        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4731        will figure out it is a promotion from the promoChar. */\r
4732     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
4733     if (!appData.highlightLastMove) {\r
4734       ClearHighlights();\r
4735       DrawPosition(FALSE, NULL);\r
4736     }\r
4737     return TRUE;\r
4738   }\r
4739   return FALSE;\r
4740 }\r
4741 \r
4742 /* Pop up promotion dialog */\r
4743 VOID\r
4744 PromotionPopup(HWND hwnd)\r
4745 {\r
4746   FARPROC lpProc;\r
4747 \r
4748   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4749   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4750     hwnd, (DLGPROC)lpProc);\r
4751   FreeProcInstance(lpProc);\r
4752 }\r
4753 \r
4754 /* Toggle ShowThinking */\r
4755 VOID\r
4756 ToggleShowThinking()\r
4757 {\r
4758   ShowThinkingEvent(!appData.showThinking);\r
4759 }\r
4760 \r
4761 VOID\r
4762 LoadGameDialog(HWND hwnd, char* title)\r
4763 {\r
4764   UINT number = 0;\r
4765   FILE *f;\r
4766   char fileTitle[MSG_SIZ];\r
4767   f = OpenFileDialog(hwnd, FALSE, "",\r
4768                      appData.oldSaveStyle ? "gam" : "pgn",\r
4769                      GAME_FILT,\r
4770                      title, &number, fileTitle, NULL);\r
4771   if (f != NULL) {\r
4772     cmailMsgLoaded = FALSE;\r
4773     if (number == 0) {\r
4774       int error = GameListBuild(f);\r
4775       if (error) {\r
4776         DisplayError("Cannot build game list", error);\r
4777       } else if (!ListEmpty(&gameList) &&\r
4778                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4779         GameListPopUp(f, fileTitle);\r
4780         return;\r
4781       }\r
4782       GameListDestroy();\r
4783       number = 1;\r
4784     }\r
4785     LoadGame(f, number, fileTitle, FALSE);\r
4786   }\r
4787 }\r
4788 \r
4789 VOID\r
4790 ChangedConsoleFont()\r
4791 {\r
4792   CHARFORMAT cfmt;\r
4793   CHARRANGE tmpsel, sel;\r
4794   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4795   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4796   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4797   PARAFORMAT paraf;\r
4798 \r
4799   cfmt.cbSize = sizeof(CHARFORMAT);\r
4800   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4801   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4802   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4803    * size.  This was undocumented in the version of MSVC++ that I had\r
4804    * when I wrote the code, but is apparently documented now.\r
4805    */\r
4806   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4807   cfmt.bCharSet = f->lf.lfCharSet;\r
4808   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4809   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4810   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4811   /* Why are the following seemingly needed too? */\r
4812   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4813   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4814   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4815   tmpsel.cpMin = 0;\r
4816   tmpsel.cpMax = -1; /*999999?*/\r
4817   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4818   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4819   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4820    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4821    */\r
4822   paraf.cbSize = sizeof(paraf);\r
4823   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4824   paraf.dxStartIndent = 0;\r
4825   paraf.dxOffset = WRAP_INDENT;\r
4826   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4827   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4828 }\r
4829 \r
4830 /*---------------------------------------------------------------------------*\\r
4831  *\r
4832  * Window Proc for main window\r
4833  *\r
4834 \*---------------------------------------------------------------------------*/\r
4835 \r
4836 /* Process messages for main window, etc. */\r
4837 LRESULT CALLBACK\r
4838 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4839 {\r
4840   FARPROC lpProc;\r
4841   int wmId, wmEvent;\r
4842   char *defName;\r
4843   FILE *f;\r
4844   UINT number;\r
4845   char fileTitle[MSG_SIZ];\r
4846   static SnapData sd;\r
4847 \r
4848   switch (message) {\r
4849 \r
4850   case WM_PAINT: /* message: repaint portion of window */\r
4851     PaintProc(hwnd);\r
4852     break;\r
4853 \r
4854   case WM_ERASEBKGND:\r
4855     if (IsIconic(hwnd)) {\r
4856       /* Cheat; change the message */\r
4857       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4858     } else {\r
4859       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4860     }\r
4861     break;\r
4862 \r
4863   case WM_LBUTTONDOWN:\r
4864   case WM_MBUTTONDOWN:\r
4865   case WM_RBUTTONDOWN:\r
4866   case WM_LBUTTONUP:\r
4867   case WM_MBUTTONUP:\r
4868   case WM_RBUTTONUP:\r
4869   case WM_MOUSEMOVE:\r
4870     MouseEvent(hwnd, message, wParam, lParam);\r
4871     break;\r
4872 \r
4873   case WM_CHAR:\r
4874     \r
4875     if (appData.icsActive) {\r
4876       if (wParam == '\t') {\r
4877         if (GetKeyState(VK_SHIFT) < 0) {\r
4878           /* shifted */\r
4879           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4880           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4881           SetFocus(h);\r
4882         } else {\r
4883           /* unshifted */\r
4884           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4885           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4886           SetFocus(h);\r
4887         }\r
4888       } else {\r
4889         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4890         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4891         SetFocus(h);\r
4892         SendMessage(h, message, wParam, lParam);\r
4893       }\r
4894     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4895       PopUpMoveDialog((char)wParam);\r
4896     }\r
4897     break;\r
4898 \r
4899   case WM_PALETTECHANGED:\r
4900     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4901       int nnew;\r
4902       HDC hdc = GetDC(hwndMain);\r
4903       SelectPalette(hdc, hPal, TRUE);\r
4904       nnew = RealizePalette(hdc);\r
4905       if (nnew > 0) {\r
4906         paletteChanged = TRUE;\r
4907 #if 0\r
4908         UpdateColors(hdc);\r
4909 #else\r
4910         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
4911 #endif\r
4912       }\r
4913       ReleaseDC(hwnd, hdc);\r
4914     }\r
4915     break;\r
4916 \r
4917   case WM_QUERYNEWPALETTE:\r
4918     if (!appData.monoMode /*&& paletteChanged*/) {\r
4919       int nnew;\r
4920       HDC hdc = GetDC(hwndMain);\r
4921       paletteChanged = FALSE;\r
4922       SelectPalette(hdc, hPal, FALSE);\r
4923       nnew = RealizePalette(hdc);\r
4924       if (nnew > 0) {\r
4925         InvalidateRect(hwnd, &boardRect, FALSE);\r
4926       }\r
4927       ReleaseDC(hwnd, hdc);\r
4928       return TRUE;\r
4929     }\r
4930     return FALSE;\r
4931 \r
4932   case WM_COMMAND: /* message: command from application menu */\r
4933     wmId    = LOWORD(wParam);\r
4934     wmEvent = HIWORD(wParam);\r
4935 \r
4936     switch (wmId) {\r
4937     case IDM_NewGame:\r
4938       ResetGameEvent();\r
4939       AnalysisPopDown();\r
4940       break;\r
4941 \r
4942     case IDM_NewGameFRC:\r
4943       if( NewGameFRC() == 0 ) {\r
4944         ResetGameEvent();\r
4945         AnalysisPopDown();\r
4946       }\r
4947       break;\r
4948 \r
4949     case IDM_NewVariant:\r
4950       NewVariantPopup(hwnd);\r
4951       break;\r
4952 \r
4953     case IDM_LoadGame:\r
4954       LoadGameDialog(hwnd, "Load Game from File");\r
4955       break;\r
4956 \r
4957     case IDM_LoadNextGame:\r
4958       ReloadGame(1);\r
4959       break;\r
4960 \r
4961     case IDM_LoadPrevGame:\r
4962       ReloadGame(-1);\r
4963       break;\r
4964 \r
4965     case IDM_ReloadGame:\r
4966       ReloadGame(0);\r
4967       break;\r
4968 \r
4969     case IDM_LoadPosition:\r
4970       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4971         Reset(FALSE, TRUE);\r
4972       }\r
4973       number = 1;\r
4974       f = OpenFileDialog(hwnd, FALSE, "",\r
4975                          appData.oldSaveStyle ? "pos" : "fen",\r
4976                          POSITION_FILT,\r
4977                          "Load Position from File", &number, fileTitle, NULL);\r
4978       if (f != NULL) {\r
4979         LoadPosition(f, number, fileTitle);\r
4980       }\r
4981       break;\r
4982 \r
4983     case IDM_LoadNextPosition:\r
4984       ReloadPosition(1);\r
4985       break;\r
4986 \r
4987     case IDM_LoadPrevPosition:\r
4988       ReloadPosition(-1);\r
4989       break;\r
4990 \r
4991     case IDM_ReloadPosition:\r
4992       ReloadPosition(0);\r
4993       break;\r
4994 \r
4995     case IDM_SaveGame:\r
4996       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4997       f = OpenFileDialog(hwnd, TRUE, defName,\r
4998                          appData.oldSaveStyle ? "gam" : "pgn",\r
4999                          GAME_FILT,\r
5000                          "Save Game to File", NULL, fileTitle, NULL);\r
5001       if (f != NULL) {\r
5002         SaveGame(f, 0, "");\r
5003       }\r
5004       break;\r
5005 \r
5006     case IDM_SavePosition:\r
5007       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5008       f = OpenFileDialog(hwnd, TRUE, defName,\r
5009                          appData.oldSaveStyle ? "pos" : "fen",\r
5010                          POSITION_FILT,\r
5011                          "Save Position to File", NULL, fileTitle, NULL);\r
5012       if (f != NULL) {\r
5013         SavePosition(f, 0, "");\r
5014       }\r
5015       break;\r
5016 \r
5017     case IDM_CopyGame:\r
5018       CopyGameToClipboard();\r
5019       break;\r
5020 \r
5021     case IDM_PasteGame:\r
5022       PasteGameFromClipboard();\r
5023       break;\r
5024 \r
5025     case IDM_CopyGameListToClipboard:\r
5026       CopyGameListToClipboard();\r
5027       break;\r
5028 \r
5029     /* [AS] Autodetect FEN or PGN data */\r
5030     case IDM_PasteAny:\r
5031       PasteGameOrFENFromClipboard();\r
5032       break;\r
5033 \r
5034     /* [AS] Move history */\r
5035     case IDM_ShowMoveHistory:\r
5036         if( MoveHistoryIsUp() ) {\r
5037             MoveHistoryPopDown();\r
5038         }\r
5039         else {\r
5040             MoveHistoryPopUp();\r
5041         }\r
5042         break;\r
5043 \r
5044     /* [AS] Eval graph */\r
5045     case IDM_ShowEvalGraph:\r
5046         if( EvalGraphIsUp() ) {\r
5047             EvalGraphPopDown();\r
5048         }\r
5049         else {\r
5050             EvalGraphPopUp();\r
5051         }\r
5052         break;\r
5053 \r
5054     /* [AS] Engine output */\r
5055     case IDM_ShowEngineOutput:\r
5056         if( EngineOutputIsUp() ) {\r
5057             EngineOutputPopDown();\r
5058         }\r
5059         else {\r
5060             EngineOutputPopUp();\r
5061         }\r
5062         break;\r
5063 \r
5064     /* [AS] User adjudication */\r
5065     case IDM_UserAdjudication_White:\r
5066         UserAdjudicationEvent( +1 );\r
5067         break;\r
5068 \r
5069     case IDM_UserAdjudication_Black:\r
5070         UserAdjudicationEvent( -1 );\r
5071         break;\r
5072 \r
5073     case IDM_UserAdjudication_Draw:\r
5074         UserAdjudicationEvent( 0 );\r
5075         break;\r
5076 \r
5077     /* [AS] Game list options dialog */\r
5078     case IDM_GameListOptions:\r
5079       GameListOptions();\r
5080       break;\r
5081 \r
5082     case IDM_CopyPosition:\r
5083       CopyFENToClipboard();\r
5084       break;\r
5085 \r
5086     case IDM_PastePosition:\r
5087       PasteFENFromClipboard();\r
5088       break;\r
5089 \r
5090     case IDM_MailMove:\r
5091       MailMoveEvent();\r
5092       break;\r
5093 \r
5094     case IDM_ReloadCMailMsg:\r
5095       Reset(TRUE, TRUE);\r
5096       ReloadCmailMsgEvent(FALSE);\r
5097       break;\r
5098 \r
5099     case IDM_Minimize:\r
5100       ShowWindow(hwnd, SW_MINIMIZE);\r
5101       break;\r
5102 \r
5103     case IDM_Exit:\r
5104       ExitEvent(0);\r
5105       break;\r
5106 \r
5107     case IDM_MachineWhite:\r
5108       MachineWhiteEvent();\r
5109       /*\r
5110        * refresh the tags dialog only if it's visible\r
5111        */\r
5112       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5113           char *tags;\r
5114           tags = PGNTags(&gameInfo);\r
5115           TagsPopUp(tags, CmailMsg());\r
5116           free(tags);\r
5117       }\r
5118       break;\r
5119 \r
5120     case IDM_MachineBlack:\r
5121       MachineBlackEvent();\r
5122       /*\r
5123        * refresh the tags dialog only if it's visible\r
5124        */\r
5125       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5126           char *tags;\r
5127           tags = PGNTags(&gameInfo);\r
5128           TagsPopUp(tags, CmailMsg());\r
5129           free(tags);\r
5130       }\r
5131       break;\r
5132 \r
5133     case IDM_TwoMachines:\r
5134       TwoMachinesEvent();\r
5135       /*\r
5136        * refresh the tags dialog only if it's visible\r
5137        */\r
5138       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5139           char *tags;\r
5140           tags = PGNTags(&gameInfo);\r
5141           TagsPopUp(tags, CmailMsg());\r
5142           free(tags);\r
5143       }\r
5144       break;\r
5145 \r
5146     case IDM_AnalysisMode:\r
5147       if (!first.analysisSupport) {\r
5148         char buf[MSG_SIZ];\r
5149         sprintf(buf, "%s does not support analysis", first.tidy);\r
5150         DisplayError(buf, 0);\r
5151       } else {\r
5152         if (!appData.showThinking) ToggleShowThinking();\r
5153         AnalyzeModeEvent();\r
5154       }\r
5155       break;\r
5156 \r
5157     case IDM_AnalyzeFile:\r
5158       if (!first.analysisSupport) {\r
5159         char buf[MSG_SIZ];\r
5160         sprintf(buf, "%s does not support analysis", first.tidy);\r
5161         DisplayError(buf, 0);\r
5162       } else {\r
5163         if (!appData.showThinking) ToggleShowThinking();\r
5164         AnalyzeFileEvent();\r
5165         LoadGameDialog(hwnd, "Analyze Game from File");\r
5166         AnalysisPeriodicEvent(1);\r
5167       }\r
5168       break;\r
5169 \r
5170     case IDM_IcsClient:\r
5171       IcsClientEvent();\r
5172       break;\r
5173 \r
5174     case IDM_EditGame:\r
5175       EditGameEvent();\r
5176       break;\r
5177 \r
5178     case IDM_EditPosition:\r
5179       EditPositionEvent();\r
5180       break;\r
5181 \r
5182     case IDM_Training:\r
5183       TrainingEvent();\r
5184       break;\r
5185 \r
5186     case IDM_ShowGameList:\r
5187       ShowGameListProc();\r
5188       break;\r
5189 \r
5190     case IDM_EditTags:\r
5191       EditTagsProc();\r
5192       break;\r
5193 \r
5194     case IDM_EditComment:\r
5195       if (commentDialogUp && editComment) {\r
5196         CommentPopDown();\r
5197       } else {\r
5198         EditCommentEvent();\r
5199       }\r
5200       break;\r
5201 \r
5202     case IDM_Pause:\r
5203       PauseEvent();\r
5204       break;\r
5205 \r
5206     case IDM_Accept:\r
5207       AcceptEvent();\r
5208       break;\r
5209 \r
5210     case IDM_Decline:\r
5211       DeclineEvent();\r
5212       break;\r
5213 \r
5214     case IDM_Rematch:\r
5215       RematchEvent();\r
5216       break;\r
5217 \r
5218     case IDM_CallFlag:\r
5219       CallFlagEvent();\r
5220       break;\r
5221 \r
5222     case IDM_Draw:\r
5223       DrawEvent();\r
5224       break;\r
5225 \r
5226     case IDM_Adjourn:\r
5227       AdjournEvent();\r
5228       break;\r
5229 \r
5230     case IDM_Abort:\r
5231       AbortEvent();\r
5232       break;\r
5233 \r
5234     case IDM_Resign:\r
5235       ResignEvent();\r
5236       break;\r
5237 \r
5238     case IDM_StopObserving:\r
5239       StopObservingEvent();\r
5240       break;\r
5241 \r
5242     case IDM_StopExamining:\r
5243       StopExaminingEvent();\r
5244       break;\r
5245 \r
5246     case IDM_TypeInMove:\r
5247       PopUpMoveDialog('\000');\r
5248       break;\r
5249 \r
5250     case IDM_Backward:\r
5251       BackwardEvent();\r
5252       SetFocus(hwndMain);\r
5253       break;\r
5254 \r
5255     case IDM_Forward:\r
5256       ForwardEvent();\r
5257       SetFocus(hwndMain);\r
5258       break;\r
5259 \r
5260     case IDM_ToStart:\r
5261       ToStartEvent();\r
5262       SetFocus(hwndMain);\r
5263       break;\r
5264 \r
5265     case IDM_ToEnd:\r
5266       ToEndEvent();\r
5267       SetFocus(hwndMain);\r
5268       break;\r
5269 \r
5270     case IDM_Revert:\r
5271       RevertEvent();\r
5272       break;\r
5273 \r
5274     case IDM_TruncateGame:\r
5275       TruncateGameEvent();\r
5276       break;\r
5277 \r
5278     case IDM_MoveNow:\r
5279       MoveNowEvent();\r
5280       break;\r
5281 \r
5282     case IDM_RetractMove:\r
5283       RetractMoveEvent();\r
5284       break;\r
5285 \r
5286     case IDM_FlipView:\r
5287       flipView = !flipView;\r
5288       DrawPosition(FALSE, NULL);\r
5289       break;\r
5290 \r
5291     case IDM_GeneralOptions:\r
5292       GeneralOptionsPopup(hwnd);\r
5293       DrawPosition(TRUE, NULL);\r
5294       break;\r
5295 \r
5296     case IDM_BoardOptions:\r
5297       BoardOptionsPopup(hwnd);\r
5298       break;\r
5299 \r
5300     case IDM_EnginePlayOptions:\r
5301       EnginePlayOptionsPopup(hwnd);\r
5302       break;\r
5303 \r
5304     case IDM_OptionsUCI:\r
5305       UciOptionsPopup(hwnd);\r
5306       break;\r
5307 \r
5308     case IDM_IcsOptions:\r
5309       IcsOptionsPopup(hwnd);\r
5310       break;\r
5311 \r
5312     case IDM_Fonts:\r
5313       FontsOptionsPopup(hwnd);\r
5314       break;\r
5315 \r
5316     case IDM_Sounds:\r
5317       SoundOptionsPopup(hwnd);\r
5318       break;\r
5319 \r
5320     case IDM_CommPort:\r
5321       CommPortOptionsPopup(hwnd);\r
5322       break;\r
5323 \r
5324     case IDM_LoadOptions:\r
5325       LoadOptionsPopup(hwnd);\r
5326       break;\r
5327 \r
5328     case IDM_SaveOptions:\r
5329       SaveOptionsPopup(hwnd);\r
5330       break;\r
5331 \r
5332     case IDM_TimeControl:\r
5333       TimeControlOptionsPopup(hwnd);\r
5334       break;\r
5335 \r
5336     case IDM_SaveSettings:\r
5337       SaveSettings(settingsFileName);\r
5338       break;\r
5339 \r
5340     case IDM_SaveSettingsOnExit:\r
5341       saveSettingsOnExit = !saveSettingsOnExit;\r
5342       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5343                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5344                                          MF_CHECKED : MF_UNCHECKED));\r
5345       break;\r
5346 \r
5347     case IDM_Hint:\r
5348       HintEvent();\r
5349       break;\r
5350 \r
5351     case IDM_Book:\r
5352       BookEvent();\r
5353       break;\r
5354 \r
5355     case IDM_AboutGame:\r
5356       AboutGameEvent();\r
5357       break;\r
5358 \r
5359     case IDM_Debug:\r
5360       appData.debugMode = !appData.debugMode;\r
5361       if (appData.debugMode) {\r
5362         char dir[MSG_SIZ];\r
5363         GetCurrentDirectory(MSG_SIZ, dir);\r
5364         SetCurrentDirectory(installDir);\r
5365         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5366         SetCurrentDirectory(dir);\r
5367         setbuf(debugFP, NULL);\r
5368       } else {\r
5369         fclose(debugFP);\r
5370         debugFP = NULL;\r
5371       }\r
5372       break;\r
5373 \r
5374     case IDM_HELPCONTENTS:\r
5375       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5376         MessageBox (GetFocus(),\r
5377                     "Unable to activate help",\r
5378                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5379       }\r
5380       break;\r
5381 \r
5382     case IDM_HELPSEARCH:\r
5383       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5384         MessageBox (GetFocus(),\r
5385                     "Unable to activate help",\r
5386                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5387       }\r
5388       break;\r
5389 \r
5390     case IDM_HELPHELP:\r
5391       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5392         MessageBox (GetFocus(),\r
5393                     "Unable to activate help",\r
5394                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5395       }\r
5396       break;\r
5397 \r
5398     case IDM_ABOUT:\r
5399       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5400       DialogBox(hInst, \r
5401         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5402         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5403       FreeProcInstance(lpProc);\r
5404       break;\r
5405 \r
5406     case IDM_DirectCommand1:\r
5407       AskQuestionEvent("Direct Command",\r
5408                        "Send to chess program:", "", "1");\r
5409       break;\r
5410     case IDM_DirectCommand2:\r
5411       AskQuestionEvent("Direct Command",\r
5412                        "Send to second chess program:", "", "2");\r
5413       break;\r
5414 \r
5415     case EP_WhitePawn:\r
5416       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5417       fromX = fromY = -1;\r
5418       break;\r
5419 \r
5420     case EP_WhiteKnight:\r
5421       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5422       fromX = fromY = -1;\r
5423       break;\r
5424 \r
5425     case EP_WhiteBishop:\r
5426       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5427       fromX = fromY = -1;\r
5428       break;\r
5429 \r
5430     case EP_WhiteRook:\r
5431       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5432       fromX = fromY = -1;\r
5433       break;\r
5434 \r
5435     case EP_WhiteQueen:\r
5436       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5437       fromX = fromY = -1;\r
5438       break;\r
5439 \r
5440     case EP_WhiteKing:\r
5441       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5442       fromX = fromY = -1;\r
5443       break;\r
5444 \r
5445     case EP_BlackPawn:\r
5446       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5447       fromX = fromY = -1;\r
5448       break;\r
5449 \r
5450     case EP_BlackKnight:\r
5451       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5452       fromX = fromY = -1;\r
5453       break;\r
5454 \r
5455     case EP_BlackBishop:\r
5456       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5457       fromX = fromY = -1;\r
5458       break;\r
5459 \r
5460     case EP_BlackRook:\r
5461       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5462       fromX = fromY = -1;\r
5463       break;\r
5464 \r
5465     case EP_BlackQueen:\r
5466       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5467       fromX = fromY = -1;\r
5468       break;\r
5469 \r
5470     case EP_BlackKing:\r
5471       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5472       fromX = fromY = -1;\r
5473       break;\r
5474 \r
5475     case EP_EmptySquare:\r
5476       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5477       fromX = fromY = -1;\r
5478       break;\r
5479 \r
5480     case EP_ClearBoard:\r
5481       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5482       fromX = fromY = -1;\r
5483       break;\r
5484 \r
5485     case EP_White:\r
5486       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5487       fromX = fromY = -1;\r
5488       break;\r
5489 \r
5490     case EP_Black:\r
5491       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5492       fromX = fromY = -1;\r
5493       break;\r
5494 \r
5495     case DP_Pawn:\r
5496       DropMenuEvent(WhitePawn, fromX, fromY);\r
5497       fromX = fromY = -1;\r
5498       break;\r
5499 \r
5500     case DP_Knight:\r
5501       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5502       fromX = fromY = -1;\r
5503       break;\r
5504 \r
5505     case DP_Bishop:\r
5506       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5507       fromX = fromY = -1;\r
5508       break;\r
5509 \r
5510     case DP_Rook:\r
5511       DropMenuEvent(WhiteRook, fromX, fromY);\r
5512       fromX = fromY = -1;\r
5513       break;\r
5514 \r
5515     case DP_Queen:\r
5516       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5517       fromX = fromY = -1;\r
5518       break;\r
5519 \r
5520     default:\r
5521       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5522     }\r
5523     break;\r
5524 \r
5525   case WM_TIMER:\r
5526     switch (wParam) {\r
5527     case CLOCK_TIMER_ID:\r
5528       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5529       clockTimerEvent = 0;\r
5530       DecrementClocks(); /* call into back end */\r
5531       break;\r
5532     case LOAD_GAME_TIMER_ID:\r
5533       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5534       loadGameTimerEvent = 0;\r
5535       AutoPlayGameLoop(); /* call into back end */\r
5536       break;\r
5537     case ANALYSIS_TIMER_ID:\r
5538       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5539           appData.periodicUpdates) {\r
5540         AnalysisPeriodicEvent(0);\r
5541       } else {\r
5542         KillTimer(hwnd, analysisTimerEvent);\r
5543         analysisTimerEvent = 0;\r
5544       }\r
5545       break;\r
5546     case DELAYED_TIMER_ID:\r
5547       KillTimer(hwnd, delayedTimerEvent);\r
5548       delayedTimerEvent = 0;\r
5549       delayedTimerCallback();\r
5550       break;\r
5551     }\r
5552     break;\r
5553 \r
5554   case WM_USER_Input:\r
5555     InputEvent(hwnd, message, wParam, lParam);\r
5556     break;\r
5557 \r
5558   /* [AS] Also move "attached" child windows */\r
5559   case WM_WINDOWPOSCHANGING:\r
5560     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5561         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5562 \r
5563         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5564             /* Window is moving */\r
5565             RECT rcMain;\r
5566 \r
5567             GetWindowRect( hwnd, &rcMain );\r
5568             \r
5569             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5570             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5571             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5572         }\r
5573     }\r
5574     break;\r
5575 \r
5576   /* [AS] Snapping */\r
5577   case WM_ENTERSIZEMOVE:\r
5578     if (hwnd == hwndMain) {\r
5579       doingSizing = TRUE;\r
5580       lastSizing = 0;\r
5581     }\r
5582     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5583     break;\r
5584 \r
5585   case WM_SIZING:\r
5586     if (hwnd == hwndMain) {\r
5587       lastSizing = wParam;\r
5588     }\r
5589     break;\r
5590 \r
5591   case WM_MOVING:\r
5592       return OnMoving( &sd, hwnd, wParam, lParam );\r
5593 \r
5594   case WM_EXITSIZEMOVE:\r
5595     if (hwnd == hwndMain) {\r
5596       RECT client;\r
5597       doingSizing = FALSE;\r
5598       InvalidateRect(hwnd, &boardRect, FALSE);\r
5599       GetClientRect(hwnd, &client);\r
5600       ResizeBoard(client.right, client.bottom, lastSizing);\r
5601       lastSizing = 0;\r
5602     }\r
5603     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5604     break;\r
5605 \r
5606   case WM_DESTROY: /* message: window being destroyed */\r
5607     PostQuitMessage(0);\r
5608     break;\r
5609 \r
5610   case WM_CLOSE:\r
5611     if (hwnd == hwndMain) {\r
5612       ExitEvent(0);\r
5613     }\r
5614     break;\r
5615 \r
5616   default:      /* Passes it on if unprocessed */\r
5617     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5618   }\r
5619   return 0;\r
5620 }\r
5621 \r
5622 /*---------------------------------------------------------------------------*\\r
5623  *\r
5624  * Misc utility routines\r
5625  *\r
5626 \*---------------------------------------------------------------------------*/\r
5627 \r
5628 /*\r
5629  * Decent random number generator, at least not as bad as Windows\r
5630  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5631  */\r
5632 unsigned int randstate;\r
5633 \r
5634 int\r
5635 myrandom(void)\r
5636 {\r
5637   randstate = randstate * 1664525 + 1013904223;\r
5638   return (int) randstate & 0x7fffffff;\r
5639 }\r
5640 \r
5641 void\r
5642 mysrandom(unsigned int seed)\r
5643 {\r
5644   randstate = seed;\r
5645 }\r
5646 \r
5647 \r
5648 /* \r
5649  * returns TRUE if user selects a different color, FALSE otherwise \r
5650  */\r
5651 \r
5652 BOOL\r
5653 ChangeColor(HWND hwnd, COLORREF *which)\r
5654 {\r
5655   static BOOL firstTime = TRUE;\r
5656   static DWORD customColors[16];\r
5657   CHOOSECOLOR cc;\r
5658   COLORREF newcolor;\r
5659   int i;\r
5660   ColorClass ccl;\r
5661 \r
5662   if (firstTime) {\r
5663     /* Make initial colors in use available as custom colors */\r
5664     /* Should we put the compiled-in defaults here instead? */\r
5665     i = 0;\r
5666     customColors[i++] = lightSquareColor & 0xffffff;\r
5667     customColors[i++] = darkSquareColor & 0xffffff;\r
5668     customColors[i++] = whitePieceColor & 0xffffff;\r
5669     customColors[i++] = blackPieceColor & 0xffffff;\r
5670     customColors[i++] = highlightSquareColor & 0xffffff;\r
5671     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5672 \r
5673     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5674       customColors[i++] = textAttribs[ccl].color;\r
5675     }\r
5676     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5677     firstTime = FALSE;\r
5678   }\r
5679 \r
5680   cc.lStructSize = sizeof(cc);\r
5681   cc.hwndOwner = hwnd;\r
5682   cc.hInstance = NULL;\r
5683   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5684   cc.lpCustColors = (LPDWORD) customColors;\r
5685   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5686 \r
5687   if (!ChooseColor(&cc)) return FALSE;\r
5688 \r
5689   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5690   if (newcolor == *which) return FALSE;\r
5691   *which = newcolor;\r
5692   return TRUE;\r
5693 \r
5694   /*\r
5695   InitDrawingColors();\r
5696   InvalidateRect(hwnd, &boardRect, FALSE);\r
5697   */\r
5698 }\r
5699 \r
5700 BOOLEAN\r
5701 MyLoadSound(MySound *ms)\r
5702 {\r
5703   BOOL ok = FALSE;\r
5704   struct stat st;\r
5705   FILE *f;\r
5706 \r
5707   if (ms->data) free(ms->data);\r
5708   ms->data = NULL;\r
5709 \r
5710   switch (ms->name[0]) {\r
5711   case NULLCHAR:\r
5712     /* Silence */\r
5713     ok = TRUE;\r
5714     break;\r
5715   case '$':\r
5716     /* System sound from Control Panel.  Don't preload here. */\r
5717     ok = TRUE;\r
5718     break;\r
5719   case '!':\r
5720     if (ms->name[1] == NULLCHAR) {\r
5721       /* "!" alone = silence */\r
5722       ok = TRUE;\r
5723     } else {\r
5724       /* Builtin wave resource.  Error if not found. */\r
5725       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5726       if (h == NULL) break;\r
5727       ms->data = (void *)LoadResource(hInst, h);\r
5728       if (h == NULL) break;\r
5729       ok = TRUE;\r
5730     }\r
5731     break;\r
5732   default:\r
5733     /* .wav file.  Error if not found. */\r
5734     f = fopen(ms->name, "rb");\r
5735     if (f == NULL) break;\r
5736     if (fstat(fileno(f), &st) < 0) break;\r
5737     ms->data = malloc(st.st_size);\r
5738     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5739     fclose(f);\r
5740     ok = TRUE;\r
5741     break;\r
5742   }\r
5743   if (!ok) {\r
5744     char buf[MSG_SIZ];\r
5745     sprintf(buf, "Error loading sound %s", ms->name);\r
5746     DisplayError(buf, GetLastError());\r
5747   }\r
5748   return ok;\r
5749 }\r
5750 \r
5751 BOOLEAN\r
5752 MyPlaySound(MySound *ms)\r
5753 {\r
5754   BOOLEAN ok = FALSE;\r
5755   switch (ms->name[0]) {\r
5756   case NULLCHAR:\r
5757     /* Silence */\r
5758     ok = TRUE;\r
5759     break;\r
5760   case '$':\r
5761     /* System sound from Control Panel (deprecated feature).\r
5762        "$" alone or an unset sound name gets default beep (still in use). */\r
5763     if (ms->name[1]) {\r
5764       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5765     }\r
5766     if (!ok) ok = MessageBeep(MB_OK);\r
5767     break; \r
5768   case '!':\r
5769     /* Builtin wave resource, or "!" alone for silence */\r
5770     if (ms->name[1]) {\r
5771       if (ms->data == NULL) return FALSE;\r
5772       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5773     } else {\r
5774       ok = TRUE;\r
5775     }\r
5776     break;\r
5777   default:\r
5778     /* .wav file.  Error if not found. */\r
5779     if (ms->data == NULL) return FALSE;\r
5780     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5781     break;\r
5782   }\r
5783   /* Don't print an error: this can happen innocently if the sound driver\r
5784      is busy; for instance, if another instance of WinBoard is playing\r
5785      a sound at about the same time. */\r
5786 #if 0\r
5787   if (!ok) {\r
5788     char buf[MSG_SIZ];\r
5789     sprintf(buf, "Error playing sound %s", ms->name);\r
5790     DisplayError(buf, GetLastError());\r
5791   }\r
5792 #endif\r
5793   return ok;\r
5794 }\r
5795 \r
5796 \r
5797 LRESULT CALLBACK\r
5798 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5799 {\r
5800   BOOL ok;\r
5801   OPENFILENAME *ofn;\r
5802   static UINT *number; /* gross that this is static */\r
5803 \r
5804   switch (message) {\r
5805   case WM_INITDIALOG: /* message: initialize dialog box */\r
5806     /* Center the dialog over the application window */\r
5807     ofn = (OPENFILENAME *) lParam;\r
5808     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5809       number = (UINT *) ofn->lCustData;\r
5810       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5811     } else {\r
5812       number = NULL;\r
5813     }\r
5814     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5815     return FALSE;  /* Allow for further processing */\r
5816 \r
5817   case WM_COMMAND:\r
5818     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5819       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5820     }\r
5821     return FALSE;  /* Allow for further processing */\r
5822   }\r
5823   return FALSE;\r
5824 }\r
5825 \r
5826 UINT APIENTRY\r
5827 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5828 {\r
5829   static UINT *number;\r
5830   OPENFILENAME *ofname;\r
5831   OFNOTIFY *ofnot;\r
5832   switch (uiMsg) {\r
5833   case WM_INITDIALOG:\r
5834     ofname = (OPENFILENAME *)lParam;\r
5835     number = (UINT *)(ofname->lCustData);\r
5836     break;\r
5837   case WM_NOTIFY:\r
5838     ofnot = (OFNOTIFY *)lParam;\r
5839     if (ofnot->hdr.code == CDN_FILEOK) {\r
5840       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5841     }\r
5842     break;\r
5843   }\r
5844   return 0;\r
5845 }\r
5846 \r
5847 \r
5848 FILE *\r
5849 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
5850                char *nameFilt, char *dlgTitle, UINT *number,\r
5851                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5852 {\r
5853   OPENFILENAME openFileName;\r
5854   char buf1[MSG_SIZ];\r
5855   FILE *f;\r
5856 \r
5857   if (fileName == NULL) fileName = buf1;\r
5858   if (defName == NULL) {\r
5859     strcpy(fileName, "*.");\r
5860     strcat(fileName, defExt);\r
5861   } else {\r
5862     strcpy(fileName, defName);\r
5863   }\r
5864   if (fileTitle) strcpy(fileTitle, "");\r
5865   if (number) *number = 0;\r
5866 \r
5867   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5868   openFileName.hwndOwner         = hwnd;\r
5869   openFileName.hInstance         = (HANDLE) hInst;\r
5870   openFileName.lpstrFilter       = nameFilt;\r
5871   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5872   openFileName.nMaxCustFilter    = 0L;\r
5873   openFileName.nFilterIndex      = 1L;\r
5874   openFileName.lpstrFile         = fileName;\r
5875   openFileName.nMaxFile          = MSG_SIZ;\r
5876   openFileName.lpstrFileTitle    = fileTitle;\r
5877   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5878   openFileName.lpstrInitialDir   = NULL;\r
5879   openFileName.lpstrTitle        = dlgTitle;\r
5880   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5881     | (write ? 0 : OFN_FILEMUSTEXIST) \r
5882     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5883     | (oldDialog ? 0 : OFN_EXPLORER);\r
5884   openFileName.nFileOffset       = 0;\r
5885   openFileName.nFileExtension    = 0;\r
5886   openFileName.lpstrDefExt       = defExt;\r
5887   openFileName.lCustData         = (LONG) number;\r
5888   openFileName.lpfnHook          = oldDialog ?\r
5889     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5890   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5891 \r
5892   if (write ? GetSaveFileName(&openFileName) : \r
5893               GetOpenFileName(&openFileName)) {\r
5894     /* open the file */\r
5895     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");\r
5896     if (f == NULL) {\r
5897       MessageBox(hwnd, "File open failed", NULL,\r
5898                  MB_OK|MB_ICONEXCLAMATION);\r
5899       return NULL;\r
5900     }\r
5901   } else {\r
5902     int err = CommDlgExtendedError();\r
5903     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5904     return FALSE;\r
5905   }\r
5906   return f;\r
5907 }\r
5908 \r
5909 \r
5910 \r
5911 VOID APIENTRY\r
5912 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5913 {\r
5914   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5915 \r
5916   /*\r
5917    * Get the first pop-up menu in the menu template. This is the\r
5918    * menu that TrackPopupMenu displays.\r
5919    */\r
5920   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5921 \r
5922   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5923 \r
5924   /*\r
5925    * TrackPopup uses screen coordinates, so convert the\r
5926    * coordinates of the mouse click to screen coordinates.\r
5927    */\r
5928   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5929 \r
5930   /* Draw and track the floating pop-up menu. */\r
5931   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5932                  pt.x, pt.y, 0, hwnd, NULL);\r
5933 \r
5934   /* Destroy the menu.*/\r
5935   DestroyMenu(hmenu);\r
5936 }\r
5937    \r
5938 typedef struct {\r
5939   HWND hDlg, hText;\r
5940   int sizeX, sizeY, newSizeX, newSizeY;\r
5941   HDWP hdwp;\r
5942 } ResizeEditPlusButtonsClosure;\r
5943 \r
5944 BOOL CALLBACK\r
5945 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5946 {\r
5947   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5948   RECT rect;\r
5949   POINT pt;\r
5950 \r
5951   if (hChild == cl->hText) return TRUE;\r
5952   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5953   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5954   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5955   ScreenToClient(cl->hDlg, &pt);\r
5956   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5957     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5958   return TRUE;\r
5959 }\r
5960 \r
5961 /* Resize a dialog that has a (rich) edit field filling most of\r
5962    the top, with a row of buttons below */\r
5963 VOID\r
5964 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5965 {\r
5966   RECT rectText;\r
5967   int newTextHeight, newTextWidth;\r
5968   ResizeEditPlusButtonsClosure cl;\r
5969   \r
5970   /*if (IsIconic(hDlg)) return;*/\r
5971   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5972   \r
5973   cl.hdwp = BeginDeferWindowPos(8);\r
5974 \r
5975   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5976   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5977   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5978   if (newTextHeight < 0) {\r
5979     newSizeY += -newTextHeight;\r
5980     newTextHeight = 0;\r
5981   }\r
5982   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5983     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5984 \r
5985   cl.hDlg = hDlg;\r
5986   cl.hText = hText;\r
5987   cl.sizeX = sizeX;\r
5988   cl.sizeY = sizeY;\r
5989   cl.newSizeX = newSizeX;\r
5990   cl.newSizeY = newSizeY;\r
5991   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5992 \r
5993   EndDeferWindowPos(cl.hdwp);\r
5994 }\r
5995 \r
5996 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5997 {\r
5998     RECT    rChild, rParent;\r
5999     int     wChild, hChild, wParent, hParent;\r
6000     int     wScreen, hScreen, xNew, yNew;\r
6001     HDC     hdc;\r
6002 \r
6003     /* Get the Height and Width of the child window */\r
6004     GetWindowRect (hwndChild, &rChild);\r
6005     wChild = rChild.right - rChild.left;\r
6006     hChild = rChild.bottom - rChild.top;\r
6007 \r
6008     /* Get the Height and Width of the parent window */\r
6009     GetWindowRect (hwndParent, &rParent);\r
6010     wParent = rParent.right - rParent.left;\r
6011     hParent = rParent.bottom - rParent.top;\r
6012 \r
6013     /* Get the display limits */\r
6014     hdc = GetDC (hwndChild);\r
6015     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6016     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6017     ReleaseDC(hwndChild, hdc);\r
6018 \r
6019     /* Calculate new X position, then adjust for screen */\r
6020     xNew = rParent.left + ((wParent - wChild) /2);\r
6021     if (xNew < 0) {\r
6022         xNew = 0;\r
6023     } else if ((xNew+wChild) > wScreen) {\r
6024         xNew = wScreen - wChild;\r
6025     }\r
6026 \r
6027     /* Calculate new Y position, then adjust for screen */\r
6028     if( mode == 0 ) {\r
6029         yNew = rParent.top  + ((hParent - hChild) /2);\r
6030     }\r
6031     else {\r
6032         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6033     }\r
6034 \r
6035     if (yNew < 0) {\r
6036         yNew = 0;\r
6037     } else if ((yNew+hChild) > hScreen) {\r
6038         yNew = hScreen - hChild;\r
6039     }\r
6040 \r
6041     /* Set it, and return */\r
6042     return SetWindowPos (hwndChild, NULL,\r
6043                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6044 }\r
6045 \r
6046 /* Center one window over another */\r
6047 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6048 {\r
6049     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6050 }\r
6051 \r
6052 /*---------------------------------------------------------------------------*\\r
6053  *\r
6054  * Startup Dialog functions\r
6055  *\r
6056 \*---------------------------------------------------------------------------*/\r
6057 void\r
6058 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6059 {\r
6060   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6061 \r
6062   while (*cd != NULL) {\r
6063     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6064     cd++;\r
6065   }\r
6066 }\r
6067 \r
6068 void\r
6069 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6070 {\r
6071   char buf1[ARG_MAX];\r
6072   int len;\r
6073 \r
6074   if (str[0] == '@') {\r
6075     FILE* f = fopen(str + 1, "r");\r
6076     if (f == NULL) {\r
6077       DisplayFatalError(str + 1, errno, 2);\r
6078       return;\r
6079     }\r
6080     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6081     fclose(f);\r
6082     buf1[len] = NULLCHAR;\r
6083     str = buf1;\r
6084   }\r
6085 \r
6086   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6087 \r
6088   for (;;) {\r
6089     char buf[MSG_SIZ];\r
6090     char *end = strchr(str, '\n');\r
6091     if (end == NULL) return;\r
6092     memcpy(buf, str, end - str);\r
6093     buf[end - str] = NULLCHAR;\r
6094     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6095     str = end + 1;\r
6096   }\r
6097 }\r
6098 \r
6099 void\r
6100 SetStartupDialogEnables(HWND hDlg)\r
6101 {\r
6102   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6103     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6104     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6105   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6106     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6107   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6108     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6109   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6110     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6111   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6112     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6113     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6114     IsDlgButtonChecked(hDlg, OPT_View));\r
6115 }\r
6116 \r
6117 char *\r
6118 QuoteForFilename(char *filename)\r
6119 {\r
6120   int dquote, space;\r
6121   dquote = strchr(filename, '"') != NULL;\r
6122   space = strchr(filename, ' ') != NULL;\r
6123   if (dquote || space) {\r
6124     if (dquote) {\r
6125       return "'";\r
6126     } else {\r
6127       return "\"";\r
6128     }\r
6129   } else {\r
6130     return "";\r
6131   }\r
6132 }\r
6133 \r
6134 VOID\r
6135 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6136 {\r
6137   char buf[MSG_SIZ];\r
6138   char *q;\r
6139 \r
6140   InitComboStringsFromOption(hwndCombo, nthnames);\r
6141   q = QuoteForFilename(nthcp);\r
6142   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6143   if (*nthdir != NULLCHAR) {\r
6144     q = QuoteForFilename(nthdir);\r
6145     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6146   }\r
6147   if (*nthcp == NULLCHAR) {\r
6148     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6149   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6150     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6151     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6152   }\r
6153 }\r
6154 \r
6155 LRESULT CALLBACK\r
6156 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6157 {\r
6158   char buf[MSG_SIZ];\r
6159   HANDLE hwndCombo;\r
6160   char *p;\r
6161 \r
6162   switch (message) {\r
6163   case WM_INITDIALOG:\r
6164     /* Center the dialog */\r
6165     CenterWindow (hDlg, GetDesktopWindow());\r
6166     /* Initialize the dialog items */\r
6167     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6168                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6169                   firstChessProgramNames);\r
6170     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6171                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6172                   secondChessProgramNames);\r
6173     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6174     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6175     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6176     if (*appData.icsHelper != NULLCHAR) {\r
6177       char *q = QuoteForFilename(appData.icsHelper);\r
6178       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6179     }\r
6180     if (*appData.icsHost == NULLCHAR) {\r
6181       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6182       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6183     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6184       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6185       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6186     }\r
6187 \r
6188     if (appData.icsActive) {\r
6189       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6190     }\r
6191     else if (appData.noChessProgram) {\r
6192       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6193     }\r
6194     else {\r
6195       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6196     }\r
6197 \r
6198     SetStartupDialogEnables(hDlg);\r
6199     return TRUE;\r
6200 \r
6201   case WM_COMMAND:\r
6202     switch (LOWORD(wParam)) {\r
6203     case IDOK:\r
6204       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6205         strcpy(buf, "/fcp=");\r
6206         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6207         p = buf;\r
6208         ParseArgs(StringGet, &p);\r
6209         strcpy(buf, "/scp=");\r
6210         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6211         p = buf;\r
6212         ParseArgs(StringGet, &p);\r
6213         appData.noChessProgram = FALSE;\r
6214         appData.icsActive = FALSE;\r
6215       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6216         strcpy(buf, "/ics /icshost=");\r
6217         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6218         p = buf;\r
6219         ParseArgs(StringGet, &p);\r
6220         if (appData.zippyPlay) {\r
6221           strcpy(buf, "/fcp=");\r
6222           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6223           p = buf;\r
6224           ParseArgs(StringGet, &p);\r
6225         }\r
6226       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6227         appData.noChessProgram = TRUE;\r
6228         appData.icsActive = FALSE;\r
6229       } else {\r
6230         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6231                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6232         return TRUE;\r
6233       }\r
6234       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6235         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6236         p = buf;\r
6237         ParseArgs(StringGet, &p);\r
6238       }\r
6239       EndDialog(hDlg, TRUE);\r
6240       return TRUE;\r
6241 \r
6242     case IDCANCEL:\r
6243       ExitEvent(0);\r
6244       return TRUE;\r
6245 \r
6246     case IDM_HELPCONTENTS:\r
6247       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6248         MessageBox (GetFocus(),\r
6249                     "Unable to activate help",\r
6250                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6251       }\r
6252       break;\r
6253 \r
6254     default:\r
6255       SetStartupDialogEnables(hDlg);\r
6256       break;\r
6257     }\r
6258     break;\r
6259   }\r
6260   return FALSE;\r
6261 }\r
6262 \r
6263 /*---------------------------------------------------------------------------*\\r
6264  *\r
6265  * About box dialog functions\r
6266  *\r
6267 \*---------------------------------------------------------------------------*/\r
6268 \r
6269 /* Process messages for "About" dialog box */\r
6270 LRESULT CALLBACK\r
6271 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6272 {\r
6273   switch (message) {\r
6274   case WM_INITDIALOG: /* message: initialize dialog box */\r
6275     /* Center the dialog over the application window */\r
6276     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6277     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6278     return (TRUE);\r
6279 \r
6280   case WM_COMMAND: /* message: received a command */\r
6281     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6282         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6283       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6284       return (TRUE);\r
6285     }\r
6286     break;\r
6287   }\r
6288   return (FALSE);\r
6289 }\r
6290 \r
6291 /*---------------------------------------------------------------------------*\\r
6292  *\r
6293  * Comment Dialog functions\r
6294  *\r
6295 \*---------------------------------------------------------------------------*/\r
6296 \r
6297 LRESULT CALLBACK\r
6298 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6299 {\r
6300   static HANDLE hwndText = NULL;\r
6301   int len, newSizeX, newSizeY, flags;\r
6302   static int sizeX, sizeY;\r
6303   char *str;\r
6304   RECT rect;\r
6305   MINMAXINFO *mmi;\r
6306 \r
6307   switch (message) {\r
6308   case WM_INITDIALOG: /* message: initialize dialog box */\r
6309     /* Initialize the dialog items */\r
6310     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6311     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6312     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6313     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6314     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6315     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6316     SetWindowText(hDlg, commentTitle);\r
6317     if (editComment) {\r
6318       SetFocus(hwndText);\r
6319     } else {\r
6320       SetFocus(GetDlgItem(hDlg, IDOK));\r
6321     }\r
6322     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6323                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6324                 MAKELPARAM(FALSE, 0));\r
6325     /* Size and position the dialog */\r
6326     if (!commentDialog) {\r
6327       commentDialog = hDlg;\r
6328       flags = SWP_NOZORDER;\r
6329       GetClientRect(hDlg, &rect);\r
6330       sizeX = rect.right;\r
6331       sizeY = rect.bottom;\r
6332       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6333           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6334         WINDOWPLACEMENT wp;\r
6335         EnsureOnScreen(&commentX, &commentY);\r
6336         wp.length = sizeof(WINDOWPLACEMENT);\r
6337         wp.flags = 0;\r
6338         wp.showCmd = SW_SHOW;\r
6339         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6340         wp.rcNormalPosition.left = commentX;\r
6341         wp.rcNormalPosition.right = commentX + commentW;\r
6342         wp.rcNormalPosition.top = commentY;\r
6343         wp.rcNormalPosition.bottom = commentY + commentH;\r
6344         SetWindowPlacement(hDlg, &wp);\r
6345 \r
6346         GetClientRect(hDlg, &rect);\r
6347         newSizeX = rect.right;\r
6348         newSizeY = rect.bottom;\r
6349         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6350                               newSizeX, newSizeY);\r
6351         sizeX = newSizeX;\r
6352         sizeY = newSizeY;\r
6353       }\r
6354     }\r
6355     return FALSE;\r
6356 \r
6357   case WM_COMMAND: /* message: received a command */\r
6358     switch (LOWORD(wParam)) {\r
6359     case IDOK:\r
6360       if (editComment) {\r
6361         char *p, *q;\r
6362         /* Read changed options from the dialog box */\r
6363         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6364         len = GetWindowTextLength(hwndText);\r
6365         str = (char *) malloc(len + 1);\r
6366         GetWindowText(hwndText, str, len + 1);\r
6367         p = q = str;\r
6368         while (*q) {\r
6369           if (*q == '\r')\r
6370             q++;\r
6371           else\r
6372             *p++ = *q++;\r
6373         }\r
6374         *p = NULLCHAR;\r
6375         ReplaceComment(commentIndex, str);\r
6376         free(str);\r
6377       }\r
6378       CommentPopDown();\r
6379       return TRUE;\r
6380 \r
6381     case IDCANCEL:\r
6382     case OPT_CancelComment:\r
6383       CommentPopDown();\r
6384       return TRUE;\r
6385 \r
6386     case OPT_ClearComment:\r
6387       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6388       break;\r
6389 \r
6390     case OPT_EditComment:\r
6391       EditCommentEvent();\r
6392       return TRUE;\r
6393 \r
6394     default:\r
6395       break;\r
6396     }\r
6397     break;\r
6398 \r
6399   case WM_SIZE:\r
6400     newSizeX = LOWORD(lParam);\r
6401     newSizeY = HIWORD(lParam);\r
6402     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6403     sizeX = newSizeX;\r
6404     sizeY = newSizeY;\r
6405     break;\r
6406 \r
6407   case WM_GETMINMAXINFO:\r
6408     /* Prevent resizing window too small */\r
6409     mmi = (MINMAXINFO *) lParam;\r
6410     mmi->ptMinTrackSize.x = 100;\r
6411     mmi->ptMinTrackSize.y = 100;\r
6412     break;\r
6413   }\r
6414   return FALSE;\r
6415 }\r
6416 \r
6417 VOID\r
6418 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6419 {\r
6420   FARPROC lpProc;\r
6421   char *p, *q;\r
6422 \r
6423   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6424 \r
6425   if (str == NULL) str = "";\r
6426   p = (char *) malloc(2 * strlen(str) + 2);\r
6427   q = p;\r
6428   while (*str) {\r
6429     if (*str == '\n') *q++ = '\r';\r
6430     *q++ = *str++;\r
6431   }\r
6432   *q = NULLCHAR;\r
6433   if (commentText != NULL) free(commentText);\r
6434 \r
6435   commentIndex = index;\r
6436   commentTitle = title;\r
6437   commentText = p;\r
6438   editComment = edit;\r
6439 \r
6440   if (commentDialog) {\r
6441     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6442     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6443   } else {\r
6444     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6445     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6446                  hwndMain, (DLGPROC)lpProc);\r
6447     FreeProcInstance(lpProc);\r
6448   }\r
6449   commentDialogUp = TRUE;\r
6450 }\r
6451 \r
6452 \r
6453 /*---------------------------------------------------------------------------*\\r
6454  *\r
6455  * Type-in move dialog functions\r
6456  * \r
6457 \*---------------------------------------------------------------------------*/\r
6458 \r
6459 LRESULT CALLBACK\r
6460 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6461 {\r
6462   char move[MSG_SIZ];\r
6463   HWND hInput;\r
6464   ChessMove moveType;\r
6465   int fromX, fromY, toX, toY;\r
6466   char promoChar;\r
6467 \r
6468   switch (message) {\r
6469   case WM_INITDIALOG:\r
6470     move[0] = (char) lParam;\r
6471     move[1] = NULLCHAR;\r
6472     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6473     hInput = GetDlgItem(hDlg, OPT_Move);\r
6474     SetWindowText(hInput, move);\r
6475     SetFocus(hInput);\r
6476     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6477     return FALSE;\r
6478 \r
6479   case WM_COMMAND:\r
6480     switch (LOWORD(wParam)) {\r
6481     case IDOK:\r
6482       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6483         gameMode != Training) {\r
6484         DisplayMoveError("Displayed move is not current");\r
6485       } else {\r
6486         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6487         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6488           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6489           if (gameMode != Training)\r
6490               forwardMostMove = currentMove;\r
6491           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6492         } else {\r
6493           DisplayMoveError("Could not parse move");\r
6494         }\r
6495       }\r
6496       EndDialog(hDlg, TRUE);\r
6497       return TRUE;\r
6498     case IDCANCEL:\r
6499       EndDialog(hDlg, FALSE);\r
6500       return TRUE;\r
6501     default:\r
6502       break;\r
6503     }\r
6504     break;\r
6505   }\r
6506   return FALSE;\r
6507 }\r
6508 \r
6509 VOID\r
6510 PopUpMoveDialog(char firstchar)\r
6511 {\r
6512     FARPROC lpProc;\r
6513     \r
6514     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6515         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6516         gameMode == AnalyzeMode || gameMode == EditGame || \r
6517         gameMode == EditPosition || gameMode == IcsExamining ||\r
6518         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6519         gameMode == Training) {\r
6520       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6521       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6522         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6523       FreeProcInstance(lpProc);\r
6524     }\r
6525 }\r
6526 \r
6527 /*---------------------------------------------------------------------------*\\r
6528  *\r
6529  *  Error dialogs\r
6530  * \r
6531 \*---------------------------------------------------------------------------*/\r
6532 \r
6533 /* Nonmodal error box */\r
6534 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6535                              WPARAM wParam, LPARAM lParam);\r
6536 \r
6537 VOID\r
6538 ErrorPopUp(char *title, char *content)\r
6539 {\r
6540   FARPROC lpProc;\r
6541   char *p, *q;\r
6542   BOOLEAN modal = hwndMain == NULL;\r
6543 \r
6544   p = content;\r
6545   q = errorMessage;\r
6546   while (*p) {\r
6547     if (*p == '\n') {\r
6548       if (modal) {\r
6549         *q++ = ' ';\r
6550         p++;\r
6551       } else {\r
6552         *q++ = '\r';\r
6553         *q++ = *p++;\r
6554       }\r
6555     } else {\r
6556       *q++ = *p++;\r
6557     }\r
6558   }\r
6559   *q = NULLCHAR;\r
6560   strncpy(errorTitle, title, sizeof(errorTitle));\r
6561   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6562   \r
6563   if (modal) {\r
6564     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6565   } else {\r
6566     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6567     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6568                  hwndMain, (DLGPROC)lpProc);\r
6569     FreeProcInstance(lpProc);\r
6570   }\r
6571 }\r
6572 \r
6573 VOID\r
6574 ErrorPopDown()\r
6575 {\r
6576   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6577   if (errorDialog == NULL) return;\r
6578   DestroyWindow(errorDialog);\r
6579   errorDialog = NULL;\r
6580 }\r
6581 \r
6582 LRESULT CALLBACK\r
6583 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6584 {\r
6585   HANDLE hwndText;\r
6586   RECT rChild;\r
6587 \r
6588   switch (message) {\r
6589   case WM_INITDIALOG:\r
6590     GetWindowRect(hDlg, &rChild);\r
6591 \r
6592     /*\r
6593     SetWindowPos(hDlg, NULL, rChild.left,\r
6594       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6595       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6596     */\r
6597 \r
6598     /* \r
6599         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6600         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6601         and it doesn't work when you resize the dialog.\r
6602         For now, just give it a default position.\r
6603     */\r
6604     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6605 \r
6606     errorDialog = hDlg;\r
6607     SetWindowText(hDlg, errorTitle);\r
6608     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6609     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6610     return FALSE;\r
6611 \r
6612   case WM_COMMAND:\r
6613     switch (LOWORD(wParam)) {\r
6614     case IDOK:\r
6615     case IDCANCEL:\r
6616       if (errorDialog == hDlg) errorDialog = NULL;\r
6617       DestroyWindow(hDlg);\r
6618       return TRUE;\r
6619 \r
6620     default:\r
6621       break;\r
6622     }\r
6623     break;\r
6624   }\r
6625   return FALSE;\r
6626 }\r
6627 \r
6628 #ifdef GOTHIC\r
6629 LRESULT CALLBACK\r
6630 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6631 {\r
6632   HANDLE hwndText;\r
6633   RECT rChild;\r
6634   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6635 \r
6636   switch (message) {\r
6637   case WM_INITDIALOG:\r
6638     GetWindowRect(hDlg, &rChild);\r
6639 \r
6640     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
6641                                                              SWP_NOZORDER);\r
6642 \r
6643     /* \r
6644         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6645         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6646         and it doesn't work when you resize the dialog.\r
6647         For now, just give it a default position.\r
6648     */\r
6649 \r
6650     SetWindowText(hDlg, errorTitle);\r
6651     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6652     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6653     return FALSE;\r
6654 \r
6655   case WM_COMMAND:\r
6656     switch (LOWORD(wParam)) {\r
6657     case IDOK:\r
6658     case IDCANCEL:\r
6659       if (errorDialog == hDlg) errorDialog = NULL;\r
6660       DestroyWindow(hDlg);\r
6661       return TRUE;\r
6662 \r
6663     default:\r
6664       break;\r
6665     }\r
6666     break;\r
6667   }\r
6668   return FALSE;\r
6669 }\r
6670 \r
6671 VOID\r
6672 GothicPopUp(char *title)\r
6673 {\r
6674   FARPROC lpProc;\r
6675   char *p, *q;\r
6676   BOOLEAN modal = hwndMain == NULL;\r
6677 \r
6678   strncpy(errorTitle, title, sizeof(errorTitle));\r
6679   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6680   \r
6681     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6682     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6683                  hwndMain, (DLGPROC)lpProc);\r
6684     FreeProcInstance(lpProc);\r
6685 }\r
6686 #endif\r
6687 \r
6688 /*---------------------------------------------------------------------------*\\r
6689  *\r
6690  *  Ics Interaction console functions\r
6691  *\r
6692 \*---------------------------------------------------------------------------*/\r
6693 \r
6694 #define HISTORY_SIZE 64\r
6695 static char *history[HISTORY_SIZE];\r
6696 int histIn = 0, histP = 0;\r
6697 \r
6698 VOID\r
6699 SaveInHistory(char *cmd)\r
6700 {\r
6701   if (history[histIn] != NULL) {\r
6702     free(history[histIn]);\r
6703     history[histIn] = NULL;\r
6704   }\r
6705   if (*cmd == NULLCHAR) return;\r
6706   history[histIn] = StrSave(cmd);\r
6707   histIn = (histIn + 1) % HISTORY_SIZE;\r
6708   if (history[histIn] != NULL) {\r
6709     free(history[histIn]);\r
6710     history[histIn] = NULL;\r
6711   }\r
6712   histP = histIn;\r
6713 }\r
6714 \r
6715 char *\r
6716 PrevInHistory(char *cmd)\r
6717 {\r
6718   int newhp;\r
6719   if (histP == histIn) {\r
6720     if (history[histIn] != NULL) free(history[histIn]);\r
6721     history[histIn] = StrSave(cmd);\r
6722   }\r
6723   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6724   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6725   histP = newhp;\r
6726   return history[histP];\r
6727 }\r
6728 \r
6729 char *\r
6730 NextInHistory()\r
6731 {\r
6732   if (histP == histIn) return NULL;\r
6733   histP = (histP + 1) % HISTORY_SIZE;\r
6734   return history[histP];\r
6735 }\r
6736 \r
6737 typedef struct {\r
6738   char *item;\r
6739   char *command;\r
6740   BOOLEAN getname;\r
6741   BOOLEAN immediate;\r
6742 } IcsTextMenuEntry;\r
6743 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
6744 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
6745 \r
6746 void\r
6747 ParseIcsTextMenu(char *icsTextMenuString)\r
6748 {\r
6749   int flags = 0;\r
6750   IcsTextMenuEntry *e = icsTextMenuEntry;\r
6751   char *p = icsTextMenuString;\r
6752   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6753     free(e->item);\r
6754     e->item = NULL;\r
6755     if (e->command != NULL) {\r
6756       free(e->command);\r
6757       e->command = NULL;\r
6758     }\r
6759     e++;\r
6760   }\r
6761   e = icsTextMenuEntry;\r
6762   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6763     if (*p == ';' || *p == '\n') {\r
6764       e->item = strdup("-");\r
6765       e->command = NULL;\r
6766       p++;\r
6767     } else if (*p == '-') {\r
6768       e->item = strdup("-");\r
6769       e->command = NULL;\r
6770       p++;\r
6771       if (*p) p++;\r
6772     } else {\r
6773       char *q, *r, *s, *t;\r
6774       char c;\r
6775       q = strchr(p, ',');\r
6776       if (q == NULL) break;\r
6777       *q = NULLCHAR;\r
6778       r = strchr(q + 1, ',');\r
6779       if (r == NULL) break;\r
6780       *r = NULLCHAR;\r
6781       s = strchr(r + 1, ',');\r
6782       if (s == NULL) break;\r
6783       *s = NULLCHAR;\r
6784       c = ';';\r
6785       t = strchr(s + 1, c);\r
6786       if (t == NULL) {\r
6787         c = '\n';\r
6788         t = strchr(s + 1, c);\r
6789       }\r
6790       if (t != NULL) *t = NULLCHAR;\r
6791       e->item = strdup(p);\r
6792       e->command = strdup(q + 1);\r
6793       e->getname = *(r + 1) != '0';\r
6794       e->immediate = *(s + 1) != '0';\r
6795       *q = ',';\r
6796       *r = ',';\r
6797       *s = ',';\r
6798       if (t == NULL) break;\r
6799       *t = c;\r
6800       p = t + 1;\r
6801     }\r
6802     e++;\r
6803   } \r
6804 }\r
6805 \r
6806 HMENU\r
6807 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6808 {\r
6809   HMENU hmenu, h;\r
6810   int i = 0;\r
6811   hmenu = LoadMenu(hInst, "TextMenu");\r
6812   h = GetSubMenu(hmenu, 0);\r
6813   while (e->item) {\r
6814     if (strcmp(e->item, "-") == 0) {\r
6815       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6816     } else {\r
6817       if (e->item[0] == '|') {\r
6818         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6819                    IDM_CommandX + i, &e->item[1]);\r
6820       } else {\r
6821         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6822       }\r
6823     }\r
6824     e++;\r
6825     i++;\r
6826   } \r
6827   return hmenu;\r
6828 }\r
6829 \r
6830 WNDPROC consoleTextWindowProc;\r
6831 \r
6832 void\r
6833 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6834 {\r
6835   char buf[MSG_SIZ], name[MSG_SIZ];\r
6836   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6837   CHARRANGE sel;\r
6838 \r
6839   if (!getname) {\r
6840     SetWindowText(hInput, command);\r
6841     if (immediate) {\r
6842       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6843     } else {\r
6844       sel.cpMin = 999999;\r
6845       sel.cpMax = 999999;\r
6846       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6847       SetFocus(hInput);\r
6848     }\r
6849     return;\r
6850   }    \r
6851   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6852   if (sel.cpMin == sel.cpMax) {\r
6853     /* Expand to surrounding word */\r
6854     TEXTRANGE tr;\r
6855     do {\r
6856       tr.chrg.cpMax = sel.cpMin;\r
6857       tr.chrg.cpMin = --sel.cpMin;\r
6858       if (sel.cpMin < 0) break;\r
6859       tr.lpstrText = name;\r
6860       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6861     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6862     sel.cpMin++;\r
6863 \r
6864     do {\r
6865       tr.chrg.cpMin = sel.cpMax;\r
6866       tr.chrg.cpMax = ++sel.cpMax;\r
6867       tr.lpstrText = name;\r
6868       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6869     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6870     sel.cpMax--;\r
6871 \r
6872     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6873       MessageBeep(MB_ICONEXCLAMATION);\r
6874       return;\r
6875     }\r
6876     tr.chrg = sel;\r
6877     tr.lpstrText = name;\r
6878     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6879   } else {\r
6880     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6881       MessageBeep(MB_ICONEXCLAMATION);\r
6882       return;\r
6883     }\r
6884     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6885   }\r
6886   if (immediate) {\r
6887     sprintf(buf, "%s %s", command, name);\r
6888     SetWindowText(hInput, buf);\r
6889     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6890   } else {\r
6891     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6892     SetWindowText(hInput, buf);\r
6893     sel.cpMin = 999999;\r
6894     sel.cpMax = 999999;\r
6895     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6896     SetFocus(hInput);\r
6897   }\r
6898 }\r
6899 \r
6900 LRESULT CALLBACK \r
6901 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6902 {\r
6903   HWND hInput;\r
6904   CHARRANGE sel;\r
6905 \r
6906   switch (message) {\r
6907   case WM_KEYDOWN:\r
6908     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6909     switch (wParam) {\r
6910     case VK_PRIOR:\r
6911       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6912       return 0;\r
6913     case VK_NEXT:\r
6914       sel.cpMin = 999999;\r
6915       sel.cpMax = 999999;\r
6916       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6917       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6918       return 0;\r
6919     }\r
6920     break;\r
6921   case WM_CHAR:\r
6922     if (wParam == '\t') {\r
6923       if (GetKeyState(VK_SHIFT) < 0) {\r
6924         /* shifted */\r
6925         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6926         if (buttonDesc[0].hwnd) {\r
6927           SetFocus(buttonDesc[0].hwnd);\r
6928         } else {\r
6929           SetFocus(hwndMain);\r
6930         }\r
6931       } else {\r
6932         /* unshifted */\r
6933         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6934       }\r
6935     } else {\r
6936       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6937       SetFocus(hInput);\r
6938       SendMessage(hInput, message, wParam, lParam);\r
6939     }\r
6940     return 0;\r
6941   case WM_PASTE:\r
6942     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6943     SetFocus(hInput);\r
6944     return SendMessage(hInput, message, wParam, lParam);\r
6945   case WM_MBUTTONDOWN:\r
6946     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6947   case WM_RBUTTONDOWN:\r
6948     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6949       /* Move selection here if it was empty */\r
6950       POINT pt;\r
6951       pt.x = LOWORD(lParam);\r
6952       pt.y = HIWORD(lParam);\r
6953       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6954       if (sel.cpMin == sel.cpMax) {\r
6955         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6956         sel.cpMax = sel.cpMin;\r
6957         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6958       }\r
6959       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6960     }\r
6961     return 0;\r
6962   case WM_RBUTTONUP:\r
6963     if (GetKeyState(VK_SHIFT) & ~1) {\r
6964       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6965         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6966     } else {\r
6967       POINT pt;\r
6968       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6969       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6970       if (sel.cpMin == sel.cpMax) {\r
6971         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6972         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6973       }\r
6974       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6975         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6976       }\r
6977       pt.x = LOWORD(lParam);\r
6978       pt.y = HIWORD(lParam);\r
6979       MenuPopup(hwnd, pt, hmenu, -1);\r
6980     }\r
6981     return 0;\r
6982   case WM_COMMAND:\r
6983     switch (LOWORD(wParam)) {\r
6984     case IDM_QuickPaste:\r
6985       {\r
6986         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6987         if (sel.cpMin == sel.cpMax) {\r
6988           MessageBeep(MB_ICONEXCLAMATION);\r
6989           return 0;\r
6990         }\r
6991         SendMessage(hwnd, WM_COPY, 0, 0);\r
6992         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6993         SendMessage(hInput, WM_PASTE, 0, 0);\r
6994         SetFocus(hInput);\r
6995         return 0;\r
6996       }\r
6997     case IDM_Cut:\r
6998       SendMessage(hwnd, WM_CUT, 0, 0);\r
6999       return 0;\r
7000     case IDM_Paste:\r
7001       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7002       return 0;\r
7003     case IDM_Copy:\r
7004       SendMessage(hwnd, WM_COPY, 0, 0);\r
7005       return 0;\r
7006     default:\r
7007       {\r
7008         int i = LOWORD(wParam) - IDM_CommandX;\r
7009         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7010             icsTextMenuEntry[i].command != NULL) {\r
7011           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7012                    icsTextMenuEntry[i].getname,\r
7013                    icsTextMenuEntry[i].immediate);\r
7014           return 0;\r
7015         }\r
7016       }\r
7017       break;\r
7018     }\r
7019     break;\r
7020   }\r
7021   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7022 }\r
7023 \r
7024 WNDPROC consoleInputWindowProc;\r
7025 \r
7026 LRESULT CALLBACK\r
7027 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7028 {\r
7029   char buf[MSG_SIZ];\r
7030   char *p;\r
7031   static BOOL sendNextChar = FALSE;\r
7032   static BOOL quoteNextChar = FALSE;\r
7033   InputSource *is = consoleInputSource;\r
7034   CHARFORMAT cf;\r
7035   CHARRANGE sel;\r
7036 \r
7037   switch (message) {\r
7038   case WM_CHAR:\r
7039     if (!appData.localLineEditing || sendNextChar) {\r
7040       is->buf[0] = (CHAR) wParam;\r
7041       is->count = 1;\r
7042       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7043       sendNextChar = FALSE;\r
7044       return 0;\r
7045     }\r
7046     if (quoteNextChar) {\r
7047       buf[0] = (char) wParam;\r
7048       buf[1] = NULLCHAR;\r
7049       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7050       quoteNextChar = FALSE;\r
7051       return 0;\r
7052     }\r
7053     switch (wParam) {\r
7054     case '\r':   /* Enter key */\r
7055       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7056       if (consoleEcho) SaveInHistory(is->buf);\r
7057       is->buf[is->count++] = '\n';\r
7058       is->buf[is->count] = NULLCHAR;\r
7059       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7060       if (consoleEcho) {\r
7061         ConsoleOutput(is->buf, is->count, TRUE);\r
7062       } else if (appData.localLineEditing) {\r
7063         ConsoleOutput("\n", 1, TRUE);\r
7064       }\r
7065       /* fall thru */\r
7066     case '\033': /* Escape key */\r
7067       SetWindowText(hwnd, "");\r
7068       cf.cbSize = sizeof(CHARFORMAT);\r
7069       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7070       if (consoleEcho) {\r
7071         cf.crTextColor = textAttribs[ColorNormal].color;\r
7072       } else {\r
7073         cf.crTextColor = COLOR_ECHOOFF;\r
7074       }\r
7075       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7076       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7077       return 0;\r
7078     case '\t':   /* Tab key */\r
7079       if (GetKeyState(VK_SHIFT) < 0) {\r
7080         /* shifted */\r
7081         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7082       } else {\r
7083         /* unshifted */\r
7084         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7085         if (buttonDesc[0].hwnd) {\r
7086           SetFocus(buttonDesc[0].hwnd);\r
7087         } else {\r
7088           SetFocus(hwndMain);\r
7089         }\r
7090       }\r
7091       return 0;\r
7092     case '\023': /* Ctrl+S */\r
7093       sendNextChar = TRUE;\r
7094       return 0;\r
7095     case '\021': /* Ctrl+Q */\r
7096       quoteNextChar = TRUE;\r
7097       return 0;\r
7098     default:\r
7099       break;\r
7100     }\r
7101     break;\r
7102   case WM_KEYDOWN:\r
7103     switch (wParam) {\r
7104     case VK_UP:\r
7105       GetWindowText(hwnd, buf, MSG_SIZ);\r
7106       p = PrevInHistory(buf);\r
7107       if (p != NULL) {\r
7108         SetWindowText(hwnd, p);\r
7109         sel.cpMin = 999999;\r
7110         sel.cpMax = 999999;\r
7111         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7112         return 0;\r
7113       }\r
7114       break;\r
7115     case VK_DOWN:\r
7116       p = NextInHistory();\r
7117       if (p != NULL) {\r
7118         SetWindowText(hwnd, p);\r
7119         sel.cpMin = 999999;\r
7120         sel.cpMax = 999999;\r
7121         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7122         return 0;\r
7123       }\r
7124       break;\r
7125     case VK_HOME:\r
7126     case VK_END:\r
7127       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7128       /* fall thru */\r
7129     case VK_PRIOR:\r
7130     case VK_NEXT:\r
7131       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7132       return 0;\r
7133     }\r
7134     break;\r
7135   case WM_MBUTTONDOWN:\r
7136     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7137       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7138     break;\r
7139   case WM_RBUTTONUP:\r
7140     if (GetKeyState(VK_SHIFT) & ~1) {\r
7141       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7142         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7143     } else {\r
7144       POINT pt;\r
7145       HMENU hmenu;\r
7146       hmenu = LoadMenu(hInst, "InputMenu");\r
7147       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7148       if (sel.cpMin == sel.cpMax) {\r
7149         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7150         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7151       }\r
7152       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7153         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7154       }\r
7155       pt.x = LOWORD(lParam);\r
7156       pt.y = HIWORD(lParam);\r
7157       MenuPopup(hwnd, pt, hmenu, -1);\r
7158     }\r
7159     return 0;\r
7160   case WM_COMMAND:\r
7161     switch (LOWORD(wParam)) { \r
7162     case IDM_Undo:\r
7163       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7164       return 0;\r
7165     case IDM_SelectAll:\r
7166       sel.cpMin = 0;\r
7167       sel.cpMax = -1; /*999999?*/\r
7168       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7169       return 0;\r
7170     case IDM_Cut:\r
7171       SendMessage(hwnd, WM_CUT, 0, 0);\r
7172       return 0;\r
7173     case IDM_Paste:\r
7174       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7175       return 0;\r
7176     case IDM_Copy:\r
7177       SendMessage(hwnd, WM_COPY, 0, 0);\r
7178       return 0;\r
7179     }\r
7180     break;\r
7181   }\r
7182   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7183 }\r
7184 \r
7185 #define CO_MAX  100000\r
7186 #define CO_TRIM   1000\r
7187 \r
7188 LRESULT CALLBACK\r
7189 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7190 {\r
7191   static SnapData sd;\r
7192   static HWND hText, hInput, hFocus;\r
7193   InputSource *is = consoleInputSource;\r
7194   RECT rect;\r
7195   static int sizeX, sizeY;\r
7196   int newSizeX, newSizeY;\r
7197   MINMAXINFO *mmi;\r
7198 \r
7199   switch (message) {\r
7200   case WM_INITDIALOG: /* message: initialize dialog box */\r
7201     hwndConsole = hDlg;\r
7202     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7203     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7204     SetFocus(hInput);\r
7205     consoleTextWindowProc = (WNDPROC)\r
7206       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7207     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7208     consoleInputWindowProc = (WNDPROC)\r
7209       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7210     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7211     Colorize(ColorNormal, TRUE);\r
7212     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7213     ChangedConsoleFont();\r
7214     GetClientRect(hDlg, &rect);\r
7215     sizeX = rect.right;\r
7216     sizeY = rect.bottom;\r
7217     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7218         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7219       WINDOWPLACEMENT wp;\r
7220       EnsureOnScreen(&consoleX, &consoleY);\r
7221       wp.length = sizeof(WINDOWPLACEMENT);\r
7222       wp.flags = 0;\r
7223       wp.showCmd = SW_SHOW;\r
7224       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7225       wp.rcNormalPosition.left = consoleX;\r
7226       wp.rcNormalPosition.right = consoleX + consoleW;\r
7227       wp.rcNormalPosition.top = consoleY;\r
7228       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7229       SetWindowPlacement(hDlg, &wp);\r
7230     }\r
7231     return FALSE;\r
7232 \r
7233   case WM_SETFOCUS:\r
7234     SetFocus(hInput);\r
7235     return 0;\r
7236 \r
7237   case WM_CLOSE:\r
7238     ExitEvent(0);\r
7239     /* not reached */\r
7240     break;\r
7241 \r
7242   case WM_SIZE:\r
7243     if (IsIconic(hDlg)) break;\r
7244     newSizeX = LOWORD(lParam);\r
7245     newSizeY = HIWORD(lParam);\r
7246     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7247       RECT rectText, rectInput;\r
7248       POINT pt;\r
7249       int newTextHeight, newTextWidth;\r
7250       GetWindowRect(hText, &rectText);\r
7251       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7252       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7253       if (newTextHeight < 0) {\r
7254         newSizeY += -newTextHeight;\r
7255         newTextHeight = 0;\r
7256       }\r
7257       SetWindowPos(hText, NULL, 0, 0,\r
7258         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7259       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7260       pt.x = rectInput.left;\r
7261       pt.y = rectInput.top + newSizeY - sizeY;\r
7262       ScreenToClient(hDlg, &pt);\r
7263       SetWindowPos(hInput, NULL, \r
7264         pt.x, pt.y, /* needs client coords */   \r
7265         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7266         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7267     }\r
7268     sizeX = newSizeX;\r
7269     sizeY = newSizeY;\r
7270     break;\r
7271 \r
7272   case WM_GETMINMAXINFO:\r
7273     /* Prevent resizing window too small */\r
7274     mmi = (MINMAXINFO *) lParam;\r
7275     mmi->ptMinTrackSize.x = 100;\r
7276     mmi->ptMinTrackSize.y = 100;\r
7277     break;\r
7278 \r
7279   /* [AS] Snapping */\r
7280   case WM_ENTERSIZEMOVE:\r
7281     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7282 \r
7283   case WM_SIZING:\r
7284     return OnSizing( &sd, hDlg, wParam, lParam );\r
7285 \r
7286   case WM_MOVING:\r
7287     return OnMoving( &sd, hDlg, wParam, lParam );\r
7288 \r
7289   case WM_EXITSIZEMOVE:\r
7290     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7291   }\r
7292 \r
7293   return DefWindowProc(hDlg, message, wParam, lParam);\r
7294 }\r
7295 \r
7296 \r
7297 VOID\r
7298 ConsoleCreate()\r
7299 {\r
7300   HWND hCons;\r
7301   if (hwndConsole) return;\r
7302   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7303   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7304 }\r
7305 \r
7306 \r
7307 VOID\r
7308 ConsoleOutput(char* data, int length, int forceVisible)\r
7309 {\r
7310   HWND hText;\r
7311   int trim, exlen;\r
7312   char *p, *q;\r
7313   char buf[CO_MAX+1];\r
7314   POINT pEnd;\r
7315   RECT rect;\r
7316   static int delayLF = 0;\r
7317   CHARRANGE savesel, sel;\r
7318 \r
7319   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7320   p = data;\r
7321   q = buf;\r
7322   if (delayLF) {\r
7323     *q++ = '\r';\r
7324     *q++ = '\n';\r
7325     delayLF = 0;\r
7326   }\r
7327   while (length--) {\r
7328     if (*p == '\n') {\r
7329       if (*++p) {\r
7330         *q++ = '\r';\r
7331         *q++ = '\n';\r
7332       } else {\r
7333         delayLF = 1;\r
7334       }\r
7335     } else if (*p == '\007') {\r
7336        MyPlaySound(&sounds[(int)SoundBell]);\r
7337        p++;\r
7338     } else {\r
7339       *q++ = *p++;\r
7340     }\r
7341   }\r
7342   *q = NULLCHAR;\r
7343   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7344   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7345   /* Save current selection */\r
7346   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7347   exlen = GetWindowTextLength(hText);\r
7348   /* Find out whether current end of text is visible */\r
7349   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7350   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7351   /* Trim existing text if it's too long */\r
7352   if (exlen + (q - buf) > CO_MAX) {\r
7353     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7354     sel.cpMin = 0;\r
7355     sel.cpMax = trim;\r
7356     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7357     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7358     exlen -= trim;\r
7359     savesel.cpMin -= trim;\r
7360     savesel.cpMax -= trim;\r
7361     if (exlen < 0) exlen = 0;\r
7362     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7363     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7364   }\r
7365   /* Append the new text */\r
7366   sel.cpMin = exlen;\r
7367   sel.cpMax = exlen;\r
7368   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7369   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7370   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7371   if (forceVisible || exlen == 0 ||\r
7372       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7373        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7374     /* Scroll to make new end of text visible if old end of text\r
7375        was visible or new text is an echo of user typein */\r
7376     sel.cpMin = 9999999;\r
7377     sel.cpMax = 9999999;\r
7378     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7379     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7380     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7381     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7382   }\r
7383   if (savesel.cpMax == exlen || forceVisible) {\r
7384     /* Move insert point to new end of text if it was at the old\r
7385        end of text or if the new text is an echo of user typein */\r
7386     sel.cpMin = 9999999;\r
7387     sel.cpMax = 9999999;\r
7388     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7389   } else {\r
7390     /* Restore previous selection */\r
7391     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7392   }\r
7393   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7394 }\r
7395 \r
7396 /*---------*/\r
7397 \r
7398 \r
7399 void\r
7400 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7401 {\r
7402   char buf[100];\r
7403   char *str;\r
7404   COLORREF oldFg, oldBg;\r
7405   HFONT oldFont;\r
7406   RECT rect;\r
7407 \r
7408   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7409 \r
7410   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7411   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7412   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7413 \r
7414   rect.left = x;\r
7415   rect.right = x + squareSize;\r
7416   rect.top  = y;\r
7417   rect.bottom = y + squareSize;\r
7418   str = buf;\r
7419 \r
7420   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7421                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7422              y, ETO_CLIPPED|ETO_OPAQUE,\r
7423              &rect, str, strlen(str), NULL);\r
7424 \r
7425   (void) SetTextColor(hdc, oldFg);\r
7426   (void) SetBkColor(hdc, oldBg);\r
7427   (void) SelectObject(hdc, oldFont);\r
7428 }\r
7429 \r
7430 void\r
7431 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7432               RECT *rect, char *color, char *flagFell)\r
7433 {\r
7434   char buf[100];\r
7435   char *str;\r
7436   COLORREF oldFg, oldBg;\r
7437   HFONT oldFont;\r
7438 \r
7439   if (appData.clockMode) {\r
7440     if (tinyLayout)\r
7441       sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7442     else\r
7443       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
7444     str = buf;\r
7445   } else {\r
7446     str = color;\r
7447   }\r
7448 \r
7449   if (highlight) {\r
7450     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7451     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7452   } else {\r
7453     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7454     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7455   }\r
7456   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7457 \r
7458   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7459              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7460              rect, str, strlen(str), NULL);\r
7461 \r
7462   (void) SetTextColor(hdc, oldFg);\r
7463   (void) SetBkColor(hdc, oldBg);\r
7464   (void) SelectObject(hdc, oldFont);\r
7465 }\r
7466 \r
7467 \r
7468 int\r
7469 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7470            OVERLAPPED *ovl)\r
7471 {\r
7472   int ok, err;\r
7473 \r
7474   /* [AS]  */\r
7475   if( count <= 0 ) {\r
7476     if (appData.debugMode) {\r
7477       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7478     }\r
7479 \r
7480     return ERROR_INVALID_USER_BUFFER;\r
7481   }\r
7482 \r
7483   ResetEvent(ovl->hEvent);\r
7484   ovl->Offset = ovl->OffsetHigh = 0;\r
7485   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7486   if (ok) {\r
7487     err = NO_ERROR;\r
7488   } else {\r
7489     err = GetLastError();\r
7490     if (err == ERROR_IO_PENDING) {\r
7491       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7492       if (ok)\r
7493         err = NO_ERROR;\r
7494       else\r
7495         err = GetLastError();\r
7496     }\r
7497   }\r
7498   return err;\r
7499 }\r
7500 \r
7501 int\r
7502 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7503             OVERLAPPED *ovl)\r
7504 {\r
7505   int ok, err;\r
7506 \r
7507   ResetEvent(ovl->hEvent);\r
7508   ovl->Offset = ovl->OffsetHigh = 0;\r
7509   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7510   if (ok) {\r
7511     err = NO_ERROR;\r
7512   } else {\r
7513     err = GetLastError();\r
7514     if (err == ERROR_IO_PENDING) {\r
7515       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7516       if (ok)\r
7517         err = NO_ERROR;\r
7518       else\r
7519         err = GetLastError();\r
7520     }\r
7521   }\r
7522   return err;\r
7523 }\r
7524 \r
7525 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7526 void CheckForInputBufferFull( InputSource * is )\r
7527 {\r
7528     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7529         /* Look for end of line */\r
7530         char * p = is->buf;\r
7531         \r
7532         while( p < is->next && *p != '\n' ) {\r
7533             p++;\r
7534         }\r
7535 \r
7536         if( p >= is->next ) {\r
7537             if (appData.debugMode) {\r
7538                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
7539             }\r
7540 \r
7541             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7542             is->count = (DWORD) -1;\r
7543             is->next = is->buf;\r
7544         }\r
7545     }\r
7546 }\r
7547 \r
7548 DWORD\r
7549 InputThread(LPVOID arg)\r
7550 {\r
7551   InputSource *is;\r
7552   OVERLAPPED ovl;\r
7553 \r
7554   is = (InputSource *) arg;\r
7555   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7556   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7557   while (is->hThread != NULL) {\r
7558     is->error = DoReadFile(is->hFile, is->next,\r
7559                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7560                            &is->count, &ovl);\r
7561     if (is->error == NO_ERROR) {\r
7562       is->next += is->count;\r
7563     } else {\r
7564       if (is->error == ERROR_BROKEN_PIPE) {\r
7565         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7566         is->count = 0;\r
7567       } else {\r
7568         is->count = (DWORD) -1;\r
7569         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7570         break; \r
7571       }\r
7572     }\r
7573 \r
7574     CheckForInputBufferFull( is );\r
7575 \r
7576     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7577 \r
7578     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7579 \r
7580     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7581   }\r
7582 \r
7583   CloseHandle(ovl.hEvent);\r
7584   CloseHandle(is->hFile);\r
7585 \r
7586   if (appData.debugMode) {\r
7587     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
7588   }\r
7589 \r
7590   return 0;\r
7591 }\r
7592 \r
7593 \r
7594 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7595 DWORD\r
7596 NonOvlInputThread(LPVOID arg)\r
7597 {\r
7598   InputSource *is;\r
7599   char *p, *q;\r
7600   int i;\r
7601   char prev;\r
7602 \r
7603   is = (InputSource *) arg;\r
7604   while (is->hThread != NULL) {\r
7605     is->error = ReadFile(is->hFile, is->next,\r
7606                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7607                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7608     if (is->error == NO_ERROR) {\r
7609       /* Change CRLF to LF */\r
7610       if (is->next > is->buf) {\r
7611         p = is->next - 1;\r
7612         i = is->count + 1;\r
7613       } else {\r
7614         p = is->next;\r
7615         i = is->count;\r
7616       }\r
7617       q = p;\r
7618       prev = NULLCHAR;\r
7619       while (i > 0) {\r
7620         if (prev == '\r' && *p == '\n') {\r
7621           *(q-1) = '\n';\r
7622           is->count--;\r
7623         } else { \r
7624           *q++ = *p;\r
7625         }\r
7626         prev = *p++;\r
7627         i--;\r
7628       }\r
7629       *q = NULLCHAR;\r
7630       is->next = q;\r
7631     } else {\r
7632       if (is->error == ERROR_BROKEN_PIPE) {\r
7633         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7634         is->count = 0; \r
7635       } else {\r
7636         is->count = (DWORD) -1;\r
7637       }\r
7638     }\r
7639 \r
7640     CheckForInputBufferFull( is );\r
7641 \r
7642     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7643 \r
7644     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7645 \r
7646     if (is->count < 0) break;  /* Quit on error */\r
7647   }\r
7648   CloseHandle(is->hFile);\r
7649   return 0;\r
7650 }\r
7651 \r
7652 DWORD\r
7653 SocketInputThread(LPVOID arg)\r
7654 {\r
7655   InputSource *is;\r
7656 \r
7657   is = (InputSource *) arg;\r
7658   while (is->hThread != NULL) {\r
7659     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7660     if ((int)is->count == SOCKET_ERROR) {\r
7661       is->count = (DWORD) -1;\r
7662       is->error = WSAGetLastError();\r
7663     } else {\r
7664       is->error = NO_ERROR;\r
7665       is->next += is->count;\r
7666       if (is->count == 0 && is->second == is) {\r
7667         /* End of file on stderr; quit with no message */\r
7668         break;\r
7669       }\r
7670     }\r
7671     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7672 \r
7673     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7674 \r
7675     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7676   }\r
7677   return 0;\r
7678 }\r
7679 \r
7680 VOID\r
7681 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7682 {\r
7683   InputSource *is;\r
7684 \r
7685   is = (InputSource *) lParam;\r
7686   if (is->lineByLine) {\r
7687     /* Feed in lines one by one */\r
7688     char *p = is->buf;\r
7689     char *q = p;\r
7690     while (q < is->next) {\r
7691       if (*q++ == '\n') {\r
7692         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7693         p = q;\r
7694       }\r
7695     }\r
7696     \r
7697     /* Move any partial line to the start of the buffer */\r
7698     q = is->buf;\r
7699     while (p < is->next) {\r
7700       *q++ = *p++;\r
7701     }\r
7702     is->next = q;\r
7703 \r
7704     if (is->error != NO_ERROR || is->count == 0) {\r
7705       /* Notify backend of the error.  Note: If there was a partial\r
7706          line at the end, it is not flushed through. */\r
7707       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7708     }\r
7709   } else {\r
7710     /* Feed in the whole chunk of input at once */\r
7711     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7712     is->next = is->buf;\r
7713   }\r
7714 }\r
7715 \r
7716 /*---------------------------------------------------------------------------*\\r
7717  *\r
7718  *  Menu enables. Used when setting various modes.\r
7719  *\r
7720 \*---------------------------------------------------------------------------*/\r
7721 \r
7722 typedef struct {\r
7723   int item;\r
7724   int flags;\r
7725 } Enables;\r
7726 \r
7727 VOID\r
7728 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7729 {\r
7730   while (enab->item > 0) {\r
7731     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7732     enab++;\r
7733   }\r
7734 }\r
7735 \r
7736 Enables gnuEnables[] = {\r
7737   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7738   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7739   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7740   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7741   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7742   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7743   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7744   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7745   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7746   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7747   { -1, -1 }\r
7748 };\r
7749 \r
7750 Enables icsEnables[] = {\r
7751   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7752   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7753   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7754   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7755   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7756   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7757   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7758   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7759   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7760   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7761   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7762   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7763   { -1, -1 }\r
7764 };\r
7765 \r
7766 #ifdef ZIPPY\r
7767 Enables zippyEnables[] = {\r
7768   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7769   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7770   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7771   { -1, -1 }\r
7772 };\r
7773 #endif\r
7774 \r
7775 Enables ncpEnables[] = {\r
7776   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7777   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7778   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7779   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7780   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7781   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7782   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7783   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7784   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7785   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7786   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7787   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7788   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7789   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7790   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7791   { -1, -1 }\r
7792 };\r
7793 \r
7794 Enables trainingOnEnables[] = {\r
7795   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7796   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7797   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7798   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7799   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7800   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7801   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7802   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7803   { -1, -1 }\r
7804 };\r
7805 \r
7806 Enables trainingOffEnables[] = {\r
7807   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7808   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7809   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7810   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7811   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7812   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7813   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7814   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7815   { -1, -1 }\r
7816 };\r
7817 \r
7818 /* These modify either ncpEnables or gnuEnables */\r
7819 Enables cmailEnables[] = {\r
7820   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7821   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7822   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7823   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7824   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7825   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7826   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7827   { -1, -1 }\r
7828 };\r
7829 \r
7830 Enables machineThinkingEnables[] = {\r
7831   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7832   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7833   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7834   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7835   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7836   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7837   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7838   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7839   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7840   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7841   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7842   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7843   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7844   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7845   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7846   { -1, -1 }\r
7847 };\r
7848 \r
7849 Enables userThinkingEnables[] = {\r
7850   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7851   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7852   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7853   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7854   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7855   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7856   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7857   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7858   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7859   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7860   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7861   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7862   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7863   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7864   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7865   { -1, -1 }\r
7866 };\r
7867 \r
7868 /*---------------------------------------------------------------------------*\\r
7869  *\r
7870  *  Front-end interface functions exported by XBoard.\r
7871  *  Functions appear in same order as prototypes in frontend.h.\r
7872  * \r
7873 \*---------------------------------------------------------------------------*/\r
7874 VOID\r
7875 ModeHighlight()\r
7876 {\r
7877   static UINT prevChecked = 0;\r
7878   static int prevPausing = 0;\r
7879   UINT nowChecked;\r
7880 \r
7881   if (pausing != prevPausing) {\r
7882     prevPausing = pausing;\r
7883     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7884                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7885     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7886   }\r
7887 \r
7888   switch (gameMode) {\r
7889   case BeginningOfGame:\r
7890     if (appData.icsActive)\r
7891       nowChecked = IDM_IcsClient;\r
7892     else if (appData.noChessProgram)\r
7893       nowChecked = IDM_EditGame;\r
7894     else\r
7895       nowChecked = IDM_MachineBlack;\r
7896     break;\r
7897   case MachinePlaysBlack:\r
7898     nowChecked = IDM_MachineBlack;\r
7899     break;\r
7900   case MachinePlaysWhite:\r
7901     nowChecked = IDM_MachineWhite;\r
7902     break;\r
7903   case TwoMachinesPlay:\r
7904     nowChecked = IDM_TwoMachines;\r
7905     break;\r
7906   case AnalyzeMode:\r
7907     nowChecked = IDM_AnalysisMode;\r
7908     break;\r
7909   case AnalyzeFile:\r
7910     nowChecked = IDM_AnalyzeFile;\r
7911     break;\r
7912   case EditGame:\r
7913     nowChecked = IDM_EditGame;\r
7914     break;\r
7915   case PlayFromGameFile:\r
7916     nowChecked = IDM_LoadGame;\r
7917     break;\r
7918   case EditPosition:\r
7919     nowChecked = IDM_EditPosition;\r
7920     break;\r
7921   case Training:\r
7922     nowChecked = IDM_Training;\r
7923     break;\r
7924   case IcsPlayingWhite:\r
7925   case IcsPlayingBlack:\r
7926   case IcsObserving:\r
7927   case IcsIdle:\r
7928     nowChecked = IDM_IcsClient;\r
7929     break;\r
7930   default:\r
7931   case EndOfGame:\r
7932     nowChecked = 0;\r
7933     break;\r
7934   }\r
7935   if (prevChecked != 0)\r
7936     (void) CheckMenuItem(GetMenu(hwndMain),\r
7937                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7938   if (nowChecked != 0)\r
7939     (void) CheckMenuItem(GetMenu(hwndMain),\r
7940                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7941 \r
7942   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7943     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7944                           MF_BYCOMMAND|MF_ENABLED);\r
7945   } else {\r
7946     (void) EnableMenuItem(GetMenu(hwndMain), \r
7947                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7948   }\r
7949 \r
7950   prevChecked = nowChecked;\r
7951 }\r
7952 \r
7953 VOID\r
7954 SetICSMode()\r
7955 {\r
7956   HMENU hmenu = GetMenu(hwndMain);\r
7957   SetMenuEnables(hmenu, icsEnables);\r
7958   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7959     MF_BYPOSITION|MF_ENABLED);\r
7960 #ifdef ZIPPY\r
7961   if (appData.zippyPlay) {\r
7962     SetMenuEnables(hmenu, zippyEnables);\r
7963   }\r
7964 #endif\r
7965 }\r
7966 \r
7967 VOID\r
7968 SetGNUMode()\r
7969 {\r
7970   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7971 }\r
7972 \r
7973 VOID\r
7974 SetNCPMode()\r
7975 {\r
7976   HMENU hmenu = GetMenu(hwndMain);\r
7977   SetMenuEnables(hmenu, ncpEnables);\r
7978   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7979     MF_BYPOSITION|MF_GRAYED);\r
7980     DrawMenuBar(hwndMain);\r
7981 }\r
7982 \r
7983 VOID\r
7984 SetCmailMode()\r
7985 {\r
7986   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7987 }\r
7988 \r
7989 VOID \r
7990 SetTrainingModeOn()\r
7991 {\r
7992   int i;\r
7993   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7994   for (i = 0; i < N_BUTTONS; i++) {\r
7995     if (buttonDesc[i].hwnd != NULL)\r
7996       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7997   }\r
7998   CommentPopDown();\r
7999 }\r
8000 \r
8001 VOID SetTrainingModeOff()\r
8002 {\r
8003   int i;\r
8004   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8005   for (i = 0; i < N_BUTTONS; i++) {\r
8006     if (buttonDesc[i].hwnd != NULL)\r
8007       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8008   }\r
8009 }\r
8010 \r
8011 \r
8012 VOID\r
8013 SetUserThinkingEnables()\r
8014 {\r
8015   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8016 }\r
8017 \r
8018 VOID\r
8019 SetMachineThinkingEnables()\r
8020 {\r
8021   HMENU hMenu = GetMenu(hwndMain);\r
8022   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8023 \r
8024   SetMenuEnables(hMenu, machineThinkingEnables);\r
8025 \r
8026   if (gameMode == MachinePlaysBlack) {\r
8027     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8028   } else if (gameMode == MachinePlaysWhite) {\r
8029     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8030   } else if (gameMode == TwoMachinesPlay) {\r
8031     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8032   }\r
8033 }\r
8034 \r
8035 \r
8036 VOID\r
8037 DisplayTitle(char *str)\r
8038 {\r
8039   char title[MSG_SIZ], *host;\r
8040   if (str[0] != NULLCHAR) {\r
8041     strcpy(title, str);\r
8042   } else if (appData.icsActive) {\r
8043     if (appData.icsCommPort[0] != NULLCHAR)\r
8044       host = "ICS";\r
8045     else \r
8046       host = appData.icsHost;\r
8047     sprintf(title, "%s: %s", szTitle, host);\r
8048   } else if (appData.noChessProgram) {\r
8049     strcpy(title, szTitle);\r
8050   } else {\r
8051     strcpy(title, szTitle);\r
8052     strcat(title, ": ");\r
8053     strcat(title, first.tidy);\r
8054   }\r
8055   SetWindowText(hwndMain, title);\r
8056 }\r
8057 \r
8058 \r
8059 VOID\r
8060 DisplayMessage(char *str1, char *str2)\r
8061 {\r
8062   HDC hdc;\r
8063   HFONT oldFont;\r
8064   int remain = MESSAGE_TEXT_MAX - 1;\r
8065   int len;\r
8066 \r
8067   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8068   messageText[0] = NULLCHAR;\r
8069   if (*str1) {\r
8070     len = strlen(str1);\r
8071     if (len > remain) len = remain;\r
8072     strncpy(messageText, str1, len);\r
8073     messageText[len] = NULLCHAR;\r
8074     remain -= len;\r
8075   }\r
8076   if (*str2 && remain >= 2) {\r
8077     if (*str1) {\r
8078       strcat(messageText, "  ");\r
8079       remain -= 2;\r
8080     }\r
8081     len = strlen(str2);\r
8082     if (len > remain) len = remain;\r
8083     strncat(messageText, str2, len);\r
8084   }\r
8085   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8086 \r
8087   if (IsIconic(hwndMain)) return;\r
8088   hdc = GetDC(hwndMain);\r
8089   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8090   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8091              &messageRect, messageText, strlen(messageText), NULL);\r
8092   (void) SelectObject(hdc, oldFont);\r
8093   (void) ReleaseDC(hwndMain, hdc);\r
8094 }\r
8095 \r
8096 VOID\r
8097 DisplayError(char *str, int error)\r
8098 {\r
8099   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8100   int len;\r
8101 \r
8102   if (error == 0) {\r
8103     strcpy(buf, str);\r
8104   } else {\r
8105     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8106                         NULL, error, LANG_NEUTRAL,\r
8107                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8108     if (len > 0) {\r
8109       sprintf(buf, "%s:\n%s", str, buf2);\r
8110     } else {\r
8111       ErrorMap *em = errmap;\r
8112       while (em->err != 0 && em->err != error) em++;\r
8113       if (em->err != 0) {\r
8114         sprintf(buf, "%s:\n%s", str, em->msg);\r
8115       } else {\r
8116         sprintf(buf, "%s:\nError code %d", str, error);\r
8117       }\r
8118     }\r
8119   }\r
8120   \r
8121   ErrorPopUp("Error", buf);\r
8122 }\r
8123 \r
8124 \r
8125 VOID\r
8126 DisplayMoveError(char *str)\r
8127 {\r
8128   fromX = fromY = -1;\r
8129   ClearHighlights();\r
8130   DrawPosition(FALSE, NULL);\r
8131   if (appData.popupMoveErrors) {\r
8132     ErrorPopUp("Error", str);\r
8133   } else {\r
8134     DisplayMessage(str, "");\r
8135     moveErrorMessageUp = TRUE;\r
8136   }\r
8137 }\r
8138 \r
8139 VOID\r
8140 DisplayFatalError(char *str, int error, int exitStatus)\r
8141 {\r
8142   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8143   int len;\r
8144   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8145 \r
8146   if (error != 0) {\r
8147     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8148                         NULL, error, LANG_NEUTRAL,\r
8149                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8150     if (len > 0) {\r
8151       sprintf(buf, "%s:\n%s", str, buf2);\r
8152     } else {\r
8153       ErrorMap *em = errmap;\r
8154       while (em->err != 0 && em->err != error) em++;\r
8155       if (em->err != 0) {\r
8156         sprintf(buf, "%s:\n%s", str, em->msg);\r
8157       } else {\r
8158         sprintf(buf, "%s:\nError code %d", str, error);\r
8159       }\r
8160     }\r
8161     str = buf;\r
8162   }\r
8163   if (appData.debugMode) {\r
8164     fprintf(debugFP, "%s: %s\n", label, str);\r
8165   }\r
8166   if (appData.popupExitMessage) {\r
8167     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8168                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8169   }\r
8170   ExitEvent(exitStatus);\r
8171 }\r
8172 \r
8173 \r
8174 VOID\r
8175 DisplayInformation(char *str)\r
8176 {\r
8177   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8178 }\r
8179 \r
8180 \r
8181 VOID\r
8182 DisplayNote(char *str)\r
8183 {\r
8184   ErrorPopUp("Note", str);\r
8185 }\r
8186 \r
8187 \r
8188 typedef struct {\r
8189   char *title, *question, *replyPrefix;\r
8190   ProcRef pr;\r
8191 } QuestionParams;\r
8192 \r
8193 LRESULT CALLBACK\r
8194 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8195 {\r
8196   static QuestionParams *qp;\r
8197   char reply[MSG_SIZ];\r
8198   int len, err;\r
8199 \r
8200   switch (message) {\r
8201   case WM_INITDIALOG:\r
8202     qp = (QuestionParams *) lParam;\r
8203     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8204     SetWindowText(hDlg, qp->title);\r
8205     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8206     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8207     return FALSE;\r
8208 \r
8209   case WM_COMMAND:\r
8210     switch (LOWORD(wParam)) {\r
8211     case IDOK:\r
8212       strcpy(reply, qp->replyPrefix);\r
8213       if (*reply) strcat(reply, " ");\r
8214       len = strlen(reply);\r
8215       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8216       strcat(reply, "\n");\r
8217       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8218       EndDialog(hDlg, TRUE);\r
8219       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8220       return TRUE;\r
8221     case IDCANCEL:\r
8222       EndDialog(hDlg, FALSE);\r
8223       return TRUE;\r
8224     default:\r
8225       break;\r
8226     }\r
8227     break;\r
8228   }\r
8229   return FALSE;\r
8230 }\r
8231 \r
8232 VOID\r
8233 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8234 {\r
8235     QuestionParams qp;\r
8236     FARPROC lpProc;\r
8237     \r
8238     qp.title = title;\r
8239     qp.question = question;\r
8240     qp.replyPrefix = replyPrefix;\r
8241     qp.pr = pr;\r
8242     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8243     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8244       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8245     FreeProcInstance(lpProc);\r
8246 }\r
8247 \r
8248 /* [AS] Pick FRC position */\r
8249 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8250 {\r
8251     static int * lpIndexFRC;\r
8252     BOOL index_is_ok;\r
8253     char buf[16];\r
8254 \r
8255     switch( message )\r
8256     {\r
8257     case WM_INITDIALOG:\r
8258         lpIndexFRC = (int *) lParam;\r
8259 \r
8260         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8261 \r
8262         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8263         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8264         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8265         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8266 \r
8267         break;\r
8268 \r
8269     case WM_COMMAND:\r
8270         switch( LOWORD(wParam) ) {\r
8271         case IDOK:\r
8272             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8273             EndDialog( hDlg, 0 );\r
8274             return TRUE;\r
8275         case IDCANCEL:\r
8276             EndDialog( hDlg, 1 );   \r
8277             return TRUE;\r
8278         case IDC_NFG_Edit:\r
8279             if( HIWORD(wParam) == EN_CHANGE ) {\r
8280                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8281 \r
8282                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8283             }\r
8284             return TRUE;\r
8285         case IDC_NFG_Random:\r
8286             sprintf( buf, "%d", myrandom() % 960 );\r
8287             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8288             return TRUE;\r
8289         }\r
8290 \r
8291         break;\r
8292     }\r
8293 \r
8294     return FALSE;\r
8295 }\r
8296 \r
8297 int NewGameFRC()\r
8298 {\r
8299     int result;\r
8300     int index = appData.defaultFrcPosition;\r
8301     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8302 \r
8303     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8304 \r
8305     if( result == 0 ) {\r
8306         appData.defaultFrcPosition = index;\r
8307     }\r
8308 \r
8309     return result;\r
8310 }\r
8311 \r
8312 /* [AS] Game list options */\r
8313 typedef struct {\r
8314     char id;\r
8315     char * name;\r
8316 } GLT_Item;\r
8317 \r
8318 static GLT_Item GLT_ItemInfo[] = {\r
8319     { GLT_EVENT,      "Event" },\r
8320     { GLT_SITE,       "Site" },\r
8321     { GLT_DATE,       "Date" },\r
8322     { GLT_ROUND,      "Round" },\r
8323     { GLT_PLAYERS,    "Players" },\r
8324     { GLT_RESULT,     "Result" },\r
8325     { GLT_WHITE_ELO,  "White Rating" },\r
8326     { GLT_BLACK_ELO,  "Black Rating" },\r
8327     { GLT_TIME_CONTROL,"Time Control" },\r
8328     { GLT_VARIANT,    "Variant" },\r
8329     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8330     { 0, 0 }\r
8331 };\r
8332 \r
8333 const char * GLT_FindItem( char id )\r
8334 {\r
8335     const char * result = 0;\r
8336 \r
8337     GLT_Item * list = GLT_ItemInfo;\r
8338 \r
8339     while( list->id != 0 ) {\r
8340         if( list->id == id ) {\r
8341             result = list->name;\r
8342             break;\r
8343         }\r
8344 \r
8345         list++;\r
8346     }\r
8347 \r
8348     return result;\r
8349 }\r
8350 \r
8351 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8352 {\r
8353     const char * name = GLT_FindItem( id );\r
8354 \r
8355     if( name != 0 ) {\r
8356         if( index >= 0 ) {\r
8357             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8358         }\r
8359         else {\r
8360             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8361         }\r
8362     }\r
8363 }\r
8364 \r
8365 void GLT_TagsToList( HWND hDlg, char * tags )\r
8366 {\r
8367     char * pc = tags;\r
8368 \r
8369     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8370 \r
8371     while( *pc ) {\r
8372         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8373         pc++;\r
8374     }\r
8375 \r
8376     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8377 \r
8378     pc = GLT_ALL_TAGS;\r
8379 \r
8380     while( *pc ) {\r
8381         if( strchr( tags, *pc ) == 0 ) {\r
8382             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8383         }\r
8384         pc++;\r
8385     }\r
8386 \r
8387     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8388 }\r
8389 \r
8390 char GLT_ListItemToTag( HWND hDlg, int index )\r
8391 {\r
8392     char result = '\0';\r
8393     char name[128];\r
8394 \r
8395     GLT_Item * list = GLT_ItemInfo;\r
8396 \r
8397     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8398         while( list->id != 0 ) {\r
8399             if( strcmp( list->name, name ) == 0 ) {\r
8400                 result = list->id;\r
8401                 break;\r
8402             }\r
8403 \r
8404             list++;\r
8405         }\r
8406     }\r
8407 \r
8408     return result;\r
8409 }\r
8410 \r
8411 void GLT_MoveSelection( HWND hDlg, int delta )\r
8412 {\r
8413     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8414     int idx2 = idx1 + delta;\r
8415     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8416 \r
8417     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8418         char buf[128];\r
8419 \r
8420         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8421         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8422         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8423         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8424     }\r
8425 }\r
8426 \r
8427 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8428 {\r
8429     static char glt[64];\r
8430     static char * lpUserGLT;\r
8431 \r
8432     switch( message )\r
8433     {\r
8434     case WM_INITDIALOG:\r
8435         lpUserGLT = (char *) lParam;\r
8436         \r
8437         strcpy( glt, lpUserGLT );\r
8438 \r
8439         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8440 \r
8441         /* Initialize list */\r
8442         GLT_TagsToList( hDlg, glt );\r
8443 \r
8444         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8445 \r
8446         break;\r
8447 \r
8448     case WM_COMMAND:\r
8449         switch( LOWORD(wParam) ) {\r
8450         case IDOK:\r
8451             {\r
8452                 char * pc = lpUserGLT;\r
8453                 int idx = 0;\r
8454                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8455                 char id;\r
8456 \r
8457                 do {\r
8458                     id = GLT_ListItemToTag( hDlg, idx );\r
8459 \r
8460                     *pc++ = id;\r
8461                     idx++;\r
8462                 } while( id != '\0' );\r
8463             }\r
8464             EndDialog( hDlg, 0 );\r
8465             return TRUE;\r
8466         case IDCANCEL:\r
8467             EndDialog( hDlg, 1 );\r
8468             return TRUE;\r
8469 \r
8470         case IDC_GLT_Default:\r
8471             strcpy( glt, GLT_DEFAULT_TAGS );\r
8472             GLT_TagsToList( hDlg, glt );\r
8473             return TRUE;\r
8474 \r
8475         case IDC_GLT_Restore:\r
8476             strcpy( glt, lpUserGLT );\r
8477             GLT_TagsToList( hDlg, glt );\r
8478             return TRUE;\r
8479 \r
8480         case IDC_GLT_Up:\r
8481             GLT_MoveSelection( hDlg, -1 );\r
8482             return TRUE;\r
8483 \r
8484         case IDC_GLT_Down:\r
8485             GLT_MoveSelection( hDlg, +1 );\r
8486             return TRUE;\r
8487         }\r
8488 \r
8489         break;\r
8490     }\r
8491 \r
8492     return FALSE;\r
8493 }\r
8494 \r
8495 int GameListOptions()\r
8496 {\r
8497     char glt[64];\r
8498     int result;\r
8499     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8500 \r
8501     strcpy( glt, appData.gameListTags );\r
8502 \r
8503     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8504 \r
8505     if( result == 0 ) {\r
8506         /* [AS] Memory leak here! */\r
8507         appData.gameListTags = strdup( glt ); \r
8508     }\r
8509 \r
8510     return result;\r
8511 }\r
8512 \r
8513 \r
8514 VOID\r
8515 DisplayIcsInteractionTitle(char *str)\r
8516 {\r
8517   char consoleTitle[MSG_SIZ];\r
8518 \r
8519   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8520   SetWindowText(hwndConsole, consoleTitle);\r
8521 }\r
8522 \r
8523 void\r
8524 DrawPosition(int fullRedraw, Board board)\r
8525 {\r
8526   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8527 }\r
8528 \r
8529 \r
8530 VOID\r
8531 ResetFrontEnd()\r
8532 {\r
8533   fromX = fromY = -1;\r
8534   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8535     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8536     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8537     dragInfo.lastpos = dragInfo.pos;\r
8538     dragInfo.start.x = dragInfo.start.y = -1;\r
8539     dragInfo.from = dragInfo.start;\r
8540     ReleaseCapture();\r
8541     DrawPosition(TRUE, NULL);\r
8542   }\r
8543 }\r
8544 \r
8545 \r
8546 VOID\r
8547 CommentPopUp(char *title, char *str)\r
8548 {\r
8549   HWND hwnd = GetActiveWindow();\r
8550   EitherCommentPopUp(0, title, str, FALSE);\r
8551   SetActiveWindow(hwnd);\r
8552 }\r
8553 \r
8554 VOID\r
8555 CommentPopDown(void)\r
8556 {\r
8557   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8558   if (commentDialog) {\r
8559     ShowWindow(commentDialog, SW_HIDE);\r
8560   }\r
8561   commentDialogUp = FALSE;\r
8562 }\r
8563 \r
8564 VOID\r
8565 EditCommentPopUp(int index, char *title, char *str)\r
8566 {\r
8567   EitherCommentPopUp(index, title, str, TRUE);\r
8568 }\r
8569 \r
8570 \r
8571 VOID\r
8572 RingBell()\r
8573 {\r
8574   MyPlaySound(&sounds[(int)SoundMove]);\r
8575 }\r
8576 \r
8577 VOID PlayIcsWinSound()\r
8578 {\r
8579   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8580 }\r
8581 \r
8582 VOID PlayIcsLossSound()\r
8583 {\r
8584   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8585 }\r
8586 \r
8587 VOID PlayIcsDrawSound()\r
8588 {\r
8589   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8590 }\r
8591 \r
8592 VOID PlayIcsUnfinishedSound()\r
8593 {\r
8594   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8595 }\r
8596 \r
8597 VOID\r
8598 PlayAlarmSound()\r
8599 {\r
8600   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8601 }\r
8602 \r
8603 \r
8604 VOID\r
8605 EchoOn()\r
8606 {\r
8607   HWND hInput;\r
8608   consoleEcho = TRUE;\r
8609   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8610   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8611   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8612 }\r
8613 \r
8614 \r
8615 VOID\r
8616 EchoOff()\r
8617 {\r
8618   CHARFORMAT cf;\r
8619   HWND hInput;\r
8620   consoleEcho = FALSE;\r
8621   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8622   /* This works OK: set text and background both to the same color */\r
8623   cf = consoleCF;\r
8624   cf.crTextColor = COLOR_ECHOOFF;\r
8625   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8626   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8627 }\r
8628 \r
8629 /* No Raw()...? */\r
8630 \r
8631 void Colorize(ColorClass cc, int continuation)\r
8632 {\r
8633   currentColorClass = cc;\r
8634   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8635   consoleCF.crTextColor = textAttribs[cc].color;\r
8636   consoleCF.dwEffects = textAttribs[cc].effects;\r
8637   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8638 }\r
8639 \r
8640 char *\r
8641 UserName()\r
8642 {\r
8643   static char buf[MSG_SIZ];\r
8644   DWORD bufsiz = MSG_SIZ;\r
8645 \r
8646   if (!GetUserName(buf, &bufsiz)) {\r
8647     /*DisplayError("Error getting user name", GetLastError());*/\r
8648     strcpy(buf, "User");\r
8649   }\r
8650   return buf;\r
8651 }\r
8652 \r
8653 char *\r
8654 HostName()\r
8655 {\r
8656   static char buf[MSG_SIZ];\r
8657   DWORD bufsiz = MSG_SIZ;\r
8658 \r
8659   if (!GetComputerName(buf, &bufsiz)) {\r
8660     /*DisplayError("Error getting host name", GetLastError());*/\r
8661     strcpy(buf, "Unknown");\r
8662   }\r
8663   return buf;\r
8664 }\r
8665 \r
8666 \r
8667 int\r
8668 ClockTimerRunning()\r
8669 {\r
8670   return clockTimerEvent != 0;\r
8671 }\r
8672 \r
8673 int\r
8674 StopClockTimer()\r
8675 {\r
8676   if (clockTimerEvent == 0) return FALSE;\r
8677   KillTimer(hwndMain, clockTimerEvent);\r
8678   clockTimerEvent = 0;\r
8679   return TRUE;\r
8680 }\r
8681 \r
8682 void\r
8683 StartClockTimer(long millisec)\r
8684 {\r
8685   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8686                              (UINT) millisec, NULL);\r
8687 }\r
8688 \r
8689 void\r
8690 DisplayWhiteClock(long timeRemaining, int highlight)\r
8691 {\r
8692   HDC hdc;\r
8693   hdc = GetDC(hwndMain);\r
8694   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8695 \r
8696   if (!IsIconic(hwndMain)) {\r
8697     DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White", flag);\r
8698   }\r
8699   if (highlight && iconCurrent == iconBlack) {\r
8700     iconCurrent = iconWhite;\r
8701     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8702     if (IsIconic(hwndMain)) {\r
8703       DrawIcon(hdc, 2, 2, iconCurrent);\r
8704     }\r
8705   }\r
8706   (void) ReleaseDC(hwndMain, hdc);\r
8707   if (hwndConsole)\r
8708     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8709 }\r
8710 \r
8711 void\r
8712 DisplayBlackClock(long timeRemaining, int highlight)\r
8713 {\r
8714   HDC hdc;\r
8715   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8716 \r
8717   hdc = GetDC(hwndMain);\r
8718   if (!IsIconic(hwndMain)) {\r
8719     DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black", flag);\r
8720   }\r
8721   if (highlight && iconCurrent == iconWhite) {\r
8722     iconCurrent = iconBlack;\r
8723     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8724     if (IsIconic(hwndMain)) {\r
8725       DrawIcon(hdc, 2, 2, iconCurrent);\r
8726     }\r
8727   }\r
8728   (void) ReleaseDC(hwndMain, hdc);\r
8729   if (hwndConsole)\r
8730     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8731 }\r
8732 \r
8733 \r
8734 int\r
8735 LoadGameTimerRunning()\r
8736 {\r
8737   return loadGameTimerEvent != 0;\r
8738 }\r
8739 \r
8740 int\r
8741 StopLoadGameTimer()\r
8742 {\r
8743   if (loadGameTimerEvent == 0) return FALSE;\r
8744   KillTimer(hwndMain, loadGameTimerEvent);\r
8745   loadGameTimerEvent = 0;\r
8746   return TRUE;\r
8747 }\r
8748 \r
8749 void\r
8750 StartLoadGameTimer(long millisec)\r
8751 {\r
8752   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8753                                 (UINT) millisec, NULL);\r
8754 }\r
8755 \r
8756 void\r
8757 AutoSaveGame()\r
8758 {\r
8759   char *defName;\r
8760   FILE *f;\r
8761   char fileTitle[MSG_SIZ];\r
8762 \r
8763   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8764   f = OpenFileDialog(hwndMain, TRUE, defName,\r
8765                      appData.oldSaveStyle ? "gam" : "pgn",\r
8766                      GAME_FILT, \r
8767                      "Save Game to File", NULL, fileTitle, NULL);\r
8768   if (f != NULL) {\r
8769     SaveGame(f, 0, "");\r
8770     fclose(f);\r
8771   }\r
8772 }\r
8773 \r
8774 \r
8775 void\r
8776 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8777 {\r
8778   if (delayedTimerEvent != 0) {\r
8779     if (appData.debugMode) {\r
8780       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8781     }\r
8782     KillTimer(hwndMain, delayedTimerEvent);\r
8783     delayedTimerEvent = 0;\r
8784     delayedTimerCallback();\r
8785   }\r
8786   delayedTimerCallback = cb;\r
8787   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8788                                 (UINT) millisec, NULL);\r
8789 }\r
8790 \r
8791 DelayedEventCallback\r
8792 GetDelayedEvent()\r
8793 {\r
8794   if (delayedTimerEvent) {\r
8795     return delayedTimerCallback;\r
8796   } else {\r
8797     return NULL;\r
8798   }\r
8799 }\r
8800 \r
8801 void\r
8802 CancelDelayedEvent()\r
8803 {\r
8804   if (delayedTimerEvent) {\r
8805     KillTimer(hwndMain, delayedTimerEvent);\r
8806     delayedTimerEvent = 0;\r
8807   }\r
8808 }\r
8809 \r
8810 /* Start a child process running the given program.\r
8811    The process's standard output can be read from "from", and its\r
8812    standard input can be written to "to".\r
8813    Exit with fatal error if anything goes wrong.\r
8814    Returns an opaque pointer that can be used to destroy the process\r
8815    later.\r
8816 */\r
8817 int\r
8818 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8819 {\r
8820 #define BUFSIZE 4096\r
8821 \r
8822   HANDLE hChildStdinRd, hChildStdinWr,\r
8823     hChildStdoutRd, hChildStdoutWr;\r
8824   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8825   SECURITY_ATTRIBUTES saAttr;\r
8826   BOOL fSuccess;\r
8827   PROCESS_INFORMATION piProcInfo;\r
8828   STARTUPINFO siStartInfo;\r
8829   ChildProc *cp;\r
8830   char buf[MSG_SIZ];\r
8831   DWORD err;\r
8832 \r
8833   if (appData.debugMode) {\r
8834     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8835   }\r
8836 \r
8837   *pr = NoProc;\r
8838 \r
8839   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8840   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8841   saAttr.bInheritHandle = TRUE;\r
8842   saAttr.lpSecurityDescriptor = NULL;\r
8843 \r
8844   /*\r
8845    * The steps for redirecting child's STDOUT:\r
8846    *     1. Create anonymous pipe to be STDOUT for child.\r
8847    *     2. Create a noninheritable duplicate of read handle,\r
8848    *         and close the inheritable read handle.\r
8849    */\r
8850 \r
8851   /* Create a pipe for the child's STDOUT. */\r
8852   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8853     return GetLastError();\r
8854   }\r
8855 \r
8856   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8857   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8858                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8859                              FALSE,     /* not inherited */\r
8860                              DUPLICATE_SAME_ACCESS);\r
8861   if (! fSuccess) {\r
8862     return GetLastError();\r
8863   }\r
8864   CloseHandle(hChildStdoutRd);\r
8865 \r
8866   /*\r
8867    * The steps for redirecting child's STDIN:\r
8868    *     1. Create anonymous pipe to be STDIN for child.\r
8869    *     2. Create a noninheritable duplicate of write handle,\r
8870    *         and close the inheritable write handle.\r
8871    */\r
8872 \r
8873   /* Create a pipe for the child's STDIN. */\r
8874   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8875     return GetLastError();\r
8876   }\r
8877 \r
8878   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8879   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8880                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8881                              FALSE,     /* not inherited */\r
8882                              DUPLICATE_SAME_ACCESS);\r
8883   if (! fSuccess) {\r
8884     return GetLastError();\r
8885   }\r
8886   CloseHandle(hChildStdinWr);\r
8887 \r
8888   /* Arrange to (1) look in dir for the child .exe file, and\r
8889    * (2) have dir be the child's working directory.  Interpret\r
8890    * dir relative to the directory WinBoard loaded from. */\r
8891   GetCurrentDirectory(MSG_SIZ, buf);\r
8892   SetCurrentDirectory(installDir);\r
8893   SetCurrentDirectory(dir);\r
8894 \r
8895   /* Now create the child process. */\r
8896 \r
8897   siStartInfo.cb = sizeof(STARTUPINFO);\r
8898   siStartInfo.lpReserved = NULL;\r
8899   siStartInfo.lpDesktop = NULL;\r
8900   siStartInfo.lpTitle = NULL;\r
8901   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8902   siStartInfo.cbReserved2 = 0;\r
8903   siStartInfo.lpReserved2 = NULL;\r
8904   siStartInfo.hStdInput = hChildStdinRd;\r
8905   siStartInfo.hStdOutput = hChildStdoutWr;\r
8906   siStartInfo.hStdError = hChildStdoutWr;\r
8907 \r
8908   fSuccess = CreateProcess(NULL,\r
8909                            cmdLine,        /* command line */\r
8910                            NULL,           /* process security attributes */\r
8911                            NULL,           /* primary thread security attrs */\r
8912                            TRUE,           /* handles are inherited */\r
8913                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8914                            NULL,           /* use parent's environment */\r
8915                            NULL,\r
8916                            &siStartInfo, /* STARTUPINFO pointer */\r
8917                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8918 \r
8919   err = GetLastError();\r
8920   SetCurrentDirectory(buf); /* return to prev directory */\r
8921   if (! fSuccess) {\r
8922     return err;\r
8923   }\r
8924 \r
8925   /* Close the handles we don't need in the parent */\r
8926   CloseHandle(piProcInfo.hThread);\r
8927   CloseHandle(hChildStdinRd);\r
8928   CloseHandle(hChildStdoutWr);\r
8929 \r
8930   /* Prepare return value */\r
8931   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8932   cp->kind = CPReal;\r
8933   cp->hProcess = piProcInfo.hProcess;\r
8934   cp->pid = piProcInfo.dwProcessId;\r
8935   cp->hFrom = hChildStdoutRdDup;\r
8936   cp->hTo = hChildStdinWrDup;\r
8937 \r
8938   *pr = (void *) cp;\r
8939 \r
8940   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8941      2000 where engines sometimes don't see the initial command(s)\r
8942      from WinBoard and hang.  I don't understand how that can happen,\r
8943      but the Sleep is harmless, so I've put it in.  Others have also\r
8944      reported what may be the same problem, so hopefully this will fix\r
8945      it for them too.  */\r
8946   Sleep(500);\r
8947 \r
8948   return NO_ERROR;\r
8949 }\r
8950 \r
8951 \r
8952 void\r
8953 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8954 {\r
8955   ChildProc *cp;\r
8956 \r
8957   cp = (ChildProc *) pr;\r
8958   if (cp == NULL) return;\r
8959 \r
8960   switch (cp->kind) {\r
8961   case CPReal:\r
8962     /* TerminateProcess is considered harmful, so... */\r
8963     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8964     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8965     /* The following doesn't work because the chess program\r
8966        doesn't "have the same console" as WinBoard.  Maybe\r
8967        we could arrange for this even though neither WinBoard\r
8968        nor the chess program uses a console for stdio? */\r
8969     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8970 \r
8971     /* [AS] Special termination modes for misbehaving programs... */\r
8972     if( signal == 9 ) {\r
8973         if ( appData.debugMode) {\r
8974             fprintf( debugFP, "Terminating process %u\n", cp->pid );\r
8975         }\r
8976 \r
8977         TerminateProcess( cp->hProcess, 0 );\r
8978     }\r
8979     else if( signal == 10 ) {\r
8980         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8981 \r
8982         if( dw != WAIT_OBJECT_0 ) {\r
8983             if ( appData.debugMode) {\r
8984                 fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );\r
8985             }\r
8986 \r
8987             TerminateProcess( cp->hProcess, 0 );\r
8988         }\r
8989     }\r
8990 \r
8991     CloseHandle(cp->hProcess);\r
8992     break;\r
8993 \r
8994   case CPComm:\r
8995     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8996     break;\r
8997 \r
8998   case CPSock:\r
8999     closesocket(cp->sock);\r
9000     WSACleanup();\r
9001     break;\r
9002 \r
9003   case CPRcmd:\r
9004     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9005     closesocket(cp->sock);\r
9006     closesocket(cp->sock2);\r
9007     WSACleanup();\r
9008     break;\r
9009   }\r
9010   free(cp);\r
9011 }\r
9012 \r
9013 void\r
9014 InterruptChildProcess(ProcRef pr)\r
9015 {\r
9016   ChildProc *cp;\r
9017 \r
9018   cp = (ChildProc *) pr;\r
9019   if (cp == NULL) return;\r
9020   switch (cp->kind) {\r
9021   case CPReal:\r
9022     /* The following doesn't work because the chess program\r
9023        doesn't "have the same console" as WinBoard.  Maybe\r
9024        we could arrange for this even though neither WinBoard\r
9025        nor the chess program uses a console for stdio */\r
9026     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9027     break;\r
9028 \r
9029   case CPComm:\r
9030   case CPSock:\r
9031     /* Can't interrupt */\r
9032     break;\r
9033 \r
9034   case CPRcmd:\r
9035     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9036     break;\r
9037   }\r
9038 }\r
9039 \r
9040 \r
9041 int\r
9042 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9043 {\r
9044   char cmdLine[MSG_SIZ];\r
9045 \r
9046   if (port[0] == NULLCHAR) {\r
9047     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9048   } else {\r
9049     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9050   }\r
9051   return StartChildProcess(cmdLine, "", pr);\r
9052 }\r
9053 \r
9054 \r
9055 /* Code to open TCP sockets */\r
9056 \r
9057 int\r
9058 OpenTCP(char *host, char *port, ProcRef *pr)\r
9059 {\r
9060   ChildProc *cp;\r
9061   int err;\r
9062   SOCKET s;\r
9063   struct sockaddr_in sa, mysa;\r
9064   struct hostent FAR *hp;\r
9065   unsigned short uport;\r
9066   WORD wVersionRequested;\r
9067   WSADATA wsaData;\r
9068 \r
9069   /* Initialize socket DLL */\r
9070   wVersionRequested = MAKEWORD(1, 1);\r
9071   err = WSAStartup(wVersionRequested, &wsaData);\r
9072   if (err != 0) return err;\r
9073 \r
9074   /* Make socket */\r
9075   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9076     err = WSAGetLastError();\r
9077     WSACleanup();\r
9078     return err;\r
9079   }\r
9080 \r
9081   /* Bind local address using (mostly) don't-care values.\r
9082    */\r
9083   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9084   mysa.sin_family = AF_INET;\r
9085   mysa.sin_addr.s_addr = INADDR_ANY;\r
9086   uport = (unsigned short) 0;\r
9087   mysa.sin_port = htons(uport);\r
9088   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9089       == SOCKET_ERROR) {\r
9090     err = WSAGetLastError();\r
9091     WSACleanup();\r
9092     return err;\r
9093   }\r
9094 \r
9095   /* Resolve remote host name */\r
9096   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9097   if (!(hp = gethostbyname(host))) {\r
9098     unsigned int b0, b1, b2, b3;\r
9099 \r
9100     err = WSAGetLastError();\r
9101 \r
9102     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9103       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9104       hp->h_addrtype = AF_INET;\r
9105       hp->h_length = 4;\r
9106       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9107       hp->h_addr_list[0] = (char *) malloc(4);\r
9108       hp->h_addr_list[0][0] = (char) b0;\r
9109       hp->h_addr_list[0][1] = (char) b1;\r
9110       hp->h_addr_list[0][2] = (char) b2;\r
9111       hp->h_addr_list[0][3] = (char) b3;\r
9112     } else {\r
9113       WSACleanup();\r
9114       return err;\r
9115     }\r
9116   }\r
9117   sa.sin_family = hp->h_addrtype;\r
9118   uport = (unsigned short) atoi(port);\r
9119   sa.sin_port = htons(uport);\r
9120   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9121 \r
9122   /* Make connection */\r
9123   if (connect(s, (struct sockaddr *) &sa,\r
9124               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9125     err = WSAGetLastError();\r
9126     WSACleanup();\r
9127     return err;\r
9128   }\r
9129 \r
9130   /* Prepare return value */\r
9131   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9132   cp->kind = CPSock;\r
9133   cp->sock = s;\r
9134   *pr = (ProcRef *) cp;\r
9135 \r
9136   return NO_ERROR;\r
9137 }\r
9138 \r
9139 int\r
9140 OpenCommPort(char *name, ProcRef *pr)\r
9141 {\r
9142   HANDLE h;\r
9143   COMMTIMEOUTS ct;\r
9144   ChildProc *cp;\r
9145   char fullname[MSG_SIZ];\r
9146 \r
9147   if (*name != '\\')\r
9148     sprintf(fullname, "\\\\.\\%s", name);\r
9149   else\r
9150     strcpy(fullname, name);\r
9151 \r
9152   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9153                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9154   if (h == (HANDLE) -1) {\r
9155     return GetLastError();\r
9156   }\r
9157   hCommPort = h;\r
9158 \r
9159   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9160 \r
9161   /* Accumulate characters until a 100ms pause, then parse */\r
9162   ct.ReadIntervalTimeout = 100;\r
9163   ct.ReadTotalTimeoutMultiplier = 0;\r
9164   ct.ReadTotalTimeoutConstant = 0;\r
9165   ct.WriteTotalTimeoutMultiplier = 0;\r
9166   ct.WriteTotalTimeoutConstant = 0;\r
9167   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9168 \r
9169   /* Prepare return value */\r
9170   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9171   cp->kind = CPComm;\r
9172   cp->hFrom = h;\r
9173   cp->hTo = h;\r
9174   *pr = (ProcRef *) cp;\r
9175 \r
9176   return NO_ERROR;\r
9177 }\r
9178 \r
9179 int\r
9180 OpenLoopback(ProcRef *pr)\r
9181 {\r
9182   DisplayFatalError("Not implemented", 0, 1);\r
9183   return NO_ERROR;\r
9184 }\r
9185 \r
9186 \r
9187 int\r
9188 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9189 {\r
9190   ChildProc *cp;\r
9191   int err;\r
9192   SOCKET s, s2, s3;\r
9193   struct sockaddr_in sa, mysa;\r
9194   struct hostent FAR *hp;\r
9195   unsigned short uport;\r
9196   WORD wVersionRequested;\r
9197   WSADATA wsaData;\r
9198   int fromPort;\r
9199   char stderrPortStr[MSG_SIZ];\r
9200 \r
9201   /* Initialize socket DLL */\r
9202   wVersionRequested = MAKEWORD(1, 1);\r
9203   err = WSAStartup(wVersionRequested, &wsaData);\r
9204   if (err != 0) return err;\r
9205 \r
9206   /* Resolve remote host name */\r
9207   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9208   if (!(hp = gethostbyname(host))) {\r
9209     unsigned int b0, b1, b2, b3;\r
9210 \r
9211     err = WSAGetLastError();\r
9212 \r
9213     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9214       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9215       hp->h_addrtype = AF_INET;\r
9216       hp->h_length = 4;\r
9217       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9218       hp->h_addr_list[0] = (char *) malloc(4);\r
9219       hp->h_addr_list[0][0] = (char) b0;\r
9220       hp->h_addr_list[0][1] = (char) b1;\r
9221       hp->h_addr_list[0][2] = (char) b2;\r
9222       hp->h_addr_list[0][3] = (char) b3;\r
9223     } else {\r
9224       WSACleanup();\r
9225       return err;\r
9226     }\r
9227   }\r
9228   sa.sin_family = hp->h_addrtype;\r
9229   uport = (unsigned short) 514;\r
9230   sa.sin_port = htons(uport);\r
9231   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9232 \r
9233   /* Bind local socket to unused "privileged" port address\r
9234    */\r
9235   s = INVALID_SOCKET;\r
9236   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9237   mysa.sin_family = AF_INET;\r
9238   mysa.sin_addr.s_addr = INADDR_ANY;\r
9239   for (fromPort = 1023;; fromPort--) {\r
9240     if (fromPort < 0) {\r
9241       WSACleanup();\r
9242       return WSAEADDRINUSE;\r
9243     }\r
9244     if (s == INVALID_SOCKET) {\r
9245       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9246         err = WSAGetLastError();\r
9247         WSACleanup();\r
9248         return err;\r
9249       }\r
9250     }\r
9251     uport = (unsigned short) fromPort;\r
9252     mysa.sin_port = htons(uport);\r
9253     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9254         == SOCKET_ERROR) {\r
9255       err = WSAGetLastError();\r
9256       if (err == WSAEADDRINUSE) continue;\r
9257       WSACleanup();\r
9258       return err;\r
9259     }\r
9260     if (connect(s, (struct sockaddr *) &sa,\r
9261       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9262       err = WSAGetLastError();\r
9263       if (err == WSAEADDRINUSE) {\r
9264         closesocket(s);\r
9265         s = -1;\r
9266         continue;\r
9267       }\r
9268       WSACleanup();\r
9269       return err;\r
9270     }\r
9271     break;\r
9272   }\r
9273 \r
9274   /* Bind stderr local socket to unused "privileged" port address\r
9275    */\r
9276   s2 = INVALID_SOCKET;\r
9277   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9278   mysa.sin_family = AF_INET;\r
9279   mysa.sin_addr.s_addr = INADDR_ANY;\r
9280   for (fromPort = 1023;; fromPort--) {\r
9281     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9282     if (fromPort < 0) {\r
9283       (void) closesocket(s);\r
9284       WSACleanup();\r
9285       return WSAEADDRINUSE;\r
9286     }\r
9287     if (s2 == INVALID_SOCKET) {\r
9288       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9289         err = WSAGetLastError();\r
9290         closesocket(s);\r
9291         WSACleanup();\r
9292         return err;\r
9293       }\r
9294     }\r
9295     uport = (unsigned short) fromPort;\r
9296     mysa.sin_port = htons(uport);\r
9297     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9298         == SOCKET_ERROR) {\r
9299       err = WSAGetLastError();\r
9300       if (err == WSAEADDRINUSE) continue;\r
9301       (void) closesocket(s);\r
9302       WSACleanup();\r
9303       return err;\r
9304     }\r
9305     if (listen(s2, 1) == SOCKET_ERROR) {\r
9306       err = WSAGetLastError();\r
9307       if (err == WSAEADDRINUSE) {\r
9308         closesocket(s2);\r
9309         s2 = INVALID_SOCKET;\r
9310         continue;\r
9311       }\r
9312       (void) closesocket(s);\r
9313       (void) closesocket(s2);\r
9314       WSACleanup();\r
9315       return err;\r
9316     }\r
9317     break;\r
9318   }\r
9319   prevStderrPort = fromPort; // remember port used\r
9320   sprintf(stderrPortStr, "%d", fromPort);\r
9321 \r
9322   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9323     err = WSAGetLastError();\r
9324     (void) closesocket(s);\r
9325     (void) closesocket(s2);\r
9326     WSACleanup();\r
9327     return err;\r
9328   }\r
9329 \r
9330   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9331     err = WSAGetLastError();\r
9332     (void) closesocket(s);\r
9333     (void) closesocket(s2);\r
9334     WSACleanup();\r
9335     return err;\r
9336   }\r
9337   if (*user == NULLCHAR) user = UserName();\r
9338   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9339     err = WSAGetLastError();\r
9340     (void) closesocket(s);\r
9341     (void) closesocket(s2);\r
9342     WSACleanup();\r
9343     return err;\r
9344   }\r
9345   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9346     err = WSAGetLastError();\r
9347     (void) closesocket(s);\r
9348     (void) closesocket(s2);\r
9349     WSACleanup();\r
9350     return err;\r
9351   }\r
9352 \r
9353   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9354     err = WSAGetLastError();\r
9355     (void) closesocket(s);\r
9356     (void) closesocket(s2);\r
9357     WSACleanup();\r
9358     return err;\r
9359   }\r
9360   (void) closesocket(s2);  /* Stop listening */\r
9361 \r
9362   /* Prepare return value */\r
9363   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9364   cp->kind = CPRcmd;\r
9365   cp->sock = s;\r
9366   cp->sock2 = s3;\r
9367   *pr = (ProcRef *) cp;\r
9368 \r
9369   return NO_ERROR;\r
9370 }\r
9371 \r
9372 \r
9373 InputSourceRef\r
9374 AddInputSource(ProcRef pr, int lineByLine,\r
9375                InputCallback func, VOIDSTAR closure)\r
9376 {\r
9377   InputSource *is, *is2 = NULL;\r
9378   ChildProc *cp = (ChildProc *) pr;\r
9379 \r
9380   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9381   is->lineByLine = lineByLine;\r
9382   is->func = func;\r
9383   is->closure = closure;\r
9384   is->second = NULL;\r
9385   is->next = is->buf;\r
9386   if (pr == NoProc) {\r
9387     is->kind = CPReal;\r
9388     consoleInputSource = is;\r
9389   } else {\r
9390     is->kind = cp->kind;\r
9391     /* \r
9392         [AS] Try to avoid a race condition if the thread is given control too early:\r
9393         we create all threads suspended so that the is->hThread variable can be\r
9394         safely assigned, then let the threads start with ResumeThread.\r
9395     */\r
9396     switch (cp->kind) {\r
9397     case CPReal:\r
9398       is->hFile = cp->hFrom;\r
9399       cp->hFrom = NULL; /* now owned by InputThread */\r
9400       is->hThread =\r
9401         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9402                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9403       break;\r
9404 \r
9405     case CPComm:\r
9406       is->hFile = cp->hFrom;\r
9407       cp->hFrom = NULL; /* now owned by InputThread */\r
9408       is->hThread =\r
9409         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9410                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9411       break;\r
9412 \r
9413     case CPSock:\r
9414       is->sock = cp->sock;\r
9415       is->hThread =\r
9416         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9417                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9418       break;\r
9419 \r
9420     case CPRcmd:\r
9421       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9422       *is2 = *is;\r
9423       is->sock = cp->sock;\r
9424       is->second = is2;\r
9425       is2->sock = cp->sock2;\r
9426       is2->second = is2;\r
9427       is->hThread =\r
9428         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9429                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9430       is2->hThread =\r
9431         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9432                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9433       break;\r
9434     }\r
9435 \r
9436     if( is->hThread != NULL ) {\r
9437         ResumeThread( is->hThread );\r
9438     }\r
9439 \r
9440     if( is2 != NULL && is2->hThread != NULL ) {\r
9441         ResumeThread( is2->hThread );\r
9442     }\r
9443   }\r
9444 \r
9445   return (InputSourceRef) is;\r
9446 }\r
9447 \r
9448 void\r
9449 RemoveInputSource(InputSourceRef isr)\r
9450 {\r
9451   InputSource *is;\r
9452 \r
9453   is = (InputSource *) isr;\r
9454   is->hThread = NULL;  /* tell thread to stop */\r
9455   CloseHandle(is->hThread);\r
9456   if (is->second != NULL) {\r
9457     is->second->hThread = NULL;\r
9458     CloseHandle(is->second->hThread);\r
9459   }\r
9460 }\r
9461 \r
9462 \r
9463 int\r
9464 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9465 {\r
9466   DWORD dOutCount;\r
9467   int outCount = SOCKET_ERROR;\r
9468   ChildProc *cp = (ChildProc *) pr;\r
9469   static OVERLAPPED ovl;\r
9470 \r
9471   if (pr == NoProc) {\r
9472     ConsoleOutput(message, count, FALSE);\r
9473     return count;\r
9474   } \r
9475 \r
9476   if (ovl.hEvent == NULL) {\r
9477     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9478   }\r
9479   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9480 \r
9481   switch (cp->kind) {\r
9482   case CPSock:\r
9483   case CPRcmd:\r
9484     outCount = send(cp->sock, message, count, 0);\r
9485     if (outCount == SOCKET_ERROR) {\r
9486       *outError = WSAGetLastError();\r
9487     } else {\r
9488       *outError = NO_ERROR;\r
9489     }\r
9490     break;\r
9491 \r
9492   case CPReal:\r
9493     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9494                   &dOutCount, NULL)) {\r
9495       *outError = NO_ERROR;\r
9496       outCount = (int) dOutCount;\r
9497     } else {\r
9498       *outError = GetLastError();\r
9499     }\r
9500     break;\r
9501 \r
9502   case CPComm:\r
9503     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9504                             &dOutCount, &ovl);\r
9505     if (*outError == NO_ERROR) {\r
9506       outCount = (int) dOutCount;\r
9507     }\r
9508     break;\r
9509   }\r
9510   return outCount;\r
9511 }\r
9512 \r
9513 int\r
9514 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9515                        long msdelay)\r
9516 {\r
9517   /* Ignore delay, not implemented for WinBoard */\r
9518   return OutputToProcess(pr, message, count, outError);\r
9519 }\r
9520 \r
9521 \r
9522 void\r
9523 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9524                         char *buf, int count, int error)\r
9525 {\r
9526   DisplayFatalError("Not implemented", 0, 1);\r
9527 }\r
9528 \r
9529 /* see wgamelist.c for Game List functions */\r
9530 /* see wedittags.c for Edit Tags functions */\r
9531 \r
9532 \r
9533 VOID\r
9534 ICSInitScript()\r
9535 {\r
9536   FILE *f;\r
9537   char buf[MSG_SIZ];\r
9538   char *dummy;\r
9539 \r
9540   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9541     f = fopen(buf, "r");\r
9542     if (f != NULL) {\r
9543       ProcessICSInitScript(f);\r
9544       fclose(f);\r
9545     }\r
9546   }\r
9547 }\r
9548 \r
9549 \r
9550 VOID\r
9551 StartAnalysisClock()\r
9552 {\r
9553   if (analysisTimerEvent) return;\r
9554   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9555                                         (UINT) 2000, NULL);\r
9556 }\r
9557 \r
9558 LRESULT CALLBACK\r
9559 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9560 {\r
9561   static HANDLE hwndText;\r
9562   RECT rect;\r
9563   static int sizeX, sizeY;\r
9564   int newSizeX, newSizeY, flags;\r
9565   MINMAXINFO *mmi;\r
9566 \r
9567   switch (message) {\r
9568   case WM_INITDIALOG: /* message: initialize dialog box */\r
9569     /* Initialize the dialog items */\r
9570     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
9571     SetWindowText(hDlg, analysisTitle);\r
9572     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
9573     /* Size and position the dialog */\r
9574     if (!analysisDialog) {\r
9575       analysisDialog = hDlg;\r
9576       flags = SWP_NOZORDER;\r
9577       GetClientRect(hDlg, &rect);\r
9578       sizeX = rect.right;\r
9579       sizeY = rect.bottom;\r
9580       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
9581           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
9582         WINDOWPLACEMENT wp;\r
9583         EnsureOnScreen(&analysisX, &analysisY);\r
9584         wp.length = sizeof(WINDOWPLACEMENT);\r
9585         wp.flags = 0;\r
9586         wp.showCmd = SW_SHOW;\r
9587         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
9588         wp.rcNormalPosition.left = analysisX;\r
9589         wp.rcNormalPosition.right = analysisX + analysisW;\r
9590         wp.rcNormalPosition.top = analysisY;\r
9591         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
9592         SetWindowPlacement(hDlg, &wp);\r
9593 \r
9594         GetClientRect(hDlg, &rect);\r
9595         newSizeX = rect.right;\r
9596         newSizeY = rect.bottom;\r
9597         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
9598                               newSizeX, newSizeY);\r
9599         sizeX = newSizeX;\r
9600         sizeY = newSizeY;\r
9601       }\r
9602     }\r
9603     return FALSE;\r
9604 \r
9605   case WM_COMMAND: /* message: received a command */\r
9606     switch (LOWORD(wParam)) {\r
9607     case IDCANCEL:\r
9608       EditGameEvent();\r
9609       return TRUE;\r
9610     default:\r
9611       break;\r
9612     }\r
9613     break;\r
9614 \r
9615   case WM_SIZE:\r
9616     newSizeX = LOWORD(lParam);\r
9617     newSizeY = HIWORD(lParam);\r
9618     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
9619     sizeX = newSizeX;\r
9620     sizeY = newSizeY;\r
9621     break;\r
9622 \r
9623   case WM_GETMINMAXINFO:\r
9624     /* Prevent resizing window too small */\r
9625     mmi = (MINMAXINFO *) lParam;\r
9626     mmi->ptMinTrackSize.x = 100;\r
9627     mmi->ptMinTrackSize.y = 100;\r
9628     break;\r
9629   }\r
9630   return FALSE;\r
9631 }\r
9632 \r
9633 VOID\r
9634 AnalysisPopUp(char* title, char* str)\r
9635 {\r
9636   FARPROC lpProc;\r
9637   char *p, *q;\r
9638 \r
9639   /* [AS] */\r
9640   EngineOutputPopUp();\r
9641   return;\r
9642 \r
9643   if (str == NULL) str = "";\r
9644   p = (char *) malloc(2 * strlen(str) + 2);\r
9645   q = p;\r
9646   while (*str) {\r
9647     if (*str == '\n') *q++ = '\r';\r
9648     *q++ = *str++;\r
9649   }\r
9650   *q = NULLCHAR;\r
9651   if (analysisText != NULL) free(analysisText);\r
9652   analysisText = p;\r
9653 \r
9654   if (analysisDialog) {\r
9655     SetWindowText(analysisDialog, title);\r
9656     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
9657     ShowWindow(analysisDialog, SW_SHOW);\r
9658   } else {\r
9659     analysisTitle = title;\r
9660     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
9661     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
9662                  hwndMain, (DLGPROC)lpProc);\r
9663     FreeProcInstance(lpProc);\r
9664   }\r
9665   analysisDialogUp = TRUE;  \r
9666 }\r
9667 \r
9668 VOID\r
9669 AnalysisPopDown()\r
9670 {\r
9671   if (analysisDialog) {\r
9672     ShowWindow(analysisDialog, SW_HIDE);\r
9673   }\r
9674   analysisDialogUp = FALSE;  \r
9675 }\r
9676 \r
9677 \r
9678 VOID\r
9679 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9680 {\r
9681   highlightInfo.sq[0].x = fromX;\r
9682   highlightInfo.sq[0].y = fromY;\r
9683   highlightInfo.sq[1].x = toX;\r
9684   highlightInfo.sq[1].y = toY;\r
9685 }\r
9686 \r
9687 VOID\r
9688 ClearHighlights()\r
9689 {\r
9690   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9691     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9692 }\r
9693 \r
9694 VOID\r
9695 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9696 {\r
9697   premoveHighlightInfo.sq[0].x = fromX;\r
9698   premoveHighlightInfo.sq[0].y = fromY;\r
9699   premoveHighlightInfo.sq[1].x = toX;\r
9700   premoveHighlightInfo.sq[1].y = toY;\r
9701 }\r
9702 \r
9703 VOID\r
9704 ClearPremoveHighlights()\r
9705 {\r
9706   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9707     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9708 }\r
9709 \r
9710 VOID\r
9711 ShutDownFrontEnd()\r
9712 {\r
9713   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9714   DeleteClipboardTempFiles();\r
9715 }\r
9716 \r
9717 void\r
9718 BoardToTop()\r
9719 {\r
9720     if (IsIconic(hwndMain))\r
9721       ShowWindow(hwndMain, SW_RESTORE);\r
9722 \r
9723     SetActiveWindow(hwndMain);\r
9724 }\r
9725 \r
9726 /*\r
9727  * Prototypes for animation support routines\r
9728  */\r
9729 static void ScreenSquare(int column, int row, POINT * pt);\r
9730 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9731      POINT frames[], int * nFrames);\r
9732 \r
9733 \r
9734 #define kFactor 4\r
9735 \r
9736 void\r
9737 AnimateMove(board, fromX, fromY, toX, toY)\r
9738      Board board;\r
9739      int fromX;\r
9740      int fromY;\r
9741      int toX;\r
9742      int toY;\r
9743 {\r
9744   ChessSquare piece;\r
9745   POINT start, finish, mid;\r
9746   POINT frames[kFactor * 2 + 1];\r
9747   int nFrames, n;\r
9748 \r
9749   if (!appData.animate) return;\r
9750   if (doingSizing) return;\r
9751   if (fromY < 0 || fromX < 0) return;\r
9752   piece = board[fromY][fromX];\r
9753   if (piece >= EmptySquare) return;\r
9754 \r
9755   ScreenSquare(fromX, fromY, &start);\r
9756   ScreenSquare(toX, toY, &finish);\r
9757 \r
9758   /* All pieces except knights move in straight line */\r
9759   if (piece != WhiteKnight && piece != BlackKnight) {\r
9760     mid.x = start.x + (finish.x - start.x) / 2;\r
9761     mid.y = start.y + (finish.y - start.y) / 2;\r
9762   } else {\r
9763     /* Knight: make diagonal movement then straight */\r
9764     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9765        mid.x = start.x + (finish.x - start.x) / 2;\r
9766        mid.y = finish.y;\r
9767      } else {\r
9768        mid.x = finish.x;\r
9769        mid.y = start.y + (finish.y - start.y) / 2;\r
9770      }\r
9771   }\r
9772   \r
9773   /* Don't use as many frames for very short moves */\r
9774   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9775     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9776   else\r
9777     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9778 \r
9779   animInfo.from.x = fromX;\r
9780   animInfo.from.y = fromY;\r
9781   animInfo.to.x = toX;\r
9782   animInfo.to.y = toY;\r
9783   animInfo.lastpos = start;\r
9784   animInfo.piece = piece;\r
9785   for (n = 0; n < nFrames; n++) {\r
9786     animInfo.pos = frames[n];\r
9787     DrawPosition(FALSE, NULL);\r
9788     animInfo.lastpos = animInfo.pos;\r
9789     Sleep(appData.animSpeed);\r
9790   }\r
9791   animInfo.pos = finish;\r
9792   DrawPosition(FALSE, NULL);\r
9793   animInfo.piece = EmptySquare;\r
9794 }\r
9795 \r
9796 /*      Convert board position to corner of screen rect and color       */\r
9797 \r
9798 static void\r
9799 ScreenSquare(column, row, pt)\r
9800      int column; int row; POINT * pt;\r
9801 {\r
9802   if (flipView) {\r
9803     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9804     pt->y = lineGap + row * (squareSize + lineGap);\r
9805   } else {\r
9806     pt->x = lineGap + column * (squareSize + lineGap);\r
9807     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9808   }\r
9809 }\r
9810 \r
9811 /*      Generate a series of frame coords from start->mid->finish.\r
9812         The movement rate doubles until the half way point is\r
9813         reached, then halves back down to the final destination,\r
9814         which gives a nice slow in/out effect. The algorithmn\r
9815         may seem to generate too many intermediates for short\r
9816         moves, but remember that the purpose is to attract the\r
9817         viewers attention to the piece about to be moved and\r
9818         then to where it ends up. Too few frames would be less\r
9819         noticeable.                                             */\r
9820 \r
9821 static void\r
9822 Tween(start, mid, finish, factor, frames, nFrames)\r
9823      POINT * start; POINT * mid;\r
9824      POINT * finish; int factor;\r
9825      POINT frames[]; int * nFrames;\r
9826 {\r
9827   int n, fraction = 1, count = 0;\r
9828 \r
9829   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9830   for (n = 0; n < factor; n++)\r
9831     fraction *= 2;\r
9832   for (n = 0; n < factor; n++) {\r
9833     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9834     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9835     count ++;\r
9836     fraction = fraction / 2;\r
9837   }\r
9838   \r
9839   /* Midpoint */\r
9840   frames[count] = *mid;\r
9841   count ++;\r
9842   \r
9843   /* Slow out, stepping 1/2, then 1/4, ... */\r
9844   fraction = 2;\r
9845   for (n = 0; n < factor; n++) {\r
9846     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9847     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9848     count ++;\r
9849     fraction = fraction * 2;\r
9850   }\r
9851   *nFrames = count;\r
9852 }\r
9853 \r
9854 void\r
9855 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9856 {\r
9857 #if 0\r
9858     char buf[256];\r
9859 \r
9860     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
9861         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
9862 \r
9863     OutputDebugString( buf );\r
9864 #endif\r
9865 \r
9866     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9867 \r
9868     EvalGraphSet( first, last, current, pvInfoList );\r
9869 }\r
9870 \r
9871 void SetProgramStats( FrontEndProgramStats * stats )\r
9872 {\r
9873 #if 0\r
9874     char buf[1024];\r
9875 \r
9876     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
9877         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
9878 \r
9879     OutputDebugString( buf );\r
9880 #endif\r
9881 \r
9882     EngineOutputUpdate( stats );\r
9883 }\r