changes from Alessandro Scotti from 20060129
[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 $
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>
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"
88
89 void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
90
91 int myrandom(void);
92 void mysrandom(unsigned int seed);
93
94 typedef struct {\r
95   ChessSquare piece;  \r
96   POINT pos;      /* window coordinates of current pos */\r
97   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
98   POINT from;     /* board coordinates of the piece's orig pos */\r
99   POINT to;       /* board coordinates of the piece's new pos */\r
100 } AnimInfo;\r
101 \r
102 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
103 \r
104 typedef struct {\r
105   POINT start;    /* window coordinates of start pos */\r
106   POINT pos;      /* window coordinates of current pos */\r
107   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
108   POINT from;     /* board coordinates of the piece's orig pos */\r
109 } DragInfo;\r
110 \r
111 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
112 \r
113 typedef struct {\r
114   POINT sq[2];    /* board coordinates of from, to squares */\r
115 } HighlightInfo;\r
116 \r
117 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
118 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
119 \r
120 /* Window class names */\r
121 char szAppName[] = "WinBoard";\r
122 char szConsoleName[] = "WBConsole";\r
123 \r
124 /* Title bar text */\r
125 char szTitle[] = "WinBoard";\r
126 char szConsoleTitle[] = "ICS Interaction";\r
127 \r
128 char *programName;\r
129 char *settingsFileName;\r
130 BOOLEAN saveSettingsOnExit;\r
131 char installDir[MSG_SIZ];\r
132 \r
133 BoardSize boardSize;\r
134 BOOLEAN chessProgram;\r
135 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
136 static int squareSize, lineGap;\r
137 static int winWidth, winHeight;\r
138 static RECT messageRect, whiteRect, blackRect;\r
139 static char messageText[MESSAGE_TEXT_MAX];\r
140 static int clockTimerEvent = 0;\r
141 static int loadGameTimerEvent = 0;\r
142 static int analysisTimerEvent = 0;\r
143 static DelayedEventCallback delayedTimerCallback;\r
144 static int delayedTimerEvent = 0;\r
145 static int buttonCount = 2;\r
146 char *icsTextMenuString;\r
147 char *icsNames;\r
148 char *firstChessProgramNames;\r
149 char *secondChessProgramNames;\r
150 \r
151 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */
152 \r
153 #define PALETTESIZE 256\r
154 \r
155 HINSTANCE hInst;          /* current instance */\r
156 HWND hwndMain = NULL;        /* root window*/\r
157 HWND hwndConsole = NULL;\r
158 BOOLEAN alwaysOnTop = FALSE;\r
159 RECT boardRect;\r
160 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
161   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
162 HPALETTE hPal;\r
163 ColorClass currentColorClass;\r
164 \r
165 HWND hCommPort = NULL;    /* currently open comm port */\r
166 static HWND hwndPause;    /* pause button */\r
167 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];\r
168 static HBRUSH lightSquareBrush, darkSquareBrush,\r
169   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
170 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
171 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
172 static HPEN gridPen = NULL;\r
173 static HPEN highlightPen = NULL;\r
174 static HPEN premovePen = NULL;\r
175 static NPLOGPALETTE pLogPal;\r
176 static BOOL paletteChanged = FALSE;\r
177 static HICON iconWhite, iconBlack, iconCurrent;\r
178 static int doingSizing = FALSE;\r
179 static int lastSizing = 0;\r
180 static int prevStderrPort;\r
181 \r
182 /* [AS] Support for background textures */
183 #define BACK_TEXTURE_MODE_DISABLED      0
184 #define BACK_TEXTURE_MODE_PLAIN         1
185 #define BACK_TEXTURE_MODE_FULL_RANDOM   2
186
187 static HBITMAP liteBackTexture = NULL;
188 static HBITMAP darkBackTexture = NULL;
189 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
190 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
191 static int backTextureSquareSize = 0;
192 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];
193
194 #if __GNUC__ && !defined(_winmajor)\r
195 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
196 #else\r
197 #define oldDialog (_winmajor < 4)\r
198 #endif\r
199 \r
200 char *defaultTextAttribs[] = \r
201 {\r
202   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
203   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
204   COLOR_NONE\r
205 };\r
206 \r
207 typedef struct {\r
208   char *name;\r
209   int squareSize;\r
210   int lineGap;\r
211   int smallLayout;\r
212   int tinyLayout;\r
213   int cliWidth, cliHeight;\r
214 } SizeInfo;\r
215 \r
216 SizeInfo sizeInfo[] = \r
217 {\r
218   { "tiny",     21, 0, 1, 1, 0, 0 },\r
219   { "teeny",    25, 1, 1, 1, 0, 0 },\r
220   { "dinky",    29, 1, 1, 1, 0, 0 },\r
221   { "petite",   33, 1, 1, 1, 0, 0 },\r
222   { "slim",     37, 2, 1, 0, 0, 0 },\r
223   { "small",    40, 2, 1, 0, 0, 0 },\r
224   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
225   { "middling", 49, 2, 0, 0, 0, 0 },\r
226   { "average",  54, 2, 0, 0, 0, 0 },\r
227   { "moderate", 58, 3, 0, 0, 0, 0 },\r
228   { "medium",   64, 3, 0, 0, 0, 0 },\r
229   { "bulky",    72, 3, 0, 0, 0, 0 },\r
230   { "large",    80, 3, 0, 0, 0, 0 },\r
231   { "big",      87, 3, 0, 0, 0, 0 },\r
232   { "huge",     95, 3, 0, 0, 0, 0 },\r
233   { "giant",    108, 3, 0, 0, 0, 0 },\r
234   { "colossal", 116, 4, 0, 0, 0, 0 },\r
235   { "titanic",  129, 4, 0, 0, 0, 0 },\r
236   { NULL, 0, 0, 0, 0, 0, 0 }\r
237 };\r
238 \r
239 #define MF(x) {x, {0, }, {0, }, 0}\r
240 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
241 {\r
242   { 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) },
243   { 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) },
244   { 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) },
245   { 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) },
246   { 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) },
247   { 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) },
248   { 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) },
249   { 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) },
250   { 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) },
251   { 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) },
252   { 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) },
253   { 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) },
254   { 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) },
255   { 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) },
256   { 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) },
257   { 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) },
258   { 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) },
259   { 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) },
260 };\r
261 \r
262 MyFont *font[NUM_SIZES][NUM_FONTS];\r
263 \r
264 typedef struct {\r
265   char *label;\r
266   int id;\r
267   HWND hwnd;\r
268   WNDPROC wndproc;\r
269 } MyButtonDesc;\r
270 \r
271 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
272 #define N_BUTTONS 5\r
273 \r
274 MyButtonDesc buttonDesc[N_BUTTONS] =\r
275 {\r
276   {"<<", IDM_ToStart, NULL, NULL},\r
277   {"<", IDM_Backward, NULL, NULL},\r
278   {"P", IDM_Pause, NULL, NULL},\r
279   {">", IDM_Forward, NULL, NULL},\r
280   {">>", IDM_ToEnd, NULL, NULL},\r
281 };\r
282 \r
283 int tinyLayout = 0, smallLayout = 0;\r
284 #define MENU_BAR_ITEMS 6\r
285 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
286   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
287   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
288 };\r
289 \r
290 \r
291 MySound sounds[(int)NSoundClasses];\r
292 MyTextAttribs textAttribs[(int)NColorClasses];\r
293 \r
294 MyColorizeAttribs colorizeAttribs[] = {\r
295   { (COLORREF)0, 0, "Shout Text" },\r
296   { (COLORREF)0, 0, "SShout/CShout" },\r
297   { (COLORREF)0, 0, "Channel 1 Text" },\r
298   { (COLORREF)0, 0, "Channel Text" },\r
299   { (COLORREF)0, 0, "Kibitz Text" },\r
300   { (COLORREF)0, 0, "Tell Text" },\r
301   { (COLORREF)0, 0, "Challenge Text" },\r
302   { (COLORREF)0, 0, "Request Text" },\r
303   { (COLORREF)0, 0, "Seek Text" },\r
304   { (COLORREF)0, 0, "Normal Text" },\r
305   { (COLORREF)0, 0, "None" }\r
306 };\r
307 \r
308 \r
309 \r
310 static char *commentTitle;\r
311 static char *commentText;\r
312 static int commentIndex;\r
313 static Boolean editComment = FALSE;\r
314 HWND commentDialog = NULL;\r
315 BOOLEAN commentDialogUp = FALSE;\r
316 static int commentX, commentY, commentH, commentW;\r
317 \r
318 static char *analysisTitle;\r
319 static char *analysisText;\r
320 HWND analysisDialog = NULL;\r
321 BOOLEAN analysisDialogUp = FALSE;\r
322 static int analysisX, analysisY, analysisH, analysisW;\r
323 \r
324 char errorTitle[MSG_SIZ];\r
325 char errorMessage[2*MSG_SIZ];\r
326 HWND errorDialog = NULL;\r
327 BOOLEAN moveErrorMessageUp = FALSE;\r
328 BOOLEAN consoleEcho = TRUE;\r
329 CHARFORMAT consoleCF;\r
330 COLORREF consoleBackgroundColor;\r
331 \r
332 char *programVersion;\r
333 \r
334 #define CPReal 1\r
335 #define CPComm 2\r
336 #define CPSock 3\r
337 #define CPRcmd 4\r
338 typedef int CPKind;\r
339 \r
340 typedef struct {\r
341   CPKind kind;\r
342   HANDLE hProcess;\r
343   DWORD pid;\r
344   HANDLE hTo;\r
345   HANDLE hFrom;\r
346   SOCKET sock;\r
347   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
348 } ChildProc;\r
349 \r
350 #define INPUT_SOURCE_BUF_SIZE 4096\r
351 \r
352 typedef struct _InputSource {\r
353   CPKind kind;\r
354   HANDLE hFile;\r
355   SOCKET sock;\r
356   int lineByLine;\r
357   HANDLE hThread;\r
358   DWORD id;\r
359   char buf[INPUT_SOURCE_BUF_SIZE];\r
360   char *next;\r
361   DWORD count;\r
362   int error;\r
363   InputCallback func;\r
364   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
365   VOIDSTAR closure;\r
366 } InputSource;\r
367 \r
368 InputSource *consoleInputSource;\r
369 \r
370 DCB dcb;\r
371 \r
372 /* forward */\r
373 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
374 VOID ConsoleCreate();\r
375 LRESULT CALLBACK\r
376   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
377 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
378 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
379 VOID ParseCommSettings(char *arg, DCB *dcb);\r
380 LRESULT CALLBACK\r
381   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
382 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
383 void ParseIcsTextMenu(char *icsTextMenuString);\r
384 VOID PopUpMoveDialog(char firstchar);\r
385 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
386
387 /* [AS] */
388 int NewGameFRC();
389 int GameListOptions();
390
391 HWND moveHistoryDialog = NULL;
392 BOOLEAN moveHistoryDialogUp = FALSE;
393
394 WindowPlacement wpMoveHistory;
395
396 HWND evalGraphDialog = NULL;
397 BOOLEAN evalGraphDialogUp = FALSE;
398
399 WindowPlacement wpEvalGraph;
400
401 HWND engineOutputDialog = NULL;
402 BOOLEAN engineOutputDialogUp = FALSE;
403
404 WindowPlacement wpEngineOutput;
405
406 VOID MoveHistoryPopUp();
407 VOID MoveHistoryPopDown();
408 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );
409 BOOL MoveHistoryIsUp();
410
411 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );
412 VOID EvalGraphPopUp();
413 VOID EvalGraphPopDown();
414 BOOL EvalGraphIsUp();
415
416 VOID EngineOutputPopUp();
417 VOID EngineOutputPopDown();
418 BOOL EngineOutputIsUp();
419 VOID EngineOutputUpdate( FrontEndProgramStats * stats );
420 \r
421 /*\r
422  * Setting "frozen" should disable all user input other than deleting\r
423  * the window.  We do this while engines are initializing themselves.\r
424  */\r
425 static int frozen = 0;\r
426 static int oldMenuItemState[MENU_BAR_ITEMS];\r
427 void FreezeUI()\r
428 {\r
429   HMENU hmenu;\r
430   int i;\r
431 \r
432   if (frozen) return;\r
433   frozen = 1;\r
434   hmenu = GetMenu(hwndMain);\r
435   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
436     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
437   }\r
438   DrawMenuBar(hwndMain);\r
439 }\r
440 \r
441 /* Undo a FreezeUI */\r
442 void ThawUI()\r
443 {\r
444   HMENU hmenu;\r
445   int i;\r
446 \r
447   if (!frozen) return;\r
448   frozen = 0;\r
449   hmenu = GetMenu(hwndMain);\r
450   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
451     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
452   }\r
453   DrawMenuBar(hwndMain);\r
454 }\r
455 \r
456 /*---------------------------------------------------------------------------*\\r
457  *\r
458  * WinMain\r
459  *\r
460 \*---------------------------------------------------------------------------*/\r
461 \r
462 int APIENTRY\r
463 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
464         LPSTR lpCmdLine, int nCmdShow)\r
465 {\r
466   MSG msg;\r
467   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
468 \r
469   debugFP = stderr;\r
470 \r
471   LoadLibrary("RICHED32.DLL");\r
472   consoleCF.cbSize = sizeof(CHARFORMAT);\r
473 \r
474   if (!InitApplication(hInstance)) {\r
475     return (FALSE);\r
476   }\r
477   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
478     return (FALSE);\r
479   }\r
480 \r
481   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
482   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
483   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
484 \r
485   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
486 \r
487   while (GetMessage(&msg, /* message structure */\r
488                     NULL, /* handle of window receiving the message */\r
489                     0,    /* lowest message to examine */\r
490                     0))   /* highest message to examine */\r
491     {\r
492       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
493           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
494           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
495           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
496           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
497           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
498           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
499           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
500           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
501           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
502         TranslateMessage(&msg); /* Translates virtual key codes */\r
503         DispatchMessage(&msg);  /* Dispatches message to window */\r
504       }\r
505     }\r
506 \r
507 \r
508   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
509 }\r
510 \r
511 /*---------------------------------------------------------------------------*\\r
512  *\r
513  * Initialization functions\r
514  *\r
515 \*---------------------------------------------------------------------------*/\r
516 \r
517 BOOL\r
518 InitApplication(HINSTANCE hInstance)\r
519 {\r
520   WNDCLASS wc;\r
521 \r
522   /* Fill in window class structure with parameters that describe the */\r
523   /* main window. */\r
524 \r
525   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
526   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
527   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
528   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
529   wc.hInstance     = hInstance;         /* Owner of this class */\r
530   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
531   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
532   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
533   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
534   wc.lpszClassName = szAppName;                 /* Name to register as */\r
535 \r
536   /* Register the window class and return success/failure code. */\r
537   if (!RegisterClass(&wc)) return FALSE;\r
538 \r
539   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
540   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
541   wc.cbClsExtra    = 0;\r
542   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
543   wc.hInstance     = hInstance;\r
544   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
545   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
546   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
547   wc.lpszMenuName  = NULL;\r
548   wc.lpszClassName = szConsoleName;\r
549 \r
550   if (!RegisterClass(&wc)) return FALSE;\r
551   return TRUE;\r
552 }\r
553 \r
554 \r
555 /* Set by InitInstance, used by EnsureOnScreen */\r
556 int screenHeight, screenWidth;\r
557 \r
558 void\r
559 EnsureOnScreen(int *x, int *y)\r
560 {\r
561   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
562   if (*x > screenWidth - 32) *x = 0;\r
563   if (*y > screenHeight - 32) *y = 0;\r
564 }\r
565 \r
566 BOOL\r
567 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
568 {\r
569   HWND hwnd; /* Main window handle. */\r
570   int ibs;\r
571   WINDOWPLACEMENT wp;\r
572   char *filepart;\r
573 \r
574   hInst = hInstance;    /* Store instance handle in our global variable */\r
575 \r
576   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
577     *filepart = NULLCHAR;\r
578   } else {\r
579     GetCurrentDirectory(MSG_SIZ, installDir);\r
580   }\r
581   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
582   if (appData.debugMode) {\r
583     debugFP = fopen(appData.nameOfDebugFile, "w");
584     setbuf(debugFP, NULL);\r
585   }\r
586 \r
587   InitBackEnd1();\r
588 \r
589   InitEngineUCI( installDir, &first );
590   InitEngineUCI( installDir, &second );
591
592   /* Create a main window for this application instance. */\r
593   hwnd = CreateWindow(szAppName, szTitle,\r
594                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
595                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
596                       NULL, NULL, hInstance, NULL);\r
597   hwndMain = hwnd;\r
598 \r
599   /* If window could not be created, return "failure" */\r
600   if (!hwnd) {\r
601     return (FALSE);\r
602   }\r
603 \r
604   iconWhite = LoadIcon(hInstance, "icon_white");\r
605   iconBlack = LoadIcon(hInstance, "icon_black");\r
606   iconCurrent = iconWhite;\r
607   InitDrawingColors();\r
608   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
609   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
610   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
611     /* Compute window size for each board size, and use the largest\r
612        size that fits on this screen as the default. */\r
613     InitDrawingSizes((BoardSize)ibs, 0);\r
614     if (boardSize == (BoardSize)-1 &&\r
615         winHeight <= screenHeight && winWidth <= screenWidth) {\r
616       boardSize = (BoardSize)ibs;\r
617     }\r
618   }\r
619   InitDrawingSizes(boardSize, 0);\r
620   InitMenuChecks();\r
621   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
622 \r
623   /* [AS] Load textures if specified */
624   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
625
626   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
627       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
628       liteBackTextureMode = appData.liteBackTextureMode;
629
630       if (liteBackTexture == NULL && appData.debugMode) {
631           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
632       }
633   }
634
635   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
636       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
637       darkBackTextureMode = appData.darkBackTextureMode;
638
639       if (darkBackTexture == NULL && appData.debugMode) {
640           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
641       }
642   }
643
644   mysrandom( (unsigned) time(NULL) );
645
646   /* Make a console window if needed */\r
647   if (appData.icsActive) {\r
648     ConsoleCreate();\r
649   }\r
650 \r
651   /* [AS] Restore layout */
652   if( wpMoveHistory.visible ) {
653       MoveHistoryPopUp();
654   }
655
656   if( wpEvalGraph.visible ) {
657       EvalGraphPopUp();
658   }
659
660   if( wpEngineOutput.visible ) {
661       EngineOutputPopUp();
662   }
663
664   InitBackEnd2();\r
665 \r
666   /* Make the window visible; update its client area; and return "success" */\r
667   EnsureOnScreen(&boardX, &boardY);\r
668   wp.length = sizeof(WINDOWPLACEMENT);\r
669   wp.flags = 0;\r
670   wp.showCmd = nCmdShow;\r
671   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
672   wp.rcNormalPosition.left = boardX;\r
673   wp.rcNormalPosition.right = boardX + winWidth;\r
674   wp.rcNormalPosition.top = boardY;\r
675   wp.rcNormalPosition.bottom = boardY + winHeight;\r
676   SetWindowPlacement(hwndMain, &wp);\r
677 \r
678   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
679                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
680
681   /* [AS] Disable the FRC stuff if not playing the proper variant */
682   if( gameInfo.variant != VariantFischeRandom ) {
683       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );
684   }
685
686   if (hwndConsole) {\r
687 #if AOT_CONSOLE\r
688     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
689                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
690 #endif\r
691     ShowWindow(hwndConsole, nCmdShow);\r
692   }\r
693   UpdateWindow(hwnd);\r
694 \r
695   return TRUE;\r
696 \r
697 }\r
698 \r
699 \r
700 typedef enum {\r
701   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
702   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
703   ArgSettingsFilename\r
704 } ArgType;\r
705 \r
706 typedef struct {\r
707   char *argName;\r
708   ArgType argType;\r
709   /***\r
710   union {\r
711     String *pString;       // ArgString\r
712     int *pInt;             // ArgInt\r
713     float *pFloat;         // ArgFloat\r
714     Boolean *pBoolean;     // ArgBoolean\r
715     COLORREF *pColor;      // ArgColor\r
716     ColorClass cc;         // ArgAttribs\r
717     String *pFilename;     // ArgFilename\r
718     BoardSize *pBoardSize; // ArgBoardSize\r
719     int whichFont;         // ArgFont\r
720     DCB *pDCB;             // ArgCommSettings\r
721     String *pFilename;     // ArgSettingsFilename\r
722   } argLoc;\r
723   ***/\r
724   LPVOID argLoc;\r
725   BOOL save;\r
726 } ArgDescriptor;\r
727 \r
728 int junk;\r
729 ArgDescriptor argDescriptors[] = {\r
730   /* positional arguments */\r
731   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
732   { "", ArgNone, NULL },\r
733   /* keyword arguments */\r
734   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
735   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
736   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
737   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
738   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
739   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
740   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
741   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
742   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
743   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
744   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
745   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
746   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
747   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
748   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
749   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
750   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
751   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
752     FALSE },\r
753   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
754     FALSE },\r
755   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
756     FALSE },\r
757   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
758   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
759     FALSE },\r
760   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
761   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
762   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
763   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
764   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
765   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
766   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
767   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
768   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
769   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
770   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
771   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
772   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
773   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
774   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
775   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
776   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
777   /*!!bitmapDirectory?*/\r
778   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
779   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
780   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
781   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
782   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
783   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
784   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
785   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
786   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
787   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
788   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
789   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
790   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
791   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
792   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
793   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
794   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
795   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
796   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
797   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
798   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
799   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
800   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
801   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
802   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
803   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
804   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
805   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
806   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
807   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
808   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
809   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
810   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
811   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
812   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
813   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
814   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
815   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
816   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
817   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
818   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
819   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
820   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
821   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
822   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
823   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
824   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
825   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
826   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
827   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
828   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
829   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
830   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
831   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
832   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
833   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
834   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
835   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
836   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
837   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
838   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
839   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
840   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
841   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
842   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
843   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
844   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
845   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
846   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
847   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
848   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
849   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
850   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
851   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
852   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
853   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
854   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
855   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
856   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
857   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
858   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
859   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
860   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
861   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
862   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
863   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
864   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
865   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
866   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
867   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
868   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
869   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
870   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
871   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
872     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
873   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
874   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
875   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
876   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
877   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
878   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
879   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */
880   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
881     TRUE }, /* must come after all fonts */\r
882   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
883   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
884     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
885   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
886   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
887   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
888   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
889   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
890   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
891   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
892   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
893   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
894   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
895   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
896   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
897   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
898   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
899   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
900   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
901   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
902   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
903   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
904   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
905   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
906   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
907   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
908   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
909   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
910   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
911   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
912   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
913 #if 0\r
914   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
915   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
916 #endif\r
917   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
918   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
919   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
920   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
921   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
922   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
923   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
924   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
925   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
926   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
927   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
928   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
929   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
930   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
931   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
932   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
933   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
934   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
935   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
936   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
937   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
938   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
939   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
940   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
941   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
942   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
943   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
944   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
945   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
946   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
947   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
948   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
949   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
950   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
951   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
952   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
953   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
954   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
955   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
956   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
957   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
958   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
959   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
960   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
961   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
962   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
963   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
964   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
965   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
966   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
967   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
968   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
969   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
970   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
971   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
972   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
973   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
974   { "highlightLastMove", ArgBoolean,\r
975     (LPVOID) &appData.highlightLastMove, TRUE },\r
976   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
977   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
978   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
979   { "highlightDragging", ArgBoolean,\r
980     (LPVOID) &appData.highlightDragging, TRUE },\r
981   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
982   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
983   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
984   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
985   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
986   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
987   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
988   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
989   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
990   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
991   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
992   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
993   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
994   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
995   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
996   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
997   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
998   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
999   { "soundShout", ArgFilename,\r
1000     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1001   { "soundSShout", ArgFilename,\r
1002     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1003   { "soundChannel1", ArgFilename,\r
1004     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1005   { "soundChannel", ArgFilename,\r
1006     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1007   { "soundKibitz", ArgFilename,\r
1008     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1009   { "soundTell", ArgFilename,\r
1010     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1011   { "soundChallenge", ArgFilename,\r
1012     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1013   { "soundRequest", ArgFilename,\r
1014     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1015   { "soundSeek", ArgFilename,\r
1016     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1017   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1018   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1019   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1020   { "soundIcsLoss", ArgFilename, \r
1021     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1022   { "soundIcsDraw", ArgFilename, \r
1023     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1024   { "soundIcsUnfinished", ArgFilename, \r
1025     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1026   { "soundIcsAlarm", ArgFilename, \r
1027     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1028   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1029   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1030   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1031   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1032   { "reuseChessPrograms", ArgBoolean,\r
1033     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1034   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1035   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1036   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1037   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1038   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1039   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1040   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1041   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1042   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1043   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1044   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1045   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1046   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1047   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1048   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1049   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1050   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1051   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1052   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1053   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1054   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1055   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1056   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1057   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1058   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1059   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1060   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1061   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1062   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1063   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1064   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1065   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1066   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1067   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1068   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1069   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1070   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1071     TRUE },\r
1072   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1073     TRUE },\r
1074   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1075   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1076   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1077   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },
1078   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },
1079   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1080   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1081   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1082   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1083   /* [AS] New features */
1084   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },
1085   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },
1086   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },
1087   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },
1088   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },
1089   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },
1090   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },
1091   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },
1092   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },
1093   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },
1094   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },
1095   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },
1096   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },
1097   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },
1098   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },
1099   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },
1100   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },
1101   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },
1102   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },
1103   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
1104   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
1105   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },
1106   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },
1107   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },
1108   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },
1109   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },
1110   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },
1111   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },
1112   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },
1113   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },
1114   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },
1115   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },
1116   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },
1117   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },
1118   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },
1119   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },
1120   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },
1121   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },
1122   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },
1123   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },
1124   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },
1125   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },
1126   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },
1127   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },
1128   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },
1129   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE },
1130   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },
1131   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },
1132
1133   /* [AS] Layout stuff */
1134   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },
1135   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },
1136   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },
1137   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },
1138   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },
1139
1140   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },
1141   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },
1142   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },
1143   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },
1144   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },
1145
1146   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },
1147   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },
1148   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },
1149   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },
1150   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },
1151
1152 #ifdef ZIPPY\r
1153   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1154   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1155   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1156   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1157   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1158   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1159   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1160   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1161   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1162   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1163   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1164   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1165   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1166     FALSE },\r
1167   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1168   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1169   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1170   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1171   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1172   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1173   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1174     FALSE },\r
1175   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1176   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1177   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1178   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1179   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1180   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1181   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1182   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1183   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1184   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1185   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1186   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1187   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1188   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1189   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1190   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1191   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1192   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1193 #endif\r
1194   { NULL, ArgNone, NULL, FALSE }\r
1195 };\r
1196 \r
1197 \r
1198 /* Kludge for indirection files on command line */\r
1199 char* lastIndirectionFilename;\r
1200 ArgDescriptor argDescriptorIndirection =\r
1201 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1202 \r
1203 \r
1204 VOID\r
1205 ExitArgError(char *msg, char *badArg)\r
1206 {\r
1207   char buf[MSG_SIZ];\r
1208 \r
1209   sprintf(buf, "%s %s", msg, badArg);\r
1210   DisplayFatalError(buf, 0, 2);\r
1211   exit(2);\r
1212 }\r
1213 \r
1214 /* Command line font name parser.  NULL name means do nothing.\r
1215    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1216    For backward compatibility, syntax without the colon is also\r
1217    accepted, but font names with digits in them won't work in that case.\r
1218 */\r
1219 VOID\r
1220 ParseFontName(char *name, MyFontParams *mfp)\r
1221 {\r
1222   char *p, *q;\r
1223   if (name == NULL) return;\r
1224   p = name;\r
1225   q = strchr(p, ':');\r
1226   if (q) {\r
1227     if (q - p >= sizeof(mfp->faceName))\r
1228       ExitArgError("Font name too long:", name);\r
1229     memcpy(mfp->faceName, p, q - p);\r
1230     mfp->faceName[q - p] = NULLCHAR;\r
1231     p = q + 1;\r
1232   } else {\r
1233     q = mfp->faceName;\r
1234     while (*p && !isdigit(*p)) {\r
1235       *q++ = *p++;\r
1236       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1237         ExitArgError("Font name too long:", name);\r
1238     }\r
1239     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1240     *q = NULLCHAR;\r
1241   }\r
1242   if (!*p) ExitArgError("Font point size missing:", name);\r
1243   mfp->pointSize = (float) atof(p);\r
1244   mfp->bold = (strchr(p, 'b') != NULL);\r
1245   mfp->italic = (strchr(p, 'i') != NULL);\r
1246   mfp->underline = (strchr(p, 'u') != NULL);\r
1247   mfp->strikeout = (strchr(p, 's') != NULL);\r
1248 }\r
1249 \r
1250 /* Color name parser.\r
1251    X version accepts X color names, but this one\r
1252    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1253 COLORREF\r
1254 ParseColorName(char *name)\r
1255 {\r
1256   int red, green, blue, count;\r
1257   char buf[MSG_SIZ];\r
1258 \r
1259   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1260   if (count != 3) {\r
1261     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1262       &red, &green, &blue);\r
1263   }\r
1264   if (count != 3) {\r
1265     sprintf(buf, "Can't parse color name %s", name);\r
1266     DisplayError(buf, 0);\r
1267     return RGB(0, 0, 0);\r
1268   }\r
1269   return PALETTERGB(red, green, blue);\r
1270 }\r
1271 \r
1272 \r
1273 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1274 {\r
1275   char *e = argValue;\r
1276   int eff = 0;\r
1277 \r
1278   while (*e) {\r
1279     if (*e == 'b')      eff |= CFE_BOLD;\r
1280     else if (*e == 'i') eff |= CFE_ITALIC;\r
1281     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1282     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1283     else if (*e == '#' || isdigit(*e)) break;\r
1284     e++;\r
1285   }\r
1286   *effects = eff;\r
1287   *color   = ParseColorName(e);\r
1288 }\r
1289 \r
1290 \r
1291 BoardSize\r
1292 ParseBoardSize(char *name)\r
1293 {\r
1294   BoardSize bs = SizeTiny;\r
1295   while (sizeInfo[bs].name != NULL) {\r
1296     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1297     bs++;\r
1298   }\r
1299   ExitArgError("Unrecognized board size value", name);\r
1300   return bs; /* not reached */\r
1301 }\r
1302 \r
1303 \r
1304 char\r
1305 StringGet(void *getClosure)\r
1306 {\r
1307   char **p = (char **) getClosure;\r
1308   return *((*p)++);\r
1309 }\r
1310 \r
1311 char\r
1312 FileGet(void *getClosure)\r
1313 {\r
1314   int c;\r
1315   FILE* f = (FILE*) getClosure;\r
1316 \r
1317   c = getc(f);\r
1318   if (c == EOF)\r
1319     return NULLCHAR;\r
1320   else\r
1321     return (char) c;\r
1322 }\r
1323 \r
1324 /* Parse settings file named "name". If file found, return the\r
1325    full name in fullname and return TRUE; else return FALSE */\r
1326 BOOLEAN\r
1327 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1328 {\r
1329   char *dummy;\r
1330   FILE *f;\r
1331 \r
1332   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1333     f = fopen(fullname, "r");\r
1334     if (f != NULL) {\r
1335       ParseArgs(FileGet, f);\r
1336       fclose(f);\r
1337       return TRUE;\r
1338     }\r
1339   }\r
1340   return FALSE;\r
1341 }\r
1342 \r
1343 VOID\r
1344 ParseArgs(GetFunc get, void *cl)\r
1345 {\r
1346   char argName[ARG_MAX];\r
1347   char argValue[ARG_MAX];\r
1348   ArgDescriptor *ad;\r
1349   char start;\r
1350   char *q;\r
1351   int i, octval;\r
1352   char ch;\r
1353   int posarg = 0;\r
1354 \r
1355   ch = get(cl);\r
1356   for (;;) {\r
1357     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1358     if (ch == NULLCHAR) break;\r
1359     if (ch == ';') {\r
1360       /* Comment to end of line */\r
1361       ch = get(cl);\r
1362       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1363       continue;\r
1364     } else if (ch == '/' || ch == '-') {\r
1365       /* Switch */\r
1366       q = argName;\r
1367       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1368              ch != '\n' && ch != '\t') {\r
1369         *q++ = ch;\r
1370         ch = get(cl);\r
1371       }\r
1372       *q = NULLCHAR;\r
1373 \r
1374       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1375         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1376 \r
1377       if (ad->argName == NULL)\r
1378         ExitArgError("Unrecognized argument", argName);\r
1379 \r
1380     } else if (ch == '@') {\r
1381       /* Indirection file */\r
1382       ad = &argDescriptorIndirection;\r
1383       ch = get(cl);\r
1384     } else {\r
1385       /* Positional argument */\r
1386       ad = &argDescriptors[posarg++];\r
1387       strcpy(argName, ad->argName);\r
1388     }\r
1389 \r
1390     if (ad->argType == ArgTrue) {\r
1391       *(Boolean *) ad->argLoc = TRUE;\r
1392       continue;\r
1393     }\r
1394     if (ad->argType == ArgFalse) {\r
1395       *(Boolean *) ad->argLoc = FALSE;\r
1396       continue;\r
1397     }\r
1398 \r
1399     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1400     if (ch == NULLCHAR || ch == '\n') {\r
1401       ExitArgError("No value provided for argument", argName);\r
1402     }\r
1403     q = argValue;\r
1404     if (ch == '{') {\r
1405       // Quoting with { }.  No characters have to (or can) be escaped.\r
1406       // Thus the string cannot contain a '}' character.\r
1407       start = ch;\r
1408       ch = get(cl);\r
1409       while (start) {\r
1410         switch (ch) {\r
1411         case NULLCHAR:\r
1412           start = NULLCHAR;\r
1413           break;\r
1414           \r
1415         case '}':\r
1416           ch = get(cl);\r
1417           start = NULLCHAR;\r
1418           break;\r
1419 \r
1420         default:\r
1421           *q++ = ch;\r
1422           ch = get(cl);\r
1423           break;\r
1424         }\r
1425       }   \r
1426     } else if (ch == '\'' || ch == '"') {\r
1427       // Quoting with ' ' or " ", with \ as escape character.\r
1428       // Inconvenient for long strings that may contain Windows filenames.\r
1429       start = ch;\r
1430       ch = get(cl);\r
1431       while (start) {\r
1432         switch (ch) {\r
1433         case NULLCHAR:\r
1434           start = NULLCHAR;\r
1435           break;\r
1436 \r
1437         default:\r
1438         not_special:\r
1439           *q++ = ch;\r
1440           ch = get(cl);\r
1441           break;\r
1442 \r
1443         case '\'':\r
1444         case '\"':\r
1445           if (ch == start) {\r
1446             ch = get(cl);\r
1447             start = NULLCHAR;\r
1448             break;\r
1449           } else {\r
1450             goto not_special;\r
1451           }\r
1452 \r
1453         case '\\':\r
1454           if (ad->argType == ArgFilename\r
1455               || ad->argType == ArgSettingsFilename) {\r
1456               goto not_special;\r
1457           }\r
1458           ch = get(cl);\r
1459           switch (ch) {\r
1460           case NULLCHAR:\r
1461             ExitArgError("Incomplete \\ escape in value for", argName);\r
1462             break;\r
1463           case 'n':\r
1464             *q++ = '\n';\r
1465             ch = get(cl);\r
1466             break;\r
1467           case 'r':\r
1468             *q++ = '\r';\r
1469             ch = get(cl);\r
1470             break;\r
1471           case 't':\r
1472             *q++ = '\t';\r
1473             ch = get(cl);\r
1474             break;\r
1475           case 'b':\r
1476             *q++ = '\b';\r
1477             ch = get(cl);\r
1478             break;\r
1479           case 'f':\r
1480             *q++ = '\f';\r
1481             ch = get(cl);\r
1482             break;\r
1483           default:\r
1484             octval = 0;\r
1485             for (i = 0; i < 3; i++) {\r
1486               if (ch >= '0' && ch <= '7') {\r
1487                 octval = octval*8 + (ch - '0');\r
1488                 ch = get(cl);\r
1489               } else {\r
1490                 break;\r
1491               }\r
1492             }\r
1493             if (i > 0) {\r
1494               *q++ = (char) octval;\r
1495             } else {\r
1496               *q++ = ch;\r
1497               ch = get(cl);\r
1498             }\r
1499             break;\r
1500           }\r
1501           break;\r
1502         }\r
1503       }\r
1504     } else {\r
1505       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1506         *q++ = ch;\r
1507         ch = get(cl);\r
1508       }\r
1509     }\r
1510     *q = NULLCHAR;\r
1511 \r
1512     switch (ad->argType) {\r
1513     case ArgInt:\r
1514       *(int *) ad->argLoc = atoi(argValue);\r
1515       break;\r
1516 \r
1517     case ArgFloat:\r
1518       *(float *) ad->argLoc = (float) atof(argValue);\r
1519       break;\r
1520 \r
1521     case ArgString:\r
1522     case ArgFilename:\r
1523       *(char **) ad->argLoc = strdup(argValue);\r
1524       break;\r
1525 \r
1526     case ArgSettingsFilename:\r
1527       {\r
1528         char fullname[MSG_SIZ];\r
1529         if (ParseSettingsFile(argValue, fullname)) {\r
1530           if (ad->argLoc != NULL) {\r
1531             *(char **) ad->argLoc = strdup(fullname);\r
1532           }\r
1533         } else {\r
1534           if (ad->argLoc != NULL) {\r
1535           } else {\r
1536             ExitArgError("Failed to open indirection file", argValue);\r
1537           }\r
1538         }\r
1539       }\r
1540       break;\r
1541 \r
1542     case ArgBoolean:\r
1543       switch (argValue[0]) {\r
1544       case 't':\r
1545       case 'T':\r
1546         *(Boolean *) ad->argLoc = TRUE;\r
1547         break;\r
1548       case 'f':\r
1549       case 'F':\r
1550         *(Boolean *) ad->argLoc = FALSE;\r
1551         break;\r
1552       default:\r
1553         ExitArgError("Unrecognized boolean argument value", argValue);\r
1554         break;\r
1555       }\r
1556       break;\r
1557 \r
1558     case ArgColor:\r
1559       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1560       break;\r
1561 \r
1562     case ArgAttribs: {\r
1563       ColorClass cc = (ColorClass)ad->argLoc;\r
1564       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1565       }\r
1566       break;\r
1567       \r
1568     case ArgBoardSize:\r
1569       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1570       break;\r
1571 \r
1572     case ArgFont:\r
1573       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1574       break;\r
1575 \r
1576     case ArgCommSettings:\r
1577       ParseCommSettings(argValue, &dcb);\r
1578       break;\r
1579 \r
1580     case ArgNone:\r
1581       ExitArgError("Unrecognized argument", argValue);\r
1582       break;\r
1583     }\r
1584   }\r
1585 }\r
1586 \r
1587 VOID\r
1588 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1589 {\r
1590   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1591   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1592   DeleteDC(hdc);\r
1593   lf->lfWidth = 0;\r
1594   lf->lfEscapement = 0;\r
1595   lf->lfOrientation = 0;\r
1596   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1597   lf->lfItalic = mfp->italic;\r
1598   lf->lfUnderline = mfp->underline;\r
1599   lf->lfStrikeOut = mfp->strikeout;\r
1600   lf->lfCharSet = DEFAULT_CHARSET;\r
1601   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1602   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1603   lf->lfQuality = DEFAULT_QUALITY;\r
1604   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1605   strcpy(lf->lfFaceName, mfp->faceName);\r
1606 }\r
1607 \r
1608 VOID\r
1609 CreateFontInMF(MyFont *mf)\r
1610 {\r
1611   LFfromMFP(&mf->lf, &mf->mfp);\r
1612   if (mf->hf) DeleteObject(mf->hf);\r
1613   mf->hf = CreateFontIndirect(&mf->lf);\r
1614 }\r
1615 \r
1616 VOID\r
1617 SetDefaultTextAttribs()\r
1618 {\r
1619   ColorClass cc;\r
1620   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1621     ParseAttribs(&textAttribs[cc].color, \r
1622                  &textAttribs[cc].effects, \r
1623                  defaultTextAttribs[cc]);\r
1624   }\r
1625 }\r
1626 \r
1627 VOID\r
1628 SetDefaultSounds()\r
1629 {\r
1630   ColorClass cc;\r
1631   SoundClass sc;\r
1632   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1633     textAttribs[cc].sound.name = strdup("");\r
1634     textAttribs[cc].sound.data = NULL;\r
1635   }\r
1636   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1637     sounds[sc].name = strdup("");\r
1638     sounds[sc].data = NULL;\r
1639   }\r
1640   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1641 }\r
1642 \r
1643 VOID\r
1644 LoadAllSounds()\r
1645 {\r
1646   ColorClass cc;\r
1647   SoundClass sc;\r
1648   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1649     MyLoadSound(&textAttribs[cc].sound);\r
1650   }\r
1651   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1652     MyLoadSound(&sounds[sc]);\r
1653   }\r
1654 }\r
1655 \r
1656 VOID\r
1657 InitAppData(LPSTR lpCmdLine)\r
1658 {\r
1659   int i, j;\r
1660   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1661   char *dummy, *p;\r
1662 \r
1663   programName = szAppName;\r
1664 \r
1665   /* Initialize to defaults */\r
1666   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1667   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1668   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1669   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1670   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1671   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1672   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1673   SetDefaultTextAttribs();\r
1674   SetDefaultSounds();\r
1675   appData.movesPerSession = MOVES_PER_SESSION;\r
1676   appData.initString = INIT_STRING;\r
1677   appData.secondInitString = INIT_STRING;\r
1678   appData.firstComputerString = COMPUTER_STRING;\r
1679   appData.secondComputerString = COMPUTER_STRING;\r
1680   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1681   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1682   appData.firstPlaysBlack = FALSE;\r
1683   appData.noChessProgram = FALSE;\r
1684   chessProgram = FALSE;\r
1685   appData.firstHost = FIRST_HOST;\r
1686   appData.secondHost = SECOND_HOST;\r
1687   appData.firstDirectory = FIRST_DIRECTORY;\r
1688   appData.secondDirectory = SECOND_DIRECTORY;\r
1689   appData.bitmapDirectory = "";\r
1690   appData.remoteShell = REMOTE_SHELL;\r
1691   appData.remoteUser = "";\r
1692   appData.timeDelay = TIME_DELAY;\r
1693   appData.timeControl = TIME_CONTROL;\r
1694   appData.timeIncrement = TIME_INCREMENT;\r
1695   appData.icsActive = FALSE;\r
1696   appData.icsHost = "";\r
1697   appData.icsPort = ICS_PORT;\r
1698   appData.icsCommPort = ICS_COMM_PORT;\r
1699   appData.icsLogon = ICS_LOGON;\r
1700   appData.icsHelper = "";\r
1701   appData.useTelnet = FALSE;\r
1702   appData.telnetProgram = TELNET_PROGRAM;\r
1703   appData.gateway = "";\r
1704   appData.loadGameFile = "";\r
1705   appData.loadGameIndex = 0;\r
1706   appData.saveGameFile = "";\r
1707   appData.autoSaveGames = FALSE;\r
1708   appData.loadPositionFile = "";\r
1709   appData.loadPositionIndex = 1;\r
1710   appData.savePositionFile = "";\r
1711   appData.matchMode = FALSE;\r
1712   appData.matchGames = 0;\r
1713   appData.monoMode = FALSE;\r
1714   appData.debugMode = FALSE;\r
1715   appData.clockMode = TRUE;\r
1716   boardSize = (BoardSize) -1; /* determine by screen size */\r
1717   appData.Iconic = FALSE; /*unused*/\r
1718   appData.searchTime = "";\r
1719   appData.searchDepth = 0;\r
1720   appData.showCoords = FALSE;\r
1721   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1722   appData.autoCallFlag = FALSE;\r
1723   appData.flipView = FALSE;\r
1724   appData.autoFlipView = TRUE;\r
1725   appData.cmailGameName = "";\r
1726   appData.alwaysPromoteToQueen = FALSE;\r
1727   appData.oldSaveStyle = FALSE;\r
1728   appData.quietPlay = FALSE;\r
1729   appData.showThinking = FALSE;\r
1730   appData.ponderNextMove = TRUE;\r
1731   appData.periodicUpdates = TRUE;\r
1732   appData.popupExitMessage = TRUE;\r
1733   appData.popupMoveErrors = FALSE;\r
1734   appData.autoObserve = FALSE;\r
1735   appData.autoComment = FALSE;\r
1736   appData.animate = TRUE;\r
1737   appData.animSpeed = 10;\r
1738   appData.animateDragging = TRUE;\r
1739   appData.highlightLastMove = TRUE;\r
1740   appData.getMoveList = TRUE;\r
1741   appData.testLegality = TRUE;\r
1742   appData.premove = TRUE;\r
1743   appData.premoveWhite = FALSE;\r
1744   appData.premoveWhiteText = "";\r
1745   appData.premoveBlack = FALSE;\r
1746   appData.premoveBlackText = "";\r
1747   appData.icsAlarm = TRUE;\r
1748   appData.icsAlarmTime = 5000;\r
1749   appData.autoRaiseBoard = TRUE;\r
1750   appData.localLineEditing = TRUE;\r
1751   appData.colorize = TRUE;\r
1752   appData.reuseFirst = TRUE;\r
1753   appData.reuseSecond = TRUE;\r
1754   appData.blindfold = FALSE;\r
1755   dcb.DCBlength = sizeof(DCB);\r
1756   dcb.BaudRate = 9600;\r
1757   dcb.fBinary = TRUE;\r
1758   dcb.fParity = FALSE;\r
1759   dcb.fOutxCtsFlow = FALSE;\r
1760   dcb.fOutxDsrFlow = FALSE;\r
1761   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1762   dcb.fDsrSensitivity = FALSE;\r
1763   dcb.fTXContinueOnXoff = TRUE;\r
1764   dcb.fOutX = FALSE;\r
1765   dcb.fInX = FALSE;\r
1766   dcb.fNull = FALSE;\r
1767   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1768   dcb.fAbortOnError = FALSE;\r
1769   dcb.wReserved = 0;\r
1770   dcb.ByteSize = 7;\r
1771   dcb.Parity = SPACEPARITY;\r
1772   dcb.StopBits = ONESTOPBIT;\r
1773   settingsFileName = SETTINGS_FILE;\r
1774   saveSettingsOnExit = TRUE;\r
1775   boardX = CW_USEDEFAULT;\r
1776   boardY = CW_USEDEFAULT;\r
1777   consoleX = CW_USEDEFAULT; \r
1778   consoleY = CW_USEDEFAULT; \r
1779   consoleW = CW_USEDEFAULT;\r
1780   consoleH = CW_USEDEFAULT;\r
1781   analysisX = CW_USEDEFAULT; \r
1782   analysisY = CW_USEDEFAULT; \r
1783   analysisW = CW_USEDEFAULT;\r
1784   analysisH = CW_USEDEFAULT;\r
1785   commentX = CW_USEDEFAULT; \r
1786   commentY = CW_USEDEFAULT; \r
1787   commentW = CW_USEDEFAULT;\r
1788   commentH = CW_USEDEFAULT;\r
1789   editTagsX = CW_USEDEFAULT; \r
1790   editTagsY = CW_USEDEFAULT; \r
1791   editTagsW = CW_USEDEFAULT;\r
1792   editTagsH = CW_USEDEFAULT;\r
1793   gameListX = CW_USEDEFAULT; \r
1794   gameListY = CW_USEDEFAULT; \r
1795   gameListW = CW_USEDEFAULT;\r
1796   gameListH = CW_USEDEFAULT;\r
1797   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1798   icsNames = ICS_NAMES;\r
1799   firstChessProgramNames = FCP_NAMES;\r
1800   secondChessProgramNames = SCP_NAMES;\r
1801   appData.initialMode = "";\r
1802   appData.variant = "normal";\r
1803   appData.firstProtocolVersion = PROTOVER;\r
1804   appData.secondProtocolVersion = PROTOVER;\r
1805   appData.showButtonBar = TRUE;\r
1806
1807    /* [AS] New properties (see comments in header file) */
1808   appData.firstScoreIsAbsolute = FALSE;
1809   appData.secondScoreIsAbsolute = FALSE;
1810   appData.saveExtendedInfoInPGN = FALSE;
1811   appData.hideThinkingFromHuman = FALSE;
1812   appData.liteBackTextureFile = "";
1813   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
1814   appData.darkBackTextureFile = "";
1815   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
1816   appData.renderPiecesWithFont = "";
1817   appData.fontToPieceTable = "";
1818   appData.fontBackColorWhite = 0;
1819   appData.fontForeColorWhite = 0;
1820   appData.fontBackColorBlack = 0;
1821   appData.fontForeColorBlack = 0;
1822   appData.fontPieceSize = 80;
1823   appData.overrideLineGap = 1;
1824   appData.adjudicateLossThreshold = 0;
1825   appData.delayBeforeQuit = 0;
1826   appData.delayAfterQuit = 0;
1827   appData.nameOfDebugFile = "winboard.debug";
1828   appData.pgnEventHeader = "Computer Chess Game";
1829   appData.defaultFrcPosition = -1;
1830   appData.gameListTags = GLT_DEFAULT_TAGS;
1831   appData.saveOutOfBookInfo = TRUE;
1832   appData.showEvalInMoveHistory = TRUE;
1833   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );
1834   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );
1835   appData.highlightMoveWithArrow = FALSE;
1836   appData.highlightArrowColor = ParseColorName( "#FFFF80" );
1837   appData.useStickyWindows = TRUE;
1838   appData.adjudicateDrawMoves = 0;
1839   appData.autoDisplayComment = TRUE;
1840   appData.autoDisplayTags = TRUE;
1841   appData.firstIsUCI = FALSE;
1842   appData.secondIsUCI = FALSE;
1843   appData.firstHasOwnBookUCI = TRUE;
1844   appData.secondHasOwnBookUCI = TRUE;
1845   appData.polyglotDir = "";
1846   appData.usePolyglotBook = FALSE;
1847   appData.polyglotBook = "";
1848   appData.defaultHashSize = 64;
1849   appData.defaultCacheSizeEGTB = 4;
1850   appData.defaultPathEGTB = "c:\\egtb";
1851
1852   InitWindowPlacement( &wpMoveHistory );
1853   InitWindowPlacement( &wpEvalGraph );
1854   InitWindowPlacement( &wpEngineOutput );
1855
1856 #ifdef ZIPPY\r
1857   appData.zippyTalk = ZIPPY_TALK;\r
1858   appData.zippyPlay = ZIPPY_PLAY;\r
1859   appData.zippyLines = ZIPPY_LINES;\r
1860   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1861   appData.zippyPassword = ZIPPY_PASSWORD;\r
1862   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1863   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1864   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1865   appData.zippyUseI = ZIPPY_USE_I;\r
1866   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1867   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1868   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1869   appData.zippyGameStart = ZIPPY_GAME_START;\r
1870   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1871   appData.zippyAbort = ZIPPY_ABORT;\r
1872   appData.zippyVariants = ZIPPY_VARIANTS;\r
1873   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1874   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1875 #endif\r
1876 \r
1877   /* Point font array elements to structures and\r
1878      parse default font names */\r
1879   for (i=0; i<NUM_FONTS; i++) {\r
1880     for (j=0; j<NUM_SIZES; j++) {\r
1881       font[j][i] = &fontRec[j][i];\r
1882       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1883     }\r
1884   }\r
1885   \r
1886   /* Parse default settings file if any */\r
1887   if (ParseSettingsFile(settingsFileName, buf)) {\r
1888     settingsFileName = strdup(buf);\r
1889   }\r
1890 \r
1891   /* Parse command line */\r
1892   ParseArgs(StringGet, &lpCmdLine);\r
1893 \r
1894   /* Propagate options that affect others */\r
1895   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1896   if (appData.icsActive || appData.noChessProgram) {\r
1897      chessProgram = FALSE;  /* not local chess program mode */\r
1898   }\r
1899 \r
1900   /* Open startup dialog if needed */\r
1901   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1902       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1903       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1904                         *appData.secondChessProgram == NULLCHAR))) {\r
1905     FARPROC lpProc;\r
1906     \r
1907     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1908     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1909     FreeProcInstance(lpProc);\r
1910   }\r
1911 \r
1912   /* Make sure save files land in the right (?) directory */\r
1913   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
1914     appData.saveGameFile = strdup(buf);\r
1915   }\r
1916   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1917     appData.savePositionFile = strdup(buf);\r
1918   }\r
1919 \r
1920   /* Finish initialization for fonts and sounds */\r
1921   for (i=0; i<NUM_FONTS; i++) {\r
1922     for (j=0; j<NUM_SIZES; j++) {\r
1923       CreateFontInMF(font[j][i]);\r
1924     }\r
1925   }\r
1926   /* xboard, and older WinBoards, controlled the move sound with the\r
1927      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1928      always turn the option on (so that the backend will call us),\r
1929      then let the user turn the sound off by setting it to silence if\r
1930      desired.  To accommodate old winboard.ini files saved by old\r
1931      versions of WinBoard, we also turn off the sound if the option\r
1932      was initially set to false. */\r
1933   if (!appData.ringBellAfterMoves) {\r
1934     sounds[(int)SoundMove].name = strdup("");\r
1935     appData.ringBellAfterMoves = TRUE;\r
1936   }\r
1937   GetCurrentDirectory(MSG_SIZ, currDir);\r
1938   SetCurrentDirectory(installDir);\r
1939   LoadAllSounds();\r
1940   SetCurrentDirectory(currDir);\r
1941 \r
1942   p = icsTextMenuString;\r
1943   if (p[0] == '@') {\r
1944     FILE* f = fopen(p + 1, "r");\r
1945     if (f == NULL) {\r
1946       DisplayFatalError(p + 1, errno, 2);\r
1947       return;\r
1948     }\r
1949     i = fread(buf, 1, sizeof(buf)-1, f);\r
1950     fclose(f);\r
1951     buf[i] = NULLCHAR;\r
1952     p = buf;\r
1953   }\r
1954   ParseIcsTextMenu(strdup(p));\r
1955 }\r
1956 \r
1957 \r
1958 VOID\r
1959 InitMenuChecks()\r
1960 {\r
1961   HMENU hmenu = GetMenu(hwndMain);\r
1962 \r
1963   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1964                         MF_BYCOMMAND|((appData.icsActive &&\r
1965                                        *appData.icsCommPort != NULLCHAR) ?\r
1966                                       MF_ENABLED : MF_GRAYED));\r
1967   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1968                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1969                                      MF_CHECKED : MF_UNCHECKED));\r
1970 }\r
1971 \r
1972 \r
1973 VOID\r
1974 SaveSettings(char* name)\r
1975 {\r
1976   FILE *f;\r
1977   ArgDescriptor *ad;\r
1978   WINDOWPLACEMENT wp;\r
1979   char dir[MSG_SIZ];\r
1980 \r
1981   if (!hwndMain) return;\r
1982 \r
1983   GetCurrentDirectory(MSG_SIZ, dir);\r
1984   SetCurrentDirectory(installDir);\r
1985   f = fopen(name, "w");\r
1986   SetCurrentDirectory(dir);\r
1987   if (f == NULL) {\r
1988     DisplayError(name, errno);\r
1989     return;\r
1990   }\r
1991   fprintf(f, ";\n");\r
1992   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
1993   fprintf(f, ";\n");\r
1994   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
1995   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
1996   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
1997   fprintf(f, ";\n");\r
1998 \r
1999   wp.length = sizeof(WINDOWPLACEMENT);\r
2000   GetWindowPlacement(hwndMain, &wp);\r
2001   boardX = wp.rcNormalPosition.left;\r
2002   boardY = wp.rcNormalPosition.top;\r
2003 \r
2004   if (hwndConsole) {\r
2005     GetWindowPlacement(hwndConsole, &wp);\r
2006     consoleX = wp.rcNormalPosition.left;\r
2007     consoleY = wp.rcNormalPosition.top;\r
2008     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2009     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2010   }\r
2011 \r
2012   if (analysisDialog) {\r
2013     GetWindowPlacement(analysisDialog, &wp);\r
2014     analysisX = wp.rcNormalPosition.left;\r
2015     analysisY = wp.rcNormalPosition.top;\r
2016     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2017     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2018   }\r
2019 \r
2020   if (commentDialog) {\r
2021     GetWindowPlacement(commentDialog, &wp);\r
2022     commentX = wp.rcNormalPosition.left;\r
2023     commentY = wp.rcNormalPosition.top;\r
2024     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2025     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2026   }\r
2027 \r
2028   if (editTagsDialog) {\r
2029     GetWindowPlacement(editTagsDialog, &wp);\r
2030     editTagsX = wp.rcNormalPosition.left;\r
2031     editTagsY = wp.rcNormalPosition.top;\r
2032     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2033     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2034   }\r
2035 \r
2036   if (gameListDialog) {\r
2037     GetWindowPlacement(gameListDialog, &wp);\r
2038     gameListX = wp.rcNormalPosition.left;\r
2039     gameListY = wp.rcNormalPosition.top;\r
2040     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2041     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2042   }\r
2043 \r
2044   /* [AS] Move history */
2045   wpMoveHistory.visible = MoveHistoryIsUp();
2046
2047   if( moveHistoryDialog ) {
2048     GetWindowPlacement(moveHistoryDialog, &wp);
2049     wpMoveHistory.x = wp.rcNormalPosition.left;
2050     wpMoveHistory.y = wp.rcNormalPosition.top;
2051     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
2052     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
2053   }
2054
2055   /* [AS] Eval graph */
2056   wpEvalGraph.visible = EvalGraphIsUp();
2057
2058   if( evalGraphDialog ) {
2059     GetWindowPlacement(evalGraphDialog, &wp);
2060     wpEvalGraph.x = wp.rcNormalPosition.left;
2061     wpEvalGraph.y = wp.rcNormalPosition.top;
2062     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
2063     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
2064   }
2065
2066   /* [AS] Engine output */
2067   wpEngineOutput.visible = EngineOutputIsUp();
2068
2069   if( engineOutputDialog ) {
2070     GetWindowPlacement(engineOutputDialog, &wp);
2071     wpEngineOutput.x = wp.rcNormalPosition.left;
2072     wpEngineOutput.y = wp.rcNormalPosition.top;
2073     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
2074     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
2075   }
2076
2077   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2078     if (!ad->save) continue;\r
2079     switch (ad->argType) {\r
2080     case ArgString:\r
2081       {\r
2082         char *p = *(char **)ad->argLoc;\r
2083         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2084           /* Quote multiline values or \-containing values\r
2085              with { } if possible */\r
2086           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2087         } else {\r
2088           /* Else quote with " " */\r
2089           fprintf(f, "/%s=\"", ad->argName);\r
2090           while (*p) {\r
2091             if (*p == '\n') fprintf(f, "\n");\r
2092             else if (*p == '\r') fprintf(f, "\\r");\r
2093             else if (*p == '\t') fprintf(f, "\\t");\r
2094             else if (*p == '\b') fprintf(f, "\\b");\r
2095             else if (*p == '\f') fprintf(f, "\\f");\r
2096             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2097             else if (*p == '\"') fprintf(f, "\\\"");\r
2098             else if (*p == '\\') fprintf(f, "\\\\");\r
2099             else putc(*p, f);\r
2100             p++;\r
2101           }\r
2102           fprintf(f, "\"\n");\r
2103         }\r
2104       }\r
2105       break;\r
2106     case ArgInt:\r
2107       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2108       break;\r
2109     case ArgFloat:\r
2110       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2111       break;\r
2112     case ArgBoolean:\r
2113       fprintf(f, "/%s=%s\n", ad->argName, \r
2114         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2115       break;\r
2116     case ArgTrue:\r
2117       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2118       break;\r
2119     case ArgFalse:\r
2120       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2121       break;\r
2122     case ArgColor:\r
2123       {\r
2124         COLORREF color = *(COLORREF *)ad->argLoc;\r
2125         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2126           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2127       }\r
2128       break;\r
2129     case ArgAttribs:\r
2130       {\r
2131         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2132         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2133           (ta->effects & CFE_BOLD) ? "b" : "",\r
2134           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2135           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2136           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2137           (ta->effects) ? " " : "",\r
2138           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2139       }\r
2140       break;\r
2141     case ArgFilename:\r
2142       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2143         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2144       } else {\r
2145         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2146       }\r
2147       break;\r
2148     case ArgBoardSize:\r
2149       fprintf(f, "/%s=%s\n", ad->argName,\r
2150               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2151       break;\r
2152     case ArgFont:\r
2153       {\r
2154         int bs;\r
2155         for (bs=0; bs<NUM_SIZES; bs++) {\r
2156           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2157           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2158           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2159             ad->argName, mfp->faceName, mfp->pointSize,\r
2160             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2161             mfp->bold ? "b" : "",\r
2162             mfp->italic ? "i" : "",\r
2163             mfp->underline ? "u" : "",\r
2164             mfp->strikeout ? "s" : "");\r
2165         }\r
2166       }\r
2167       break;\r
2168     case ArgCommSettings:\r
2169       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2170     }\r
2171   }\r
2172   fclose(f);\r
2173 }\r
2174 \r
2175 \r
2176 \r
2177 /*---------------------------------------------------------------------------*\\r
2178  *\r
2179  * GDI board drawing routines\r
2180  *\r
2181 \*---------------------------------------------------------------------------*/\r
2182 \r
2183 /* [AS] Draw square using background texture */
2184 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
2185 {
2186     XFORM   x;
2187
2188     if( mode == 0 ) {
2189         return; /* Should never happen! */
2190     }
2191
2192     SetGraphicsMode( dst, GM_ADVANCED );
2193
2194     switch( mode ) {
2195     case 1:
2196         /* Identity */
2197         break;
2198     case 2:
2199         /* X reflection */
2200         x.eM11 = -1.0;
2201         x.eM12 = 0;
2202         x.eM21 = 0;
2203         x.eM22 = 1.0;
2204         x.eDx = (FLOAT) dw + dx - 1;
2205         x.eDy = 0;
2206         dx = 0;
2207         SetWorldTransform( dst, &x );
2208         break;
2209     case 3:
2210         /* Y reflection */
2211         x.eM11 = 1.0;
2212         x.eM12 = 0;
2213         x.eM21 = 0;
2214         x.eM22 = -1.0;
2215         x.eDx = 0;
2216         x.eDy = (FLOAT) dh + dy - 1;
2217         dy = 0;
2218         SetWorldTransform( dst, &x );
2219         break;
2220     case 4:
2221         /* X/Y flip */
2222         x.eM11 = 0;
2223         x.eM12 = 1.0;
2224         x.eM21 = 1.0;
2225         x.eM22 = 0;
2226         x.eDx = (FLOAT) dx;
2227         x.eDy = (FLOAT) dy;
2228         dx = 0;
2229         dy = 0;
2230         SetWorldTransform( dst, &x );
2231         break;
2232     }
2233
2234     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
2235
2236     x.eM11 = 1.0;
2237     x.eM12 = 0;
2238     x.eM21 = 0;
2239     x.eM22 = 1.0;
2240     x.eDx = 0;
2241     x.eDy = 0;
2242     SetWorldTransform( dst, &x );
2243
2244     ModifyWorldTransform( dst, 0, MWT_IDENTITY );
2245 }
2246
2247 /* [AS] */
2248 enum {
2249     PM_WP = 0,
2250     PM_WN = 1,
2251     PM_WB = 2,
2252     PM_WR = 3,
2253     PM_WQ = 4,
2254     PM_WK = 5,
2255     PM_BP = 6,
2256     PM_BN = 7,
2257     PM_BB = 8,
2258     PM_BR = 9,
2259     PM_BQ = 10,
2260     PM_BK = 11
2261 };
2262
2263 static HFONT hPieceFont = NULL;
2264 static HBITMAP hPieceMask[12];
2265 static HBITMAP hPieceFace[12];
2266 static int fontBitmapSquareSize = 0;
2267 static char pieceToFontChar[12] = { 'p', 'n', 'b', 'r', 'q', 'k', 'o', 'm', 'v', 't', 'w', 'l' };
2268
2269 static BOOL SetPieceToFontCharTable( const char * map )
2270 {
2271     BOOL result = FALSE;
2272
2273     if( map != NULL && strlen(map) == 12 ) {
2274         int i;
2275
2276         for( i=0; i<12; i++ ) {
2277             pieceToFontChar[i] = map[i];
2278         }
2279
2280         result = TRUE;
2281     }
2282
2283     return result;
2284 }
2285
2286 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
2287 {
2288     HBRUSH hbrush;
2289     BYTE r1 = GetRValue( color );
2290     BYTE g1 = GetGValue( color );
2291     BYTE b1 = GetBValue( color );
2292     BYTE r2 = r1 / 2;
2293     BYTE g2 = g1 / 2;
2294     BYTE b2 = b1 / 2;
2295     RECT rc;
2296
2297     /* Create a uniform background first */
2298     hbrush = CreateSolidBrush( color );
2299     SetRect( &rc, 0, 0, squareSize, squareSize );
2300     FillRect( hdc, &rc, hbrush );
2301     DeleteObject( hbrush );
2302
2303     if( mode == 1 ) {
2304         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
2305         int steps = squareSize / 2;
2306         int i;
2307
2308         for( i=0; i<steps; i++ ) {
2309             BYTE r = r1 - (r1-r2) * i / steps;
2310             BYTE g = g1 - (g1-g2) * i / steps;
2311             BYTE b = b1 - (b1-b2) * i / steps;
2312
2313             hbrush = CreateSolidBrush( RGB(r,g,b) );
2314             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
2315             FillRect( hdc, &rc, hbrush );
2316             DeleteObject(hbrush);
2317         }
2318     }
2319     else if( mode == 2 ) {
2320         /* Diagonal gradient, good more or less for every piece */
2321         POINT triangle[3];
2322         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
2323         HBRUSH hbrush_old;
2324         int steps = squareSize;
2325         int i;
2326
2327         triangle[0].x = squareSize - steps;
2328         triangle[0].y = squareSize;
2329         triangle[1].x = squareSize;
2330         triangle[1].y = squareSize;
2331         triangle[2].x = squareSize;
2332         triangle[2].y = squareSize - steps;
2333
2334         for( i=0; i<steps; i++ ) {
2335             BYTE r = r1 - (r1-r2) * i / steps;
2336             BYTE g = g1 - (g1-g2) * i / steps;
2337             BYTE b = b1 - (b1-b2) * i / steps;
2338
2339             hbrush = CreateSolidBrush( RGB(r,g,b) );
2340             hbrush_old = SelectObject( hdc, hbrush );
2341             Polygon( hdc, triangle, 3 );
2342             SelectObject( hdc, hbrush_old );
2343             DeleteObject(hbrush);
2344             triangle[0].x++;
2345             triangle[2].y++;
2346         }
2347
2348         SelectObject( hdc, hpen );
2349     }
2350 }
2351
2352 /*
2353     [AS] The method I use to create the bitmaps it a bit tricky, but it
2354     seems to work ok. The main problem here is to find the "inside" of a chess
2355     piece: follow the steps as explained below.
2356 */
2357 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
2358 {
2359     HBITMAP hbm;
2360     HBITMAP hbm_old;
2361     COLORREF chroma = RGB(0xFF,0x00,0xFF);
2362     RECT rc;
2363     SIZE sz;
2364     POINT pt;
2365     int backColor = whitePieceColor;
2366     int foreColor = blackPieceColor;
2367     int shapeIndex = index < 6 ? index+6 : index;
2368
2369     if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
2370         backColor = appData.fontBackColorWhite;
2371         foreColor = appData.fontForeColorWhite;
2372     }
2373     else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
2374         backColor = appData.fontBackColorBlack;
2375         foreColor = appData.fontForeColorBlack;
2376     }
2377
2378     /* Mask */
2379     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
2380
2381     hbm_old = SelectObject( hdc, hbm );
2382
2383     rc.left = 0;
2384     rc.top = 0;
2385     rc.right = squareSize;
2386     rc.bottom = squareSize;
2387
2388     /* Step 1: background is now black */
2389     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
2390
2391     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
2392
2393     pt.x = (squareSize - sz.cx) / 2;
2394     pt.y = (squareSize - sz.cy) / 2;
2395
2396     SetBkMode( hdc, TRANSPARENT );
2397     SetTextColor( hdc, chroma );
2398     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
2399     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
2400
2401     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
2402     /* Step 3: the area outside the piece is filled with white */
2403     FloodFill( hdc, 0, 0, chroma );
2404     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
2405     /*
2406         Step 4: this is the tricky part, the area inside the piece is filled with black,
2407         but if the start point is not inside the piece we're lost!
2408         There should be a better way to do this... if we could create a region or path
2409         from the fill operation we would be fine for example.
2410     */
2411     FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
2412
2413     SetTextColor( hdc, 0 );
2414     /*
2415         Step 5: some fonts have "disconnected" areas that are skipped by the fill:
2416         draw the piece again in black for safety.
2417     */
2418     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
2419
2420     SelectObject( hdc, hbm_old );
2421
2422     if( hPieceMask[index] != NULL ) {
2423         DeleteObject( hPieceMask[index] );
2424     }
2425
2426     hPieceMask[index] = hbm;
2427
2428     /* Face */
2429     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
2430
2431     SelectObject( hdc, hbm );
2432
2433     {
2434         HDC dc1 = CreateCompatibleDC( hdc_window );
2435         HDC dc2 = CreateCompatibleDC( hdc_window );
2436         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
2437
2438         SelectObject( dc1, hPieceMask[index] );
2439         SelectObject( dc2, bm2 );
2440         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
2441         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
2442
2443         /*
2444             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
2445             the piece background and deletes (makes transparent) the rest.
2446             Thanks to that mask, we are free to paint the background with the greates
2447             freedom, as we'll be able to mask off the unwanted parts when finished.
2448             We use this, to make gradients and give the pieces a "roundish" look.
2449         */
2450         SetPieceBackground( hdc, backColor, 2 );
2451         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
2452
2453         DeleteDC( dc2 );
2454         DeleteDC( dc1 );
2455         DeleteObject( bm2 );
2456     }
2457
2458     SetTextColor( hdc, foreColor );
2459     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
2460
2461     SelectObject( hdc, hbm_old );
2462
2463     if( hPieceFace[index] != NULL ) {
2464         DeleteObject( hPieceFace[index] );
2465     }
2466
2467     hPieceFace[index] = hbm;
2468 }
2469
2470 static int TranslatePieceToFontPiece( int piece )
2471 {
2472     switch( piece ) {
2473     case BlackPawn:
2474         return PM_BP;
2475     case BlackKnight:
2476         return PM_BN;
2477     case BlackBishop:
2478         return PM_BB;
2479     case BlackRook:
2480         return PM_BR;
2481     case BlackQueen:
2482         return PM_BQ;
2483     case BlackKing:
2484         return PM_BK;
2485     case WhitePawn:
2486         return PM_WP;
2487     case WhiteKnight:
2488         return PM_WN;
2489     case WhiteBishop:
2490         return PM_WB;
2491     case WhiteRook:
2492         return PM_WR;
2493     case WhiteQueen:
2494         return PM_WQ;
2495     case WhiteKing:
2496         return PM_WK;
2497     }
2498
2499     return 0;
2500 }
2501
2502 void CreatePiecesFromFont()
2503 {
2504     LOGFONT lf;
2505     HDC hdc_window = NULL;
2506     HDC hdc = NULL;
2507     HFONT hfont_old;
2508     int fontHeight;
2509     int i;
2510
2511     if( fontBitmapSquareSize < 0 ) {
2512         /* Something went seriously wrong in the past: do not try to recreate fonts! */
2513         return;
2514     }
2515
2516     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
2517         fontBitmapSquareSize = -1;
2518         return;
2519     }
2520
2521     if( fontBitmapSquareSize != squareSize ) {
2522         hdc_window = GetDC( hwndMain );
2523         hdc = CreateCompatibleDC( hdc_window );
2524
2525         if( hPieceFont != NULL ) {
2526             DeleteObject( hPieceFont );
2527         }
2528         else {
2529             for( i=0; i<12; i++ ) {
2530                 hPieceMask[i] = NULL;
2531                 hPieceFace[i] = NULL;
2532             }
2533         }
2534
2535         fontHeight = 75;
2536
2537         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
2538             fontHeight = appData.fontPieceSize;
2539         }
2540
2541         fontHeight = (fontHeight * squareSize) / 100;
2542
2543         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
2544         lf.lfWidth = 0;
2545         lf.lfEscapement = 0;
2546         lf.lfOrientation = 0;
2547         lf.lfWeight = FW_NORMAL;
2548         lf.lfItalic = 0;
2549         lf.lfUnderline = 0;
2550         lf.lfStrikeOut = 0;
2551         lf.lfCharSet = DEFAULT_CHARSET;
2552         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
2553         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2554         lf.lfQuality = PROOF_QUALITY;
2555         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
2556         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
2557         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
2558
2559         hPieceFont = CreateFontIndirect( &lf );
2560
2561         if( hPieceFont == NULL ) {
2562             fontBitmapSquareSize = -2;
2563         }
2564         else {
2565             /* Setup font-to-piece character table */
2566             if( ! SetPieceToFontCharTable(appData.fontToPieceTable) ) {
2567                 /* No (or wrong) global settings, try to detect the font */
2568                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
2569                     /* Alpha */
2570                     SetPieceToFontCharTable("phbrqkojntwl");
2571                 }
2572                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
2573                     /* DiagramTT* family */
2574                     SetPieceToFontCharTable("PNLRQKpnlrqk");
2575                 }
2576                 else {
2577                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
2578                     SetPieceToFontCharTable("pnbrqkomvtwl");
2579                 }
2580             }
2581
2582             /* Create bitmaps */
2583             hfont_old = SelectObject( hdc, hPieceFont );
2584
2585             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );
2586             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );
2587             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );
2588             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );
2589             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );
2590             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );
2591             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );
2592             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );
2593             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );
2594             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );
2595             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );
2596             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );
2597
2598             SelectObject( hdc, hfont_old );
2599
2600             fontBitmapSquareSize = squareSize;
2601         }
2602     }
2603
2604     if( hdc != NULL ) {
2605         DeleteDC( hdc );
2606     }
2607
2608     if( hdc_window != NULL ) {
2609         ReleaseDC( hwndMain, hdc_window );
2610     }
2611 }
2612
2613 HBITMAP\r
2614 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2615 {\r
2616   char name[128];\r
2617 \r
2618   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2619   if (gameInfo.event &&\r
2620       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2621       strcmp(name, "k80s") == 0) {\r
2622     strcpy(name, "tim");\r
2623   }\r
2624   return LoadBitmap(hinst, name);\r
2625 }\r
2626 \r
2627 \r
2628 /* Insert a color into the program's logical palette\r
2629    structure.  This code assumes the given color is\r
2630    the result of the RGB or PALETTERGB macro, and it\r
2631    knows how those macros work (which is documented).\r
2632 */\r
2633 VOID\r
2634 InsertInPalette(COLORREF color)\r
2635 {\r
2636   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2637 \r
2638   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2639     DisplayFatalError("Too many colors", 0, 1);\r
2640     pLogPal->palNumEntries--;\r
2641     return;\r
2642   }\r
2643 \r
2644   pe->peFlags = (char) 0;\r
2645   pe->peRed = (char) (0xFF & color);\r
2646   pe->peGreen = (char) (0xFF & (color >> 8));\r
2647   pe->peBlue = (char) (0xFF & (color >> 16));\r
2648   return;\r
2649 }\r
2650 \r
2651 \r
2652 VOID\r
2653 InitDrawingColors()\r
2654 {\r
2655   if (pLogPal == NULL) {\r
2656     /* Allocate enough memory for a logical palette with\r
2657      * PALETTESIZE entries and set the size and version fields\r
2658      * of the logical palette structure.\r
2659      */\r
2660     pLogPal = (NPLOGPALETTE)\r
2661       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2662                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2663     pLogPal->palVersion    = 0x300;\r
2664   }\r
2665   pLogPal->palNumEntries = 0;\r
2666 \r
2667   InsertInPalette(lightSquareColor);\r
2668   InsertInPalette(darkSquareColor);\r
2669   InsertInPalette(whitePieceColor);\r
2670   InsertInPalette(blackPieceColor);\r
2671   InsertInPalette(highlightSquareColor);\r
2672   InsertInPalette(premoveHighlightColor);\r
2673 \r
2674   /*  create a logical color palette according the information\r
2675    *  in the LOGPALETTE structure.\r
2676    */\r
2677   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2678 \r
2679   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2680   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2681   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2682   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2683   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2684
2685   /* [AS] Force rendering of the font-based pieces */
2686   if( fontBitmapSquareSize > 0 ) {
2687     fontBitmapSquareSize = 0;
2688   }
2689 }\r
2690 \r
2691 \r
2692 int\r
2693 BoardWidth(int boardSize)\r
2694 {\r
2695   int lineGap = sizeInfo[boardSize].lineGap;
2696
2697   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
2698       lineGap = appData.overrideLineGap;
2699   }
2700
2701   return (BOARD_SIZE + 1) * lineGap +
2702           BOARD_SIZE * sizeInfo[boardSize].squareSize;\r
2703 }\r
2704 \r
2705 /* Respond to board resize by dragging edge */\r
2706 VOID\r
2707 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2708 {\r
2709   BoardSize newSize = NUM_SIZES - 1;\r
2710   static int recurse = 0;\r
2711   if (IsIconic(hwndMain)) return;\r
2712   if (recurse > 0) return;\r
2713   recurse++;\r
2714   while (newSize > 0 &&\r
2715          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2716           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2717     newSize--;\r
2718   } \r
2719   boardSize = newSize;\r
2720   InitDrawingSizes(boardSize, flags);\r
2721   recurse--;\r
2722 }\r
2723 \r
2724 \r
2725 \r
2726 VOID\r
2727 InitDrawingSizes(BoardSize boardSize, int flags)\r
2728 {\r
2729   int i, boardWidth;\r
2730   ChessSquare piece;\r
2731   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2732   HDC hdc;\r
2733   SIZE clockSize, messageSize;\r
2734   HFONT oldFont;\r
2735   char buf[MSG_SIZ];\r
2736   char *str;\r
2737   HMENU hmenu = GetMenu(hwndMain);\r
2738   RECT crect, wrect;\r
2739   int offby;\r
2740   LOGBRUSH logbrush;\r
2741 \r
2742   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2743   smallLayout = sizeInfo[boardSize].smallLayout;\r
2744   squareSize = sizeInfo[boardSize].squareSize;\r
2745   lineGap = sizeInfo[boardSize].lineGap;\r
2746 \r
2747   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
2748       lineGap = appData.overrideLineGap;
2749   }
2750
2751   if (tinyLayout != oldTinyLayout) {\r
2752     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2753     if (tinyLayout) {\r
2754       style &= ~WS_SYSMENU;\r
2755       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2756                  "&Minimize\tCtrl+F4");\r
2757     } else {\r
2758       style |= WS_SYSMENU;\r
2759       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2760     }\r
2761     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2762 \r
2763     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2764       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2765         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2766     }\r
2767     DrawMenuBar(hwndMain);\r
2768   }\r
2769 \r
2770   boardWidth = BoardWidth(boardSize);\r
2771 \r
2772   /* Get text area sizes */\r
2773   hdc = GetDC(hwndMain);\r
2774   if (appData.clockMode) {\r
2775     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2776   } else {\r
2777     sprintf(buf, "White");\r
2778   }\r
2779   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2780   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2781   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2782   str = "We only care about the height here";\r
2783   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2784   SelectObject(hdc, oldFont);\r
2785   ReleaseDC(hwndMain, hdc);\r
2786 \r
2787   /* Compute where everything goes */\r
2788   whiteRect.left = OUTER_MARGIN;\r
2789   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2790   whiteRect.top = OUTER_MARGIN;\r
2791   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2792 \r
2793   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2794   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2795   blackRect.top = whiteRect.top;\r
2796   blackRect.bottom = whiteRect.bottom;\r
2797 \r
2798   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2799   if (appData.showButtonBar) {\r
2800     messageRect.right = blackRect.right\r
2801       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2802   } else {\r
2803     messageRect.right = blackRect.right;\r
2804   }\r
2805   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2806   messageRect.bottom = messageRect.top + messageSize.cy;\r
2807 \r
2808   boardRect.left = whiteRect.left;\r
2809   boardRect.right = boardRect.left + boardWidth;\r
2810   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2811   boardRect.bottom = boardRect.top + boardWidth;\r
2812 \r
2813   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2814   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2815   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2816   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2817     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2818   GetWindowRect(hwndMain, &wrect);\r
2819   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2820                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2821   /* compensate if menu bar wrapped */\r
2822   GetClientRect(hwndMain, &crect);\r
2823   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2824   winHeight += offby;\r
2825   switch (flags) {\r
2826   case WMSZ_TOPLEFT:\r
2827     SetWindowPos(hwndMain, NULL, \r
2828                  wrect.right - winWidth, wrect.bottom - winHeight, \r
2829                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2830     break;\r
2831 \r
2832   case WMSZ_TOPRIGHT:\r
2833   case WMSZ_TOP:\r
2834     SetWindowPos(hwndMain, NULL, \r
2835                  wrect.left, wrect.bottom - winHeight, \r
2836                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2837     break;\r
2838 \r
2839   case WMSZ_BOTTOMLEFT:\r
2840   case WMSZ_LEFT:\r
2841     SetWindowPos(hwndMain, NULL, \r
2842                  wrect.right - winWidth, wrect.top, \r
2843                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2844     break;\r
2845 \r
2846   case WMSZ_BOTTOMRIGHT:\r
2847   case WMSZ_BOTTOM:\r
2848   case WMSZ_RIGHT:\r
2849   default:\r
2850     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2851                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2852     break;\r
2853   }\r
2854 \r
2855   hwndPause = NULL;\r
2856   for (i = 0; i < N_BUTTONS; i++) {\r
2857     if (buttonDesc[i].hwnd != NULL) {\r
2858       DestroyWindow(buttonDesc[i].hwnd);\r
2859       buttonDesc[i].hwnd = NULL;\r
2860     }\r
2861     if (appData.showButtonBar) {\r
2862       buttonDesc[i].hwnd =\r
2863         CreateWindow("BUTTON", buttonDesc[i].label,\r
2864                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2865                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2866                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2867                      (HMENU) buttonDesc[i].id,\r
2868                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2869       if (tinyLayout) {\r
2870         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2871                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2872                     MAKELPARAM(FALSE, 0));\r
2873       }\r
2874       if (buttonDesc[i].id == IDM_Pause)\r
2875         hwndPause = buttonDesc[i].hwnd;\r
2876       buttonDesc[i].wndproc = (WNDPROC)\r
2877         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2878     }\r
2879   }\r
2880   if (gridPen != NULL) DeleteObject(gridPen);\r
2881   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2882   if (premovePen != NULL) DeleteObject(premovePen);\r
2883   if (lineGap != 0) {\r
2884     logbrush.lbStyle = BS_SOLID;\r
2885     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2886     gridPen =\r
2887       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2888                    lineGap, &logbrush, 0, NULL);\r
2889     logbrush.lbColor = highlightSquareColor;\r
2890     highlightPen =\r
2891       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2892                    lineGap, &logbrush, 0, NULL);\r
2893 \r
2894     logbrush.lbColor = premoveHighlightColor; \r
2895     premovePen =\r
2896       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2897                    lineGap, &logbrush, 0, NULL);\r
2898 \r
2899     for (i = 0; i < BOARD_SIZE + 1; i++) {\r
2900       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2901       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;\r
2902       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2903         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2904       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2905         BOARD_SIZE * (squareSize + lineGap);\r
2906       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =\r
2907         gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +\r
2908         lineGap / 2 + (i * (squareSize + lineGap));\r
2909       gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =\r
2910         boardRect.top + BOARD_SIZE * (squareSize + lineGap);\r
2911       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2912     }\r
2913   }\r
2914 \r
2915   if (boardSize == oldBoardSize) return;\r
2916   oldBoardSize = boardSize;\r
2917   oldTinyLayout = tinyLayout;\r
2918 \r
2919   /* Load piece bitmaps for this board size */\r
2920   for (i=0; i<=2; i++) {\r
2921     for (piece = WhitePawn;\r
2922          (int) piece <= (int) WhiteKing;\r
2923          piece = (ChessSquare) ((int) piece + 1)) {\r
2924       if (pieceBitmap[i][piece] != NULL)\r
2925         DeleteObject(pieceBitmap[i][piece]);\r
2926     }\r
2927   }\r
2928 \r
2929   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2930   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2931   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2932   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2933   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2934   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2935   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2936   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2937   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2938   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2939   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2940   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2941   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2942   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2943   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2944   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2945   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2946   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2947 \r
2948 }\r
2949 \r
2950 HBITMAP\r
2951 PieceBitmap(ChessSquare p, int kind)\r
2952 {\r
2953   if ((int) p >= (int) BlackPawn)\r
2954     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2955 \r
2956   return pieceBitmap[kind][(int) p];\r
2957 }\r
2958 \r
2959 /***************************************************************/\r
2960 \r
2961 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2962 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2963 /*\r
2964 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2965 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2966 */\r
2967 \r
2968 VOID\r
2969 SquareToPos(int row, int column, int * x, int * y)\r
2970 {\r
2971   if (flipView) {\r
2972     *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
2973     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2974   } else {\r
2975     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2976     *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
2977   }\r
2978 }\r
2979 \r
2980 VOID\r
2981 DrawCoordsOnDC(HDC hdc)\r
2982 {\r
2983   static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};\r
2984   static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};\r
2985   char str[2] = { NULLCHAR, NULLCHAR };\r
2986   int oldMode, oldAlign, x, y, start, i;\r
2987   HFONT oldFont;\r
2988   HBRUSH oldBrush;\r
2989 \r
2990   if (!appData.showCoords)\r
2991     return;\r
2992 \r
2993   start = flipView ? 0 : 8;\r
2994 \r
2995   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2996   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2997   oldAlign = GetTextAlign(hdc);\r
2998   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2999 \r
3000   y = boardRect.top + lineGap;\r
3001   x = boardRect.left + lineGap;\r
3002 \r
3003   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3004   for (i = 0; i < 8; i++) {\r
3005     str[0] = files[start + i];\r
3006     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3007     y += squareSize + lineGap;\r
3008   }\r
3009 \r
3010   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3011   for (i = 0; i < 8; i++) {\r
3012     str[0] = ranks[start + i];\r
3013     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3014     x += squareSize + lineGap;\r
3015   }    \r
3016 \r
3017   SelectObject(hdc, oldBrush);\r
3018   SetBkMode(hdc, oldMode);\r
3019   SetTextAlign(hdc, oldAlign);\r
3020   SelectObject(hdc, oldFont);\r
3021 }\r
3022 \r
3023 VOID\r
3024 DrawGridOnDC(HDC hdc)\r
3025 {\r
3026   HPEN oldPen;\r
3027  \r
3028   if (lineGap != 0) {\r
3029     oldPen = SelectObject(hdc, gridPen);\r
3030     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);\r
3031     SelectObject(hdc, oldPen);\r
3032   }\r
3033 }\r
3034 \r
3035 #define HIGHLIGHT_PEN 0\r
3036 #define PREMOVE_PEN   1\r
3037 \r
3038 VOID\r
3039 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3040 {\r
3041   int x1, y1;\r
3042   HPEN oldPen, hPen;\r
3043   if (lineGap == 0) return;\r
3044   if (flipView) {\r
3045     x1 = boardRect.left +\r
3046       lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);\r
3047     y1 = boardRect.top +\r
3048       lineGap/2 + y * (squareSize + lineGap);\r
3049   } else {\r
3050     x1 = boardRect.left +\r
3051       lineGap/2 + x * (squareSize + lineGap);\r
3052     y1 = boardRect.top +\r
3053       lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);\r
3054   }\r
3055   hPen = pen ? premovePen : highlightPen;\r
3056   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3057   MoveToEx(hdc, x1, y1, NULL);\r
3058   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3059   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3060   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3061   LineTo(hdc, x1, y1);\r
3062   SelectObject(hdc, oldPen);\r
3063 }\r
3064 \r
3065 VOID\r
3066 DrawHighlightsOnDC(HDC hdc)\r
3067 {\r
3068   int i;\r
3069   for (i=0; i<2; i++) {\r
3070     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3071       DrawHighlightOnDC(hdc, TRUE,\r
3072                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3073                         HIGHLIGHT_PEN);\r
3074   }\r
3075   for (i=0; i<2; i++) {\r
3076     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3077         premoveHighlightInfo.sq[i].y >= 0) {\r
3078         DrawHighlightOnDC(hdc, TRUE,\r
3079                           premoveHighlightInfo.sq[i].x, \r
3080                           premoveHighlightInfo.sq[i].y,\r
3081                           PREMOVE_PEN);\r
3082     }\r
3083   }\r
3084 }\r
3085 \r
3086 /* Note: sqcolor is used only in monoMode */\r
3087 /* Note that this code is largely duplicated in woptions.c,\r
3088    function DrawSampleSquare, so that needs to be updated too */\r
3089 VOID\r
3090 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3091 {\r
3092   HBITMAP oldBitmap;\r
3093   HBRUSH oldBrush;\r
3094 \r
3095   if (appData.blindfold) return;\r
3096 \r
3097   /* [AS] Use font-based pieces if needed */
3098   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
3099     /* Create piece bitmaps, or do nothing if piece set is up to date */
3100     CreatePiecesFromFont();
3101
3102     if( fontBitmapSquareSize == squareSize ) {
3103         int index = TranslatePieceToFontPiece( piece );
3104
3105         SelectObject( tmphdc, hPieceMask[ index ] );
3106
3107         BitBlt( hdc,
3108             x, y,
3109             squareSize, squareSize,
3110             tmphdc,
3111             0, 0,
3112             SRCAND );
3113
3114         SelectObject( tmphdc, hPieceFace[ index ] );
3115
3116         BitBlt( hdc,
3117             x, y,
3118             squareSize, squareSize,
3119             tmphdc,
3120             0, 0,
3121             SRCPAINT );
3122
3123         return;
3124     }
3125   }
3126
3127   if (appData.monoMode) {\r
3128     SelectObject(tmphdc, PieceBitmap(piece, \r
3129       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3130     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3131            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3132   } else {\r
3133     if (color) {\r
3134       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3135       oldBrush = SelectObject(hdc, whitePieceBrush);\r
3136       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3137 #if 0\r
3138       /* Use black piece color for outline of white pieces */\r
3139       /* Not sure this looks really good (though xboard does it).\r
3140          Maybe better to have another selectable color, default black */\r
3141       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3142       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3143       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3144 #else\r
3145       /* Use black for outline of white pieces */\r
3146       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3147       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);\r
3148 #endif\r
3149     } else {\r
3150 #if 0\r
3151       /* Use white piece color for details of black pieces */\r
3152       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3153          WHITE_PIECE ones aren't always the right shape. */\r
3154       /* Not sure this looks really good (though xboard does it).\r
3155          Maybe better to have another selectable color, default medium gray? */\r
3156       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3157       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3158       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3159       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3160       SelectObject(hdc, blackPieceBrush);\r
3161       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3162 #else\r
3163       /* Use square color for details of black pieces */\r
3164       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3165       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3166       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3167 #endif\r
3168     }\r
3169     SelectObject(hdc, oldBrush);\r
3170     SelectObject(tmphdc, oldBitmap);\r
3171   }\r
3172 }\r
3173 \r
3174 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
3175 int GetBackTextureMode( int algo )
3176 {
3177     int result = BACK_TEXTURE_MODE_DISABLED;
3178
3179     switch( algo )
3180     {
3181         case BACK_TEXTURE_MODE_PLAIN:
3182             result = 1; /* Always use identity map */
3183             break;
3184         case BACK_TEXTURE_MODE_FULL_RANDOM:
3185             result = 1 + (myrandom() % 3); /* Pick a transformation at random */
3186             break;
3187     }
3188
3189     return result;
3190 }
3191
3192 /*
3193     [AS] Compute and save texture drawing info, otherwise we may not be able
3194     to handle redraws cleanly (as random numbers would always be different).
3195 */
3196 VOID RebuildTextureSquareInfo()
3197 {
3198     BITMAP bi;
3199     int lite_w = 0;
3200     int lite_h = 0;
3201     int dark_w = 0;
3202     int dark_h = 0;
3203     int row;
3204     int col;
3205
3206     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
3207
3208     if( liteBackTexture != NULL ) {
3209         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
3210             lite_w = bi.bmWidth;
3211             lite_h = bi.bmHeight;
3212         }
3213     }
3214
3215     if( darkBackTexture != NULL ) {
3216         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
3217             dark_w = bi.bmWidth;
3218             dark_h = bi.bmHeight;
3219         }
3220     }
3221
3222     for( row=0; row<BOARD_SIZE; row++ ) {
3223         for( col=0; col<BOARD_SIZE; col++ ) {
3224             if( (col + row) & 1 ) {
3225                 /* Lite square */
3226                 if( lite_w >= squareSize && lite_h >= squareSize ) {
3227                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_SIZE;
3228                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_SIZE;
3229                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
3230                 }
3231             }
3232             else {
3233                 /* Dark square */
3234                 if( dark_w >= squareSize && dark_h >= squareSize ) {
3235                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_SIZE;
3236                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_SIZE;
3237                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
3238                 }
3239             }
3240         }
3241     }
3242 }
3243
3244 /* [AS] Arrow highlighting support */
3245
3246 static int A_WIDTH = 5; /* Width of arrow body */
3247
3248 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
3249 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
3250
3251 static double Sqr( double x )
3252 {
3253     return x*x;
3254 }
3255
3256 static int Round( double x )
3257 {
3258     return (int) (x + 0.5);
3259 }
3260
3261 /* Draw an arrow between two points using current settings */
3262 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
3263 {
3264     POINT arrow[7];
3265     double dx, dy, j, k, x, y;
3266
3267     if( d_x == s_x ) {
3268         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
3269
3270         arrow[0].x = s_x + A_WIDTH;
3271         arrow[0].y = s_y;
3272
3273         arrow[1].x = s_x + A_WIDTH;
3274         arrow[1].y = d_y - h;
3275
3276         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
3277         arrow[2].y = d_y - h;
3278
3279         arrow[3].x = d_x;
3280         arrow[3].y = d_y;
3281
3282         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
3283         arrow[4].y = d_y - h;
3284
3285         arrow[5].x = s_x - A_WIDTH;
3286         arrow[5].y = d_y - h;
3287
3288         arrow[6].x = s_x - A_WIDTH;
3289         arrow[6].y = s_y;
3290     }
3291     else if( d_y == s_y ) {
3292         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
3293
3294         arrow[0].x = s_x;
3295         arrow[0].y = s_y + A_WIDTH;
3296
3297         arrow[1].x = d_x - w;
3298         arrow[1].y = s_y + A_WIDTH;
3299
3300         arrow[2].x = d_x - w;
3301         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
3302
3303         arrow[3].x = d_x;
3304         arrow[3].y = d_y;
3305
3306         arrow[4].x = d_x - w;
3307         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
3308
3309         arrow[5].x = d_x - w;
3310         arrow[5].y = s_y - A_WIDTH;
3311
3312         arrow[6].x = s_x;
3313         arrow[6].y = s_y - A_WIDTH;
3314     }
3315     else {
3316         /* [AS] Needed a lot of paper for this! :-) */
3317         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
3318         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
3319
3320         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
3321
3322         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
3323
3324         x = s_x;
3325         y = s_y;
3326
3327         arrow[0].x = Round(x - j);
3328         arrow[0].y = Round(y + j*dx);
3329
3330         arrow[1].x = Round(x + j);
3331         arrow[1].y = Round(y - j*dx);
3332
3333         if( d_x > s_x ) {
3334             x = (double) d_x - k;
3335             y = (double) d_y - k*dy;
3336         }
3337         else {
3338             x = (double) d_x + k;
3339             y = (double) d_y + k*dy;
3340         }
3341
3342         arrow[2].x = Round(x + j);
3343         arrow[2].y = Round(y - j*dx);
3344
3345         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
3346         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
3347
3348         arrow[4].x = d_x;
3349         arrow[4].y = d_y;
3350
3351         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
3352         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
3353
3354         arrow[6].x = Round(x - j);
3355         arrow[6].y = Round(y + j*dx);
3356     }
3357
3358     Polygon( hdc, arrow, 7 );
3359 }
3360
3361 /* [AS] Draw an arrow between two squares */
3362 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
3363 {
3364     int s_x, s_y, d_x, d_y;
3365     HPEN hpen;
3366     HPEN holdpen;
3367     HBRUSH hbrush;
3368     HBRUSH holdbrush;
3369     LOGBRUSH stLB;
3370
3371     if( s_col == d_col && s_row == d_row ) {
3372         return;
3373     }
3374
3375     /* Get source and destination points */
3376     SquareToPos( s_row, s_col, &s_x, &s_y);
3377     SquareToPos( d_row, d_col, &d_x, &d_y);
3378
3379     if( d_y > s_y ) {
3380         d_y += squareSize / 4;
3381     }
3382     else if( d_y < s_y ) {
3383         d_y += 3 * squareSize / 4;
3384     }
3385     else {
3386         d_y += squareSize / 2;
3387     }
3388
3389     if( d_x > s_x ) {
3390         d_x += squareSize / 4;
3391     }
3392     else if( d_x < s_x ) {
3393         d_x += 3 * squareSize / 4;
3394     }
3395     else {
3396         d_x += squareSize / 2;
3397     }
3398
3399     s_x += squareSize / 2;
3400     s_y += squareSize / 2;
3401
3402     /* Adjust width */
3403     A_WIDTH = squareSize / 14;
3404
3405     /* Draw */
3406     stLB.lbStyle = BS_SOLID;
3407     stLB.lbColor = appData.highlightArrowColor;
3408     stLB.lbHatch = 0;
3409
3410     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
3411     holdpen = SelectObject( hdc, hpen );
3412     hbrush = CreateBrushIndirect( &stLB );
3413     holdbrush = SelectObject( hdc, hbrush );
3414
3415     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
3416
3417     SelectObject( hdc, holdpen );
3418     SelectObject( hdc, holdbrush );
3419     DeleteObject( hpen );
3420     DeleteObject( hbrush );
3421 }
3422
3423 BOOL HasHighlightInfo()
3424 {
3425     BOOL result = FALSE;
3426
3427     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
3428         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
3429     {
3430         result = TRUE;
3431     }
3432
3433     return result;
3434 }
3435
3436 BOOL IsDrawArrowEnabled()
3437 {
3438     BOOL result = FALSE;
3439
3440     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
3441         result = TRUE;
3442     }
3443
3444     return result;
3445 }
3446
3447 VOID DrawArrowHighlight( HDC hdc )
3448 {
3449     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
3450         DrawArrowBetweenSquares( hdc,
3451             highlightInfo.sq[0].x, highlightInfo.sq[0].y,
3452             highlightInfo.sq[1].x, highlightInfo.sq[1].y );
3453     }
3454 }
3455
3456 HRGN GetArrowHighlightClipRegion( HDC hdc )
3457 {
3458     HRGN result = NULL;
3459
3460     if( HasHighlightInfo() ) {
3461         int x1, y1, x2, y2;
3462         int sx, sy, dx, dy;
3463
3464         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
3465         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
3466
3467         sx = MIN( x1, x2 );
3468         sy = MIN( y1, y2 );
3469         dx = MAX( x1, x2 ) + squareSize;
3470         dy = MAX( y1, y2 ) + squareSize;
3471
3472         result = CreateRectRgn( sx, sy, dx, dy );
3473     }
3474
3475     return result;
3476 }
3477
3478 /*
3479     Warning: this function modifies the behavior of several other functions.
3480
3481     Basically, Winboard is optimized to avoid drawing the whole board if not strictly
3482     needed. Unfortunately, the decision whether or not to perform a full or partial
3483     repaint is scattered all over the place, which is not good for features such as
3484     "arrow highlighting" that require a full repaint of the board.
3485
3486     So, I've tried to patch the code where I thought it made sense (e.g. after or during
3487     user interaction, when speed is not so important) but especially to avoid errors
3488     in the displayed graphics.
3489
3490     In such patched places, I always try refer to this function so there is a single
3491     place to maintain knowledge.
3492
3493     To restore the original behavior, just return FALSE unconditionally.
3494 */
3495 BOOL IsFullRepaintPreferrable()
3496 {
3497     BOOL result = FALSE;
3498
3499     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
3500         /* Arrow may appear on the board */
3501         result = TRUE;
3502     }
3503
3504     return result;
3505 }
3506
3507 /*
3508     This function is called by DrawPosition to know whether a full repaint must
3509     be forced or not.
3510
3511     Only DrawPosition may directly call this function, which makes use of
3512     some state information. Other function should call DrawPosition specifying
3513     the repaint flag, and can use IsFullRepaintPreferrable if needed.
3514 */
3515 BOOL DrawPositionNeedsFullRepaint()
3516 {
3517     BOOL result = FALSE;
3518
3519     /*
3520         Probably a slightly better policy would be to trigger a full repaint
3521         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
3522         but animation is fast enough that it's difficult to notice.
3523     */
3524     if( animInfo.piece == EmptySquare ) {
3525         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {
3526             result = TRUE;
3527         }
3528     }
3529
3530     return result;
3531 }
3532
3533 VOID\r
3534 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3535 {\r
3536   int row, column, x, y, square_color, piece_color;\r
3537   ChessSquare piece;\r
3538   HBRUSH oldBrush;\r
3539   HDC texture_hdc = NULL;
3540
3541   /* [AS] Initialize background textures if needed */
3542   if( liteBackTexture != NULL || darkBackTexture != NULL ) {
3543       if( backTextureSquareSize != squareSize ) {
3544           backTextureSquareSize = squareSize;
3545           RebuildTextureSquareInfo();
3546       }
3547
3548       texture_hdc = CreateCompatibleDC( hdc );
3549   }
3550 \r
3551   for (row = 0; row < BOARD_SIZE; row++) {\r
3552     for (column = 0; column < BOARD_SIZE; column++) {\r
3553   \r
3554       SquareToPos(row, column, &x, &y);\r
3555 \r
3556       piece = board[row][column];\r
3557 \r
3558       square_color = ((column + row) % 2) == 1;\r
3559       piece_color = (int) piece < (int) BlackPawn;\r
3560 \r
3561       if (appData.monoMode) {\r
3562         if (piece == EmptySquare) {\r
3563           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3564                  square_color ? WHITENESS : BLACKNESS);\r
3565         } else {\r
3566           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3567         }\r
3568       }
3569       else if( backTextureSquareInfo[row][column].mode > 0 ) {
3570           /* [AS] Draw the square using a texture bitmap */
3571           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
3572
3573           DrawTile( x, y,
3574               squareSize, squareSize,
3575               hdc,
3576               texture_hdc,
3577               backTextureSquareInfo[row][column].mode,
3578               backTextureSquareInfo[row][column].x,
3579               backTextureSquareInfo[row][column].y );
3580
3581           SelectObject( texture_hdc, hbm );
3582
3583           if (piece != EmptySquare) {
3584               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
3585           }
3586       }
3587       else {
3588         oldBrush = SelectObject(hdc, square_color ?\r
3589                                 lightSquareBrush : darkSquareBrush);\r
3590         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3591         SelectObject(hdc, oldBrush);\r
3592         if (piece != EmptySquare)\r
3593           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3594       }\r
3595     }\r
3596   }\r
3597
3598   if( texture_hdc != NULL ) {
3599     DeleteDC( texture_hdc );
3600   }
3601 }\r
3602 \r
3603 #define MAX_CLIPS 200   /* more than enough */\r
3604 \r
3605 VOID\r
3606 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3607 {\r
3608   static Board lastReq, lastDrawn;\r
3609   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3610   static int lastDrawnFlipView = 0;\r
3611   static int lastReqValid = 0, lastDrawnValid = 0;\r
3612   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3613   HDC tmphdc;\r
3614   HDC hdcmem;\r
3615   HBITMAP bufferBitmap;\r
3616   HBITMAP oldBitmap;\r
3617   RECT Rect;\r
3618   HRGN clips[MAX_CLIPS];\r
3619   ChessSquare dragged_piece = EmptySquare;\r
3620 \r
3621   /* I'm undecided on this - this function figures out whether a full\r
3622    * repaint is necessary on its own, so there's no real reason to have the\r
3623    * caller tell it that.  I think this can safely be set to FALSE - but\r
3624    * if we trust the callers not to request full repaints unnessesarily, then\r
3625    * we could skip some clipping work.  In other words, only request a full\r
3626    * redraw when the majority of pieces have changed positions (ie. flip, \r
3627    * gamestart and similar)  --Hawk\r
3628    */\r
3629   Boolean fullrepaint = repaint;\r
3630 \r
3631   if( DrawPositionNeedsFullRepaint() ) {
3632       fullrepaint = TRUE;
3633   }
3634
3635 #if 0
3636   if( fullrepaint ) {
3637       static int repaint_count = 0;
3638       char buf[128];
3639
3640       repaint_count++;
3641       sprintf( buf, "FULL repaint: %d\n", repaint_count );
3642       OutputDebugString( buf );
3643   }
3644 #endif
3645
3646   if (board == NULL) {\r
3647     if (!lastReqValid) {\r
3648       return;\r
3649     }\r
3650     board = lastReq;\r
3651   } else {\r
3652     CopyBoard(lastReq, board);\r
3653     lastReqValid = 1;\r
3654   }\r
3655 \r
3656   if (doingSizing) {\r
3657     return;\r
3658   }\r
3659 \r
3660   if (IsIconic(hwndMain)) {\r
3661     return;\r
3662   }\r
3663 \r
3664   if (hdc == NULL) {\r
3665     hdc = GetDC(hwndMain);\r
3666     if (!appData.monoMode) {\r
3667       SelectPalette(hdc, hPal, FALSE);\r
3668       RealizePalette(hdc);\r
3669     }\r
3670     releaseDC = TRUE;\r
3671   } else {\r
3672     releaseDC = FALSE;\r
3673   }\r
3674 \r
3675 #if 0\r
3676   fprintf(debugFP, "*******************************\n"\r
3677                    "repaint = %s\n"\r
3678                    "dragInfo.from (%d,%d)\n"\r
3679                    "dragInfo.start (%d,%d)\n"\r
3680                    "dragInfo.pos (%d,%d)\n"\r
3681                    "dragInfo.lastpos (%d,%d)\n", \r
3682                     repaint ? "TRUE" : "FALSE",\r
3683                     dragInfo.from.x, dragInfo.from.y, \r
3684                     dragInfo.start.x, dragInfo.start.y,\r
3685                     dragInfo.pos.x, dragInfo.pos.y,\r
3686                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
3687   fprintf(debugFP, "prev:  ");\r
3688   for (row = 0; row < 8; row++) {\r
3689     for (column = 0; column < 8; column++) {\r
3690       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
3691     }\r
3692   }\r
3693   fprintf(debugFP, "\n");\r
3694   fprintf(debugFP, "board: ");\r
3695   for (row = 0; row < 8; row++) {\r
3696     for (column = 0; column < 8; column++) {\r
3697       fprintf(debugFP, "%d ", board[row][column]);\r
3698     }\r
3699   }\r
3700   fprintf(debugFP, "\n");\r
3701   fflush(debugFP);\r
3702 #endif\r
3703 \r
3704   /* Create some work-DCs */\r
3705   hdcmem = CreateCompatibleDC(hdc);\r
3706   tmphdc = CreateCompatibleDC(hdc);\r
3707 \r
3708   /* Figure out which squares need updating by comparing the \r
3709    * newest board with the last drawn board and checking if\r
3710    * flipping has changed.\r
3711    */\r
3712   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3713     for (row = 0; row < 8; row++) {\r
3714       for (column = 0; column < 8; column++) {\r
3715         if (lastDrawn[row][column] != board[row][column]) {\r
3716           SquareToPos(row, column, &x, &y);\r
3717           clips[num_clips++] =\r
3718             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3719         }\r
3720       }\r
3721     }\r
3722     for (i=0; i<2; i++) {\r
3723       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3724           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3725         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3726             lastDrawnHighlight.sq[i].y >= 0) {\r
3727           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3728                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3729           clips[num_clips++] =\r
3730             CreateRectRgn(x - lineGap, y - lineGap, \r
3731                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3732         }\r
3733         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3734           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3735           clips[num_clips++] =\r
3736             CreateRectRgn(x - lineGap, y - lineGap, \r
3737                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3738         }\r
3739       }\r
3740     }\r
3741     for (i=0; i<2; i++) {\r
3742       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3743           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3744         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3745             lastDrawnPremove.sq[i].y >= 0) {\r
3746           SquareToPos(lastDrawnPremove.sq[i].y,\r
3747                       lastDrawnPremove.sq[i].x, &x, &y);\r
3748           clips[num_clips++] =\r
3749             CreateRectRgn(x - lineGap, y - lineGap, \r
3750                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3751         }\r
3752         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3753             premoveHighlightInfo.sq[i].y >= 0) {\r
3754           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3755                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3756           clips[num_clips++] =\r
3757             CreateRectRgn(x - lineGap, y - lineGap, \r
3758                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3759         }\r
3760       }\r
3761     }\r
3762   } else {\r
3763     fullrepaint = TRUE;\r
3764   }\r
3765 \r
3766   /* Create a buffer bitmap - this is the actual bitmap\r
3767    * being written to.  When all the work is done, we can\r
3768    * copy it to the real DC (the screen).  This avoids\r
3769    * the problems with flickering.\r
3770    */\r
3771   GetClientRect(hwndMain, &Rect);\r
3772   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3773                                         Rect.bottom-Rect.top+1);\r
3774   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3775   if (!appData.monoMode) {\r
3776     SelectPalette(hdcmem, hPal, FALSE);\r
3777   }\r
3778 \r
3779   /* Create clips for dragging */\r
3780   if (!fullrepaint) {\r
3781     if (dragInfo.from.x >= 0) {\r
3782       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3783       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3784     }\r
3785     if (dragInfo.start.x >= 0) {\r
3786       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3787       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3788     }\r
3789     if (dragInfo.pos.x >= 0) {\r
3790       x = dragInfo.pos.x - squareSize / 2;\r
3791       y = dragInfo.pos.y - squareSize / 2;\r
3792       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3793     }\r
3794     if (dragInfo.lastpos.x >= 0) {\r
3795       x = dragInfo.lastpos.x - squareSize / 2;\r
3796       y = dragInfo.lastpos.y - squareSize / 2;\r
3797       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3798     }\r
3799   }\r
3800 \r
3801   /* If dragging is in progress, we temporarely remove the piece */\r
3802   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3803     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3804     board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3805   }\r
3806 \r
3807   /* Are we animating a move?  \r
3808    * If so, \r
3809    *   - remove the piece from the board (temporarely)\r
3810    *   - calculate the clipping region\r
3811    */\r
3812   if (!fullrepaint) {\r
3813     if (animInfo.piece != EmptySquare) {\r
3814       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
3815       x = boardRect.left + animInfo.lastpos.x;\r
3816       y = boardRect.top + animInfo.lastpos.y;\r
3817       x2 = boardRect.left + animInfo.pos.x;\r
3818       y2 = boardRect.top + animInfo.pos.y;\r
3819       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
3820       /* Slight kludge.  The real problem is that after AnimateMove is\r
3821          done, the position on the screen does not match lastDrawn.\r
3822          This currently causes trouble only on e.p. captures in\r
3823          atomic, where the piece moves to an empty square and then\r
3824          explodes.  The old and new positions both had an empty square\r
3825          at the destination, but animation has drawn a piece there and\r
3826          we have to remember to erase it. */\r
3827       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
3828     }\r
3829   }\r
3830 \r
3831   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
3832   if (num_clips == 0)\r
3833     fullrepaint = TRUE;\r
3834 \r
3835   /* Set clipping on the memory DC */\r
3836   if (!fullrepaint) {\r
3837     SelectClipRgn(hdcmem, clips[0]);\r
3838     for (x = 1; x < num_clips; x++) {\r
3839       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
3840         abort();  // this should never ever happen!\r
3841     }\r
3842   }\r
3843 \r
3844   /* Do all the drawing to the memory DC */\r
3845   DrawGridOnDC(hdcmem);\r
3846   DrawHighlightsOnDC(hdcmem);\r
3847   DrawBoardOnDC(hdcmem, board, tmphdc);\r
3848
3849   if( appData.highlightMoveWithArrow ) {
3850     DrawArrowHighlight(hdcmem);
3851   }
3852
3853   DrawCoordsOnDC(hdcmem);\r
3854 \r
3855   /* Put the dragged piece back into place and draw it */\r
3856   if (dragged_piece != EmptySquare) {\r
3857     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
3858     x = dragInfo.pos.x - squareSize / 2;\r
3859     y = dragInfo.pos.y - squareSize / 2;\r
3860     DrawPieceOnDC(hdcmem, dragged_piece,\r
3861                   ((int) dragged_piece < (int) BlackPawn), \r
3862                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
3863   }   \r
3864   \r
3865   /* Put the animated piece back into place and draw it */\r
3866   if (animInfo.piece != EmptySquare) {\r
3867     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
3868     x = boardRect.left + animInfo.pos.x;\r
3869     y = boardRect.top + animInfo.pos.y;\r
3870     DrawPieceOnDC(hdcmem, animInfo.piece,\r
3871                   ((int) animInfo.piece < (int) BlackPawn),\r
3872                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
3873   }\r
3874 \r
3875   /* Release the bufferBitmap by selecting in the old bitmap \r
3876    * and delete the memory DC\r
3877    */\r
3878   SelectObject(hdcmem, oldBitmap);\r
3879   DeleteDC(hdcmem);\r
3880 \r
3881   /* Set clipping on the target DC */\r
3882   if (!fullrepaint) {\r
3883     SelectClipRgn(hdc, clips[0]);\r
3884     for (x = 1; x < num_clips; x++) {\r
3885       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
3886         abort();   // this should never ever happen!\r
3887     } \r
3888   }\r
3889 \r
3890   /* Copy the new bitmap onto the screen in one go.\r
3891    * This way we avoid any flickering\r
3892    */\r
3893   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
3894   BitBlt(hdc, boardRect.left, boardRect.top,\r
3895          boardRect.right - boardRect.left,\r
3896          boardRect.bottom - boardRect.top,\r
3897          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
3898   SelectObject(tmphdc, oldBitmap);\r
3899 \r
3900   /* Massive cleanup */\r
3901   for (x = 0; x < num_clips; x++)\r
3902     DeleteObject(clips[x]);\r
3903 \r
3904   DeleteDC(tmphdc);\r
3905   DeleteObject(bufferBitmap);\r
3906 \r
3907   if (releaseDC) \r
3908     ReleaseDC(hwndMain, hdc);\r
3909   \r
3910   if (lastDrawnFlipView != flipView) {\r
3911     if (flipView)\r
3912       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
3913     else\r
3914       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
3915   }\r
3916 \r
3917   CopyBoard(lastDrawn, board);\r
3918   lastDrawnHighlight = highlightInfo;\r
3919   lastDrawnPremove   = premoveHighlightInfo;\r
3920   lastDrawnFlipView = flipView;\r
3921   lastDrawnValid = 1;\r
3922 }\r
3923 \r
3924 \r
3925 /*---------------------------------------------------------------------------*\\r
3926 | CLIENT PAINT PROCEDURE\r
3927 |   This is the main event-handler for the WM_PAINT message.\r
3928 |\r
3929 \*---------------------------------------------------------------------------*/\r
3930 VOID\r
3931 PaintProc(HWND hwnd)\r
3932 {\r
3933   HDC         hdc;\r
3934   PAINTSTRUCT ps;\r
3935   HFONT       oldFont;\r
3936 \r
3937   if(hdc = BeginPaint(hwnd, &ps)) {\r
3938     if (IsIconic(hwnd)) {\r
3939       DrawIcon(hdc, 2, 2, iconCurrent);\r
3940     } else {\r
3941       if (!appData.monoMode) {\r
3942         SelectPalette(hdc, hPal, FALSE);\r
3943         RealizePalette(hdc);\r
3944       }\r
3945       HDCDrawPosition(hdc, 1, NULL);\r
3946       oldFont =\r
3947         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3948       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
3949                  ETO_CLIPPED|ETO_OPAQUE,\r
3950                  &messageRect, messageText, strlen(messageText), NULL);\r
3951       SelectObject(hdc, oldFont);\r
3952       DisplayBothClocks();\r
3953     }\r
3954     EndPaint(hwnd,&ps);\r
3955   }\r
3956 \r
3957   return;\r
3958 }\r
3959 \r
3960 \r
3961 /*\r
3962  * If the user selects on a border boundary, return -1; if off the board,\r
3963  *   return -2.  Otherwise map the event coordinate to the square.\r
3964  * The offset boardRect.left or boardRect.top must already have been\r
3965  *   subtracted from x.\r
3966  */\r
3967 int\r
3968 EventToSquare(int x)\r
3969 {\r
3970   if (x <= 0)\r
3971     return -2;\r
3972   if (x < lineGap)\r
3973     return -1;\r
3974   x -= lineGap;\r
3975   if ((x % (squareSize + lineGap)) >= squareSize)\r
3976     return -1;\r
3977   x /= (squareSize + lineGap);\r
3978   if (x >= BOARD_SIZE)\r
3979     return -2;\r
3980   return x;\r
3981 }\r
3982 \r
3983 typedef struct {\r
3984   char piece;\r
3985   int command;\r
3986   char* name;\r
3987 } DropEnable;\r
3988 \r
3989 DropEnable dropEnables[] = {\r
3990   { 'P', DP_Pawn, "Pawn" },\r
3991   { 'N', DP_Knight, "Knight" },\r
3992   { 'B', DP_Bishop, "Bishop" },\r
3993   { 'R', DP_Rook, "Rook" },\r
3994   { 'Q', DP_Queen, "Queen" },\r
3995 };\r
3996 \r
3997 VOID\r
3998 SetupDropMenu(HMENU hmenu)\r
3999 {\r
4000   int i, count, enable;\r
4001   char *p;\r
4002   extern char white_holding[], black_holding[];\r
4003   char item[MSG_SIZ];\r
4004 \r
4005   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4006     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4007                dropEnables[i].piece);\r
4008     count = 0;\r
4009     while (p && *p++ == dropEnables[i].piece) count++;\r
4010     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4011     enable = count > 0 || !appData.testLegality\r
4012       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4013                       && !appData.icsActive);\r
4014     ModifyMenu(hmenu, dropEnables[i].command,\r
4015                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4016                dropEnables[i].command, item);\r
4017   }\r
4018 }\r
4019 \r
4020 static int fromX = -1, fromY = -1, toX, toY;\r
4021 \r
4022 /* Event handler for mouse messages */\r
4023 VOID\r
4024 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4025 {\r
4026   int x, y;\r
4027   POINT pt;\r
4028   static int recursive = 0;\r
4029   HMENU hmenu;\r
4030   BOOLEAN needsRedraw = FALSE;
4031   BOOLEAN saveAnimate;\r
4032   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
4033   static BOOLEAN sameAgain = FALSE;\r
4034 \r
4035   if (recursive) {\r
4036     if (message == WM_MBUTTONUP) {\r
4037       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4038          to the middle button: we simulate pressing the left button too!\r
4039          */\r
4040       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4041       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4042     }\r
4043     return;\r
4044   }\r
4045   recursive++;\r
4046   \r
4047   pt.x = LOWORD(lParam);\r
4048   pt.y = HIWORD(lParam);\r
4049   x = EventToSquare(pt.x - boardRect.left);\r
4050   y = EventToSquare(pt.y - boardRect.top);\r
4051   if (!flipView && y >= 0) {\r
4052     y = BOARD_SIZE - 1 - y;\r
4053   }\r
4054   if (flipView && x >= 0) {\r
4055     x = BOARD_SIZE - 1 - x;\r
4056   }\r
4057 \r
4058   switch (message) {\r
4059   case WM_LBUTTONDOWN:\r
4060     ErrorPopDown();\r
4061     sameAgain = FALSE;\r
4062     if (y == -2) {\r
4063       /* Downclick vertically off board; check if on clock */\r
4064       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4065         if (gameMode == EditPosition) {\r
4066           SetWhiteToPlayEvent();\r
4067         } else if (gameMode == IcsPlayingBlack ||\r
4068                    gameMode == MachinePlaysWhite) {\r
4069           CallFlagEvent();\r
4070         }\r
4071       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4072         if (gameMode == EditPosition) {\r
4073           SetBlackToPlayEvent();\r
4074         } else if (gameMode == IcsPlayingWhite ||\r
4075                    gameMode == MachinePlaysBlack) {\r
4076           CallFlagEvent();\r
4077         }\r
4078       }\r
4079       if (!appData.highlightLastMove) {\r
4080         ClearHighlights();\r
4081         DrawPosition(forceFullRepaint || FALSE, NULL);
4082       }\r
4083       fromX = fromY = -1;\r
4084       dragInfo.start.x = dragInfo.start.y = -1;\r
4085       dragInfo.from = dragInfo.start;\r
4086       break;\r
4087     } else if (x < 0 || y < 0) {\r
4088       break;\r
4089     } else if (fromX == x && fromY == y) {\r
4090       /* Downclick on same square again */\r
4091       ClearHighlights();\r
4092       DrawPosition(forceFullRepaint || FALSE, NULL);
4093       sameAgain = TRUE;  \r
4094     } else if (fromX != -1) {\r
4095       /* Downclick on different square */\r
4096       ChessSquare pdown, pup;\r
4097       pdown = boards[currentMove][fromY][fromX];\r
4098       pup = boards[currentMove][y][x];\r
4099       if (gameMode == EditPosition ||\r
4100           !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
4101              WhitePawn <= pup && pup <= WhiteKing) ||\r
4102             (BlackPawn <= pdown && pdown <= BlackKing &&\r
4103              BlackPawn <= pup && pup <= BlackKing))) {\r
4104         /* EditPosition, empty square, or different color piece;\r
4105            click-click move is possible */\r
4106         toX = x;\r
4107         toY = y;\r
4108         if (IsPromotion(fromX, fromY, toX, toY)) {\r
4109           if (appData.alwaysPromoteToQueen) {\r
4110             UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
4111             if (!appData.highlightLastMove) {\r
4112               ClearHighlights();\r
4113               DrawPosition(forceFullRepaint || FALSE, NULL);
4114             }\r
4115           } else {\r
4116             SetHighlights(fromX, fromY, toX, toY);\r
4117             DrawPosition(forceFullRepaint || FALSE, NULL);
4118             PromotionPopup(hwnd);\r
4119           }\r
4120         } else {        /* not a promotion */\r
4121           if (appData.animate || appData.highlightLastMove) {\r
4122             SetHighlights(fromX, fromY, toX, toY);\r
4123           } else {\r
4124             ClearHighlights();\r
4125           }\r
4126           UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
4127           if (appData.animate && !appData.highlightLastMove) {\r
4128             ClearHighlights();\r
4129             DrawPosition(forceFullRepaint || FALSE, NULL);
4130           }\r
4131         }\r
4132         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4133         fromX = fromY = -1;\r
4134         break;\r
4135       }\r
4136       ClearHighlights();\r
4137       DrawPosition(forceFullRepaint || FALSE, NULL);
4138     }\r
4139     /* First downclick, or restart on a square with same color piece */\r
4140     if (!frozen && OKToStartUserMove(x, y)) {\r
4141       fromX = x;\r
4142       fromY = y;\r
4143       dragInfo.lastpos = pt;\r
4144       dragInfo.from.x = fromX;\r
4145       dragInfo.from.y = fromY;\r
4146       dragInfo.start = dragInfo.from;\r
4147       SetCapture(hwndMain);\r
4148     } else {\r
4149       fromX = fromY = -1;\r
4150       dragInfo.start.x = dragInfo.start.y = -1;\r
4151       dragInfo.from = dragInfo.start;\r
4152       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
4153     }\r
4154     break;\r
4155 \r
4156   case WM_LBUTTONUP:\r
4157     ReleaseCapture();\r
4158     if (fromX == -1) break;\r
4159     if (x == fromX && y == fromY) {\r
4160       dragInfo.from.x = dragInfo.from.y = -1;\r
4161       /* Upclick on same square */\r
4162       if (sameAgain) {\r
4163         /* Clicked same square twice: abort click-click move */\r
4164         fromX = fromY = -1;\r
4165         gotPremove = 0;\r
4166         ClearPremoveHighlights();\r
4167       } else {\r
4168         /* First square clicked: start click-click move */\r
4169         SetHighlights(fromX, fromY, -1, -1);\r
4170       }\r
4171       DrawPosition(forceFullRepaint || FALSE, NULL);
4172     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4173       /* Errant click; ignore */\r
4174       break;\r
4175     } else {\r
4176       /* Finish drag move */\r
4177       dragInfo.from.x = dragInfo.from.y = -1;\r
4178       toX = x;\r
4179       toY = y;\r
4180       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4181       appData.animate = appData.animate && !appData.animateDragging;\r
4182       if (IsPromotion(fromX, fromY, toX, toY)) {\r
4183         if (appData.alwaysPromoteToQueen) {\r
4184           UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
4185         } else {\r
4186           DrawPosition(forceFullRepaint || FALSE, NULL);
4187           PromotionPopup(hwnd);\r
4188         }\r
4189       } else {\r
4190         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
4191       }\r
4192       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4193       appData.animate = saveAnimate;\r
4194       fromX = fromY = -1;\r
4195       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4196         ClearHighlights();\r
4197       }\r
4198       if (appData.animate || appData.animateDragging ||\r
4199           appData.highlightDragging || gotPremove) {\r
4200         DrawPosition(forceFullRepaint || FALSE, NULL);
4201       }\r
4202     }\r
4203     dragInfo.start.x = dragInfo.start.y = -1; \r
4204     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4205     break;\r
4206 \r
4207   case WM_MOUSEMOVE:\r
4208     if ((appData.animateDragging || appData.highlightDragging)\r
4209         && (wParam & MK_LBUTTON)\r
4210         && dragInfo.from.x >= 0)
4211     {
4212       BOOL full_repaint = FALSE;
4213
4214       if (appData.animateDragging) {\r
4215         dragInfo.pos = pt;\r
4216       }\r
4217       if (appData.highlightDragging) {\r
4218         SetHighlights(fromX, fromY, x, y);\r
4219         if( IsDrawArrowEnabled() && (x < 0 || x > 7 || y < 0 || y > y) ) {
4220             full_repaint = TRUE;
4221       }\r
4222       }
4223
4224       DrawPosition( full_repaint, NULL);
4225
4226       dragInfo.lastpos = dragInfo.pos;\r
4227     }\r
4228     break;\r
4229 \r
4230   case WM_MBUTTONDOWN:\r
4231   case WM_RBUTTONDOWN:\r
4232     ErrorPopDown();\r
4233     ReleaseCapture();\r
4234     fromX = fromY = -1;\r
4235     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4236     dragInfo.start.x = dragInfo.start.y = -1;\r
4237     dragInfo.from = dragInfo.start;\r
4238     dragInfo.lastpos = dragInfo.pos;\r
4239     if (appData.highlightDragging) {\r
4240       ClearHighlights();\r
4241     }\r
4242     DrawPosition(TRUE, NULL);\r
4243 \r
4244     switch (gameMode) {\r
4245     case EditPosition:\r
4246     case IcsExamining:\r
4247       if (x < 0 || y < 0) break;\r
4248       fromX = x;\r
4249       fromY = y;\r
4250       if (message == WM_MBUTTONDOWN) {\r
4251         buttonCount = 3;  /* even if system didn't think so */\r
4252         if (wParam & MK_SHIFT) \r
4253           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4254         else\r
4255           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4256       } else { /* message == WM_RBUTTONDOWN */\r
4257 #if 0\r
4258         if (buttonCount == 3) {\r
4259           if (wParam & MK_SHIFT) \r
4260             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4261           else\r
4262             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4263         } else {\r
4264           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4265         }\r
4266 #else\r
4267         /* Just have one menu, on the right button.  Windows users don't\r
4268            think to try the middle one, and sometimes other software steals\r
4269            it, or it doesn't really exist. */\r
4270         MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4271 #endif\r
4272       }\r
4273       break;\r
4274     case IcsPlayingWhite:\r
4275     case IcsPlayingBlack:\r
4276     case EditGame:\r
4277     case MachinePlaysWhite:\r
4278     case MachinePlaysBlack:\r
4279       if (appData.testLegality &&\r
4280           gameInfo.variant != VariantBughouse &&\r
4281           gameInfo.variant != VariantCrazyhouse) break;\r
4282       if (x < 0 || y < 0) break;\r
4283       fromX = x;\r
4284       fromY = y;\r
4285       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4286       SetupDropMenu(hmenu);\r
4287       MenuPopup(hwnd, pt, hmenu, -1);\r
4288       break;\r
4289     default:\r
4290       break;\r
4291     }\r
4292     break;\r
4293   }\r
4294 \r
4295   recursive--;\r
4296 }\r
4297 \r
4298 /* Preprocess messages for buttons in main window */\r
4299 LRESULT CALLBACK\r
4300 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4301 {\r
4302   int id = GetWindowLong(hwnd, GWL_ID);\r
4303   int i, dir;\r
4304 \r
4305   for (i=0; i<N_BUTTONS; i++) {\r
4306     if (buttonDesc[i].id == id) break;\r
4307   }\r
4308   if (i == N_BUTTONS) return 0;\r
4309   switch (message) {\r
4310   case WM_KEYDOWN:\r
4311     switch (wParam) {\r
4312     case VK_LEFT:\r
4313     case VK_RIGHT:\r
4314       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4315       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4316       return TRUE;\r
4317     }\r
4318     break;\r
4319   case WM_CHAR:\r
4320     switch (wParam) {\r
4321     case '\r':\r
4322       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4323       return TRUE;\r
4324     case '\t':\r
4325       if (appData.icsActive) {\r
4326         if (GetKeyState(VK_SHIFT) < 0) {\r
4327           /* shifted */\r
4328           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4329           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4330           SetFocus(h);\r
4331         } else {\r
4332           /* unshifted */\r
4333           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4334           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4335           SetFocus(h);\r
4336         }\r
4337         return TRUE;\r
4338       }\r
4339       break;\r
4340     default:\r
4341       if (appData.icsActive) {\r
4342         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4343         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4344         SetFocus(h);\r
4345         SendMessage(h, WM_CHAR, wParam, lParam);\r
4346         return TRUE;\r
4347       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4348         PopUpMoveDialog((char)wParam);\r
4349       }\r
4350       break;\r
4351     }\r
4352     break;\r
4353   }\r
4354   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4355 }\r
4356 \r
4357 /* Process messages for Promotion dialog box */\r
4358 LRESULT CALLBACK\r
4359 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4360 {\r
4361   char promoChar;\r
4362 \r
4363   switch (message) {\r
4364   case WM_INITDIALOG: /* message: initialize dialog box */\r
4365     /* Center the dialog over the application window */\r
4366     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4367     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4368       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4369        gameInfo.variant == VariantGiveaway) ?\r
4370                SW_SHOW : SW_HIDE);\r
4371     return TRUE;\r
4372 \r
4373   case WM_COMMAND: /* message: received a command */\r
4374     switch (LOWORD(wParam)) {\r
4375     case IDCANCEL:\r
4376       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4377       ClearHighlights();\r
4378       DrawPosition(FALSE, NULL);\r
4379       return TRUE;\r
4380     case PB_King:\r
4381       promoChar = 'k';\r
4382       break;\r
4383     case PB_Queen:\r
4384       promoChar = 'q';\r
4385       break;\r
4386     case PB_Rook:\r
4387       promoChar = 'r';\r
4388       break;\r
4389     case PB_Bishop:\r
4390       promoChar = 'b';\r
4391       break;\r
4392     case PB_Knight:\r
4393       promoChar = 'n';\r
4394       break;\r
4395     default:\r
4396       return FALSE;\r
4397     }\r
4398     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4399     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4400     if (!appData.highlightLastMove) {\r
4401       ClearHighlights();\r
4402       DrawPosition(FALSE, NULL);\r
4403     }\r
4404     return TRUE;\r
4405   }\r
4406   return FALSE;\r
4407 }\r
4408 \r
4409 /* Pop up promotion dialog */\r
4410 VOID\r
4411 PromotionPopup(HWND hwnd)\r
4412 {\r
4413   FARPROC lpProc;\r
4414 \r
4415   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4416   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4417     hwnd, (DLGPROC)lpProc);\r
4418   FreeProcInstance(lpProc);\r
4419 }\r
4420 \r
4421 /* Toggle ShowThinking */\r
4422 VOID\r
4423 ToggleShowThinking()\r
4424 {\r
4425   ShowThinkingEvent(!appData.showThinking);\r
4426 }\r
4427 \r
4428 VOID\r
4429 LoadGameDialog(HWND hwnd, char* title)\r
4430 {\r
4431   UINT number = 0;\r
4432   FILE *f;\r
4433   char fileTitle[MSG_SIZ];\r
4434   f = OpenFileDialog(hwnd, FALSE, "",\r
4435                      appData.oldSaveStyle ? "gam" : "pgn",\r
4436                      GAME_FILT,\r
4437                      title, &number, fileTitle, NULL);\r
4438   if (f != NULL) {\r
4439     cmailMsgLoaded = FALSE;\r
4440     if (number == 0) {\r
4441       int error = GameListBuild(f);\r
4442       if (error) {\r
4443         DisplayError("Cannot build game list", error);\r
4444       } else if (!ListEmpty(&gameList) &&\r
4445                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4446         GameListPopUp(f, fileTitle);\r
4447         return;\r
4448       }\r
4449       GameListDestroy();\r
4450       number = 1;\r
4451     }\r
4452     LoadGame(f, number, fileTitle, FALSE);\r
4453   }\r
4454 }\r
4455 \r
4456 VOID\r
4457 ChangedConsoleFont()\r
4458 {\r
4459   CHARFORMAT cfmt;\r
4460   CHARRANGE tmpsel, sel;\r
4461   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4462   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4463   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4464   PARAFORMAT paraf;\r
4465 \r
4466   cfmt.cbSize = sizeof(CHARFORMAT);\r
4467   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4468   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4469   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4470    * size.  This was undocumented in the version of MSVC++ that I had\r
4471    * when I wrote the code, but is apparently documented now.\r
4472    */\r
4473   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4474   cfmt.bCharSet = f->lf.lfCharSet;\r
4475   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4476   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4477   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4478   /* Why are the following seemingly needed too? */\r
4479   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4480   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4481   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4482   tmpsel.cpMin = 0;\r
4483   tmpsel.cpMax = -1; /*999999?*/\r
4484   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4485   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4486   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4487    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4488    */\r
4489   paraf.cbSize = sizeof(paraf);\r
4490   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4491   paraf.dxStartIndent = 0;\r
4492   paraf.dxOffset = WRAP_INDENT;\r
4493   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4494   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4495 }\r
4496 \r
4497 /*---------------------------------------------------------------------------*\\r
4498  *\r
4499  * Window Proc for main window\r
4500  *\r
4501 \*---------------------------------------------------------------------------*/\r
4502 \r
4503 /* Process messages for main window, etc. */\r
4504 LRESULT CALLBACK\r
4505 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4506 {\r
4507   FARPROC lpProc;\r
4508   int wmId, wmEvent;\r
4509   char *defName;\r
4510   FILE *f;\r
4511   UINT number;\r
4512   char fileTitle[MSG_SIZ];\r
4513   static SnapData sd;
4514 \r
4515   switch (message) {\r
4516 \r
4517   case WM_PAINT: /* message: repaint portion of window */\r
4518     PaintProc(hwnd);\r
4519     break;\r
4520 \r
4521   case WM_ERASEBKGND:\r
4522     if (IsIconic(hwnd)) {\r
4523       /* Cheat; change the message */\r
4524       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4525     } else {\r
4526       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4527     }\r
4528     break;\r
4529 \r
4530   case WM_LBUTTONDOWN:\r
4531   case WM_MBUTTONDOWN:\r
4532   case WM_RBUTTONDOWN:\r
4533   case WM_LBUTTONUP:\r
4534   case WM_MBUTTONUP:\r
4535   case WM_RBUTTONUP:\r
4536   case WM_MOUSEMOVE:\r
4537     MouseEvent(hwnd, message, wParam, lParam);\r
4538     break;\r
4539 \r
4540   case WM_CHAR:\r
4541     \r
4542     if (appData.icsActive) {\r
4543       if (wParam == '\t') {\r
4544         if (GetKeyState(VK_SHIFT) < 0) {\r
4545           /* shifted */\r
4546           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4547           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4548           SetFocus(h);\r
4549         } else {\r
4550           /* unshifted */\r
4551           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4552           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4553           SetFocus(h);\r
4554         }\r
4555       } else {\r
4556         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4557         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4558         SetFocus(h);\r
4559         SendMessage(h, message, wParam, lParam);\r
4560       }\r
4561     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4562       PopUpMoveDialog((char)wParam);\r
4563     }\r
4564     break;\r
4565 \r
4566   case WM_PALETTECHANGED:\r
4567     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4568       int nnew;\r
4569       HDC hdc = GetDC(hwndMain);\r
4570       SelectPalette(hdc, hPal, TRUE);\r
4571       nnew = RealizePalette(hdc);\r
4572       if (nnew > 0) {\r
4573         paletteChanged = TRUE;\r
4574 #if 0\r
4575         UpdateColors(hdc);\r
4576 #else\r
4577         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
4578 #endif\r
4579       }\r
4580       ReleaseDC(hwnd, hdc);\r
4581     }\r
4582     break;\r
4583 \r
4584   case WM_QUERYNEWPALETTE:\r
4585     if (!appData.monoMode /*&& paletteChanged*/) {\r
4586       int nnew;\r
4587       HDC hdc = GetDC(hwndMain);\r
4588       paletteChanged = FALSE;\r
4589       SelectPalette(hdc, hPal, FALSE);\r
4590       nnew = RealizePalette(hdc);\r
4591       if (nnew > 0) {\r
4592         InvalidateRect(hwnd, &boardRect, FALSE);\r
4593       }\r
4594       ReleaseDC(hwnd, hdc);\r
4595       return TRUE;\r
4596     }\r
4597     return FALSE;\r
4598 \r
4599   case WM_COMMAND: /* message: command from application menu */\r
4600     wmId    = LOWORD(wParam);\r
4601     wmEvent = HIWORD(wParam);\r
4602 \r
4603     switch (wmId) {\r
4604     case IDM_NewGame:\r
4605       ResetGameEvent();\r
4606       AnalysisPopDown();\r
4607       break;\r
4608 \r
4609     case IDM_NewGameFRC:
4610       if( NewGameFRC() == 0 ) {
4611         ResetGameEvent();
4612         AnalysisPopDown();
4613       }
4614       break;
4615
4616     case IDM_LoadGame:\r
4617       LoadGameDialog(hwnd, "Load Game from File");\r
4618       break;\r
4619 \r
4620     case IDM_LoadNextGame:\r
4621       ReloadGame(1);\r
4622       break;\r
4623 \r
4624     case IDM_LoadPrevGame:\r
4625       ReloadGame(-1);\r
4626       break;\r
4627 \r
4628     case IDM_ReloadGame:\r
4629       ReloadGame(0);\r
4630       break;\r
4631 \r
4632     case IDM_LoadPosition:\r
4633       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4634         Reset(FALSE, TRUE);\r
4635       }\r
4636       number = 1;\r
4637       f = OpenFileDialog(hwnd, FALSE, "",\r
4638                          appData.oldSaveStyle ? "pos" : "fen",\r
4639                          POSITION_FILT,\r
4640                          "Load Position from File", &number, fileTitle, NULL);\r
4641       if (f != NULL) {\r
4642         LoadPosition(f, number, fileTitle);\r
4643       }\r
4644       break;\r
4645 \r
4646     case IDM_LoadNextPosition:\r
4647       ReloadPosition(1);\r
4648       break;\r
4649 \r
4650     case IDM_LoadPrevPosition:\r
4651       ReloadPosition(-1);\r
4652       break;\r
4653 \r
4654     case IDM_ReloadPosition:\r
4655       ReloadPosition(0);\r
4656       break;\r
4657 \r
4658     case IDM_SaveGame:\r
4659       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4660       f = OpenFileDialog(hwnd, TRUE, defName,\r
4661                          appData.oldSaveStyle ? "gam" : "pgn",\r
4662                          GAME_FILT,\r
4663                          "Save Game to File", NULL, fileTitle, NULL);\r
4664       if (f != NULL) {\r
4665         SaveGame(f, 0, "");\r
4666       }\r
4667       break;\r
4668 \r
4669     case IDM_SavePosition:\r
4670       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4671       f = OpenFileDialog(hwnd, TRUE, defName,\r
4672                          appData.oldSaveStyle ? "pos" : "fen",\r
4673                          POSITION_FILT,\r
4674                          "Save Position to File", NULL, fileTitle, NULL);\r
4675       if (f != NULL) {\r
4676         SavePosition(f, 0, "");\r
4677       }\r
4678       break;\r
4679 \r
4680     case IDM_CopyGame:\r
4681       CopyGameToClipboard();\r
4682       break;\r
4683 \r
4684     case IDM_PasteGame:\r
4685       PasteGameFromClipboard();\r
4686       break;\r
4687 \r
4688     case IDM_CopyGameListToClipboard:
4689       CopyGameListToClipboard();
4690       break;
4691
4692     /* [AS] Autodetect FEN or PGN data */
4693     case IDM_PasteAny:
4694       PasteGameOrFENFromClipboard();
4695       break;
4696
4697     /* [AS] Move history */
4698     case IDM_ShowMoveHistory:
4699         if( MoveHistoryIsUp() ) {
4700             MoveHistoryPopDown();
4701         }
4702         else {
4703             MoveHistoryPopUp();
4704         }
4705         break;
4706
4707     /* [AS] Eval graph */
4708     case IDM_ShowEvalGraph:
4709         if( EvalGraphIsUp() ) {
4710             EvalGraphPopDown();
4711         }
4712         else {
4713             EvalGraphPopUp();
4714         }
4715         break;
4716
4717     /* [AS] Engine output */
4718     case IDM_ShowEngineOutput:
4719         if( EngineOutputIsUp() ) {
4720             EngineOutputPopDown();
4721         }
4722         else {
4723             EngineOutputPopUp();
4724         }
4725         break;
4726
4727     /* [AS] User adjudication */
4728     case IDM_UserAdjudication_White:
4729         UserAdjudicationEvent( +1 );
4730         break;
4731
4732     case IDM_UserAdjudication_Black:
4733         UserAdjudicationEvent( -1 );
4734         break;
4735
4736     case IDM_UserAdjudication_Draw:
4737         UserAdjudicationEvent( 0 );
4738         break;
4739
4740     /* [AS] Game list options dialog */
4741     case IDM_GameListOptions:
4742       GameListOptions();
4743       break;
4744
4745     case IDM_CopyPosition:\r
4746       CopyFENToClipboard();\r
4747       break;\r
4748 \r
4749     case IDM_PastePosition:\r
4750       PasteFENFromClipboard();\r
4751       break;\r
4752 \r
4753     case IDM_MailMove:\r
4754       MailMoveEvent();\r
4755       break;\r
4756 \r
4757     case IDM_ReloadCMailMsg:\r
4758       Reset(TRUE, TRUE);\r
4759       ReloadCmailMsgEvent(FALSE);\r
4760       break;\r
4761 \r
4762     case IDM_Minimize:\r
4763       ShowWindow(hwnd, SW_MINIMIZE);\r
4764       break;\r
4765 \r
4766     case IDM_Exit:\r
4767       ExitEvent(0);\r
4768       break;\r
4769 \r
4770     case IDM_MachineWhite:\r
4771       MachineWhiteEvent();\r
4772       /*\r
4773        * refresh the tags dialog only if it's visible\r
4774        */\r
4775       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4776           char *tags;\r
4777           tags = PGNTags(&gameInfo);\r
4778           TagsPopUp(tags, CmailMsg());\r
4779           free(tags);\r
4780       }\r
4781       break;\r
4782 \r
4783     case IDM_MachineBlack:\r
4784       MachineBlackEvent();\r
4785       /*\r
4786        * refresh the tags dialog only if it's visible\r
4787        */\r
4788       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
4789           char *tags;\r
4790           tags = PGNTags(&gameInfo);\r
4791           TagsPopUp(tags, CmailMsg());\r
4792           free(tags);\r
4793       }\r
4794       break;\r
4795 \r
4796     case IDM_TwoMachines:\r
4797       TwoMachinesEvent();\r
4798       /*\r
4799        * refresh the tags dialog only if it's visible\r
4800        */\r
4801       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
4802           char *tags;\r
4803           tags = PGNTags(&gameInfo);\r
4804           TagsPopUp(tags, CmailMsg());\r
4805           free(tags);\r
4806       }\r
4807       break;\r
4808 \r
4809     case IDM_AnalysisMode:\r
4810       if (!first.analysisSupport) {\r
4811         char buf[MSG_SIZ];\r
4812         sprintf(buf, "%s does not support analysis", first.tidy);\r
4813         DisplayError(buf, 0);\r
4814       } else {\r
4815         if (!appData.showThinking) ToggleShowThinking();\r
4816         AnalyzeModeEvent();\r
4817       }\r
4818       break;\r
4819 \r
4820     case IDM_AnalyzeFile:\r
4821       if (!first.analysisSupport) {\r
4822         char buf[MSG_SIZ];\r
4823         sprintf(buf, "%s does not support analysis", first.tidy);\r
4824         DisplayError(buf, 0);\r
4825       } else {\r
4826         if (!appData.showThinking) ToggleShowThinking();\r
4827         AnalyzeFileEvent();\r
4828         LoadGameDialog(hwnd, "Analyze Game from File");\r
4829         AnalysisPeriodicEvent(1);\r
4830       }\r
4831       break;\r
4832 \r
4833     case IDM_IcsClient:\r
4834       IcsClientEvent();\r
4835       break;\r
4836 \r
4837     case IDM_EditGame:\r
4838       EditGameEvent();\r
4839       break;\r
4840 \r
4841     case IDM_EditPosition:\r
4842       EditPositionEvent();\r
4843       break;\r
4844 \r
4845     case IDM_Training:\r
4846       TrainingEvent();\r
4847       break;\r
4848 \r
4849     case IDM_ShowGameList:\r
4850       ShowGameListProc();\r
4851       break;\r
4852 \r
4853     case IDM_EditTags:\r
4854       EditTagsProc();\r
4855       break;\r
4856 \r
4857     case IDM_EditComment:\r
4858       if (commentDialogUp && editComment) {\r
4859         CommentPopDown();\r
4860       } else {\r
4861         EditCommentEvent();\r
4862       }\r
4863       break;\r
4864 \r
4865     case IDM_Pause:\r
4866       PauseEvent();\r
4867       break;\r
4868 \r
4869     case IDM_Accept:\r
4870       AcceptEvent();\r
4871       break;\r
4872 \r
4873     case IDM_Decline:\r
4874       DeclineEvent();\r
4875       break;\r
4876 \r
4877     case IDM_Rematch:\r
4878       RematchEvent();\r
4879       break;\r
4880 \r
4881     case IDM_CallFlag:\r
4882       CallFlagEvent();\r
4883       break;\r
4884 \r
4885     case IDM_Draw:\r
4886       DrawEvent();\r
4887       break;\r
4888 \r
4889     case IDM_Adjourn:\r
4890       AdjournEvent();\r
4891       break;\r
4892 \r
4893     case IDM_Abort:\r
4894       AbortEvent();\r
4895       break;\r
4896 \r
4897     case IDM_Resign:\r
4898       ResignEvent();\r
4899       break;\r
4900 \r
4901     case IDM_StopObserving:\r
4902       StopObservingEvent();\r
4903       break;\r
4904 \r
4905     case IDM_StopExamining:\r
4906       StopExaminingEvent();\r
4907       break;\r
4908 \r
4909     case IDM_TypeInMove:\r
4910       PopUpMoveDialog('\000');\r
4911       break;\r
4912 \r
4913     case IDM_Backward:\r
4914       BackwardEvent();\r
4915       SetFocus(hwndMain);\r
4916       break;\r
4917 \r
4918     case IDM_Forward:\r
4919       ForwardEvent();\r
4920       SetFocus(hwndMain);\r
4921       break;\r
4922 \r
4923     case IDM_ToStart:\r
4924       ToStartEvent();\r
4925       SetFocus(hwndMain);\r
4926       break;\r
4927 \r
4928     case IDM_ToEnd:\r
4929       ToEndEvent();\r
4930       SetFocus(hwndMain);\r
4931       break;\r
4932 \r
4933     case IDM_Revert:\r
4934       RevertEvent();\r
4935       break;\r
4936 \r
4937     case IDM_TruncateGame:\r
4938       TruncateGameEvent();\r
4939       break;\r
4940 \r
4941     case IDM_MoveNow:\r
4942       MoveNowEvent();\r
4943       break;\r
4944 \r
4945     case IDM_RetractMove:\r
4946       RetractMoveEvent();\r
4947       break;\r
4948 \r
4949     case IDM_FlipView:\r
4950       flipView = !flipView;\r
4951       DrawPosition(FALSE, NULL);\r
4952       break;\r
4953 \r
4954     case IDM_GeneralOptions:\r
4955       GeneralOptionsPopup(hwnd);\r
4956       DrawPosition(TRUE, NULL);
4957       break;\r
4958 \r
4959     case IDM_BoardOptions:\r
4960       BoardOptionsPopup(hwnd);\r
4961       break;\r
4962 \r
4963     case IDM_EnginePlayOptions:
4964       EnginePlayOptionsPopup(hwnd);
4965       break;
4966
4967     case IDM_OptionsUCI:
4968       UciOptionsPopup(hwnd);
4969       break;
4970
4971     case IDM_IcsOptions:\r
4972       IcsOptionsPopup(hwnd);\r
4973       break;\r
4974 \r
4975     case IDM_Fonts:\r
4976       FontsOptionsPopup(hwnd);\r
4977       break;\r
4978 \r
4979     case IDM_Sounds:\r
4980       SoundOptionsPopup(hwnd);\r
4981       break;\r
4982 \r
4983     case IDM_CommPort:\r
4984       CommPortOptionsPopup(hwnd);\r
4985       break;\r
4986 \r
4987     case IDM_LoadOptions:\r
4988       LoadOptionsPopup(hwnd);\r
4989       break;\r
4990 \r
4991     case IDM_SaveOptions:\r
4992       SaveOptionsPopup(hwnd);\r
4993       break;\r
4994 \r
4995     case IDM_TimeControl:\r
4996       TimeControlOptionsPopup(hwnd);\r
4997       break;\r
4998 \r
4999     case IDM_SaveSettings:\r
5000       SaveSettings(settingsFileName);\r
5001       break;\r
5002 \r
5003     case IDM_SaveSettingsOnExit:\r
5004       saveSettingsOnExit = !saveSettingsOnExit;\r
5005       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5006                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5007                                          MF_CHECKED : MF_UNCHECKED));\r
5008       break;\r
5009 \r
5010     case IDM_Hint:\r
5011       HintEvent();\r
5012       break;\r
5013 \r
5014     case IDM_Book:\r
5015       BookEvent();\r
5016       break;\r
5017 \r
5018     case IDM_AboutGame:\r
5019       AboutGameEvent();\r
5020       break;\r
5021 \r
5022     case IDM_Debug:\r
5023       appData.debugMode = !appData.debugMode;\r
5024       if (appData.debugMode) {\r
5025         char dir[MSG_SIZ];\r
5026         GetCurrentDirectory(MSG_SIZ, dir);\r
5027         SetCurrentDirectory(installDir);\r
5028         debugFP = fopen(appData.nameOfDebugFile, "w");
5029         SetCurrentDirectory(dir);\r
5030         setbuf(debugFP, NULL);\r
5031       } else {\r
5032         fclose(debugFP);\r
5033         debugFP = NULL;\r
5034       }\r
5035       break;\r
5036 \r
5037     case IDM_HELPCONTENTS:\r
5038       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5039         MessageBox (GetFocus(),\r
5040                     "Unable to activate help",\r
5041                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5042       }\r
5043       break;\r
5044 \r
5045     case IDM_HELPSEARCH:\r
5046       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5047         MessageBox (GetFocus(),\r
5048                     "Unable to activate help",\r
5049                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5050       }\r
5051       break;\r
5052 \r
5053     case IDM_HELPHELP:\r
5054       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5055         MessageBox (GetFocus(),\r
5056                     "Unable to activate help",\r
5057                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5058       }\r
5059       break;\r
5060 \r
5061     case IDM_ABOUT:\r
5062       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5063       DialogBox(hInst, \r
5064         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5065         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5066       FreeProcInstance(lpProc);\r
5067       break;\r
5068 \r
5069     case IDM_DirectCommand1:\r
5070       AskQuestionEvent("Direct Command",\r
5071                        "Send to chess program:", "", "1");\r
5072       break;\r
5073     case IDM_DirectCommand2:\r
5074       AskQuestionEvent("Direct Command",\r
5075                        "Send to second chess program:", "", "2");\r
5076       break;\r
5077 \r
5078     case EP_WhitePawn:\r
5079       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5080       fromX = fromY = -1;\r
5081       break;\r
5082 \r
5083     case EP_WhiteKnight:\r
5084       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5085       fromX = fromY = -1;\r
5086       break;\r
5087 \r
5088     case EP_WhiteBishop:\r
5089       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5090       fromX = fromY = -1;\r
5091       break;\r
5092 \r
5093     case EP_WhiteRook:\r
5094       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5095       fromX = fromY = -1;\r
5096       break;\r
5097 \r
5098     case EP_WhiteQueen:\r
5099       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5100       fromX = fromY = -1;\r
5101       break;\r
5102 \r
5103     case EP_WhiteKing:\r
5104       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5105       fromX = fromY = -1;\r
5106       break;\r
5107 \r
5108     case EP_BlackPawn:\r
5109       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5110       fromX = fromY = -1;\r
5111       break;\r
5112 \r
5113     case EP_BlackKnight:\r
5114       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5115       fromX = fromY = -1;\r
5116       break;\r
5117 \r
5118     case EP_BlackBishop:\r
5119       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5120       fromX = fromY = -1;\r
5121       break;\r
5122 \r
5123     case EP_BlackRook:\r
5124       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5125       fromX = fromY = -1;\r
5126       break;\r
5127 \r
5128     case EP_BlackQueen:\r
5129       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5130       fromX = fromY = -1;\r
5131       break;\r
5132 \r
5133     case EP_BlackKing:\r
5134       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5135       fromX = fromY = -1;\r
5136       break;\r
5137 \r
5138     case EP_EmptySquare:\r
5139       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5140       fromX = fromY = -1;\r
5141       break;\r
5142 \r
5143     case EP_ClearBoard:\r
5144       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5145       fromX = fromY = -1;\r
5146       break;\r
5147 \r
5148     case EP_White:\r
5149       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5150       fromX = fromY = -1;\r
5151       break;\r
5152 \r
5153     case EP_Black:\r
5154       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5155       fromX = fromY = -1;\r
5156       break;\r
5157 \r
5158     case DP_Pawn:\r
5159       DropMenuEvent(WhitePawn, fromX, fromY);\r
5160       fromX = fromY = -1;\r
5161       break;\r
5162 \r
5163     case DP_Knight:\r
5164       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5165       fromX = fromY = -1;\r
5166       break;\r
5167 \r
5168     case DP_Bishop:\r
5169       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5170       fromX = fromY = -1;\r
5171       break;\r
5172 \r
5173     case DP_Rook:\r
5174       DropMenuEvent(WhiteRook, fromX, fromY);\r
5175       fromX = fromY = -1;\r
5176       break;\r
5177 \r
5178     case DP_Queen:\r
5179       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5180       fromX = fromY = -1;\r
5181       break;\r
5182 \r
5183     default:\r
5184       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5185     }\r
5186     break;\r
5187 \r
5188   case WM_TIMER:\r
5189     switch (wParam) {\r
5190     case CLOCK_TIMER_ID:\r
5191       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5192       clockTimerEvent = 0;\r
5193       DecrementClocks(); /* call into back end */\r
5194       break;\r
5195     case LOAD_GAME_TIMER_ID:\r
5196       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5197       loadGameTimerEvent = 0;\r
5198       AutoPlayGameLoop(); /* call into back end */\r
5199       break;\r
5200     case ANALYSIS_TIMER_ID:\r
5201       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5202           appData.periodicUpdates) {\r
5203         AnalysisPeriodicEvent(0);\r
5204       } else {\r
5205         KillTimer(hwnd, analysisTimerEvent);\r
5206         analysisTimerEvent = 0;\r
5207       }\r
5208       break;\r
5209     case DELAYED_TIMER_ID:\r
5210       KillTimer(hwnd, delayedTimerEvent);\r
5211       delayedTimerEvent = 0;\r
5212       delayedTimerCallback();\r
5213       break;\r
5214     }\r
5215     break;\r
5216 \r
5217   case WM_USER_Input:\r
5218     InputEvent(hwnd, message, wParam, lParam);\r
5219     break;\r
5220 \r
5221   /* [AS] Also move "attached" child windows */
5222   case WM_WINDOWPOSCHANGING:
5223     if( hwnd == hwndMain && appData.useStickyWindows ) {
5224         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
5225
5226         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
5227             /* Window is moving */
5228             RECT rcMain;
5229
5230             GetWindowRect( hwnd, &rcMain );
5231
5232             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
5233             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
5234             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
5235         }
5236     }
5237     break;
5238
5239   /* [AS] Snapping */
5240   case WM_ENTERSIZEMOVE:\r
5241     if (hwnd == hwndMain) {\r
5242       doingSizing = TRUE;\r
5243       lastSizing = 0;\r
5244     }\r
5245     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
5246     break;\r
5247 \r
5248   case WM_SIZING:\r
5249     if (hwnd == hwndMain) {\r
5250       lastSizing = wParam;\r
5251     }\r
5252     break;\r
5253 \r
5254   case WM_MOVING:
5255       return OnMoving( &sd, hwnd, wParam, lParam );
5256
5257   case WM_EXITSIZEMOVE:\r
5258     if (hwnd == hwndMain) {\r
5259       RECT client;\r
5260       doingSizing = FALSE;\r
5261       InvalidateRect(hwnd, &boardRect, FALSE);\r
5262       GetClientRect(hwnd, &client);\r
5263       ResizeBoard(client.right, client.bottom, lastSizing);\r
5264       lastSizing = 0;\r
5265     }\r
5266     return OnExitSizeMove( &sd, hwnd, wParam, lParam );
5267     break;\r
5268 \r
5269   case WM_DESTROY: /* message: window being destroyed */\r
5270     PostQuitMessage(0);\r
5271     break;\r
5272 \r
5273   case WM_CLOSE:\r
5274     if (hwnd == hwndMain) {\r
5275       ExitEvent(0);\r
5276     }\r
5277     break;\r
5278 \r
5279   default:      /* Passes it on if unprocessed */\r
5280     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5281   }\r
5282   return 0;\r
5283 }\r
5284 \r
5285 /*---------------------------------------------------------------------------*\\r
5286  *\r
5287  * Misc utility routines\r
5288  *\r
5289 \*---------------------------------------------------------------------------*/\r
5290 \r
5291 /*\r
5292  * Decent random number generator, at least not as bad as Windows\r
5293  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5294  */\r
5295 unsigned int randstate;\r
5296 \r
5297 int\r
5298 myrandom(void)\r
5299 {\r
5300   randstate = randstate * 1664525 + 1013904223;\r
5301   return (int) randstate & 0x7fffffff;\r
5302 }\r
5303 \r
5304 void\r
5305 mysrandom(unsigned int seed)\r
5306 {\r
5307   randstate = seed;\r
5308 }\r
5309 \r
5310 \r
5311 /* \r
5312  * returns TRUE if user selects a different color, FALSE otherwise \r
5313  */\r
5314 \r
5315 BOOL\r
5316 ChangeColor(HWND hwnd, COLORREF *which)\r
5317 {\r
5318   static BOOL firstTime = TRUE;\r
5319   static DWORD customColors[16];\r
5320   CHOOSECOLOR cc;\r
5321   COLORREF newcolor;\r
5322   int i;\r
5323   ColorClass ccl;\r
5324 \r
5325   if (firstTime) {\r
5326     /* Make initial colors in use available as custom colors */\r
5327     /* Should we put the compiled-in defaults here instead? */\r
5328     i = 0;\r
5329     customColors[i++] = lightSquareColor & 0xffffff;\r
5330     customColors[i++] = darkSquareColor & 0xffffff;\r
5331     customColors[i++] = whitePieceColor & 0xffffff;\r
5332     customColors[i++] = blackPieceColor & 0xffffff;\r
5333     customColors[i++] = highlightSquareColor & 0xffffff;\r
5334     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5335 \r
5336     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5337       customColors[i++] = textAttribs[ccl].color;\r
5338     }\r
5339     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5340     firstTime = FALSE;\r
5341   }\r
5342 \r
5343   cc.lStructSize = sizeof(cc);\r
5344   cc.hwndOwner = hwnd;\r
5345   cc.hInstance = NULL;\r
5346   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5347   cc.lpCustColors = (LPDWORD) customColors;\r
5348   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5349 \r
5350   if (!ChooseColor(&cc)) return FALSE;\r
5351 \r
5352   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5353   if (newcolor == *which) return FALSE;\r
5354   *which = newcolor;\r
5355   return TRUE;\r
5356 \r
5357   /*\r
5358   InitDrawingColors();\r
5359   InvalidateRect(hwnd, &boardRect, FALSE);\r
5360   */\r
5361 }\r
5362 \r
5363 BOOLEAN\r
5364 MyLoadSound(MySound *ms)\r
5365 {\r
5366   BOOL ok = FALSE;\r
5367   struct stat st;\r
5368   FILE *f;\r
5369 \r
5370   if (ms->data) free(ms->data);\r
5371   ms->data = NULL;\r
5372 \r
5373   switch (ms->name[0]) {\r
5374   case NULLCHAR:\r
5375     /* Silence */\r
5376     ok = TRUE;\r
5377     break;\r
5378   case '$':\r
5379     /* System sound from Control Panel.  Don't preload here. */\r
5380     ok = TRUE;\r
5381     break;\r
5382   case '!':\r
5383     if (ms->name[1] == NULLCHAR) {\r
5384       /* "!" alone = silence */\r
5385       ok = TRUE;\r
5386     } else {\r
5387       /* Builtin wave resource.  Error if not found. */\r
5388       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5389       if (h == NULL) break;\r
5390       ms->data = (void *)LoadResource(hInst, h);\r
5391       if (h == NULL) break;\r
5392       ok = TRUE;\r
5393     }\r
5394     break;\r
5395   default:\r
5396     /* .wav file.  Error if not found. */\r
5397     f = fopen(ms->name, "rb");\r
5398     if (f == NULL) break;\r
5399     if (fstat(fileno(f), &st) < 0) break;\r
5400     ms->data = malloc(st.st_size);\r
5401     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5402     fclose(f);\r
5403     ok = TRUE;\r
5404     break;\r
5405   }\r
5406   if (!ok) {\r
5407     char buf[MSG_SIZ];\r
5408     sprintf(buf, "Error loading sound %s", ms->name);\r
5409     DisplayError(buf, GetLastError());\r
5410   }\r
5411   return ok;\r
5412 }\r
5413 \r
5414 BOOLEAN\r
5415 MyPlaySound(MySound *ms)\r
5416 {\r
5417   BOOLEAN ok = FALSE;\r
5418   switch (ms->name[0]) {\r
5419   case NULLCHAR:\r
5420     /* Silence */\r
5421     ok = TRUE;\r
5422     break;\r
5423   case '$':\r
5424     /* System sound from Control Panel (deprecated feature).\r
5425        "$" alone or an unset sound name gets default beep (still in use). */\r
5426     if (ms->name[1]) {\r
5427       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5428     }\r
5429     if (!ok) ok = MessageBeep(MB_OK);\r
5430     break; \r
5431   case '!':\r
5432     /* Builtin wave resource, or "!" alone for silence */\r
5433     if (ms->name[1]) {\r
5434       if (ms->data == NULL) return FALSE;\r
5435       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5436     } else {\r
5437       ok = TRUE;\r
5438     }\r
5439     break;\r
5440   default:\r
5441     /* .wav file.  Error if not found. */\r
5442     if (ms->data == NULL) return FALSE;\r
5443     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5444     break;\r
5445   }\r
5446   /* Don't print an error: this can happen innocently if the sound driver\r
5447      is busy; for instance, if another instance of WinBoard is playing\r
5448      a sound at about the same time. */\r
5449 #if 0\r
5450   if (!ok) {\r
5451     char buf[MSG_SIZ];\r
5452     sprintf(buf, "Error playing sound %s", ms->name);\r
5453     DisplayError(buf, GetLastError());\r
5454   }\r
5455 #endif\r
5456   return ok;\r
5457 }\r
5458 \r
5459 \r
5460 LRESULT CALLBACK\r
5461 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5462 {\r
5463   BOOL ok;\r
5464   OPENFILENAME *ofn;\r
5465   static UINT *number; /* gross that this is static */\r
5466 \r
5467   switch (message) {\r
5468   case WM_INITDIALOG: /* message: initialize dialog box */\r
5469     /* Center the dialog over the application window */\r
5470     ofn = (OPENFILENAME *) lParam;\r
5471     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5472       number = (UINT *) ofn->lCustData;\r
5473       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5474     } else {\r
5475       number = NULL;\r
5476     }\r
5477     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5478     return FALSE;  /* Allow for further processing */\r
5479 \r
5480   case WM_COMMAND:\r
5481     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5482       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5483     }\r
5484     return FALSE;  /* Allow for further processing */\r
5485   }\r
5486   return FALSE;\r
5487 }\r
5488 \r
5489 UINT APIENTRY\r
5490 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5491 {\r
5492   static UINT *number;\r
5493   OPENFILENAME *ofname;\r
5494   OFNOTIFY *ofnot;\r
5495   switch (uiMsg) {\r
5496   case WM_INITDIALOG:\r
5497     ofname = (OPENFILENAME *)lParam;\r
5498     number = (UINT *)(ofname->lCustData);\r
5499     break;\r
5500   case WM_NOTIFY:\r
5501     ofnot = (OFNOTIFY *)lParam;\r
5502     if (ofnot->hdr.code == CDN_FILEOK) {\r
5503       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5504     }\r
5505     break;\r
5506   }\r
5507   return 0;\r
5508 }\r
5509 \r
5510 \r
5511 FILE *\r
5512 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
5513                char *nameFilt, char *dlgTitle, UINT *number,\r
5514                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5515 {\r
5516   OPENFILENAME openFileName;\r
5517   char buf1[MSG_SIZ];\r
5518   FILE *f;\r
5519 \r
5520   if (fileName == NULL) fileName = buf1;\r
5521   if (defName == NULL) {\r
5522     strcpy(fileName, "*.");\r
5523     strcat(fileName, defExt);\r
5524   } else {\r
5525     strcpy(fileName, defName);\r
5526   }\r
5527   if (fileTitle) strcpy(fileTitle, "");\r
5528   if (number) *number = 0;\r
5529 \r
5530   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5531   openFileName.hwndOwner         = hwnd;\r
5532   openFileName.hInstance         = (HANDLE) hInst;\r
5533   openFileName.lpstrFilter       = nameFilt;\r
5534   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5535   openFileName.nMaxCustFilter    = 0L;\r
5536   openFileName.nFilterIndex      = 1L;\r
5537   openFileName.lpstrFile         = fileName;\r
5538   openFileName.nMaxFile          = MSG_SIZ;\r
5539   openFileName.lpstrFileTitle    = fileTitle;\r
5540   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5541   openFileName.lpstrInitialDir   = NULL;\r
5542   openFileName.lpstrTitle        = dlgTitle;\r
5543   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5544     | (write ? 0 : OFN_FILEMUSTEXIST) \r
5545     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5546     | (oldDialog ? 0 : OFN_EXPLORER);\r
5547   openFileName.nFileOffset       = 0;\r
5548   openFileName.nFileExtension    = 0;\r
5549   openFileName.lpstrDefExt       = defExt;\r
5550   openFileName.lCustData         = (LONG) number;\r
5551   openFileName.lpfnHook          = oldDialog ?\r
5552     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5553   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5554 \r
5555   if (write ? GetSaveFileName(&openFileName) : \r
5556               GetOpenFileName(&openFileName)) {\r
5557     /* open the file */\r
5558     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");\r
5559     if (f == NULL) {\r
5560       MessageBox(hwnd, "File open failed", NULL,\r
5561                  MB_OK|MB_ICONEXCLAMATION);\r
5562       return NULL;\r
5563     }\r
5564   } else {\r
5565     int err = CommDlgExtendedError();\r
5566     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5567     return FALSE;\r
5568   }\r
5569   return f;\r
5570 }\r
5571 \r
5572 \r
5573 \r
5574 VOID APIENTRY\r
5575 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5576 {\r
5577   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5578 \r
5579   /*\r
5580    * Get the first pop-up menu in the menu template. This is the\r
5581    * menu that TrackPopupMenu displays.\r
5582    */\r
5583   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5584 \r
5585   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5586 \r
5587   /*\r
5588    * TrackPopup uses screen coordinates, so convert the\r
5589    * coordinates of the mouse click to screen coordinates.\r
5590    */\r
5591   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5592 \r
5593   /* Draw and track the floating pop-up menu. */\r
5594   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5595                  pt.x, pt.y, 0, hwnd, NULL);\r
5596 \r
5597   /* Destroy the menu.*/\r
5598   DestroyMenu(hmenu);\r
5599 }\r
5600    \r
5601 typedef struct {\r
5602   HWND hDlg, hText;\r
5603   int sizeX, sizeY, newSizeX, newSizeY;\r
5604   HDWP hdwp;\r
5605 } ResizeEditPlusButtonsClosure;\r
5606 \r
5607 BOOL CALLBACK\r
5608 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5609 {\r
5610   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5611   RECT rect;\r
5612   POINT pt;\r
5613 \r
5614   if (hChild == cl->hText) return TRUE;\r
5615   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5616   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5617   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5618   ScreenToClient(cl->hDlg, &pt);\r
5619   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5620     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5621   return TRUE;\r
5622 }\r
5623 \r
5624 /* Resize a dialog that has a (rich) edit field filling most of\r
5625    the top, with a row of buttons below */\r
5626 VOID\r
5627 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5628 {\r
5629   RECT rectText;\r
5630   int newTextHeight, newTextWidth;\r
5631   ResizeEditPlusButtonsClosure cl;\r
5632   \r
5633   /*if (IsIconic(hDlg)) return;*/\r
5634   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5635   \r
5636   cl.hdwp = BeginDeferWindowPos(8);\r
5637 \r
5638   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5639   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5640   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5641   if (newTextHeight < 0) {\r
5642     newSizeY += -newTextHeight;\r
5643     newTextHeight = 0;\r
5644   }\r
5645   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5646     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5647 \r
5648   cl.hDlg = hDlg;\r
5649   cl.hText = hText;\r
5650   cl.sizeX = sizeX;\r
5651   cl.sizeY = sizeY;\r
5652   cl.newSizeX = newSizeX;\r
5653   cl.newSizeY = newSizeY;\r
5654   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5655 \r
5656   EndDeferWindowPos(cl.hdwp);\r
5657 }\r
5658 \r
5659 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
5660 {\r
5661     RECT    rChild, rParent;\r
5662     int     wChild, hChild, wParent, hParent;\r
5663     int     wScreen, hScreen, xNew, yNew;\r
5664     HDC     hdc;\r
5665 \r
5666     /* Get the Height and Width of the child window */\r
5667     GetWindowRect (hwndChild, &rChild);\r
5668     wChild = rChild.right - rChild.left;\r
5669     hChild = rChild.bottom - rChild.top;\r
5670 \r
5671     /* Get the Height and Width of the parent window */\r
5672     GetWindowRect (hwndParent, &rParent);\r
5673     wParent = rParent.right - rParent.left;\r
5674     hParent = rParent.bottom - rParent.top;\r
5675 \r
5676     /* Get the display limits */\r
5677     hdc = GetDC (hwndChild);\r
5678     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5679     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5680     ReleaseDC(hwndChild, hdc);\r
5681 \r
5682     /* Calculate new X position, then adjust for screen */\r
5683     xNew = rParent.left + ((wParent - wChild) /2);\r
5684     if (xNew < 0) {\r
5685         xNew = 0;\r
5686     } else if ((xNew+wChild) > wScreen) {\r
5687         xNew = wScreen - wChild;\r
5688     }\r
5689 \r
5690     /* Calculate new Y position, then adjust for screen */\r
5691     if( mode == 0 ) {
5692     yNew = rParent.top  + ((hParent - hChild) /2);\r
5693     }
5694     else {
5695         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
5696     }
5697
5698     if (yNew < 0) {\r
5699         yNew = 0;\r
5700     } else if ((yNew+hChild) > hScreen) {\r
5701         yNew = hScreen - hChild;\r
5702     }\r
5703 \r
5704     /* Set it, and return */\r
5705     return SetWindowPos (hwndChild, NULL,\r
5706                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5707 }\r
5708 \r
5709 /* Center one window over another */
5710 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
5711 {
5712     return CenterWindowEx( hwndChild, hwndParent, 0 );
5713 }
5714
5715 /*---------------------------------------------------------------------------*\\r
5716  *\r
5717  * Startup Dialog functions\r
5718  *\r
5719 \*---------------------------------------------------------------------------*/\r
5720 void\r
5721 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5722 {\r
5723   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5724 \r
5725   while (*cd != NULL) {\r
5726     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5727     cd++;\r
5728   }\r
5729 }\r
5730 \r
5731 void\r
5732 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5733 {\r
5734   char buf1[ARG_MAX];\r
5735   int len;\r
5736 \r
5737   if (str[0] == '@') {\r
5738     FILE* f = fopen(str + 1, "r");\r
5739     if (f == NULL) {\r
5740       DisplayFatalError(str + 1, errno, 2);\r
5741       return;\r
5742     }\r
5743     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5744     fclose(f);\r
5745     buf1[len] = NULLCHAR;\r
5746     str = buf1;\r
5747   }\r
5748 \r
5749   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5750 \r
5751   for (;;) {\r
5752     char buf[MSG_SIZ];\r
5753     char *end = strchr(str, '\n');\r
5754     if (end == NULL) return;\r
5755     memcpy(buf, str, end - str);\r
5756     buf[end - str] = NULLCHAR;\r
5757     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5758     str = end + 1;\r
5759   }\r
5760 }\r
5761 \r
5762 void\r
5763 SetStartupDialogEnables(HWND hDlg)\r
5764 {\r
5765   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5766     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5767     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5768   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5769     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5770   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5771     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5772   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5773     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5774   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5775     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5776     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5777     IsDlgButtonChecked(hDlg, OPT_View));\r
5778 }\r
5779 \r
5780 char *\r
5781 QuoteForFilename(char *filename)\r
5782 {\r
5783   int dquote, space;\r
5784   dquote = strchr(filename, '"') != NULL;\r
5785   space = strchr(filename, ' ') != NULL;\r
5786   if (dquote || space) {\r
5787     if (dquote) {\r
5788       return "'";\r
5789     } else {\r
5790       return "\"";\r
5791     }\r
5792   } else {\r
5793     return "";\r
5794   }\r
5795 }\r
5796 \r
5797 VOID\r
5798 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
5799 {\r
5800   char buf[MSG_SIZ];\r
5801   char *q;\r
5802 \r
5803   InitComboStringsFromOption(hwndCombo, nthnames);\r
5804   q = QuoteForFilename(nthcp);\r
5805   sprintf(buf, "%s%s%s", q, nthcp, q);\r
5806   if (*nthdir != NULLCHAR) {\r
5807     q = QuoteForFilename(nthdir);\r
5808     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
5809   }\r
5810   if (*nthcp == NULLCHAR) {\r
5811     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5812   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5813     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5814     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5815   }\r
5816 }\r
5817 \r
5818 LRESULT CALLBACK\r
5819 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5820 {\r
5821   char buf[MSG_SIZ];\r
5822   HANDLE hwndCombo;\r
5823   char *p;\r
5824 \r
5825   switch (message) {\r
5826   case WM_INITDIALOG:\r
5827     /* Center the dialog */\r
5828     CenterWindow (hDlg, GetDesktopWindow());\r
5829     /* Initialize the dialog items */\r
5830     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
5831                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
5832                   firstChessProgramNames);\r
5833     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5834                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
5835                   secondChessProgramNames);\r
5836     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
5837     InitComboStringsFromOption(hwndCombo, icsNames);    \r
5838     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
5839     if (*appData.icsHelper != NULLCHAR) {\r
5840       char *q = QuoteForFilename(appData.icsHelper);\r
5841       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
5842     }\r
5843     if (*appData.icsHost == NULLCHAR) {\r
5844       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
5845       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
5846     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
5847       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
5848       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
5849     }\r
5850
5851     if (appData.icsActive) {
5852       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
5853     }
5854     else if (appData.noChessProgram) {
5855       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
5856     }\r
5857     else {
5858       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
5859     }
5860
5861     SetStartupDialogEnables(hDlg);\r
5862     return TRUE;\r
5863 \r
5864   case WM_COMMAND:\r
5865     switch (LOWORD(wParam)) {\r
5866     case IDOK:\r
5867       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
5868         strcpy(buf, "/fcp=");\r
5869         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5870         p = buf;\r
5871         ParseArgs(StringGet, &p);\r
5872         strcpy(buf, "/scp=");\r
5873         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5874         p = buf;\r
5875         ParseArgs(StringGet, &p);\r
5876         appData.noChessProgram = FALSE;\r
5877         appData.icsActive = FALSE;\r
5878       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
5879         strcpy(buf, "/ics /icshost=");\r
5880         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5881         p = buf;\r
5882         ParseArgs(StringGet, &p);\r
5883         if (appData.zippyPlay) {\r
5884           strcpy(buf, "/fcp=");\r
5885           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
5886           p = buf;\r
5887           ParseArgs(StringGet, &p);\r
5888         }\r
5889       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
5890         appData.noChessProgram = TRUE;\r
5891         appData.icsActive = FALSE;\r
5892       } else {\r
5893         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
5894                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
5895         return TRUE;\r
5896       }\r
5897       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
5898         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
5899         p = buf;\r
5900         ParseArgs(StringGet, &p);\r
5901       }\r
5902       EndDialog(hDlg, TRUE);\r
5903       return TRUE;\r
5904 \r
5905     case IDCANCEL:\r
5906       ExitEvent(0);\r
5907       return TRUE;\r
5908 \r
5909     case IDM_HELPCONTENTS:\r
5910       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5911         MessageBox (GetFocus(),\r
5912                     "Unable to activate help",\r
5913                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5914       }\r
5915       break;\r
5916 \r
5917     default:\r
5918       SetStartupDialogEnables(hDlg);\r
5919       break;\r
5920     }\r
5921     break;\r
5922   }\r
5923   return FALSE;\r
5924 }\r
5925 \r
5926 /*---------------------------------------------------------------------------*\\r
5927  *\r
5928  * About box dialog functions\r
5929  *\r
5930 \*---------------------------------------------------------------------------*/\r
5931 \r
5932 /* Process messages for "About" dialog box */\r
5933 LRESULT CALLBACK\r
5934 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5935 {\r
5936   switch (message) {\r
5937   case WM_INITDIALOG: /* message: initialize dialog box */\r
5938     /* Center the dialog over the application window */\r
5939     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5940     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
5941     return (TRUE);\r
5942 \r
5943   case WM_COMMAND: /* message: received a command */\r
5944     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
5945         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
5946       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5947       return (TRUE);\r
5948     }\r
5949     break;\r
5950   }\r
5951   return (FALSE);\r
5952 }\r
5953 \r
5954 /*---------------------------------------------------------------------------*\\r
5955  *\r
5956  * Comment Dialog functions\r
5957  *\r
5958 \*---------------------------------------------------------------------------*/\r
5959 \r
5960 LRESULT CALLBACK\r
5961 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5962 {\r
5963   static HANDLE hwndText = NULL;\r
5964   int len, newSizeX, newSizeY, flags;\r
5965   static int sizeX, sizeY;\r
5966   char *str;\r
5967   RECT rect;\r
5968   MINMAXINFO *mmi;\r
5969 \r
5970   switch (message) {\r
5971   case WM_INITDIALOG: /* message: initialize dialog box */\r
5972     /* Initialize the dialog items */\r
5973     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
5974     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
5975     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
5976     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
5977     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
5978     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
5979     SetWindowText(hDlg, commentTitle);\r
5980     if (editComment) {\r
5981       SetFocus(hwndText);\r
5982     } else {\r
5983       SetFocus(GetDlgItem(hDlg, IDOK));\r
5984     }\r
5985     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
5986                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
5987                 MAKELPARAM(FALSE, 0));\r
5988     /* Size and position the dialog */\r
5989     if (!commentDialog) {\r
5990       commentDialog = hDlg;\r
5991       flags = SWP_NOZORDER;\r
5992       GetClientRect(hDlg, &rect);\r
5993       sizeX = rect.right;\r
5994       sizeY = rect.bottom;\r
5995       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
5996           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
5997         WINDOWPLACEMENT wp;\r
5998         EnsureOnScreen(&commentX, &commentY);\r
5999         wp.length = sizeof(WINDOWPLACEMENT);\r
6000         wp.flags = 0;\r
6001         wp.showCmd = SW_SHOW;\r
6002         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6003         wp.rcNormalPosition.left = commentX;\r
6004         wp.rcNormalPosition.right = commentX + commentW;\r
6005         wp.rcNormalPosition.top = commentY;\r
6006         wp.rcNormalPosition.bottom = commentY + commentH;\r
6007         SetWindowPlacement(hDlg, &wp);\r
6008 \r
6009         GetClientRect(hDlg, &rect);\r
6010         newSizeX = rect.right;\r
6011         newSizeY = rect.bottom;\r
6012         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6013                               newSizeX, newSizeY);\r
6014         sizeX = newSizeX;\r
6015         sizeY = newSizeY;\r
6016       }\r
6017     }\r
6018     return FALSE;\r
6019 \r
6020   case WM_COMMAND: /* message: received a command */\r
6021     switch (LOWORD(wParam)) {\r
6022     case IDOK:\r
6023       if (editComment) {\r
6024         char *p, *q;\r
6025         /* Read changed options from the dialog box */\r
6026         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6027         len = GetWindowTextLength(hwndText);\r
6028         str = (char *) malloc(len + 1);\r
6029         GetWindowText(hwndText, str, len + 1);\r
6030         p = q = str;\r
6031         while (*q) {\r
6032           if (*q == '\r')\r
6033             q++;\r
6034           else\r
6035             *p++ = *q++;\r
6036         }\r
6037         *p = NULLCHAR;\r
6038         ReplaceComment(commentIndex, str);\r
6039         free(str);\r
6040       }\r
6041       CommentPopDown();\r
6042       return TRUE;\r
6043 \r
6044     case IDCANCEL:\r
6045     case OPT_CancelComment:\r
6046       CommentPopDown();\r
6047       return TRUE;\r
6048 \r
6049     case OPT_ClearComment:\r
6050       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6051       break;\r
6052 \r
6053     case OPT_EditComment:\r
6054       EditCommentEvent();\r
6055       return TRUE;\r
6056 \r
6057     default:\r
6058       break;\r
6059     }\r
6060     break;\r
6061 \r
6062   case WM_SIZE:\r
6063     newSizeX = LOWORD(lParam);\r
6064     newSizeY = HIWORD(lParam);\r
6065     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6066     sizeX = newSizeX;\r
6067     sizeY = newSizeY;\r
6068     break;\r
6069 \r
6070   case WM_GETMINMAXINFO:\r
6071     /* Prevent resizing window too small */\r
6072     mmi = (MINMAXINFO *) lParam;\r
6073     mmi->ptMinTrackSize.x = 100;\r
6074     mmi->ptMinTrackSize.y = 100;\r
6075     break;\r
6076   }\r
6077   return FALSE;\r
6078 }\r
6079 \r
6080 VOID\r
6081 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6082 {\r
6083   FARPROC lpProc;\r
6084   char *p, *q;\r
6085 \r
6086   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6087 \r
6088   if (str == NULL) str = "";\r
6089   p = (char *) malloc(2 * strlen(str) + 2);\r
6090   q = p;\r
6091   while (*str) {\r
6092     if (*str == '\n') *q++ = '\r';\r
6093     *q++ = *str++;\r
6094   }\r
6095   *q = NULLCHAR;\r
6096   if (commentText != NULL) free(commentText);\r
6097 \r
6098   commentIndex = index;\r
6099   commentTitle = title;\r
6100   commentText = p;\r
6101   editComment = edit;\r
6102 \r
6103   if (commentDialog) {\r
6104     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6105     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6106   } else {\r
6107     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6108     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6109                  hwndMain, (DLGPROC)lpProc);\r
6110     FreeProcInstance(lpProc);\r
6111   }\r
6112   commentDialogUp = TRUE;\r
6113 }\r
6114 \r
6115 \r
6116 /*---------------------------------------------------------------------------*\\r
6117  *\r
6118  * Type-in move dialog functions\r
6119  * \r
6120 \*---------------------------------------------------------------------------*/\r
6121 \r
6122 LRESULT CALLBACK\r
6123 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6124 {\r
6125   char move[MSG_SIZ];\r
6126   HWND hInput;\r
6127   ChessMove moveType;\r
6128   int fromX, fromY, toX, toY;\r
6129   char promoChar;\r
6130 \r
6131   switch (message) {\r
6132   case WM_INITDIALOG:\r
6133     move[0] = (char) lParam;\r
6134     move[1] = NULLCHAR;\r
6135     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
6136     hInput = GetDlgItem(hDlg, OPT_Move);\r
6137     SetWindowText(hInput, move);\r
6138     SetFocus(hInput);\r
6139     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6140     return FALSE;\r
6141 \r
6142   case WM_COMMAND:\r
6143     switch (LOWORD(wParam)) {\r
6144     case IDOK:\r
6145       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6146         gameMode != Training) {\r
6147         DisplayMoveError("Displayed move is not current");\r
6148       } else {\r
6149         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6150         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6151           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6152           if (gameMode != Training)\r
6153               forwardMostMove = currentMove;\r
6154           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6155         } else {\r
6156           DisplayMoveError("Could not parse move");\r
6157         }\r
6158       }\r
6159       EndDialog(hDlg, TRUE);\r
6160       return TRUE;\r
6161     case IDCANCEL:\r
6162       EndDialog(hDlg, FALSE);\r
6163       return TRUE;\r
6164     default:\r
6165       break;\r
6166     }\r
6167     break;\r
6168   }\r
6169   return FALSE;\r
6170 }\r
6171 \r
6172 VOID\r
6173 PopUpMoveDialog(char firstchar)\r
6174 {\r
6175     FARPROC lpProc;\r
6176     \r
6177     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6178         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6179         gameMode == AnalyzeMode || gameMode == EditGame || \r
6180         gameMode == EditPosition || gameMode == IcsExamining ||\r
6181         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6182         gameMode == Training) {\r
6183       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6184       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6185         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6186       FreeProcInstance(lpProc);\r
6187     }\r
6188 }\r
6189 \r
6190 /*---------------------------------------------------------------------------*\\r
6191  *\r
6192  *  Error dialogs\r
6193  * \r
6194 \*---------------------------------------------------------------------------*/\r
6195 \r
6196 /* Nonmodal error box */\r
6197 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6198                              WPARAM wParam, LPARAM lParam);\r
6199 \r
6200 VOID\r
6201 ErrorPopUp(char *title, char *content)\r
6202 {\r
6203   FARPROC lpProc;\r
6204   char *p, *q;\r
6205   BOOLEAN modal = hwndMain == NULL;\r
6206 \r
6207   p = content;\r
6208   q = errorMessage;\r
6209   while (*p) {\r
6210     if (*p == '\n') {\r
6211       if (modal) {\r
6212         *q++ = ' ';\r
6213         p++;\r
6214       } else {\r
6215         *q++ = '\r';\r
6216         *q++ = *p++;\r
6217       }\r
6218     } else {\r
6219       *q++ = *p++;\r
6220     }\r
6221   }\r
6222   *q = NULLCHAR;\r
6223   strncpy(errorTitle, title, sizeof(errorTitle));\r
6224   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6225   \r
6226   if (modal) {\r
6227     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6228   } else {\r
6229     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6230     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6231                  hwndMain, (DLGPROC)lpProc);\r
6232     FreeProcInstance(lpProc);\r
6233   }\r
6234 }\r
6235 \r
6236 VOID\r
6237 ErrorPopDown()\r
6238 {\r
6239   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6240   if (errorDialog == NULL) return;\r
6241   DestroyWindow(errorDialog);\r
6242   errorDialog = NULL;\r
6243 }\r
6244 \r
6245 LRESULT CALLBACK\r
6246 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6247 {\r
6248   HANDLE hwndText;\r
6249   RECT rChild;\r
6250 \r
6251   switch (message) {\r
6252   case WM_INITDIALOG:\r
6253     GetWindowRect(hDlg, &rChild);\r
6254
6255     /*
6256     SetWindowPos(hDlg, NULL, rChild.left,\r
6257       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6258       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6259     */
6260
6261     /*
6262         [AS] It seems that the above code wants to move the dialog up in the "caption
6263         area" of the main window, but it uses the dialog height as an hard-coded constant,
6264         and it doesn't work when you resize the dialog.
6265         For now, just give it a default position.
6266     */
6267     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
6268
6269     errorDialog = hDlg;\r
6270     SetWindowText(hDlg, errorTitle);\r
6271     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6272     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6273     return FALSE;\r
6274 \r
6275   case WM_COMMAND:\r
6276     switch (LOWORD(wParam)) {\r
6277     case IDOK:\r
6278     case IDCANCEL:\r
6279       if (errorDialog == hDlg) errorDialog = NULL;\r
6280       DestroyWindow(hDlg);\r
6281       return TRUE;\r
6282 \r
6283     default:\r
6284       break;\r
6285     }\r
6286     break;\r
6287   }\r
6288   return FALSE;\r
6289 }\r
6290 \r
6291 /*---------------------------------------------------------------------------*\\r
6292  *\r
6293  *  Ics Interaction console functions\r
6294  *\r
6295 \*---------------------------------------------------------------------------*/\r
6296 \r
6297 #define HISTORY_SIZE 64\r
6298 static char *history[HISTORY_SIZE];\r
6299 int histIn = 0, histP = 0;\r
6300 \r
6301 VOID\r
6302 SaveInHistory(char *cmd)\r
6303 {\r
6304   if (history[histIn] != NULL) {\r
6305     free(history[histIn]);\r
6306     history[histIn] = NULL;\r
6307   }\r
6308   if (*cmd == NULLCHAR) return;\r
6309   history[histIn] = StrSave(cmd);\r
6310   histIn = (histIn + 1) % HISTORY_SIZE;\r
6311   if (history[histIn] != NULL) {\r
6312     free(history[histIn]);\r
6313     history[histIn] = NULL;\r
6314   }\r
6315   histP = histIn;\r
6316 }\r
6317 \r
6318 char *\r
6319 PrevInHistory(char *cmd)\r
6320 {\r
6321   int newhp;\r
6322   if (histP == histIn) {\r
6323     if (history[histIn] != NULL) free(history[histIn]);\r
6324     history[histIn] = StrSave(cmd);\r
6325   }\r
6326   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6327   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6328   histP = newhp;\r
6329   return history[histP];\r
6330 }\r
6331 \r
6332 char *\r
6333 NextInHistory()\r
6334 {\r
6335   if (histP == histIn) return NULL;\r
6336   histP = (histP + 1) % HISTORY_SIZE;\r
6337   return history[histP];\r
6338 }\r
6339 \r
6340 typedef struct {\r
6341   char *item;\r
6342   char *command;\r
6343   BOOLEAN getname;\r
6344   BOOLEAN immediate;\r
6345 } IcsTextMenuEntry;\r
6346 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
6347 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
6348 \r
6349 void\r
6350 ParseIcsTextMenu(char *icsTextMenuString)\r
6351 {\r
6352   int flags = 0;\r
6353   IcsTextMenuEntry *e = icsTextMenuEntry;\r
6354   char *p = icsTextMenuString;\r
6355   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6356     free(e->item);\r
6357     e->item = NULL;\r
6358     if (e->command != NULL) {\r
6359       free(e->command);\r
6360       e->command = NULL;\r
6361     }\r
6362     e++;\r
6363   }\r
6364   e = icsTextMenuEntry;\r
6365   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6366     if (*p == ';' || *p == '\n') {\r
6367       e->item = strdup("-");\r
6368       e->command = NULL;\r
6369       p++;\r
6370     } else if (*p == '-') {\r
6371       e->item = strdup("-");\r
6372       e->command = NULL;\r
6373       p++;\r
6374       if (*p) p++;\r
6375     } else {\r
6376       char *q, *r, *s, *t;\r
6377       char c;\r
6378       q = strchr(p, ',');\r
6379       if (q == NULL) break;\r
6380       *q = NULLCHAR;\r
6381       r = strchr(q + 1, ',');\r
6382       if (r == NULL) break;\r
6383       *r = NULLCHAR;\r
6384       s = strchr(r + 1, ',');\r
6385       if (s == NULL) break;\r
6386       *s = NULLCHAR;\r
6387       c = ';';\r
6388       t = strchr(s + 1, c);\r
6389       if (t == NULL) {\r
6390         c = '\n';\r
6391         t = strchr(s + 1, c);\r
6392       }\r
6393       if (t != NULL) *t = NULLCHAR;\r
6394       e->item = strdup(p);\r
6395       e->command = strdup(q + 1);\r
6396       e->getname = *(r + 1) != '0';\r
6397       e->immediate = *(s + 1) != '0';\r
6398       *q = ',';\r
6399       *r = ',';\r
6400       *s = ',';\r
6401       if (t == NULL) break;\r
6402       *t = c;\r
6403       p = t + 1;\r
6404     }\r
6405     e++;\r
6406   } \r
6407 }\r
6408 \r
6409 HMENU\r
6410 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6411 {\r
6412   HMENU hmenu, h;\r
6413   int i = 0;\r
6414   hmenu = LoadMenu(hInst, "TextMenu");\r
6415   h = GetSubMenu(hmenu, 0);\r
6416   while (e->item) {\r
6417     if (strcmp(e->item, "-") == 0) {\r
6418       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6419     } else {\r
6420       if (e->item[0] == '|') {\r
6421         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6422                    IDM_CommandX + i, &e->item[1]);\r
6423       } else {\r
6424         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6425       }\r
6426     }\r
6427     e++;\r
6428     i++;\r
6429   } \r
6430   return hmenu;\r
6431 }\r
6432 \r
6433 WNDPROC consoleTextWindowProc;\r
6434 \r
6435 void\r
6436 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6437 {\r
6438   char buf[MSG_SIZ], name[MSG_SIZ];\r
6439   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6440   CHARRANGE sel;\r
6441 \r
6442   if (!getname) {\r
6443     SetWindowText(hInput, command);\r
6444     if (immediate) {\r
6445       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6446     } else {\r
6447       sel.cpMin = 999999;\r
6448       sel.cpMax = 999999;\r
6449       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6450       SetFocus(hInput);\r
6451     }\r
6452     return;\r
6453   }    \r
6454   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6455   if (sel.cpMin == sel.cpMax) {\r
6456     /* Expand to surrounding word */\r
6457     TEXTRANGE tr;\r
6458     do {\r
6459       tr.chrg.cpMax = sel.cpMin;\r
6460       tr.chrg.cpMin = --sel.cpMin;\r
6461       if (sel.cpMin < 0) break;\r
6462       tr.lpstrText = name;\r
6463       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6464     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6465     sel.cpMin++;\r
6466 \r
6467     do {\r
6468       tr.chrg.cpMin = sel.cpMax;\r
6469       tr.chrg.cpMax = ++sel.cpMax;\r
6470       tr.lpstrText = name;\r
6471       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6472     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6473     sel.cpMax--;\r
6474 \r
6475     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6476       MessageBeep(MB_ICONEXCLAMATION);\r
6477       return;\r
6478     }\r
6479     tr.chrg = sel;\r
6480     tr.lpstrText = name;\r
6481     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6482   } else {\r
6483     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6484       MessageBeep(MB_ICONEXCLAMATION);\r
6485       return;\r
6486     }\r
6487     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6488   }\r
6489   if (immediate) {\r
6490     sprintf(buf, "%s %s", command, name);\r
6491     SetWindowText(hInput, buf);\r
6492     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6493   } else {\r
6494     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6495     SetWindowText(hInput, buf);\r
6496     sel.cpMin = 999999;\r
6497     sel.cpMax = 999999;\r
6498     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6499     SetFocus(hInput);\r
6500   }\r
6501 }\r
6502 \r
6503 LRESULT CALLBACK \r
6504 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6505 {\r
6506   HWND hInput;\r
6507   CHARRANGE sel;\r
6508 \r
6509   switch (message) {\r
6510   case WM_KEYDOWN:\r
6511     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6512     switch (wParam) {\r
6513     case VK_PRIOR:\r
6514       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6515       return 0;\r
6516     case VK_NEXT:\r
6517       sel.cpMin = 999999;\r
6518       sel.cpMax = 999999;\r
6519       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6520       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6521       return 0;\r
6522     }\r
6523     break;\r
6524   case WM_CHAR:\r
6525     if (wParam == '\t') {\r
6526       if (GetKeyState(VK_SHIFT) < 0) {\r
6527         /* shifted */\r
6528         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6529         if (buttonDesc[0].hwnd) {\r
6530           SetFocus(buttonDesc[0].hwnd);\r
6531         } else {\r
6532           SetFocus(hwndMain);\r
6533         }\r
6534       } else {\r
6535         /* unshifted */\r
6536         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6537       }\r
6538     } else {\r
6539       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6540       SetFocus(hInput);\r
6541       SendMessage(hInput, message, wParam, lParam);\r
6542     }\r
6543     return 0;\r
6544   case WM_PASTE:\r
6545     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6546     SetFocus(hInput);\r
6547     return SendMessage(hInput, message, wParam, lParam);\r
6548   case WM_MBUTTONDOWN:\r
6549     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6550   case WM_RBUTTONDOWN:\r
6551     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6552       /* Move selection here if it was empty */\r
6553       POINT pt;\r
6554       pt.x = LOWORD(lParam);\r
6555       pt.y = HIWORD(lParam);\r
6556       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6557       if (sel.cpMin == sel.cpMax) {\r
6558         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6559         sel.cpMax = sel.cpMin;\r
6560         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6561       }\r
6562       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6563     }\r
6564     return 0;\r
6565   case WM_RBUTTONUP:\r
6566     if (GetKeyState(VK_SHIFT) & ~1) {\r
6567       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6568         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6569     } else {\r
6570       POINT pt;\r
6571       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6572       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6573       if (sel.cpMin == sel.cpMax) {\r
6574         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6575         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6576       }\r
6577       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6578         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6579       }\r
6580       pt.x = LOWORD(lParam);\r
6581       pt.y = HIWORD(lParam);\r
6582       MenuPopup(hwnd, pt, hmenu, -1);\r
6583     }\r
6584     return 0;\r
6585   case WM_COMMAND:\r
6586     switch (LOWORD(wParam)) {\r
6587     case IDM_QuickPaste:\r
6588       {\r
6589         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6590         if (sel.cpMin == sel.cpMax) {\r
6591           MessageBeep(MB_ICONEXCLAMATION);\r
6592           return 0;\r
6593         }\r
6594         SendMessage(hwnd, WM_COPY, 0, 0);\r
6595         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6596         SendMessage(hInput, WM_PASTE, 0, 0);\r
6597         SetFocus(hInput);\r
6598         return 0;\r
6599       }\r
6600     case IDM_Cut:\r
6601       SendMessage(hwnd, WM_CUT, 0, 0);\r
6602       return 0;\r
6603     case IDM_Paste:\r
6604       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6605       return 0;\r
6606     case IDM_Copy:\r
6607       SendMessage(hwnd, WM_COPY, 0, 0);\r
6608       return 0;\r
6609     default:\r
6610       {\r
6611         int i = LOWORD(wParam) - IDM_CommandX;\r
6612         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6613             icsTextMenuEntry[i].command != NULL) {\r
6614           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6615                    icsTextMenuEntry[i].getname,\r
6616                    icsTextMenuEntry[i].immediate);\r
6617           return 0;\r
6618         }\r
6619       }\r
6620       break;\r
6621     }\r
6622     break;\r
6623   }\r
6624   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6625 }\r
6626 \r
6627 WNDPROC consoleInputWindowProc;\r
6628 \r
6629 LRESULT CALLBACK\r
6630 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6631 {\r
6632   char buf[MSG_SIZ];\r
6633   char *p;\r
6634   static BOOL sendNextChar = FALSE;\r
6635   static BOOL quoteNextChar = FALSE;\r
6636   InputSource *is = consoleInputSource;\r
6637   CHARFORMAT cf;\r
6638   CHARRANGE sel;\r
6639 \r
6640   switch (message) {\r
6641   case WM_CHAR:\r
6642     if (!appData.localLineEditing || sendNextChar) {\r
6643       is->buf[0] = (CHAR) wParam;\r
6644       is->count = 1;\r
6645       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6646       sendNextChar = FALSE;\r
6647       return 0;\r
6648     }\r
6649     if (quoteNextChar) {\r
6650       buf[0] = (char) wParam;\r
6651       buf[1] = NULLCHAR;\r
6652       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6653       quoteNextChar = FALSE;\r
6654       return 0;\r
6655     }\r
6656     switch (wParam) {\r
6657     case '\r':   /* Enter key */\r
6658       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6659       if (consoleEcho) SaveInHistory(is->buf);\r
6660       is->buf[is->count++] = '\n';\r
6661       is->buf[is->count] = NULLCHAR;\r
6662       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6663       if (consoleEcho) {\r
6664         ConsoleOutput(is->buf, is->count, TRUE);\r
6665       } else if (appData.localLineEditing) {\r
6666         ConsoleOutput("\n", 1, TRUE);\r
6667       }\r
6668       /* fall thru */\r
6669     case '\033': /* Escape key */\r
6670       SetWindowText(hwnd, "");\r
6671       cf.cbSize = sizeof(CHARFORMAT);\r
6672       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6673       if (consoleEcho) {\r
6674         cf.crTextColor = textAttribs[ColorNormal].color;\r
6675       } else {\r
6676         cf.crTextColor = COLOR_ECHOOFF;\r
6677       }\r
6678       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6679       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6680       return 0;\r
6681     case '\t':   /* Tab key */\r
6682       if (GetKeyState(VK_SHIFT) < 0) {\r
6683         /* shifted */\r
6684         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6685       } else {\r
6686         /* unshifted */\r
6687         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6688         if (buttonDesc[0].hwnd) {\r
6689           SetFocus(buttonDesc[0].hwnd);\r
6690         } else {\r
6691           SetFocus(hwndMain);\r
6692         }\r
6693       }\r
6694       return 0;\r
6695     case '\023': /* Ctrl+S */\r
6696       sendNextChar = TRUE;\r
6697       return 0;\r
6698     case '\021': /* Ctrl+Q */\r
6699       quoteNextChar = TRUE;\r
6700       return 0;\r
6701     default:\r
6702       break;\r
6703     }\r
6704     break;\r
6705   case WM_KEYDOWN:\r
6706     switch (wParam) {\r
6707     case VK_UP:\r
6708       GetWindowText(hwnd, buf, MSG_SIZ);\r
6709       p = PrevInHistory(buf);\r
6710       if (p != NULL) {\r
6711         SetWindowText(hwnd, p);\r
6712         sel.cpMin = 999999;\r
6713         sel.cpMax = 999999;\r
6714         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6715         return 0;\r
6716       }\r
6717       break;\r
6718     case VK_DOWN:\r
6719       p = NextInHistory();\r
6720       if (p != NULL) {\r
6721         SetWindowText(hwnd, p);\r
6722         sel.cpMin = 999999;\r
6723         sel.cpMax = 999999;\r
6724         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6725         return 0;\r
6726       }\r
6727       break;\r
6728     case VK_HOME:\r
6729     case VK_END:\r
6730       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6731       /* fall thru */\r
6732     case VK_PRIOR:\r
6733     case VK_NEXT:\r
6734       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
6735       return 0;\r
6736     }\r
6737     break;\r
6738   case WM_MBUTTONDOWN:\r
6739     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6740       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6741     break;\r
6742   case WM_RBUTTONUP:\r
6743     if (GetKeyState(VK_SHIFT) & ~1) {\r
6744       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6745         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6746     } else {\r
6747       POINT pt;\r
6748       HMENU hmenu;\r
6749       hmenu = LoadMenu(hInst, "InputMenu");\r
6750       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6751       if (sel.cpMin == sel.cpMax) {\r
6752         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6753         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
6754       }\r
6755       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6756         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6757       }\r
6758       pt.x = LOWORD(lParam);\r
6759       pt.y = HIWORD(lParam);\r
6760       MenuPopup(hwnd, pt, hmenu, -1);\r
6761     }\r
6762     return 0;\r
6763   case WM_COMMAND:\r
6764     switch (LOWORD(wParam)) { \r
6765     case IDM_Undo:\r
6766       SendMessage(hwnd, EM_UNDO, 0, 0);\r
6767       return 0;\r
6768     case IDM_SelectAll:\r
6769       sel.cpMin = 0;\r
6770       sel.cpMax = -1; /*999999?*/\r
6771       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6772       return 0;\r
6773     case IDM_Cut:\r
6774       SendMessage(hwnd, WM_CUT, 0, 0);\r
6775       return 0;\r
6776     case IDM_Paste:\r
6777       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6778       return 0;\r
6779     case IDM_Copy:\r
6780       SendMessage(hwnd, WM_COPY, 0, 0);\r
6781       return 0;\r
6782     }\r
6783     break;\r
6784   }\r
6785   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
6786 }\r
6787 \r
6788 #define CO_MAX  100000\r
6789 #define CO_TRIM   1000\r
6790 \r
6791 LRESULT CALLBACK\r
6792 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6793 {\r
6794   static SnapData sd;
6795   static HWND hText, hInput, hFocus;\r
6796   InputSource *is = consoleInputSource;\r
6797   RECT rect;\r
6798   static int sizeX, sizeY;\r
6799   int newSizeX, newSizeY;\r
6800   MINMAXINFO *mmi;\r
6801 \r
6802   switch (message) {\r
6803   case WM_INITDIALOG: /* message: initialize dialog box */\r
6804     hwndConsole = hDlg;\r
6805     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
6806     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
6807     SetFocus(hInput);\r
6808     consoleTextWindowProc = (WNDPROC)\r
6809       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
6810     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6811     consoleInputWindowProc = (WNDPROC)\r
6812       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
6813     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6814     Colorize(ColorNormal, TRUE);\r
6815     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
6816     ChangedConsoleFont();\r
6817     GetClientRect(hDlg, &rect);\r
6818     sizeX = rect.right;\r
6819     sizeY = rect.bottom;\r
6820     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
6821         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
6822       WINDOWPLACEMENT wp;\r
6823       EnsureOnScreen(&consoleX, &consoleY);\r
6824       wp.length = sizeof(WINDOWPLACEMENT);\r
6825       wp.flags = 0;\r
6826       wp.showCmd = SW_SHOW;\r
6827       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6828       wp.rcNormalPosition.left = consoleX;\r
6829       wp.rcNormalPosition.right = consoleX + consoleW;\r
6830       wp.rcNormalPosition.top = consoleY;\r
6831       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
6832       SetWindowPlacement(hDlg, &wp);\r
6833     }\r
6834     return FALSE;\r
6835 \r
6836   case WM_SETFOCUS:\r
6837     SetFocus(hInput);\r
6838     return 0;\r
6839 \r
6840   case WM_CLOSE:\r
6841     ExitEvent(0);\r
6842     /* not reached */\r
6843     break;\r
6844 \r
6845   case WM_SIZE:\r
6846     if (IsIconic(hDlg)) break;\r
6847     newSizeX = LOWORD(lParam);\r
6848     newSizeY = HIWORD(lParam);\r
6849     if (sizeX != newSizeX || sizeY != newSizeY) {\r
6850       RECT rectText, rectInput;\r
6851       POINT pt;\r
6852       int newTextHeight, newTextWidth;\r
6853       GetWindowRect(hText, &rectText);\r
6854       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6855       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6856       if (newTextHeight < 0) {\r
6857         newSizeY += -newTextHeight;\r
6858         newTextHeight = 0;\r
6859       }\r
6860       SetWindowPos(hText, NULL, 0, 0,\r
6861         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6862       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
6863       pt.x = rectInput.left;\r
6864       pt.y = rectInput.top + newSizeY - sizeY;\r
6865       ScreenToClient(hDlg, &pt);\r
6866       SetWindowPos(hInput, NULL, \r
6867         pt.x, pt.y, /* needs client coords */   \r
6868         rectInput.right - rectInput.left + newSizeX - sizeX,\r
6869         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
6870     }\r
6871     sizeX = newSizeX;\r
6872     sizeY = newSizeY;\r
6873     break;\r
6874 \r
6875   case WM_GETMINMAXINFO:\r
6876     /* Prevent resizing window too small */\r
6877     mmi = (MINMAXINFO *) lParam;\r
6878     mmi->ptMinTrackSize.x = 100;\r
6879     mmi->ptMinTrackSize.y = 100;\r
6880     break;\r
6881
6882   /* [AS] Snapping */
6883   case WM_ENTERSIZEMOVE:
6884     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
6885
6886   case WM_SIZING:
6887     return OnSizing( &sd, hDlg, wParam, lParam );
6888
6889   case WM_MOVING:
6890     return OnMoving( &sd, hDlg, wParam, lParam );
6891
6892   case WM_EXITSIZEMOVE:
6893     return OnExitSizeMove( &sd, hDlg, wParam, lParam );
6894   }\r
6895
6896   return DefWindowProc(hDlg, message, wParam, lParam);\r
6897 }\r
6898 \r
6899 \r
6900 VOID\r
6901 ConsoleCreate()\r
6902 {\r
6903   HWND hCons;\r
6904   if (hwndConsole) return;\r
6905   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
6906   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
6907 }\r
6908 \r
6909 \r
6910 VOID\r
6911 ConsoleOutput(char* data, int length, int forceVisible)\r
6912 {\r
6913   HWND hText;\r
6914   int trim, exlen;\r
6915   char *p, *q;\r
6916   char buf[CO_MAX+1];\r
6917   POINT pEnd;\r
6918   RECT rect;\r
6919   static int delayLF = 0;\r
6920   CHARRANGE savesel, sel;\r
6921 \r
6922   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
6923   p = data;\r
6924   q = buf;\r
6925   if (delayLF) {\r
6926     *q++ = '\r';\r
6927     *q++ = '\n';\r
6928     delayLF = 0;\r
6929   }\r
6930   while (length--) {\r
6931     if (*p == '\n') {\r
6932       if (*++p) {\r
6933         *q++ = '\r';\r
6934         *q++ = '\n';\r
6935       } else {\r
6936         delayLF = 1;\r
6937       }\r
6938     } else if (*p == '\007') {\r
6939        MyPlaySound(&sounds[(int)SoundBell]);\r
6940        p++;\r
6941     } else {\r
6942       *q++ = *p++;\r
6943     }\r
6944   }\r
6945   *q = NULLCHAR;\r
6946   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
6947   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6948   /* Save current selection */\r
6949   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
6950   exlen = GetWindowTextLength(hText);\r
6951   /* Find out whether current end of text is visible */\r
6952   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
6953   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
6954   /* Trim existing text if it's too long */\r
6955   if (exlen + (q - buf) > CO_MAX) {\r
6956     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
6957     sel.cpMin = 0;\r
6958     sel.cpMax = trim;\r
6959     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6960     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
6961     exlen -= trim;\r
6962     savesel.cpMin -= trim;\r
6963     savesel.cpMax -= trim;\r
6964     if (exlen < 0) exlen = 0;\r
6965     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
6966     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
6967   }\r
6968   /* Append the new text */\r
6969   sel.cpMin = exlen;\r
6970   sel.cpMax = exlen;\r
6971   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6972   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
6973   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
6974   if (forceVisible || exlen == 0 ||\r
6975       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
6976        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
6977     /* Scroll to make new end of text visible if old end of text\r
6978        was visible or new text is an echo of user typein */\r
6979     sel.cpMin = 9999999;\r
6980     sel.cpMax = 9999999;\r
6981     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6982     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6983     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
6984     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
6985   }\r
6986   if (savesel.cpMax == exlen || forceVisible) {\r
6987     /* Move insert point to new end of text if it was at the old\r
6988        end of text or if the new text is an echo of user typein */\r
6989     sel.cpMin = 9999999;\r
6990     sel.cpMax = 9999999;\r
6991     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6992   } else {\r
6993     /* Restore previous selection */\r
6994     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
6995   }\r
6996   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
6997 }\r
6998 \r
6999 /*---------*/\r
7000 \r
7001 \r
7002 void\r
7003 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7004               RECT *rect, char *color)\r
7005 {\r
7006   char buf[100];\r
7007   char *str;\r
7008   COLORREF oldFg, oldBg;\r
7009   HFONT oldFont;\r
7010 \r
7011   if (appData.clockMode) {\r
7012     if (tinyLayout)\r
7013       sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));\r
7014     else\r
7015       sprintf(buf, "%s: %s", color, TimeString(timeRemaining));\r
7016     str = buf;\r
7017   } else {\r
7018     str = color;\r
7019   }\r
7020 \r
7021   if (highlight) {\r
7022     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7023     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7024   } else {\r
7025     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7026     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7027   }\r
7028   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7029 \r
7030   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7031              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7032              rect, str, strlen(str), NULL);\r
7033 \r
7034   (void) SetTextColor(hdc, oldFg);\r
7035   (void) SetBkColor(hdc, oldBg);\r
7036   (void) SelectObject(hdc, oldFont);\r
7037 }\r
7038 \r
7039 \r
7040 int\r
7041 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7042            OVERLAPPED *ovl)\r
7043 {\r
7044   int ok, err;\r
7045 \r
7046   /* [AS]  */
7047   if( count <= 0 ) {
7048     if (appData.debugMode) {
7049       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
7050     }
7051
7052     return ERROR_INVALID_USER_BUFFER;
7053   }
7054
7055   ResetEvent(ovl->hEvent);\r
7056   ovl->Offset = ovl->OffsetHigh = 0;\r
7057   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7058   if (ok) {\r
7059     err = NO_ERROR;\r
7060   } else {\r
7061     err = GetLastError();\r
7062     if (err == ERROR_IO_PENDING) {\r
7063       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7064       if (ok)\r
7065         err = NO_ERROR;\r
7066       else\r
7067         err = GetLastError();\r
7068     }\r
7069   }\r
7070   return err;\r
7071 }\r
7072 \r
7073 int\r
7074 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7075             OVERLAPPED *ovl)\r
7076 {\r
7077   int ok, err;\r
7078 \r
7079   ResetEvent(ovl->hEvent);\r
7080   ovl->Offset = ovl->OffsetHigh = 0;\r
7081   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7082   if (ok) {\r
7083     err = NO_ERROR;\r
7084   } else {\r
7085     err = GetLastError();\r
7086     if (err == ERROR_IO_PENDING) {\r
7087       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7088       if (ok)\r
7089         err = NO_ERROR;\r
7090       else\r
7091         err = GetLastError();\r
7092     }\r
7093   }\r
7094   return err;\r
7095 }\r
7096 \r
7097 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
7098 void CheckForInputBufferFull( InputSource * is )
7099 {
7100     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
7101         /* Look for end of line */
7102         char * p = is->buf;
7103
7104         while( p < is->next && *p != '\n' ) {
7105             p++;
7106         }
7107
7108         if( p >= is->next ) {
7109             if (appData.debugMode) {
7110                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );
7111             }
7112
7113             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
7114             is->count = (DWORD) -1;
7115             is->next = is->buf;
7116         }
7117     }
7118 }
7119 \r
7120 DWORD\r
7121 InputThread(LPVOID arg)\r
7122 {\r
7123   InputSource *is;\r
7124   OVERLAPPED ovl;\r
7125 \r
7126   is = (InputSource *) arg;\r
7127   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7128   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7129   while (is->hThread != NULL) {\r
7130     is->error = DoReadFile(is->hFile, is->next,\r
7131                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7132                            &is->count, &ovl);\r
7133     if (is->error == NO_ERROR) {\r
7134       is->next += is->count;\r
7135     } else {\r
7136       if (is->error == ERROR_BROKEN_PIPE) {\r
7137         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7138         is->count = 0;\r
7139       } else {\r
7140         is->count = (DWORD) -1;\r
7141         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
7142         break;
7143       }\r
7144     }\r
7145
7146     CheckForInputBufferFull( is );
7147
7148     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7149
7150     if( is->count == ((DWORD) -1) ) break; /* [AS] */
7151
7152     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7153   }\r
7154
7155   CloseHandle(ovl.hEvent);\r
7156   CloseHandle(is->hFile);\r
7157
7158   if (appData.debugMode) {
7159     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );
7160   }
7161
7162   return 0;\r
7163 }\r
7164 \r
7165 \r
7166 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7167 DWORD\r
7168 NonOvlInputThread(LPVOID arg)\r
7169 {\r
7170   InputSource *is;\r
7171   char *p, *q;\r
7172   int i;\r
7173   char prev;\r
7174 \r
7175   is = (InputSource *) arg;\r
7176   while (is->hThread != NULL) {\r
7177     is->error = ReadFile(is->hFile, is->next,\r
7178                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7179                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7180     if (is->error == NO_ERROR) {\r
7181       /* Change CRLF to LF */\r
7182       if (is->next > is->buf) {\r
7183         p = is->next - 1;\r
7184         i = is->count + 1;\r
7185       } else {\r
7186         p = is->next;\r
7187         i = is->count;\r
7188       }\r
7189       q = p;\r
7190       prev = NULLCHAR;\r
7191       while (i > 0) {\r
7192         if (prev == '\r' && *p == '\n') {\r
7193           *(q-1) = '\n';\r
7194           is->count--;\r
7195         } else { \r
7196           *q++ = *p;\r
7197         }\r
7198         prev = *p++;\r
7199         i--;\r
7200       }\r
7201       *q = NULLCHAR;\r
7202       is->next = q;\r
7203     } else {\r
7204       if (is->error == ERROR_BROKEN_PIPE) {\r
7205         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7206         is->count = 0; \r
7207       } else {\r
7208         is->count = (DWORD) -1;\r
7209       }\r
7210     }\r
7211
7212     CheckForInputBufferFull( is );
7213
7214     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7215
7216     if( is->count == ((DWORD) -1) ) break; /* [AS] */
7217
7218     if (is->count < 0) break;  /* Quit on error */\r
7219   }\r
7220   CloseHandle(is->hFile);\r
7221   return 0;\r
7222 }\r
7223 \r
7224 DWORD\r
7225 SocketInputThread(LPVOID arg)\r
7226 {\r
7227   InputSource *is;\r
7228 \r
7229   is = (InputSource *) arg;\r
7230   while (is->hThread != NULL) {\r
7231     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7232     if ((int)is->count == SOCKET_ERROR) {\r
7233       is->count = (DWORD) -1;\r
7234       is->error = WSAGetLastError();\r
7235     } else {\r
7236       is->error = NO_ERROR;\r
7237       is->next += is->count;\r
7238       if (is->count == 0 && is->second == is) {\r
7239         /* End of file on stderr; quit with no message */\r
7240         break;\r
7241       }\r
7242     }\r
7243     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7244
7245     if( is->count == ((DWORD) -1) ) break; /* [AS] */
7246
7247     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7248   }\r
7249   return 0;\r
7250 }\r
7251 \r
7252 VOID\r
7253 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7254 {\r
7255   InputSource *is;\r
7256 \r
7257   is = (InputSource *) lParam;\r
7258   if (is->lineByLine) {\r
7259     /* Feed in lines one by one */\r
7260     char *p = is->buf;\r
7261     char *q = p;\r
7262     while (q < is->next) {\r
7263       if (*q++ == '\n') {\r
7264         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7265         p = q;\r
7266       }\r
7267     }\r
7268
7269     /* Move any partial line to the start of the buffer */\r
7270     q = is->buf;\r
7271     while (p < is->next) {\r
7272       *q++ = *p++;\r
7273     }\r
7274     is->next = q;\r
7275
7276     if (is->error != NO_ERROR || is->count == 0) {\r
7277       /* Notify backend of the error.  Note: If there was a partial\r
7278          line at the end, it is not flushed through. */\r
7279       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7280     }\r
7281   } else {\r
7282     /* Feed in the whole chunk of input at once */\r
7283     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7284     is->next = is->buf;\r
7285   }\r
7286 }\r
7287 \r
7288 /*---------------------------------------------------------------------------*\\r
7289  *\r
7290  *  Menu enables. Used when setting various modes.\r
7291  *\r
7292 \*---------------------------------------------------------------------------*/\r
7293 \r
7294 typedef struct {\r
7295   int item;\r
7296   int flags;\r
7297 } Enables;\r
7298 \r
7299 VOID\r
7300 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7301 {\r
7302   while (enab->item > 0) {\r
7303     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7304     enab++;\r
7305   }\r
7306 }\r
7307 \r
7308 Enables gnuEnables[] = {\r
7309   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7310   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7311   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7312   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7313   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7314   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7315   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7316   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7317   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7318   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7319   { -1, -1 }\r
7320 };\r
7321 \r
7322 Enables icsEnables[] = {\r
7323   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7324   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7325   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7326   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7327   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7328   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7329   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7330   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7331   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7332   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7333   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7334   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7335   { -1, -1 }\r
7336 };\r
7337 \r
7338 #ifdef ZIPPY\r
7339 Enables zippyEnables[] = {\r
7340   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7341   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7342   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7343   { -1, -1 }\r
7344 };\r
7345 #endif\r
7346 \r
7347 Enables ncpEnables[] = {\r
7348   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7349   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7350   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7351   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7352   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7353   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7354   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7355   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7356   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7357   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7358   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7359   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7360   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7361   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7362   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7363   { -1, -1 }\r
7364 };\r
7365 \r
7366 Enables trainingOnEnables[] = {\r
7367   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7368   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7369   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7370   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7371   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7372   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7373   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7374   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7375   { -1, -1 }\r
7376 };\r
7377 \r
7378 Enables trainingOffEnables[] = {\r
7379   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7380   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7381   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7382   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7383   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7384   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7385   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7386   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7387   { -1, -1 }\r
7388 };\r
7389 \r
7390 /* These modify either ncpEnables or gnuEnables */\r
7391 Enables cmailEnables[] = {\r
7392   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7393   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7394   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7395   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7396   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7397   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7398   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7399   { -1, -1 }\r
7400 };\r
7401 \r
7402 Enables machineThinkingEnables[] = {\r
7403   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7404   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7405   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7406   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7407   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7408   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7409   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7410   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7411   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7412   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7413   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7414   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7415   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7416   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7417   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7418   { -1, -1 }\r
7419 };\r
7420 \r
7421 Enables userThinkingEnables[] = {\r
7422   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7423   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7424   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7425   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7426   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7427   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7428   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7429   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7430   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7431   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7432   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7433   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7434   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7435   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7436   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7437   { -1, -1 }\r
7438 };\r
7439 \r
7440 /*---------------------------------------------------------------------------*\\r
7441  *\r
7442  *  Front-end interface functions exported by XBoard.\r
7443  *  Functions appear in same order as prototypes in frontend.h.\r
7444  * \r
7445 \*---------------------------------------------------------------------------*/\r
7446 VOID\r
7447 ModeHighlight()\r
7448 {\r
7449   static UINT prevChecked = 0;\r
7450   static int prevPausing = 0;\r
7451   UINT nowChecked;\r
7452 \r
7453   if (pausing != prevPausing) {\r
7454     prevPausing = pausing;\r
7455     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7456                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7457     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7458   }\r
7459 \r
7460   switch (gameMode) {\r
7461   case BeginningOfGame:\r
7462     if (appData.icsActive)\r
7463       nowChecked = IDM_IcsClient;\r
7464     else if (appData.noChessProgram)\r
7465       nowChecked = IDM_EditGame;\r
7466     else\r
7467       nowChecked = IDM_MachineBlack;\r
7468     break;\r
7469   case MachinePlaysBlack:\r
7470     nowChecked = IDM_MachineBlack;\r
7471     break;\r
7472   case MachinePlaysWhite:\r
7473     nowChecked = IDM_MachineWhite;\r
7474     break;\r
7475   case TwoMachinesPlay:\r
7476     nowChecked = IDM_TwoMachines;\r
7477     break;\r
7478   case AnalyzeMode:\r
7479     nowChecked = IDM_AnalysisMode;\r
7480     break;\r
7481   case AnalyzeFile:\r
7482     nowChecked = IDM_AnalyzeFile;\r
7483     break;\r
7484   case EditGame:\r
7485     nowChecked = IDM_EditGame;\r
7486     break;\r
7487   case PlayFromGameFile:\r
7488     nowChecked = IDM_LoadGame;\r
7489     break;\r
7490   case EditPosition:\r
7491     nowChecked = IDM_EditPosition;\r
7492     break;\r
7493   case Training:\r
7494     nowChecked = IDM_Training;\r
7495     break;\r
7496   case IcsPlayingWhite:\r
7497   case IcsPlayingBlack:\r
7498   case IcsObserving:\r
7499   case IcsIdle:\r
7500     nowChecked = IDM_IcsClient;\r
7501     break;\r
7502   default:\r
7503   case EndOfGame:\r
7504     nowChecked = 0;\r
7505     break;\r
7506   }\r
7507   if (prevChecked != 0)\r
7508     (void) CheckMenuItem(GetMenu(hwndMain),\r
7509                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7510   if (nowChecked != 0)\r
7511     (void) CheckMenuItem(GetMenu(hwndMain),\r
7512                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7513 \r
7514   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7515     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7516                           MF_BYCOMMAND|MF_ENABLED);\r
7517   } else {\r
7518     (void) EnableMenuItem(GetMenu(hwndMain), \r
7519                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7520   }\r
7521 \r
7522   prevChecked = nowChecked;\r
7523 }\r
7524 \r
7525 VOID\r
7526 SetICSMode()\r
7527 {\r
7528   HMENU hmenu = GetMenu(hwndMain);\r
7529   SetMenuEnables(hmenu, icsEnables);\r
7530   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7531     MF_BYPOSITION|MF_ENABLED);\r
7532 #ifdef ZIPPY\r
7533   if (appData.zippyPlay) {\r
7534     SetMenuEnables(hmenu, zippyEnables);\r
7535   }\r
7536 #endif\r
7537 }\r
7538 \r
7539 VOID\r
7540 SetGNUMode()\r
7541 {\r
7542   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7543 }\r
7544 \r
7545 VOID\r
7546 SetNCPMode()\r
7547 {\r
7548   HMENU hmenu = GetMenu(hwndMain);\r
7549   SetMenuEnables(hmenu, ncpEnables);\r
7550   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7551     MF_BYPOSITION|MF_GRAYED);\r
7552     DrawMenuBar(hwndMain);\r
7553 }\r
7554 \r
7555 VOID\r
7556 SetCmailMode()\r
7557 {\r
7558   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7559 }\r
7560 \r
7561 VOID \r
7562 SetTrainingModeOn()\r
7563 {\r
7564   int i;\r
7565   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7566   for (i = 0; i < N_BUTTONS; i++) {\r
7567     if (buttonDesc[i].hwnd != NULL)\r
7568       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7569   }\r
7570   CommentPopDown();\r
7571 }\r
7572 \r
7573 VOID SetTrainingModeOff()\r
7574 {\r
7575   int i;\r
7576   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7577   for (i = 0; i < N_BUTTONS; i++) {\r
7578     if (buttonDesc[i].hwnd != NULL)\r
7579       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7580   }\r
7581 }\r
7582 \r
7583 \r
7584 VOID\r
7585 SetUserThinkingEnables()\r
7586 {\r
7587   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7588 }\r
7589 \r
7590 VOID\r
7591 SetMachineThinkingEnables()\r
7592 {\r
7593   HMENU hMenu = GetMenu(hwndMain);\r
7594   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7595 \r
7596   SetMenuEnables(hMenu, machineThinkingEnables);\r
7597 \r
7598   if (gameMode == MachinePlaysBlack) {\r
7599     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7600   } else if (gameMode == MachinePlaysWhite) {\r
7601     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7602   } else if (gameMode == TwoMachinesPlay) {\r
7603     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
7604   }\r
7605 }\r
7606 \r
7607 \r
7608 VOID\r
7609 DisplayTitle(char *str)\r
7610 {\r
7611   char title[MSG_SIZ], *host;\r
7612   if (str[0] != NULLCHAR) {\r
7613     strcpy(title, str);\r
7614   } else if (appData.icsActive) {\r
7615     if (appData.icsCommPort[0] != NULLCHAR)\r
7616       host = "ICS";\r
7617     else \r
7618       host = appData.icsHost;\r
7619     sprintf(title, "%s: %s", szTitle, host);\r
7620   } else if (appData.noChessProgram) {\r
7621     strcpy(title, szTitle);\r
7622   } else {\r
7623     strcpy(title, szTitle);\r
7624     strcat(title, ": ");\r
7625     strcat(title, first.tidy);\r
7626   }\r
7627   SetWindowText(hwndMain, title);\r
7628 }\r
7629 \r
7630 \r
7631 VOID\r
7632 DisplayMessage(char *str1, char *str2)\r
7633 {\r
7634   HDC hdc;\r
7635   HFONT oldFont;\r
7636   int remain = MESSAGE_TEXT_MAX - 1;\r
7637   int len;\r
7638 \r
7639   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
7640   messageText[0] = NULLCHAR;\r
7641   if (*str1) {\r
7642     len = strlen(str1);\r
7643     if (len > remain) len = remain;\r
7644     strncpy(messageText, str1, len);\r
7645     messageText[len] = NULLCHAR;\r
7646     remain -= len;\r
7647   }\r
7648   if (*str2 && remain >= 2) {\r
7649     if (*str1) {\r
7650       strcat(messageText, "  ");\r
7651       remain -= 2;\r
7652     }\r
7653     len = strlen(str2);\r
7654     if (len > remain) len = remain;\r
7655     strncat(messageText, str2, len);\r
7656   }\r
7657   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
7658 \r
7659   if (IsIconic(hwndMain)) return;\r
7660   hdc = GetDC(hwndMain);\r
7661   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
7662   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
7663              &messageRect, messageText, strlen(messageText), NULL);\r
7664   (void) SelectObject(hdc, oldFont);\r
7665   (void) ReleaseDC(hwndMain, hdc);\r
7666 }\r
7667 \r
7668 VOID\r
7669 DisplayError(char *str, int error)\r
7670 {\r
7671   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
7672   int len;\r
7673 \r
7674   if (error == 0) {\r
7675     strcpy(buf, str);\r
7676   } else {\r
7677     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7678                         NULL, error, LANG_NEUTRAL,\r
7679                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7680     if (len > 0) {\r
7681       sprintf(buf, "%s:\n%s", str, buf2);\r
7682     } else {\r
7683       ErrorMap *em = errmap;\r
7684       while (em->err != 0 && em->err != error) em++;\r
7685       if (em->err != 0) {\r
7686         sprintf(buf, "%s:\n%s", str, em->msg);\r
7687       } else {\r
7688         sprintf(buf, "%s:\nError code %d", str, error);\r
7689       }\r
7690     }\r
7691   }\r
7692   \r
7693   ErrorPopUp("Error", buf);\r
7694 }\r
7695 \r
7696 \r
7697 VOID\r
7698 DisplayMoveError(char *str)\r
7699 {\r
7700   fromX = fromY = -1;\r
7701   ClearHighlights();\r
7702   DrawPosition(FALSE, NULL);\r
7703   if (appData.popupMoveErrors) {\r
7704     ErrorPopUp("Error", str);\r
7705   } else {\r
7706     DisplayMessage(str, "");\r
7707     moveErrorMessageUp = TRUE;\r
7708   }\r
7709 }\r
7710 \r
7711 VOID\r
7712 DisplayFatalError(char *str, int error, int exitStatus)\r
7713 {\r
7714   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
7715   int len;\r
7716   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
7717 \r
7718   if (error != 0) {\r
7719     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7720                         NULL, error, LANG_NEUTRAL,\r
7721                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7722     if (len > 0) {\r
7723       sprintf(buf, "%s:\n%s", str, buf2);\r
7724     } else {\r
7725       ErrorMap *em = errmap;\r
7726       while (em->err != 0 && em->err != error) em++;\r
7727       if (em->err != 0) {\r
7728         sprintf(buf, "%s:\n%s", str, em->msg);\r
7729       } else {\r
7730         sprintf(buf, "%s:\nError code %d", str, error);\r
7731       }\r
7732     }\r
7733     str = buf;\r
7734   }\r
7735   if (appData.debugMode) {\r
7736     fprintf(debugFP, "%s: %s\n", label, str);\r
7737   }\r
7738   if (appData.popupExitMessage) {\r
7739     (void) MessageBox(hwndMain, str, label, MB_OK|\r
7740                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
7741   }\r
7742   ExitEvent(exitStatus);\r
7743 }\r
7744 \r
7745 \r
7746 VOID\r
7747 DisplayInformation(char *str)\r
7748 {\r
7749   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
7750 }\r
7751 \r
7752 \r
7753 VOID\r
7754 DisplayNote(char *str)\r
7755 {\r
7756   ErrorPopUp("Note", str);\r
7757 }\r
7758 \r
7759 \r
7760 typedef struct {\r
7761   char *title, *question, *replyPrefix;\r
7762   ProcRef pr;\r
7763 } QuestionParams;\r
7764 \r
7765 LRESULT CALLBACK\r
7766 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7767 {\r
7768   static QuestionParams *qp;\r
7769   char reply[MSG_SIZ];\r
7770   int len, err;\r
7771 \r
7772   switch (message) {\r
7773   case WM_INITDIALOG:\r
7774     qp = (QuestionParams *) lParam;\r
7775     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
7776     SetWindowText(hDlg, qp->title);\r
7777     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
7778     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
7779     return FALSE;\r
7780 \r
7781   case WM_COMMAND:\r
7782     switch (LOWORD(wParam)) {\r
7783     case IDOK:\r
7784       strcpy(reply, qp->replyPrefix);\r
7785       if (*reply) strcat(reply, " ");\r
7786       len = strlen(reply);\r
7787       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
7788       strcat(reply, "\n");\r
7789       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
7790       EndDialog(hDlg, TRUE);\r
7791       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
7792       return TRUE;\r
7793     case IDCANCEL:\r
7794       EndDialog(hDlg, FALSE);\r
7795       return TRUE;\r
7796     default:\r
7797       break;\r
7798     }\r
7799     break;\r
7800   }\r
7801   return FALSE;\r
7802 }\r
7803 \r
7804 VOID\r
7805 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
7806 {\r
7807     QuestionParams qp;\r
7808     FARPROC lpProc;\r
7809     \r
7810     qp.title = title;\r
7811     qp.question = question;\r
7812     qp.replyPrefix = replyPrefix;\r
7813     qp.pr = pr;\r
7814     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
7815     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
7816       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
7817     FreeProcInstance(lpProc);\r
7818 }\r
7819 \r
7820 /* [AS] Pick FRC position */
7821 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
7822 {
7823     static int * lpIndexFRC;
7824     BOOL index_is_ok;
7825     char buf[16];
7826
7827     switch( message )
7828     {
7829     case WM_INITDIALOG:
7830         lpIndexFRC = (int *) lParam;
7831
7832         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
7833
7834         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
7835         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
7836         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
7837         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
7838
7839         break;
7840
7841     case WM_COMMAND:
7842         switch( LOWORD(wParam) ) {
7843         case IDOK:
7844             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
7845             EndDialog( hDlg, 0 );
7846             return TRUE;
7847         case IDCANCEL:
7848             EndDialog( hDlg, 1 );
7849             return TRUE;
7850         case IDC_NFG_Edit:
7851             if( HIWORD(wParam) == EN_CHANGE ) {
7852                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
7853
7854                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
7855             }
7856             return TRUE;
7857         case IDC_NFG_Random:
7858             sprintf( buf, "%d", myrandom() % 960 );
7859             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
7860             return TRUE;
7861         }
7862
7863         break;
7864     }
7865
7866     return FALSE;
7867 }
7868
7869 int NewGameFRC()
7870 {
7871     int result;
7872     int index = appData.defaultFrcPosition;
7873     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
7874
7875     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
7876
7877     if( result == 0 ) {
7878         appData.defaultFrcPosition = index;
7879     }
7880
7881     return result;
7882 }
7883
7884 /* [AS] Game list options */
7885 typedef struct {
7886     char id;
7887     char * name;
7888 } GLT_Item;
7889
7890 static GLT_Item GLT_ItemInfo[] = {
7891     { GLT_EVENT,      "Event" },
7892     { GLT_SITE,       "Site" },
7893     { GLT_DATE,       "Date" },
7894     { GLT_ROUND,      "Round" },
7895     { GLT_PLAYERS,    "Players" },
7896     { GLT_RESULT,     "Result" },
7897     { GLT_WHITE_ELO,  "White Rating" },
7898     { GLT_BLACK_ELO,  "Black Rating" },
7899     { GLT_TIME_CONTROL,"Time Control" },
7900     { GLT_VARIANT,    "Variant" },
7901     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },
7902     { 0, 0 }
7903 };
7904
7905 const char * GLT_FindItem( char id )
7906 {
7907     const char * result = 0;
7908
7909     GLT_Item * list = GLT_ItemInfo;
7910
7911     while( list->id != 0 ) {
7912         if( list->id == id ) {
7913             result = list->name;
7914             break;
7915         }
7916
7917         list++;
7918     }
7919
7920     return result;
7921 }
7922
7923 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )
7924 {
7925     const char * name = GLT_FindItem( id );
7926
7927     if( name != 0 ) {
7928         if( index >= 0 ) {
7929             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );
7930         }
7931         else {
7932             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );
7933         }
7934     }
7935 }
7936
7937 void GLT_TagsToList( HWND hDlg, char * tags )
7938 {
7939     char * pc = tags;
7940
7941     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
7942
7943     while( *pc ) {
7944         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
7945         pc++;
7946     }
7947
7948     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );
7949
7950     pc = GLT_ALL_TAGS;
7951
7952     while( *pc ) {
7953         if( strchr( tags, *pc ) == 0 ) {
7954             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
7955         }
7956         pc++;
7957     }
7958
7959     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
7960 }
7961
7962 char GLT_ListItemToTag( HWND hDlg, int index )
7963 {
7964     char result = '\0';
7965     char name[128];
7966
7967     GLT_Item * list = GLT_ItemInfo;
7968
7969     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {
7970         while( list->id != 0 ) {
7971             if( strcmp( list->name, name ) == 0 ) {
7972                 result = list->id;
7973                 break;
7974             }
7975
7976             list++;
7977         }
7978     }
7979
7980     return result;
7981 }
7982
7983 void GLT_MoveSelection( HWND hDlg, int delta )
7984 {
7985     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
7986     int idx2 = idx1 + delta;
7987     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
7988
7989     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
7990         char buf[128];
7991
7992         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
7993         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
7994         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
7995         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
7996     }
7997 }
7998
7999 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
8000 {
8001     static char glt[64];
8002     static char * lpUserGLT;
8003
8004     switch( message )
8005     {
8006     case WM_INITDIALOG:
8007         lpUserGLT = (char *) lParam;
8008
8009         strcpy( glt, lpUserGLT );
8010
8011         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
8012
8013         /* Initialize list */
8014         GLT_TagsToList( hDlg, glt );
8015
8016         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
8017
8018         break;
8019
8020     case WM_COMMAND:
8021         switch( LOWORD(wParam) ) {
8022         case IDOK:
8023             {
8024                 char * pc = lpUserGLT;
8025                 int idx = 0;
8026                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
8027                 char id;
8028
8029                 do {
8030                     id = GLT_ListItemToTag( hDlg, idx );
8031
8032                     *pc++ = id;
8033                     idx++;
8034                 } while( id != '\0' );
8035             }
8036             EndDialog( hDlg, 0 );
8037             return TRUE;
8038         case IDCANCEL:
8039             EndDialog( hDlg, 1 );
8040             return TRUE;
8041
8042         case IDC_GLT_Default:
8043             strcpy( glt, GLT_DEFAULT_TAGS );
8044             GLT_TagsToList( hDlg, glt );
8045             return TRUE;
8046
8047         case IDC_GLT_Restore:
8048             strcpy( glt, lpUserGLT );
8049             GLT_TagsToList( hDlg, glt );
8050             return TRUE;
8051
8052         case IDC_GLT_Up:
8053             GLT_MoveSelection( hDlg, -1 );
8054             return TRUE;
8055
8056         case IDC_GLT_Down:
8057             GLT_MoveSelection( hDlg, +1 );
8058             return TRUE;
8059         }
8060
8061         break;
8062     }
8063
8064     return FALSE;
8065 }
8066
8067 int GameListOptions()
8068 {
8069     char glt[64];
8070     int result;
8071     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
8072
8073     strcpy( glt, appData.gameListTags );
8074
8075     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );
8076
8077     if( result == 0 ) {
8078         /* [AS] Memory leak here! */
8079         appData.gameListTags = strdup( glt );
8080     }
8081
8082     return result;
8083 }
8084
8085 \r
8086 VOID\r
8087 DisplayIcsInteractionTitle(char *str)\r
8088 {\r
8089   char consoleTitle[MSG_SIZ];\r
8090 \r
8091   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8092   SetWindowText(hwndConsole, consoleTitle);\r
8093 }\r
8094 \r
8095 void\r
8096 DrawPosition(int fullRedraw, Board board)\r
8097 {\r
8098   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8099 }\r
8100 \r
8101 \r
8102 VOID\r
8103 ResetFrontEnd()\r
8104 {\r
8105   fromX = fromY = -1;\r
8106   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8107     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8108     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8109     dragInfo.lastpos = dragInfo.pos;\r
8110     dragInfo.start.x = dragInfo.start.y = -1;\r
8111     dragInfo.from = dragInfo.start;\r
8112     ReleaseCapture();\r
8113     DrawPosition(TRUE, NULL);\r
8114   }\r
8115 }\r
8116 \r
8117 \r
8118 VOID\r
8119 CommentPopUp(char *title, char *str)\r
8120 {\r
8121   HWND hwnd = GetActiveWindow();\r
8122   EitherCommentPopUp(0, title, str, FALSE);\r
8123   SetActiveWindow(hwnd);\r
8124 }\r
8125 \r
8126 VOID\r
8127 CommentPopDown(void)\r
8128 {\r
8129   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8130   if (commentDialog) {\r
8131     ShowWindow(commentDialog, SW_HIDE);\r
8132   }\r
8133   commentDialogUp = FALSE;\r
8134 }\r
8135 \r
8136 VOID\r
8137 EditCommentPopUp(int index, char *title, char *str)\r
8138 {\r
8139   EitherCommentPopUp(index, title, str, TRUE);\r
8140 }\r
8141 \r
8142 \r
8143 VOID\r
8144 RingBell()\r
8145 {\r
8146   MyPlaySound(&sounds[(int)SoundMove]);\r
8147 }\r
8148 \r
8149 VOID PlayIcsWinSound()\r
8150 {\r
8151   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8152 }\r
8153 \r
8154 VOID PlayIcsLossSound()\r
8155 {\r
8156   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8157 }\r
8158 \r
8159 VOID PlayIcsDrawSound()\r
8160 {\r
8161   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8162 }\r
8163 \r
8164 VOID PlayIcsUnfinishedSound()\r
8165 {\r
8166   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8167 }\r
8168 \r
8169 VOID\r
8170 PlayAlarmSound()\r
8171 {\r
8172   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8173 }\r
8174 \r
8175 \r
8176 VOID\r
8177 EchoOn()\r
8178 {\r
8179   HWND hInput;\r
8180   consoleEcho = TRUE;\r
8181   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8182   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8183   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8184 }\r
8185 \r
8186 \r
8187 VOID\r
8188 EchoOff()\r
8189 {\r
8190   CHARFORMAT cf;\r
8191   HWND hInput;\r
8192   consoleEcho = FALSE;\r
8193   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8194   /* This works OK: set text and background both to the same color */\r
8195   cf = consoleCF;\r
8196   cf.crTextColor = COLOR_ECHOOFF;\r
8197   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8198   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8199 }\r
8200 \r
8201 /* No Raw()...? */\r
8202 \r
8203 void Colorize(ColorClass cc, int continuation)\r
8204 {\r
8205   currentColorClass = cc;\r
8206   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8207   consoleCF.crTextColor = textAttribs[cc].color;\r
8208   consoleCF.dwEffects = textAttribs[cc].effects;\r
8209   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8210 }\r
8211 \r
8212 char *\r
8213 UserName()\r
8214 {\r
8215   static char buf[MSG_SIZ];\r
8216   DWORD bufsiz = MSG_SIZ;\r
8217 \r
8218   if (!GetUserName(buf, &bufsiz)) {\r
8219     /*DisplayError("Error getting user name", GetLastError());*/\r
8220     strcpy(buf, "User");\r
8221   }\r
8222   return buf;\r
8223 }\r
8224 \r
8225 char *\r
8226 HostName()\r
8227 {\r
8228   static char buf[MSG_SIZ];\r
8229   DWORD bufsiz = MSG_SIZ;\r
8230 \r
8231   if (!GetComputerName(buf, &bufsiz)) {\r
8232     /*DisplayError("Error getting host name", GetLastError());*/\r
8233     strcpy(buf, "Unknown");\r
8234   }\r
8235   return buf;\r
8236 }\r
8237 \r
8238 \r
8239 int\r
8240 ClockTimerRunning()\r
8241 {\r
8242   return clockTimerEvent != 0;\r
8243 }\r
8244 \r
8245 int\r
8246 StopClockTimer()\r
8247 {\r
8248   if (clockTimerEvent == 0) return FALSE;\r
8249   KillTimer(hwndMain, clockTimerEvent);\r
8250   clockTimerEvent = 0;\r
8251   return TRUE;\r
8252 }\r
8253 \r
8254 void\r
8255 StartClockTimer(long millisec)\r
8256 {\r
8257   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8258                              (UINT) millisec, NULL);\r
8259 }\r
8260 \r
8261 void\r
8262 DisplayWhiteClock(long timeRemaining, int highlight)\r
8263 {\r
8264   HDC hdc;\r
8265   hdc = GetDC(hwndMain);\r
8266   if (!IsIconic(hwndMain)) {\r
8267     DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");\r
8268   }\r
8269   if (highlight && iconCurrent == iconBlack) {\r
8270     iconCurrent = iconWhite;\r
8271     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8272     if (IsIconic(hwndMain)) {\r
8273       DrawIcon(hdc, 2, 2, iconCurrent);\r
8274     }\r
8275   }\r
8276   (void) ReleaseDC(hwndMain, hdc);\r
8277   if (hwndConsole)\r
8278     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8279 }\r
8280 \r
8281 void\r
8282 DisplayBlackClock(long timeRemaining, int highlight)\r
8283 {\r
8284   HDC hdc;\r
8285   hdc = GetDC(hwndMain);\r
8286   if (!IsIconic(hwndMain)) {\r
8287     DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");\r
8288   }\r
8289   if (highlight && iconCurrent == iconWhite) {\r
8290     iconCurrent = iconBlack;\r
8291     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8292     if (IsIconic(hwndMain)) {\r
8293       DrawIcon(hdc, 2, 2, iconCurrent);\r
8294     }\r
8295   }\r
8296   (void) ReleaseDC(hwndMain, hdc);\r
8297   if (hwndConsole)\r
8298     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8299 }\r
8300 \r
8301 \r
8302 int\r
8303 LoadGameTimerRunning()\r
8304 {\r
8305   return loadGameTimerEvent != 0;\r
8306 }\r
8307 \r
8308 int\r
8309 StopLoadGameTimer()\r
8310 {\r
8311   if (loadGameTimerEvent == 0) return FALSE;\r
8312   KillTimer(hwndMain, loadGameTimerEvent);\r
8313   loadGameTimerEvent = 0;\r
8314   return TRUE;\r
8315 }\r
8316 \r
8317 void\r
8318 StartLoadGameTimer(long millisec)\r
8319 {\r
8320   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8321                                 (UINT) millisec, NULL);\r
8322 }\r
8323 \r
8324 void\r
8325 AutoSaveGame()\r
8326 {\r
8327   char *defName;\r
8328   FILE *f;\r
8329   char fileTitle[MSG_SIZ];\r
8330 \r
8331   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8332   f = OpenFileDialog(hwndMain, TRUE, defName,\r
8333                      appData.oldSaveStyle ? "gam" : "pgn",\r
8334                      GAME_FILT, \r
8335                      "Save Game to File", NULL, fileTitle, NULL);\r
8336   if (f != NULL) {\r
8337     SaveGame(f, 0, "");\r
8338     fclose(f);\r
8339   }\r
8340 }\r
8341 \r
8342 \r
8343 void\r
8344 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8345 {\r
8346   if (delayedTimerEvent != 0) {\r
8347     if (appData.debugMode) {\r
8348       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8349     }\r
8350     KillTimer(hwndMain, delayedTimerEvent);\r
8351     delayedTimerEvent = 0;\r
8352     delayedTimerCallback();\r
8353   }\r
8354   delayedTimerCallback = cb;\r
8355   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8356                                 (UINT) millisec, NULL);\r
8357 }\r
8358 \r
8359 DelayedEventCallback\r
8360 GetDelayedEvent()\r
8361 {\r
8362   if (delayedTimerEvent) {\r
8363     return delayedTimerCallback;\r
8364   } else {\r
8365     return NULL;\r
8366   }\r
8367 }\r
8368 \r
8369 void\r
8370 CancelDelayedEvent()\r
8371 {\r
8372   if (delayedTimerEvent) {\r
8373     KillTimer(hwndMain, delayedTimerEvent);\r
8374     delayedTimerEvent = 0;\r
8375   }\r
8376 }\r
8377 \r
8378 /* Start a child process running the given program.\r
8379    The process's standard output can be read from "from", and its\r
8380    standard input can be written to "to".\r
8381    Exit with fatal error if anything goes wrong.\r
8382    Returns an opaque pointer that can be used to destroy the process\r
8383    later.\r
8384 */\r
8385 int\r
8386 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8387 {\r
8388 #define BUFSIZE 4096\r
8389 \r
8390   HANDLE hChildStdinRd, hChildStdinWr,\r
8391     hChildStdoutRd, hChildStdoutWr;\r
8392   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8393   SECURITY_ATTRIBUTES saAttr;\r
8394   BOOL fSuccess;\r
8395   PROCESS_INFORMATION piProcInfo;\r
8396   STARTUPINFO siStartInfo;\r
8397   ChildProc *cp;\r
8398   char buf[MSG_SIZ];\r
8399   DWORD err;\r
8400 \r
8401   if (appData.debugMode) {\r
8402     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8403   }\r
8404 \r
8405   *pr = NoProc;\r
8406 \r
8407   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8408   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8409   saAttr.bInheritHandle = TRUE;\r
8410   saAttr.lpSecurityDescriptor = NULL;\r
8411 \r
8412   /*\r
8413    * The steps for redirecting child's STDOUT:\r
8414    *     1. Create anonymous pipe to be STDOUT for child.\r
8415    *     2. Create a noninheritable duplicate of read handle,\r
8416    *         and close the inheritable read handle.\r
8417    */\r
8418 \r
8419   /* Create a pipe for the child's STDOUT. */\r
8420   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8421     return GetLastError();\r
8422   }\r
8423 \r
8424   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8425   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8426                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8427                              FALSE,     /* not inherited */\r
8428                              DUPLICATE_SAME_ACCESS);\r
8429   if (! fSuccess) {\r
8430     return GetLastError();\r
8431   }\r
8432   CloseHandle(hChildStdoutRd);\r
8433 \r
8434   /*\r
8435    * The steps for redirecting child's STDIN:\r
8436    *     1. Create anonymous pipe to be STDIN for child.\r
8437    *     2. Create a noninheritable duplicate of write handle,\r
8438    *         and close the inheritable write handle.\r
8439    */\r
8440 \r
8441   /* Create a pipe for the child's STDIN. */\r
8442   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8443     return GetLastError();\r
8444   }\r
8445 \r
8446   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8447   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8448                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8449                              FALSE,     /* not inherited */\r
8450                              DUPLICATE_SAME_ACCESS);\r
8451   if (! fSuccess) {\r
8452     return GetLastError();\r
8453   }\r
8454   CloseHandle(hChildStdinWr);\r
8455 \r
8456   /* Arrange to (1) look in dir for the child .exe file, and\r
8457    * (2) have dir be the child's working directory.  Interpret\r
8458    * dir relative to the directory WinBoard loaded from. */\r
8459   GetCurrentDirectory(MSG_SIZ, buf);\r
8460   SetCurrentDirectory(installDir);\r
8461   SetCurrentDirectory(dir);\r
8462 \r
8463   /* Now create the child process. */\r
8464 \r
8465   siStartInfo.cb = sizeof(STARTUPINFO);\r
8466   siStartInfo.lpReserved = NULL;\r
8467   siStartInfo.lpDesktop = NULL;\r
8468   siStartInfo.lpTitle = NULL;\r
8469   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8470   siStartInfo.cbReserved2 = 0;\r
8471   siStartInfo.lpReserved2 = NULL;\r
8472   siStartInfo.hStdInput = hChildStdinRd;\r
8473   siStartInfo.hStdOutput = hChildStdoutWr;\r
8474   siStartInfo.hStdError = hChildStdoutWr;\r
8475 \r
8476   fSuccess = CreateProcess(NULL,\r
8477                            cmdLine,        /* command line */\r
8478                            NULL,           /* process security attributes */\r
8479                            NULL,           /* primary thread security attrs */\r
8480                            TRUE,           /* handles are inherited */\r
8481                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8482                            NULL,           /* use parent's environment */\r
8483                            NULL,\r
8484                            &siStartInfo, /* STARTUPINFO pointer */\r
8485                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8486 \r
8487   err = GetLastError();\r
8488   SetCurrentDirectory(buf); /* return to prev directory */\r
8489   if (! fSuccess) {\r
8490     return err;\r
8491   }\r
8492 \r
8493   /* Close the handles we don't need in the parent */\r
8494   CloseHandle(piProcInfo.hThread);\r
8495   CloseHandle(hChildStdinRd);\r
8496   CloseHandle(hChildStdoutWr);\r
8497 \r
8498   /* Prepare return value */\r
8499   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8500   cp->kind = CPReal;\r
8501   cp->hProcess = piProcInfo.hProcess;\r
8502   cp->pid = piProcInfo.dwProcessId;\r
8503   cp->hFrom = hChildStdoutRdDup;\r
8504   cp->hTo = hChildStdinWrDup;\r
8505 \r
8506   *pr = (void *) cp;\r
8507 \r
8508   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8509      2000 where engines sometimes don't see the initial command(s)\r
8510      from WinBoard and hang.  I don't understand how that can happen,\r
8511      but the Sleep is harmless, so I've put it in.  Others have also\r
8512      reported what may be the same problem, so hopefully this will fix\r
8513      it for them too.  */\r
8514   Sleep(500);\r
8515 \r
8516   return NO_ERROR;\r
8517 }\r
8518 \r
8519 \r
8520 void\r
8521 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8522 {\r
8523   ChildProc *cp;\r
8524 \r
8525   cp = (ChildProc *) pr;\r
8526   if (cp == NULL) return;\r
8527 \r
8528   switch (cp->kind) {\r
8529   case CPReal:\r
8530     /* TerminateProcess is considered harmful, so... */\r
8531     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8532     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8533     /* The following doesn't work because the chess program\r
8534        doesn't "have the same console" as WinBoard.  Maybe\r
8535        we could arrange for this even though neither WinBoard\r
8536        nor the chess program uses a console for stdio? */\r
8537     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8538
8539     /* [AS] Special termination modes for misbehaving programs... */
8540     if( signal == 9 ) {
8541         if ( appData.debugMode) {
8542             fprintf( debugFP, "Terminating process %u\n", cp->pid );
8543         }
8544
8545         TerminateProcess( cp->hProcess, 0 );
8546     }
8547     else if( signal == 10 ) {
8548         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
8549
8550         if( dw != WAIT_OBJECT_0 ) {
8551             if ( appData.debugMode) {
8552                 fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );
8553             }
8554
8555             TerminateProcess( cp->hProcess, 0 );
8556         }
8557     }
8558
8559     CloseHandle(cp->hProcess);\r
8560     break;\r
8561 \r
8562   case CPComm:\r
8563     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8564     break;\r
8565 \r
8566   case CPSock:\r
8567     closesocket(cp->sock);\r
8568     WSACleanup();\r
8569     break;\r
8570 \r
8571   case CPRcmd:\r
8572     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8573     closesocket(cp->sock);\r
8574     closesocket(cp->sock2);\r
8575     WSACleanup();\r
8576     break;\r
8577   }\r
8578   free(cp);\r
8579 }\r
8580 \r
8581 void\r
8582 InterruptChildProcess(ProcRef pr)\r
8583 {\r
8584   ChildProc *cp;\r
8585 \r
8586   cp = (ChildProc *) pr;\r
8587   if (cp == NULL) return;\r
8588   switch (cp->kind) {\r
8589   case CPReal:\r
8590     /* The following doesn't work because the chess program\r
8591        doesn't "have the same console" as WinBoard.  Maybe\r
8592        we could arrange for this even though neither WinBoard\r
8593        nor the chess program uses a console for stdio */\r
8594     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8595     break;\r
8596 \r
8597   case CPComm:\r
8598   case CPSock:\r
8599     /* Can't interrupt */\r
8600     break;\r
8601 \r
8602   case CPRcmd:\r
8603     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8604     break;\r
8605   }\r
8606 }\r
8607 \r
8608 \r
8609 int\r
8610 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8611 {\r
8612   char cmdLine[MSG_SIZ];\r
8613 \r
8614   if (port[0] == NULLCHAR) {\r
8615     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8616   } else {\r
8617     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8618   }\r
8619   return StartChildProcess(cmdLine, "", pr);\r
8620 }\r
8621 \r
8622 \r
8623 /* Code to open TCP sockets */\r
8624 \r
8625 int\r
8626 OpenTCP(char *host, char *port, ProcRef *pr)\r
8627 {\r
8628   ChildProc *cp;\r
8629   int err;\r
8630   SOCKET s;\r
8631   struct sockaddr_in sa, mysa;\r
8632   struct hostent FAR *hp;\r
8633   unsigned short uport;\r
8634   WORD wVersionRequested;\r
8635   WSADATA wsaData;\r
8636 \r
8637   /* Initialize socket DLL */\r
8638   wVersionRequested = MAKEWORD(1, 1);\r
8639   err = WSAStartup(wVersionRequested, &wsaData);\r
8640   if (err != 0) return err;\r
8641 \r
8642   /* Make socket */\r
8643   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8644     err = WSAGetLastError();\r
8645     WSACleanup();\r
8646     return err;\r
8647   }\r
8648 \r
8649   /* Bind local address using (mostly) don't-care values.\r
8650    */\r
8651   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8652   mysa.sin_family = AF_INET;\r
8653   mysa.sin_addr.s_addr = INADDR_ANY;\r
8654   uport = (unsigned short) 0;\r
8655   mysa.sin_port = htons(uport);\r
8656   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8657       == SOCKET_ERROR) {\r
8658     err = WSAGetLastError();\r
8659     WSACleanup();\r
8660     return err;\r
8661   }\r
8662 \r
8663   /* Resolve remote host name */\r
8664   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8665   if (!(hp = gethostbyname(host))) {\r
8666     unsigned int b0, b1, b2, b3;\r
8667 \r
8668     err = WSAGetLastError();\r
8669 \r
8670     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8671       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8672       hp->h_addrtype = AF_INET;\r
8673       hp->h_length = 4;\r
8674       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8675       hp->h_addr_list[0] = (char *) malloc(4);\r
8676       hp->h_addr_list[0][0] = (char) b0;\r
8677       hp->h_addr_list[0][1] = (char) b1;\r
8678       hp->h_addr_list[0][2] = (char) b2;\r
8679       hp->h_addr_list[0][3] = (char) b3;\r
8680     } else {\r
8681       WSACleanup();\r
8682       return err;\r
8683     }\r
8684   }\r
8685   sa.sin_family = hp->h_addrtype;\r
8686   uport = (unsigned short) atoi(port);\r
8687   sa.sin_port = htons(uport);\r
8688   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8689 \r
8690   /* Make connection */\r
8691   if (connect(s, (struct sockaddr *) &sa,\r
8692               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8693     err = WSAGetLastError();\r
8694     WSACleanup();\r
8695     return err;\r
8696   }\r
8697 \r
8698   /* Prepare return value */\r
8699   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8700   cp->kind = CPSock;\r
8701   cp->sock = s;\r
8702   *pr = (ProcRef *) cp;\r
8703 \r
8704   return NO_ERROR;\r
8705 }\r
8706 \r
8707 int\r
8708 OpenCommPort(char *name, ProcRef *pr)\r
8709 {\r
8710   HANDLE h;\r
8711   COMMTIMEOUTS ct;\r
8712   ChildProc *cp;\r
8713   char fullname[MSG_SIZ];\r
8714 \r
8715   if (*name != '\\')\r
8716     sprintf(fullname, "\\\\.\\%s", name);\r
8717   else\r
8718     strcpy(fullname, name);\r
8719 \r
8720   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
8721                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
8722   if (h == (HANDLE) -1) {\r
8723     return GetLastError();\r
8724   }\r
8725   hCommPort = h;\r
8726 \r
8727   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
8728 \r
8729   /* Accumulate characters until a 100ms pause, then parse */\r
8730   ct.ReadIntervalTimeout = 100;\r
8731   ct.ReadTotalTimeoutMultiplier = 0;\r
8732   ct.ReadTotalTimeoutConstant = 0;\r
8733   ct.WriteTotalTimeoutMultiplier = 0;\r
8734   ct.WriteTotalTimeoutConstant = 0;\r
8735   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
8736 \r
8737   /* Prepare return value */\r
8738   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8739   cp->kind = CPComm;\r
8740   cp->hFrom = h;\r
8741   cp->hTo = h;\r
8742   *pr = (ProcRef *) cp;\r
8743 \r
8744   return NO_ERROR;\r
8745 }\r
8746 \r
8747 int\r
8748 OpenLoopback(ProcRef *pr)\r
8749 {\r
8750   DisplayFatalError("Not implemented", 0, 1);\r
8751   return NO_ERROR;\r
8752 }\r
8753 \r
8754 \r
8755 int\r
8756 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
8757 {\r
8758   ChildProc *cp;\r
8759   int err;\r
8760   SOCKET s, s2, s3;\r
8761   struct sockaddr_in sa, mysa;\r
8762   struct hostent FAR *hp;\r
8763   unsigned short uport;\r
8764   WORD wVersionRequested;\r
8765   WSADATA wsaData;\r
8766   int fromPort;\r
8767   char stderrPortStr[MSG_SIZ];\r
8768 \r
8769   /* Initialize socket DLL */\r
8770   wVersionRequested = MAKEWORD(1, 1);\r
8771   err = WSAStartup(wVersionRequested, &wsaData);\r
8772   if (err != 0) return err;\r
8773 \r
8774   /* Resolve remote host name */\r
8775   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8776   if (!(hp = gethostbyname(host))) {\r
8777     unsigned int b0, b1, b2, b3;\r
8778 \r
8779     err = WSAGetLastError();\r
8780 \r
8781     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8782       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8783       hp->h_addrtype = AF_INET;\r
8784       hp->h_length = 4;\r
8785       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8786       hp->h_addr_list[0] = (char *) malloc(4);\r
8787       hp->h_addr_list[0][0] = (char) b0;\r
8788       hp->h_addr_list[0][1] = (char) b1;\r
8789       hp->h_addr_list[0][2] = (char) b2;\r
8790       hp->h_addr_list[0][3] = (char) b3;\r
8791     } else {\r
8792       WSACleanup();\r
8793       return err;\r
8794     }\r
8795   }\r
8796   sa.sin_family = hp->h_addrtype;\r
8797   uport = (unsigned short) 514;\r
8798   sa.sin_port = htons(uport);\r
8799   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8800 \r
8801   /* Bind local socket to unused "privileged" port address\r
8802    */\r
8803   s = INVALID_SOCKET;\r
8804   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8805   mysa.sin_family = AF_INET;\r
8806   mysa.sin_addr.s_addr = INADDR_ANY;\r
8807   for (fromPort = 1023;; fromPort--) {\r
8808     if (fromPort < 0) {\r
8809       WSACleanup();\r
8810       return WSAEADDRINUSE;\r
8811     }\r
8812     if (s == INVALID_SOCKET) {\r
8813       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8814         err = WSAGetLastError();\r
8815         WSACleanup();\r
8816         return err;\r
8817       }\r
8818     }\r
8819     uport = (unsigned short) fromPort;\r
8820     mysa.sin_port = htons(uport);\r
8821     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8822         == SOCKET_ERROR) {\r
8823       err = WSAGetLastError();\r
8824       if (err == WSAEADDRINUSE) continue;\r
8825       WSACleanup();\r
8826       return err;\r
8827     }\r
8828     if (connect(s, (struct sockaddr *) &sa,\r
8829       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8830       err = WSAGetLastError();\r
8831       if (err == WSAEADDRINUSE) {\r
8832         closesocket(s);\r
8833         s = -1;\r
8834         continue;\r
8835       }\r
8836       WSACleanup();\r
8837       return err;\r
8838     }\r
8839     break;\r
8840   }\r
8841 \r
8842   /* Bind stderr local socket to unused "privileged" port address\r
8843    */\r
8844   s2 = INVALID_SOCKET;\r
8845   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8846   mysa.sin_family = AF_INET;\r
8847   mysa.sin_addr.s_addr = INADDR_ANY;\r
8848   for (fromPort = 1023;; fromPort--) {\r
8849     if (fromPort == prevStderrPort) continue; // don't reuse port\r
8850     if (fromPort < 0) {\r
8851       (void) closesocket(s);\r
8852       WSACleanup();\r
8853       return WSAEADDRINUSE;\r
8854     }\r
8855     if (s2 == INVALID_SOCKET) {\r
8856       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8857         err = WSAGetLastError();\r
8858         closesocket(s);\r
8859         WSACleanup();\r
8860         return err;\r
8861       }\r
8862     }\r
8863     uport = (unsigned short) fromPort;\r
8864     mysa.sin_port = htons(uport);\r
8865     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8866         == SOCKET_ERROR) {\r
8867       err = WSAGetLastError();\r
8868       if (err == WSAEADDRINUSE) continue;\r
8869       (void) closesocket(s);\r
8870       WSACleanup();\r
8871       return err;\r
8872     }\r
8873     if (listen(s2, 1) == SOCKET_ERROR) {\r
8874       err = WSAGetLastError();\r
8875       if (err == WSAEADDRINUSE) {\r
8876         closesocket(s2);\r
8877         s2 = INVALID_SOCKET;\r
8878         continue;\r
8879       }\r
8880       (void) closesocket(s);\r
8881       (void) closesocket(s2);\r
8882       WSACleanup();\r
8883       return err;\r
8884     }\r
8885     break;\r
8886   }\r
8887   prevStderrPort = fromPort; // remember port used\r
8888   sprintf(stderrPortStr, "%d", fromPort);\r
8889 \r
8890   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
8891     err = WSAGetLastError();\r
8892     (void) closesocket(s);\r
8893     (void) closesocket(s2);\r
8894     WSACleanup();\r
8895     return err;\r
8896   }\r
8897 \r
8898   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
8899     err = WSAGetLastError();\r
8900     (void) closesocket(s);\r
8901     (void) closesocket(s2);\r
8902     WSACleanup();\r
8903     return err;\r
8904   }\r
8905   if (*user == NULLCHAR) user = UserName();\r
8906   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
8907     err = WSAGetLastError();\r
8908     (void) closesocket(s);\r
8909     (void) closesocket(s2);\r
8910     WSACleanup();\r
8911     return err;\r
8912   }\r
8913   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
8914     err = WSAGetLastError();\r
8915     (void) closesocket(s);\r
8916     (void) closesocket(s2);\r
8917     WSACleanup();\r
8918     return err;\r
8919   }\r
8920 \r
8921   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
8922     err = WSAGetLastError();\r
8923     (void) closesocket(s);\r
8924     (void) closesocket(s2);\r
8925     WSACleanup();\r
8926     return err;\r
8927   }\r
8928   (void) closesocket(s2);  /* Stop listening */\r
8929 \r
8930   /* Prepare return value */\r
8931   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8932   cp->kind = CPRcmd;\r
8933   cp->sock = s;\r
8934   cp->sock2 = s3;\r
8935   *pr = (ProcRef *) cp;\r
8936 \r
8937   return NO_ERROR;\r
8938 }\r
8939 \r
8940 \r
8941 InputSourceRef\r
8942 AddInputSource(ProcRef pr, int lineByLine,\r
8943                InputCallback func, VOIDSTAR closure)\r
8944 {\r
8945   InputSource *is, *is2 = NULL;
8946   ChildProc *cp = (ChildProc *) pr;\r
8947 \r
8948   is = (InputSource *) calloc(1, sizeof(InputSource));\r
8949   is->lineByLine = lineByLine;\r
8950   is->func = func;\r
8951   is->closure = closure;\r
8952   is->second = NULL;\r
8953   is->next = is->buf;\r
8954   if (pr == NoProc) {\r
8955     is->kind = CPReal;\r
8956     consoleInputSource = is;\r
8957   } else {\r
8958     is->kind = cp->kind;\r
8959     /*
8960         [AS] Try to avoid a race condition if the thread is given control too early:
8961         we create all threads suspended so that the is->hThread variable can be
8962         safely assigned, then let the threads start with ResumeThread.
8963     */
8964     switch (cp->kind) {\r
8965     case CPReal:\r
8966       is->hFile = cp->hFrom;\r
8967       cp->hFrom = NULL; /* now owned by InputThread */\r
8968       is->hThread =\r
8969         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
8970                      (LPVOID) is, CREATE_SUSPENDED, &is->id);
8971       break;\r
8972 \r
8973     case CPComm:\r
8974       is->hFile = cp->hFrom;\r
8975       cp->hFrom = NULL; /* now owned by InputThread */\r
8976       is->hThread =\r
8977         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
8978                      (LPVOID) is, CREATE_SUSPENDED, &is->id);
8979       break;\r
8980 \r
8981     case CPSock:\r
8982       is->sock = cp->sock;\r
8983       is->hThread =\r
8984         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
8985                      (LPVOID) is, CREATE_SUSPENDED, &is->id);
8986       break;\r
8987 \r
8988     case CPRcmd:\r
8989       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
8990       *is2 = *is;\r
8991       is->sock = cp->sock;\r
8992       is->second = is2;\r
8993       is2->sock = cp->sock2;\r
8994       is2->second = is2;\r
8995       is->hThread =\r
8996         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
8997                      (LPVOID) is, CREATE_SUSPENDED, &is->id);
8998       is2->hThread =\r
8999         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9000                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
9001       break;\r
9002     }\r
9003
9004     if( is->hThread != NULL ) {
9005         ResumeThread( is->hThread );
9006   }\r
9007
9008     if( is2 != NULL && is2->hThread != NULL ) {
9009         ResumeThread( is2->hThread );
9010     }
9011   }
9012
9013   return (InputSourceRef) is;\r
9014 }\r
9015 \r
9016 void\r
9017 RemoveInputSource(InputSourceRef isr)\r
9018 {\r
9019   InputSource *is;\r
9020 \r
9021   is = (InputSource *) isr;\r
9022   is->hThread = NULL;  /* tell thread to stop */\r
9023   CloseHandle(is->hThread);\r
9024   if (is->second != NULL) {\r
9025     is->second->hThread = NULL;\r
9026     CloseHandle(is->second->hThread);\r
9027   }\r
9028 }\r
9029 \r
9030 \r
9031 int\r
9032 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9033 {\r
9034   DWORD dOutCount;\r
9035   int outCount = SOCKET_ERROR;\r
9036   ChildProc *cp = (ChildProc *) pr;\r
9037   static OVERLAPPED ovl;\r
9038 \r
9039   if (pr == NoProc) {\r
9040     ConsoleOutput(message, count, FALSE);\r
9041     return count;\r
9042   } \r
9043 \r
9044   if (ovl.hEvent == NULL) {\r
9045     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9046   }\r
9047   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9048 \r
9049   switch (cp->kind) {\r
9050   case CPSock:\r
9051   case CPRcmd:\r
9052     outCount = send(cp->sock, message, count, 0);\r
9053     if (outCount == SOCKET_ERROR) {\r
9054       *outError = WSAGetLastError();\r
9055     } else {\r
9056       *outError = NO_ERROR;\r
9057     }\r
9058     break;\r
9059 \r
9060   case CPReal:\r
9061     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9062                   &dOutCount, NULL)) {\r
9063       *outError = NO_ERROR;\r
9064       outCount = (int) dOutCount;\r
9065     } else {\r
9066       *outError = GetLastError();\r
9067     }\r
9068     break;\r
9069 \r
9070   case CPComm:\r
9071     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9072                             &dOutCount, &ovl);\r
9073     if (*outError == NO_ERROR) {\r
9074       outCount = (int) dOutCount;\r
9075     }\r
9076     break;\r
9077   }\r
9078   return outCount;\r
9079 }\r
9080 \r
9081 int\r
9082 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9083                        long msdelay)\r
9084 {\r
9085   /* Ignore delay, not implemented for WinBoard */\r
9086   return OutputToProcess(pr, message, count, outError);\r
9087 }\r
9088 \r
9089 \r
9090 void\r
9091 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9092                         char *buf, int count, int error)\r
9093 {\r
9094   DisplayFatalError("Not implemented", 0, 1);\r
9095 }\r
9096 \r
9097 /* see wgamelist.c for Game List functions */\r
9098 /* see wedittags.c for Edit Tags functions */\r
9099 \r
9100 \r
9101 VOID\r
9102 ICSInitScript()\r
9103 {\r
9104   FILE *f;\r
9105   char buf[MSG_SIZ];\r
9106   char *dummy;\r
9107 \r
9108   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9109     f = fopen(buf, "r");\r
9110     if (f != NULL) {\r
9111       ProcessICSInitScript(f);\r
9112       fclose(f);\r
9113     }\r
9114   }\r
9115 }\r
9116 \r
9117 \r
9118 VOID\r
9119 StartAnalysisClock()\r
9120 {\r
9121   if (analysisTimerEvent) return;\r
9122   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9123                                         (UINT) 2000, NULL);\r
9124 }\r
9125 \r
9126 LRESULT CALLBACK\r
9127 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9128 {\r
9129   static HANDLE hwndText;\r
9130   RECT rect;\r
9131   static int sizeX, sizeY;\r
9132   int newSizeX, newSizeY, flags;\r
9133   MINMAXINFO *mmi;\r
9134 \r
9135   switch (message) {\r
9136   case WM_INITDIALOG: /* message: initialize dialog box */\r
9137     /* Initialize the dialog items */\r
9138     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
9139     SetWindowText(hDlg, analysisTitle);\r
9140     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
9141     /* Size and position the dialog */\r
9142     if (!analysisDialog) {\r
9143       analysisDialog = hDlg;\r
9144       flags = SWP_NOZORDER;\r
9145       GetClientRect(hDlg, &rect);\r
9146       sizeX = rect.right;\r
9147       sizeY = rect.bottom;\r
9148       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
9149           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
9150         WINDOWPLACEMENT wp;\r
9151         EnsureOnScreen(&analysisX, &analysisY);\r
9152         wp.length = sizeof(WINDOWPLACEMENT);\r
9153         wp.flags = 0;\r
9154         wp.showCmd = SW_SHOW;\r
9155         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
9156         wp.rcNormalPosition.left = analysisX;\r
9157         wp.rcNormalPosition.right = analysisX + analysisW;\r
9158         wp.rcNormalPosition.top = analysisY;\r
9159         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
9160         SetWindowPlacement(hDlg, &wp);\r
9161 \r
9162         GetClientRect(hDlg, &rect);\r
9163         newSizeX = rect.right;\r
9164         newSizeY = rect.bottom;\r
9165         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
9166                               newSizeX, newSizeY);\r
9167         sizeX = newSizeX;\r
9168         sizeY = newSizeY;\r
9169       }\r
9170     }\r
9171     return FALSE;\r
9172 \r
9173   case WM_COMMAND: /* message: received a command */\r
9174     switch (LOWORD(wParam)) {\r
9175     case IDCANCEL:\r
9176       EditGameEvent();\r
9177       return TRUE;\r
9178     default:\r
9179       break;\r
9180     }\r
9181     break;\r
9182 \r
9183   case WM_SIZE:\r
9184     newSizeX = LOWORD(lParam);\r
9185     newSizeY = HIWORD(lParam);\r
9186     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
9187     sizeX = newSizeX;\r
9188     sizeY = newSizeY;\r
9189     break;\r
9190 \r
9191   case WM_GETMINMAXINFO:\r
9192     /* Prevent resizing window too small */\r
9193     mmi = (MINMAXINFO *) lParam;\r
9194     mmi->ptMinTrackSize.x = 100;\r
9195     mmi->ptMinTrackSize.y = 100;\r
9196     break;\r
9197   }\r
9198   return FALSE;\r
9199 }\r
9200 \r
9201 VOID\r
9202 AnalysisPopUp(char* title, char* str)\r
9203 {\r
9204   FARPROC lpProc;\r
9205   char *p, *q;\r
9206 \r
9207   /* [AS] */
9208   EngineOutputPopUp();
9209   return;
9210
9211   if (str == NULL) str = "";\r
9212   p = (char *) malloc(2 * strlen(str) + 2);\r
9213   q = p;\r
9214   while (*str) {\r
9215     if (*str == '\n') *q++ = '\r';\r
9216     *q++ = *str++;\r
9217   }\r
9218   *q = NULLCHAR;\r
9219   if (analysisText != NULL) free(analysisText);\r
9220   analysisText = p;\r
9221 \r
9222   if (analysisDialog) {\r
9223     SetWindowText(analysisDialog, title);\r
9224     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
9225     ShowWindow(analysisDialog, SW_SHOW);\r
9226   } else {\r
9227     analysisTitle = title;\r
9228     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
9229     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
9230                  hwndMain, (DLGPROC)lpProc);\r
9231     FreeProcInstance(lpProc);\r
9232   }\r
9233   analysisDialogUp = TRUE;  \r
9234 }\r
9235 \r
9236 VOID\r
9237 AnalysisPopDown()\r
9238 {\r
9239   if (analysisDialog) {\r
9240     ShowWindow(analysisDialog, SW_HIDE);\r
9241   }\r
9242   analysisDialogUp = FALSE;  \r
9243 }\r
9244 \r
9245 \r
9246 VOID\r
9247 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9248 {\r
9249   highlightInfo.sq[0].x = fromX;\r
9250   highlightInfo.sq[0].y = fromY;\r
9251   highlightInfo.sq[1].x = toX;\r
9252   highlightInfo.sq[1].y = toY;\r
9253 }\r
9254 \r
9255 VOID\r
9256 ClearHighlights()\r
9257 {\r
9258   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9259     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9260 }\r
9261 \r
9262 VOID\r
9263 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9264 {\r
9265   premoveHighlightInfo.sq[0].x = fromX;\r
9266   premoveHighlightInfo.sq[0].y = fromY;\r
9267   premoveHighlightInfo.sq[1].x = toX;\r
9268   premoveHighlightInfo.sq[1].y = toY;\r
9269 }\r
9270 \r
9271 VOID\r
9272 ClearPremoveHighlights()\r
9273 {\r
9274   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9275     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9276 }\r
9277 \r
9278 VOID\r
9279 ShutDownFrontEnd()\r
9280 {\r
9281   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9282   DeleteClipboardTempFiles();\r
9283 }\r
9284 \r
9285 void\r
9286 BoardToTop()\r
9287 {\r
9288     if (IsIconic(hwndMain))\r
9289       ShowWindow(hwndMain, SW_RESTORE);\r
9290 \r
9291     SetActiveWindow(hwndMain);\r
9292 }\r
9293 \r
9294 /*\r
9295  * Prototypes for animation support routines\r
9296  */\r
9297 static void ScreenSquare(int column, int row, POINT * pt);\r
9298 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9299      POINT frames[], int * nFrames);\r
9300 \r
9301 \r
9302 #define kFactor 4\r
9303 \r
9304 void\r
9305 AnimateMove(board, fromX, fromY, toX, toY)\r
9306      Board board;\r
9307      int fromX;\r
9308      int fromY;\r
9309      int toX;\r
9310      int toY;\r
9311 {\r
9312   ChessSquare piece;\r
9313   POINT start, finish, mid;\r
9314   POINT frames[kFactor * 2 + 1];\r
9315   int nFrames, n;\r
9316 \r
9317   if (!appData.animate) return;\r
9318   if (doingSizing) return;\r
9319   if (fromY < 0 || fromX < 0) return;\r
9320   piece = board[fromY][fromX];\r
9321   if (piece >= EmptySquare) return;\r
9322 \r
9323   ScreenSquare(fromX, fromY, &start);\r
9324   ScreenSquare(toX, toY, &finish);\r
9325 \r
9326   /* All pieces except knights move in straight line */\r
9327   if (piece != WhiteKnight && piece != BlackKnight) {\r
9328     mid.x = start.x + (finish.x - start.x) / 2;\r
9329     mid.y = start.y + (finish.y - start.y) / 2;\r
9330   } else {\r
9331     /* Knight: make diagonal movement then straight */\r
9332     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9333        mid.x = start.x + (finish.x - start.x) / 2;\r
9334        mid.y = finish.y;\r
9335      } else {\r
9336        mid.x = finish.x;\r
9337        mid.y = start.y + (finish.y - start.y) / 2;\r
9338      }\r
9339   }\r
9340   \r
9341   /* Don't use as many frames for very short moves */\r
9342   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9343     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9344   else\r
9345     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9346 \r
9347   animInfo.from.x = fromX;\r
9348   animInfo.from.y = fromY;\r
9349   animInfo.to.x = toX;\r
9350   animInfo.to.y = toY;\r
9351   animInfo.lastpos = start;\r
9352   animInfo.piece = piece;\r
9353   for (n = 0; n < nFrames; n++) {\r
9354     animInfo.pos = frames[n];\r
9355     DrawPosition(FALSE, NULL);\r
9356     animInfo.lastpos = animInfo.pos;\r
9357     Sleep(appData.animSpeed);\r
9358   }\r
9359   animInfo.pos = finish;\r
9360   DrawPosition(FALSE, NULL);\r
9361   animInfo.piece = EmptySquare;\r
9362 }\r
9363 \r
9364 /*      Convert board position to corner of screen rect and color       */\r
9365 \r
9366 static void\r
9367 ScreenSquare(column, row, pt)\r
9368      int column; int row; POINT * pt;\r
9369 {\r
9370   if (flipView) {\r
9371     pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
9372     pt->y = lineGap + row * (squareSize + lineGap);\r
9373   } else {\r
9374     pt->x = lineGap + column * (squareSize + lineGap);\r
9375     pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
9376   }\r
9377 }\r
9378 \r
9379 /*      Generate a series of frame coords from start->mid->finish.\r
9380         The movement rate doubles until the half way point is\r
9381         reached, then halves back down to the final destination,\r
9382         which gives a nice slow in/out effect. The algorithmn\r
9383         may seem to generate too many intermediates for short\r
9384         moves, but remember that the purpose is to attract the\r
9385         viewers attention to the piece about to be moved and\r
9386         then to where it ends up. Too few frames would be less\r
9387         noticeable.                                             */\r
9388 \r
9389 static void\r
9390 Tween(start, mid, finish, factor, frames, nFrames)\r
9391      POINT * start; POINT * mid;\r
9392      POINT * finish; int factor;\r
9393      POINT frames[]; int * nFrames;\r
9394 {\r
9395   int n, fraction = 1, count = 0;\r
9396 \r
9397   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9398   for (n = 0; n < factor; n++)\r
9399     fraction *= 2;\r
9400   for (n = 0; n < factor; n++) {\r
9401     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9402     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9403     count ++;\r
9404     fraction = fraction / 2;\r
9405   }\r
9406   \r
9407   /* Midpoint */\r
9408   frames[count] = *mid;\r
9409   count ++;\r
9410   \r
9411   /* Slow out, stepping 1/2, then 1/4, ... */\r
9412   fraction = 2;\r
9413   for (n = 0; n < factor; n++) {\r
9414     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9415     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9416     count ++;\r
9417     fraction = fraction * 2;\r
9418   }\r
9419   *nFrames = count;\r
9420 }\r
9421 \r
9422 void\r
9423 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
9424 {\r
9425 #if 0
9426     char buf[256];
9427 \r
9428     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",
9429         first, last, current, current >= 0 ? movelist[current] : "n/a" );
9430 \r
9431     OutputDebugString( buf );
9432 #endif
9433
9434     MoveHistorySet( movelist, first, last, current, pvInfoList );
9435
9436     EvalGraphSet( first, last, current, pvInfoList );
9437 }
9438
9439 void SetProgramStats( FrontEndProgramStats * stats )
9440 {
9441 #if 0
9442     char buf[1024];
9443
9444     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",
9445         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );
9446
9447     OutputDebugString( buf );
9448 #endif
9449
9450     EngineOutputUpdate( stats );
9451 }