changes from H.G. Muller; version 4.3.2
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 \r
96 typedef struct {\r
97   ChessSquare piece;  \r
98   POINT pos;      /* window coordinates of current pos */\r
99   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
100   POINT from;     /* board coordinates of the piece's orig pos */\r
101   POINT to;       /* board coordinates of the piece's new pos */\r
102 } AnimInfo;\r
103 \r
104 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
105 \r
106 typedef struct {\r
107   POINT start;    /* window coordinates of start pos */\r
108   POINT pos;      /* window coordinates of current pos */\r
109   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
110   POINT from;     /* board coordinates of the piece's orig pos */\r
111 } DragInfo;\r
112 \r
113 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
114 \r
115 typedef struct {\r
116   POINT sq[2];    /* board coordinates of from, to squares */\r
117 } HighlightInfo;\r
118 \r
119 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
120 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
121 \r
122 /* Window class names */\r
123 char szAppName[] = "WinBoard";\r
124 char szConsoleName[] = "WBConsole";\r
125 \r
126 /* Title bar text */\r
127 char szTitle[] = "WinBoard";\r
128 char szConsoleTitle[] = "ICS Interaction";\r
129 \r
130 char *programName;\r
131 char *settingsFileName;\r
132 BOOLEAN saveSettingsOnExit;\r
133 char installDir[MSG_SIZ];\r
134 \r
135 BoardSize boardSize;\r
136 BOOLEAN chessProgram;\r
137 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
138 static int squareSize, lineGap;\r
139 static int winWidth, winHeight;\r
140 static RECT messageRect, whiteRect, blackRect;\r
141 static char messageText[MESSAGE_TEXT_MAX];\r
142 static int clockTimerEvent = 0;\r
143 static int loadGameTimerEvent = 0;\r
144 static int analysisTimerEvent = 0;\r
145 static DelayedEventCallback delayedTimerCallback;\r
146 static int delayedTimerEvent = 0;\r
147 static int buttonCount = 2;\r
148 char *icsTextMenuString;\r
149 char *icsNames;\r
150 char *firstChessProgramNames;\r
151 char *secondChessProgramNames;\r
152 \r
153 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
154 \r
155 #define PALETTESIZE 256\r
156 \r
157 HINSTANCE hInst;          /* current instance */\r
158 HWND hwndMain = NULL;        /* root window*/\r
159 HWND hwndConsole = NULL;\r
160 BOOLEAN alwaysOnTop = FALSE;\r
161 RECT boardRect;\r
162 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
163   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
164 HPALETTE hPal;\r
165 ColorClass currentColorClass;\r
166 \r
167 HWND hCommPort = NULL;    /* currently open comm port */\r
168 static HWND hwndPause;    /* pause button */\r
169 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
170 static HBRUSH lightSquareBrush, darkSquareBrush,\r
171   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
172 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
173 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
174 static HPEN gridPen = NULL;\r
175 static HPEN highlightPen = NULL;\r
176 static HPEN premovePen = NULL;\r
177 static NPLOGPALETTE pLogPal;\r
178 static BOOL paletteChanged = FALSE;\r
179 static HICON iconWhite, iconBlack, iconCurrent;\r
180 static int doingSizing = FALSE;\r
181 static int lastSizing = 0;\r
182 static int prevStderrPort;\r
183 \r
184 /* [AS] Support for background textures */\r
185 #define BACK_TEXTURE_MODE_DISABLED      0\r
186 #define BACK_TEXTURE_MODE_PLAIN         1\r
187 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
188 \r
189 static HBITMAP liteBackTexture = NULL;\r
190 static HBITMAP darkBackTexture = NULL;\r
191 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
192 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
193 static int backTextureSquareSize = 0;\r
194 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
195 \r
196 #if __GNUC__ && !defined(_winmajor)\r
197 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
198 #else\r
199 #define oldDialog (_winmajor < 4)\r
200 #endif\r
201 \r
202 char *defaultTextAttribs[] = \r
203 {\r
204   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
205   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
206   COLOR_NONE\r
207 };\r
208 \r
209 typedef struct {\r
210   char *name;\r
211   int squareSize;\r
212   int lineGap;\r
213   int smallLayout;\r
214   int tinyLayout;\r
215   int cliWidth, cliHeight;\r
216 } SizeInfo;\r
217 \r
218 SizeInfo sizeInfo[] = \r
219 {\r
220   { "tiny",     21, 0, 1, 1, 0, 0 },\r
221   { "teeny",    25, 1, 1, 1, 0, 0 },\r
222   { "dinky",    29, 1, 1, 1, 0, 0 },\r
223   { "petite",   33, 1, 1, 1, 0, 0 },\r
224   { "slim",     37, 2, 1, 0, 0, 0 },\r
225   { "small",    40, 2, 1, 0, 0, 0 },\r
226   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
227   { "middling", 49, 2, 0, 0, 0, 0 },\r
228   { "average",  54, 2, 0, 0, 0, 0 },\r
229   { "moderate", 58, 3, 0, 0, 0, 0 },\r
230   { "medium",   64, 3, 0, 0, 0, 0 },\r
231   { "bulky",    72, 3, 0, 0, 0, 0 },\r
232   { "large",    80, 3, 0, 0, 0, 0 },\r
233   { "big",      87, 3, 0, 0, 0, 0 },\r
234   { "huge",     95, 3, 0, 0, 0, 0 },\r
235   { "giant",    108, 3, 0, 0, 0, 0 },\r
236   { "colossal", 116, 4, 0, 0, 0, 0 },\r
237   { "titanic",  129, 4, 0, 0, 0, 0 },\r
238   { NULL, 0, 0, 0, 0, 0, 0 }\r
239 };\r
240 \r
241 #define MF(x) {x, {0, }, {0, }, 0}\r
242 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
243 {\r
244   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
245   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
246   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
247   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
248   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
249   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
250   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
251   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
252   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
253   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
254   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
255   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
256   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
257   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
258   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
259   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
260   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
261   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
262 };\r
263 \r
264 MyFont *font[NUM_SIZES][NUM_FONTS];\r
265 \r
266 typedef struct {\r
267   char *label;\r
268   int id;\r
269   HWND hwnd;\r
270   WNDPROC wndproc;\r
271 } MyButtonDesc;\r
272 \r
273 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
274 #define N_BUTTONS 5\r
275 \r
276 MyButtonDesc buttonDesc[N_BUTTONS] =\r
277 {\r
278   {"<<", IDM_ToStart, NULL, NULL},\r
279   {"<", IDM_Backward, NULL, NULL},\r
280   {"P", IDM_Pause, NULL, NULL},\r
281   {">", IDM_Forward, NULL, NULL},\r
282   {">>", IDM_ToEnd, NULL, NULL},\r
283 };\r
284 \r
285 int tinyLayout = 0, smallLayout = 0;\r
286 #define MENU_BAR_ITEMS 6\r
287 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
288   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
289   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
290 };\r
291 \r
292 \r
293 MySound sounds[(int)NSoundClasses];\r
294 MyTextAttribs textAttribs[(int)NColorClasses];\r
295 \r
296 MyColorizeAttribs colorizeAttribs[] = {\r
297   { (COLORREF)0, 0, "Shout Text" },\r
298   { (COLORREF)0, 0, "SShout/CShout" },\r
299   { (COLORREF)0, 0, "Channel 1 Text" },\r
300   { (COLORREF)0, 0, "Channel Text" },\r
301   { (COLORREF)0, 0, "Kibitz Text" },\r
302   { (COLORREF)0, 0, "Tell Text" },\r
303   { (COLORREF)0, 0, "Challenge Text" },\r
304   { (COLORREF)0, 0, "Request Text" },\r
305   { (COLORREF)0, 0, "Seek Text" },\r
306   { (COLORREF)0, 0, "Normal Text" },\r
307   { (COLORREF)0, 0, "None" }\r
308 };\r
309 \r
310 \r
311 \r
312 static char *commentTitle;\r
313 static char *commentText;\r
314 static int commentIndex;\r
315 static Boolean editComment = FALSE;\r
316 HWND commentDialog = NULL;\r
317 BOOLEAN commentDialogUp = FALSE;\r
318 static int commentX, commentY, commentH, commentW;\r
319 \r
320 static char *analysisTitle;\r
321 static char *analysisText;\r
322 HWND analysisDialog = NULL;\r
323 BOOLEAN analysisDialogUp = FALSE;\r
324 static int analysisX, analysisY, analysisH, analysisW;\r
325 \r
326 char errorTitle[MSG_SIZ];\r
327 char errorMessage[2*MSG_SIZ];\r
328 HWND errorDialog = NULL;\r
329 BOOLEAN moveErrorMessageUp = FALSE;\r
330 BOOLEAN consoleEcho = TRUE;\r
331 CHARFORMAT consoleCF;\r
332 COLORREF consoleBackgroundColor;\r
333 \r
334 char *programVersion;\r
335 \r
336 #define CPReal 1\r
337 #define CPComm 2\r
338 #define CPSock 3\r
339 #define CPRcmd 4\r
340 typedef int CPKind;\r
341 \r
342 typedef struct {\r
343   CPKind kind;\r
344   HANDLE hProcess;\r
345   DWORD pid;\r
346   HANDLE hTo;\r
347   HANDLE hFrom;\r
348   SOCKET sock;\r
349   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
350 } ChildProc;\r
351 \r
352 #define INPUT_SOURCE_BUF_SIZE 4096\r
353 \r
354 typedef struct _InputSource {\r
355   CPKind kind;\r
356   HANDLE hFile;\r
357   SOCKET sock;\r
358   int lineByLine;\r
359   HANDLE hThread;\r
360   DWORD id;\r
361   char buf[INPUT_SOURCE_BUF_SIZE];\r
362   char *next;\r
363   DWORD count;\r
364   int error;\r
365   InputCallback func;\r
366   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
367   VOIDSTAR closure;\r
368 } InputSource;\r
369 \r
370 InputSource *consoleInputSource;\r
371 \r
372 DCB dcb;\r
373 \r
374 /* forward */\r
375 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
376 VOID ConsoleCreate();\r
377 LRESULT CALLBACK\r
378   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
379 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
380 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
381 VOID ParseCommSettings(char *arg, DCB *dcb);\r
382 LRESULT CALLBACK\r
383   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
384 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
385 void ParseIcsTextMenu(char *icsTextMenuString);\r
386 VOID PopUpMoveDialog(char firstchar);\r
387 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
388 \r
389 /* [AS] */\r
390 int NewGameFRC();\r
391 int GameListOptions();\r
392 \r
393 HWND moveHistoryDialog = NULL;\r
394 BOOLEAN moveHistoryDialogUp = FALSE;\r
395 \r
396 WindowPlacement wpMoveHistory;\r
397 \r
398 HWND evalGraphDialog = NULL;\r
399 BOOLEAN evalGraphDialogUp = FALSE;\r
400 \r
401 WindowPlacement wpEvalGraph;\r
402 \r
403 HWND engineOutputDialog = NULL;\r
404 BOOLEAN engineOutputDialogUp = FALSE;\r
405 \r
406 WindowPlacement wpEngineOutput;\r
407 \r
408 VOID MoveHistoryPopUp();\r
409 VOID MoveHistoryPopDown();\r
410 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
411 BOOL MoveHistoryIsUp();\r
412 \r
413 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
414 VOID EvalGraphPopUp();\r
415 VOID EvalGraphPopDown();\r
416 BOOL EvalGraphIsUp();\r
417 \r
418 VOID EngineOutputPopUp();\r
419 VOID EngineOutputPopDown();\r
420 BOOL EngineOutputIsUp();\r
421 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
422 \r
423 VOID GothicPopUp(char *title);\r
424 /*\r
425  * Setting "frozen" should disable all user input other than deleting\r
426  * the window.  We do this while engines are initializing themselves.\r
427  */\r
428 static int frozen = 0;\r
429 static int oldMenuItemState[MENU_BAR_ITEMS];\r
430 void FreezeUI()\r
431 {\r
432   HMENU hmenu;\r
433   int i;\r
434 \r
435   if (frozen) return;\r
436   frozen = 1;\r
437   hmenu = GetMenu(hwndMain);\r
438   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
439     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
440   }\r
441   DrawMenuBar(hwndMain);\r
442 }\r
443 \r
444 /* Undo a FreezeUI */\r
445 void ThawUI()\r
446 {\r
447   HMENU hmenu;\r
448   int i;\r
449 \r
450   if (!frozen) return;\r
451   frozen = 0;\r
452   hmenu = GetMenu(hwndMain);\r
453   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
454     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
455   }\r
456   DrawMenuBar(hwndMain);\r
457 }\r
458 \r
459 /*---------------------------------------------------------------------------*\\r
460  *\r
461  * WinMain\r
462  *\r
463 \*---------------------------------------------------------------------------*/\r
464 \r
465 int APIENTRY\r
466 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
467         LPSTR lpCmdLine, int nCmdShow)\r
468 {\r
469   MSG msg;\r
470   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
471 \r
472   debugFP = stderr;\r
473 \r
474   LoadLibrary("RICHED32.DLL");\r
475   consoleCF.cbSize = sizeof(CHARFORMAT);\r
476 \r
477   if (!InitApplication(hInstance)) {\r
478     return (FALSE);\r
479   }\r
480   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
481     return (FALSE);\r
482   }\r
483 \r
484   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
485   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
486   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
487 \r
488   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
489 \r
490   while (GetMessage(&msg, /* message structure */\r
491                     NULL, /* handle of window receiving the message */\r
492                     0,    /* lowest message to examine */\r
493                     0))   /* highest message to examine */\r
494     {\r
495       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
496           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
497           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
498           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
499           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
500           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
501           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
502           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
503           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
504           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
505         TranslateMessage(&msg); /* Translates virtual key codes */\r
506         DispatchMessage(&msg);  /* Dispatches message to window */\r
507       }\r
508     }\r
509 \r
510 \r
511   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
512 }\r
513 \r
514 /*---------------------------------------------------------------------------*\\r
515  *\r
516  * Initialization functions\r
517  *\r
518 \*---------------------------------------------------------------------------*/\r
519 \r
520 BOOL\r
521 InitApplication(HINSTANCE hInstance)\r
522 {\r
523   WNDCLASS wc;\r
524 \r
525   /* Fill in window class structure with parameters that describe the */\r
526   /* main window. */\r
527 \r
528   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
529   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
530   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
531   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
532   wc.hInstance     = hInstance;         /* Owner of this class */\r
533   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
534   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
535   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
536   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
537   wc.lpszClassName = szAppName;                 /* Name to register as */\r
538 \r
539   /* Register the window class and return success/failure code. */\r
540   if (!RegisterClass(&wc)) return FALSE;\r
541 \r
542   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
543   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
544   wc.cbClsExtra    = 0;\r
545   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
546   wc.hInstance     = hInstance;\r
547   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
548   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
549   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
550   wc.lpszMenuName  = NULL;\r
551   wc.lpszClassName = szConsoleName;\r
552 \r
553   if (!RegisterClass(&wc)) return FALSE;\r
554   return TRUE;\r
555 }\r
556 \r
557 \r
558 /* Set by InitInstance, used by EnsureOnScreen */\r
559 int screenHeight, screenWidth;\r
560 \r
561 void\r
562 EnsureOnScreen(int *x, int *y)\r
563 {\r
564   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
565   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
566   if (*x > screenWidth - 32) *x = 0;\r
567   if (*y > screenHeight - 32) *y = 0;\r
568   if (*x < 10) *x = 10;\r
569   if (*y < gap) *y = gap;\r
570 }\r
571 \r
572 BOOL\r
573 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
574 {\r
575   HWND hwnd; /* Main window handle. */\r
576   int ibs;\r
577   WINDOWPLACEMENT wp;\r
578   char *filepart;\r
579 \r
580   hInst = hInstance;    /* Store instance handle in our global variable */\r
581 \r
582   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
583     *filepart = NULLCHAR;\r
584   } else {\r
585     GetCurrentDirectory(MSG_SIZ, installDir);\r
586   }\r
587   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
588   if (appData.debugMode) {\r
589     debugFP = fopen(appData.nameOfDebugFile, "w");\r
590     setbuf(debugFP, NULL);\r
591   }\r
592 \r
593   InitBackEnd1();\r
594 \r
595   InitEngineUCI( installDir, &first );\r
596   InitEngineUCI( installDir, &second );\r
597 \r
598   /* Create a main window for this application instance. */\r
599   hwnd = CreateWindow(szAppName, szTitle,\r
600                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
601                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
602                       NULL, NULL, hInstance, NULL);\r
603   hwndMain = hwnd;\r
604 \r
605   /* If window could not be created, return "failure" */\r
606   if (!hwnd) {\r
607     return (FALSE);\r
608   }\r
609 \r
610   iconWhite = LoadIcon(hInstance, "icon_white");\r
611   iconBlack = LoadIcon(hInstance, "icon_black");\r
612   iconCurrent = iconWhite;\r
613   InitDrawingColors();\r
614   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
615   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
616   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
617     /* Compute window size for each board size, and use the largest\r
618        size that fits on this screen as the default. */\r
619     InitDrawingSizes((BoardSize)ibs, 0);\r
620     if (boardSize == (BoardSize)-1 &&\r
621         winHeight <= screenHeight\r
622            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
623         && winWidth <= screenWidth) {\r
624       boardSize = (BoardSize)ibs;\r
625     }\r
626   }\r
627   InitDrawingSizes(boardSize, 0);\r
628   InitMenuChecks();\r
629   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
630 \r
631   /* [AS] Load textures if specified */\r
632   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
633   \r
634   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
635       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
636       liteBackTextureMode = appData.liteBackTextureMode;\r
637 \r
638       if (liteBackTexture == NULL && appData.debugMode) {\r
639           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
640       }\r
641   }\r
642   \r
643   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
644       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
645       darkBackTextureMode = appData.darkBackTextureMode;\r
646 \r
647       if (darkBackTexture == NULL && appData.debugMode) {\r
648           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
649       }\r
650   }\r
651 \r
652   mysrandom( (unsigned) time(NULL) );\r
653 \r
654   /* Make a console window if needed */\r
655   if (appData.icsActive) {\r
656     ConsoleCreate();\r
657   }\r
658 \r
659   /* [AS] Restore layout */\r
660   if( wpMoveHistory.visible ) {\r
661       MoveHistoryPopUp();\r
662   }\r
663 \r
664   if( wpEvalGraph.visible ) {\r
665       EvalGraphPopUp();\r
666   }\r
667 \r
668   if( wpEngineOutput.visible ) {\r
669       EngineOutputPopUp();\r
670   }\r
671 \r
672   InitBackEnd2();\r
673 \r
674   /* Make the window visible; update its client area; and return "success" */\r
675   EnsureOnScreen(&boardX, &boardY);\r
676   wp.length = sizeof(WINDOWPLACEMENT);\r
677   wp.flags = 0;\r
678   wp.showCmd = nCmdShow;\r
679   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
680   wp.rcNormalPosition.left = boardX;\r
681   wp.rcNormalPosition.right = boardX + winWidth;\r
682   wp.rcNormalPosition.top = boardY;\r
683   wp.rcNormalPosition.bottom = boardY + winHeight;\r
684   SetWindowPlacement(hwndMain, &wp);\r
685 \r
686   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
687                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
688 \r
689   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
690   if( gameInfo.variant != VariantFischeRandom ) {\r
691       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
692   }\r
693 #ifdef FAIRY\r
694 #ifdef GOTHIC\r
695   /* [HGM] Gothic licensing requirement */\r
696   if(gameInfo.variant == VariantGothic)\r
697       GothicPopUp(GOTHIC);\r
698 #endif // GOTHIC\r
699 #endif // FAIRY\r
700   if (hwndConsole) {\r
701 #if AOT_CONSOLE\r
702     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
703                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
704 #endif\r
705     ShowWindow(hwndConsole, nCmdShow);\r
706   }\r
707   UpdateWindow(hwnd);\r
708 \r
709   return TRUE;\r
710 \r
711 }\r
712 \r
713 \r
714 typedef enum {\r
715   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
716   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
717   ArgSettingsFilename\r
718 } ArgType;\r
719 \r
720 typedef struct {\r
721   char *argName;\r
722   ArgType argType;\r
723   /***\r
724   union {\r
725     String *pString;       // ArgString\r
726     int *pInt;             // ArgInt\r
727     float *pFloat;         // ArgFloat\r
728     Boolean *pBoolean;     // ArgBoolean\r
729     COLORREF *pColor;      // ArgColor\r
730     ColorClass cc;         // ArgAttribs\r
731     String *pFilename;     // ArgFilename\r
732     BoardSize *pBoardSize; // ArgBoardSize\r
733     int whichFont;         // ArgFont\r
734     DCB *pDCB;             // ArgCommSettings\r
735     String *pFilename;     // ArgSettingsFilename\r
736   } argLoc;\r
737   ***/\r
738   LPVOID argLoc;\r
739   BOOL save;\r
740 } ArgDescriptor;\r
741 \r
742 int junk;\r
743 ArgDescriptor argDescriptors[] = {\r
744   /* positional arguments */\r
745   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
746   { "", ArgNone, NULL },\r
747   /* keyword arguments */\r
748   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
749   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
750   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
751   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
752   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
753   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
754   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
755   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
756   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
757   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
758   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
759   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
760   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
761   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
762   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
763   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
764   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
765   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
766     FALSE },\r
767   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
768     FALSE },\r
769   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
770     FALSE },\r
771   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
772   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
773     FALSE },\r
774   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
775   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
776   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
777   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
778   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
779   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
780   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
781   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
782   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
783   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
784   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
785   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
786   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
787   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
788   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
789   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
790   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
791   /*!!bitmapDirectory?*/\r
792   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
793   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
794   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
795   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
796   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
797   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
798   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
799   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
800   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
801   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
802   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
803   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
804   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
805   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
806   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
807   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
808   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
809   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
810   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
811   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
812   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
813   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
814   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
815   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
816   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
817   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
818   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
819   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
820   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
821   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
822   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
823   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
824   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
825   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
826   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
827   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
828   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
829   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
830   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
831   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
832   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
833   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
834   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
835   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
836   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
837   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
838   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
839   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
840   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
841   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
842   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
843   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
844   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
845   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
846   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
847   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
848   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
849   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
850   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
851   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
852   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
853   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
854   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
855   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
856   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
857   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
858   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
859   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
860   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
861   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
862   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
863   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
864   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
865   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
866   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
867   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
868   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
869   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
870   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
871   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
872   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
873   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
874   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
875   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
876   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
877   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
878   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
879   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
880   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
881   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
882   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
883   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
884   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
885   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
886     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
887   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
888   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
889   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
890   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
891   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
892   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
893   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
894   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
895     TRUE }, /* must come after all fonts */\r
896   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
897   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
898     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
899   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
900   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
901   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
902   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
903   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
904   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
905   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
906   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
907   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
908   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
909   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
910   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
911   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
912   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
913   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
914   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
915   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
916   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
917   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
918   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
919   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
920   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
921   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
922   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
923   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
924   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
925   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
926   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
927 #if 0\r
928   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
929   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
930 #endif\r
931   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
932   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
933   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
934   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
935   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
936   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
937   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
938   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
939   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
940   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
941   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
942   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
943   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
944   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
945   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
946   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
947   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
948   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
949   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
950   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
951   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
952   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
953   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
954   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
955   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
956   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
957   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
958   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
959   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
960   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
961   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
962   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
963   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
964   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
965   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
966   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
967   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
968   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
969   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
970   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
971   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
972   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
973   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
974   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
975   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
976   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
977   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
978   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
979   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
980   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
981   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
982   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
983   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
984   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
985   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
986   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
987   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
988   { "highlightLastMove", ArgBoolean,\r
989     (LPVOID) &appData.highlightLastMove, TRUE },\r
990   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
991   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
992   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
993   { "highlightDragging", ArgBoolean,\r
994     (LPVOID) &appData.highlightDragging, TRUE },\r
995   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
996   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
997   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
998   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
999   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1000   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1001   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1002   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1003   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1004   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1005   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1006   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1007   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1008   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1009   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1010   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1011   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1012   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1013   { "soundShout", ArgFilename,\r
1014     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1015   { "soundSShout", ArgFilename,\r
1016     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1017   { "soundChannel1", ArgFilename,\r
1018     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1019   { "soundChannel", ArgFilename,\r
1020     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1021   { "soundKibitz", ArgFilename,\r
1022     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1023   { "soundTell", ArgFilename,\r
1024     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1025   { "soundChallenge", ArgFilename,\r
1026     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1027   { "soundRequest", ArgFilename,\r
1028     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1029   { "soundSeek", ArgFilename,\r
1030     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1031   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1032   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1033   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1034   { "soundIcsLoss", ArgFilename, \r
1035     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1036   { "soundIcsDraw", ArgFilename, \r
1037     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1038   { "soundIcsUnfinished", ArgFilename, \r
1039     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1040   { "soundIcsAlarm", ArgFilename, \r
1041     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1042   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1043   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1044   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1045   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1046   { "reuseChessPrograms", ArgBoolean,\r
1047     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1048   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1049   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1050   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1051   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1052   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1053   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1054   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1055   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1056   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1057   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1058   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1059   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1060   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1061   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1062   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1063   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1064   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1065   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1066   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1067   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1068   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1069   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1070   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1071   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1072   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1073   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1074   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1075   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1076   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1077   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1078   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1079   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1080   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1081   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1082   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1083   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1084   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1085     TRUE },\r
1086   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1087     TRUE },\r
1088   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1089   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1090   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1091   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1092   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1093   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1094   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1095   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1096   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1097   /* [AS] New features */\r
1098   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1099   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1100   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1101   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1102   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1103   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1104   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1105   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1106   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1107   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1108   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1109   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1110   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1111   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1112   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1113   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1114   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1115   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1116   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1117   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1118   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1119   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1120   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1121   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1122   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1123   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1124   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1125   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1126   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1127   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1128   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1129   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1130   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1131   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1132   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1133   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1134   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1135   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1136   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1137   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1138   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1139   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1140   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1141   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1142   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1143   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1144   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1145   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1146 \r
1147   /* [AS] Layout stuff */\r
1148   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1149   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1150   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1151   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1152   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1153 \r
1154   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1155   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1156   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1157   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1158   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1159 \r
1160   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1161   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1162   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1163   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1164   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1165 \r
1166   /* [HGM] User-selectable board size */\r
1167   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1168   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1169   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, FALSE },\r
1170   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1171   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1172   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1173 \r
1174 #ifdef ZIPPY\r
1175   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1176   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1177   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1178   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1179   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1180   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1181   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1182   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1183   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1184   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1185   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1186   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1187   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1188     FALSE },\r
1189   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1190   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1191   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1192   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1193   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1194   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1195   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1196     FALSE },\r
1197   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1198   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1199   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1200   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1201   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1202   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1203   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1204   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1205   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1206   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1207   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1208   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1209   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1210   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1211   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1212   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1213   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1214   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1215 #endif\r
1216   { NULL, ArgNone, NULL, FALSE }\r
1217 };\r
1218 \r
1219 \r
1220 /* Kludge for indirection files on command line */\r
1221 char* lastIndirectionFilename;\r
1222 ArgDescriptor argDescriptorIndirection =\r
1223 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1224 \r
1225 \r
1226 VOID\r
1227 ExitArgError(char *msg, char *badArg)\r
1228 {\r
1229   char buf[MSG_SIZ];\r
1230 \r
1231   sprintf(buf, "%s %s", msg, badArg);\r
1232   DisplayFatalError(buf, 0, 2);\r
1233   exit(2);\r
1234 }\r
1235 \r
1236 /* Command line font name parser.  NULL name means do nothing.\r
1237    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1238    For backward compatibility, syntax without the colon is also\r
1239    accepted, but font names with digits in them won't work in that case.\r
1240 */\r
1241 VOID\r
1242 ParseFontName(char *name, MyFontParams *mfp)\r
1243 {\r
1244   char *p, *q;\r
1245   if (name == NULL) return;\r
1246   p = name;\r
1247   q = strchr(p, ':');\r
1248   if (q) {\r
1249     if (q - p >= sizeof(mfp->faceName))\r
1250       ExitArgError("Font name too long:", name);\r
1251     memcpy(mfp->faceName, p, q - p);\r
1252     mfp->faceName[q - p] = NULLCHAR;\r
1253     p = q + 1;\r
1254   } else {\r
1255     q = mfp->faceName;\r
1256     while (*p && !isdigit(*p)) {\r
1257       *q++ = *p++;\r
1258       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1259         ExitArgError("Font name too long:", name);\r
1260     }\r
1261     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1262     *q = NULLCHAR;\r
1263   }\r
1264   if (!*p) ExitArgError("Font point size missing:", name);\r
1265   mfp->pointSize = (float) atof(p);\r
1266   mfp->bold = (strchr(p, 'b') != NULL);\r
1267   mfp->italic = (strchr(p, 'i') != NULL);\r
1268   mfp->underline = (strchr(p, 'u') != NULL);\r
1269   mfp->strikeout = (strchr(p, 's') != NULL);\r
1270 }\r
1271 \r
1272 /* Color name parser.\r
1273    X version accepts X color names, but this one\r
1274    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1275 COLORREF\r
1276 ParseColorName(char *name)\r
1277 {\r
1278   int red, green, blue, count;\r
1279   char buf[MSG_SIZ];\r
1280 \r
1281   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1282   if (count != 3) {\r
1283     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1284       &red, &green, &blue);\r
1285   }\r
1286   if (count != 3) {\r
1287     sprintf(buf, "Can't parse color name %s", name);\r
1288     DisplayError(buf, 0);\r
1289     return RGB(0, 0, 0);\r
1290   }\r
1291   return PALETTERGB(red, green, blue);\r
1292 }\r
1293 \r
1294 \r
1295 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1296 {\r
1297   char *e = argValue;\r
1298   int eff = 0;\r
1299 \r
1300   while (*e) {\r
1301     if (*e == 'b')      eff |= CFE_BOLD;\r
1302     else if (*e == 'i') eff |= CFE_ITALIC;\r
1303     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1304     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1305     else if (*e == '#' || isdigit(*e)) break;\r
1306     e++;\r
1307   }\r
1308   *effects = eff;\r
1309   *color   = ParseColorName(e);\r
1310 }\r
1311 \r
1312 \r
1313 BoardSize\r
1314 ParseBoardSize(char *name)\r
1315 {\r
1316   BoardSize bs = SizeTiny;\r
1317   while (sizeInfo[bs].name != NULL) {\r
1318     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1319     bs++;\r
1320   }\r
1321   ExitArgError("Unrecognized board size value", name);\r
1322   return bs; /* not reached */\r
1323 }\r
1324 \r
1325 \r
1326 char\r
1327 StringGet(void *getClosure)\r
1328 {\r
1329   char **p = (char **) getClosure;\r
1330   return *((*p)++);\r
1331 }\r
1332 \r
1333 char\r
1334 FileGet(void *getClosure)\r
1335 {\r
1336   int c;\r
1337   FILE* f = (FILE*) getClosure;\r
1338 \r
1339   c = getc(f);\r
1340   if (c == EOF)\r
1341     return NULLCHAR;\r
1342   else\r
1343     return (char) c;\r
1344 }\r
1345 \r
1346 /* Parse settings file named "name". If file found, return the\r
1347    full name in fullname and return TRUE; else return FALSE */\r
1348 BOOLEAN\r
1349 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1350 {\r
1351   char *dummy;\r
1352   FILE *f;\r
1353 \r
1354   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1355     f = fopen(fullname, "r");\r
1356     if (f != NULL) {\r
1357       ParseArgs(FileGet, f);\r
1358       fclose(f);\r
1359       return TRUE;\r
1360     }\r
1361   }\r
1362   return FALSE;\r
1363 }\r
1364 \r
1365 VOID\r
1366 ParseArgs(GetFunc get, void *cl)\r
1367 {\r
1368   char argName[ARG_MAX];\r
1369   char argValue[ARG_MAX];\r
1370   ArgDescriptor *ad;\r
1371   char start;\r
1372   char *q;\r
1373   int i, octval;\r
1374   char ch;\r
1375   int posarg = 0;\r
1376 \r
1377   ch = get(cl);\r
1378   for (;;) {\r
1379     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1380     if (ch == NULLCHAR) break;\r
1381     if (ch == ';') {\r
1382       /* Comment to end of line */\r
1383       ch = get(cl);\r
1384       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1385       continue;\r
1386     } else if (ch == '/' || ch == '-') {\r
1387       /* Switch */\r
1388       q = argName;\r
1389       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1390              ch != '\n' && ch != '\t') {\r
1391         *q++ = ch;\r
1392         ch = get(cl);\r
1393       }\r
1394       *q = NULLCHAR;\r
1395 \r
1396       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1397         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1398 \r
1399       if (ad->argName == NULL)\r
1400         ExitArgError("Unrecognized argument", argName);\r
1401 \r
1402     } else if (ch == '@') {\r
1403       /* Indirection file */\r
1404       ad = &argDescriptorIndirection;\r
1405       ch = get(cl);\r
1406     } else {\r
1407       /* Positional argument */\r
1408       ad = &argDescriptors[posarg++];\r
1409       strcpy(argName, ad->argName);\r
1410     }\r
1411 \r
1412     if (ad->argType == ArgTrue) {\r
1413       *(Boolean *) ad->argLoc = TRUE;\r
1414       continue;\r
1415     }\r
1416     if (ad->argType == ArgFalse) {\r
1417       *(Boolean *) ad->argLoc = FALSE;\r
1418       continue;\r
1419     }\r
1420 \r
1421     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1422     if (ch == NULLCHAR || ch == '\n') {\r
1423       ExitArgError("No value provided for argument", argName);\r
1424     }\r
1425     q = argValue;\r
1426     if (ch == '{') {\r
1427       // Quoting with { }.  No characters have to (or can) be escaped.\r
1428       // Thus the string cannot contain a '}' character.\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         case '}':\r
1438           ch = get(cl);\r
1439           start = NULLCHAR;\r
1440           break;\r
1441 \r
1442         default:\r
1443           *q++ = ch;\r
1444           ch = get(cl);\r
1445           break;\r
1446         }\r
1447       }   \r
1448     } else if (ch == '\'' || ch == '"') {\r
1449       // Quoting with ' ' or " ", with \ as escape character.\r
1450       // Inconvenient for long strings that may contain Windows filenames.\r
1451       start = ch;\r
1452       ch = get(cl);\r
1453       while (start) {\r
1454         switch (ch) {\r
1455         case NULLCHAR:\r
1456           start = NULLCHAR;\r
1457           break;\r
1458 \r
1459         default:\r
1460         not_special:\r
1461           *q++ = ch;\r
1462           ch = get(cl);\r
1463           break;\r
1464 \r
1465         case '\'':\r
1466         case '\"':\r
1467           if (ch == start) {\r
1468             ch = get(cl);\r
1469             start = NULLCHAR;\r
1470             break;\r
1471           } else {\r
1472             goto not_special;\r
1473           }\r
1474 \r
1475         case '\\':\r
1476           if (ad->argType == ArgFilename\r
1477               || ad->argType == ArgSettingsFilename) {\r
1478               goto not_special;\r
1479           }\r
1480           ch = get(cl);\r
1481           switch (ch) {\r
1482           case NULLCHAR:\r
1483             ExitArgError("Incomplete \\ escape in value for", argName);\r
1484             break;\r
1485           case 'n':\r
1486             *q++ = '\n';\r
1487             ch = get(cl);\r
1488             break;\r
1489           case 'r':\r
1490             *q++ = '\r';\r
1491             ch = get(cl);\r
1492             break;\r
1493           case 't':\r
1494             *q++ = '\t';\r
1495             ch = get(cl);\r
1496             break;\r
1497           case 'b':\r
1498             *q++ = '\b';\r
1499             ch = get(cl);\r
1500             break;\r
1501           case 'f':\r
1502             *q++ = '\f';\r
1503             ch = get(cl);\r
1504             break;\r
1505           default:\r
1506             octval = 0;\r
1507             for (i = 0; i < 3; i++) {\r
1508               if (ch >= '0' && ch <= '7') {\r
1509                 octval = octval*8 + (ch - '0');\r
1510                 ch = get(cl);\r
1511               } else {\r
1512                 break;\r
1513               }\r
1514             }\r
1515             if (i > 0) {\r
1516               *q++ = (char) octval;\r
1517             } else {\r
1518               *q++ = ch;\r
1519               ch = get(cl);\r
1520             }\r
1521             break;\r
1522           }\r
1523           break;\r
1524         }\r
1525       }\r
1526     } else {\r
1527       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1528         *q++ = ch;\r
1529         ch = get(cl);\r
1530       }\r
1531     }\r
1532     *q = NULLCHAR;\r
1533 \r
1534     switch (ad->argType) {\r
1535     case ArgInt:\r
1536       *(int *) ad->argLoc = atoi(argValue);\r
1537       break;\r
1538 \r
1539     case ArgFloat:\r
1540       *(float *) ad->argLoc = (float) atof(argValue);\r
1541       break;\r
1542 \r
1543     case ArgString:\r
1544     case ArgFilename:\r
1545       *(char **) ad->argLoc = strdup(argValue);\r
1546       break;\r
1547 \r
1548     case ArgSettingsFilename:\r
1549       {\r
1550         char fullname[MSG_SIZ];\r
1551         if (ParseSettingsFile(argValue, fullname)) {\r
1552           if (ad->argLoc != NULL) {\r
1553             *(char **) ad->argLoc = strdup(fullname);\r
1554           }\r
1555         } else {\r
1556           if (ad->argLoc != NULL) {\r
1557           } else {\r
1558             ExitArgError("Failed to open indirection file", argValue);\r
1559           }\r
1560         }\r
1561       }\r
1562       break;\r
1563 \r
1564     case ArgBoolean:\r
1565       switch (argValue[0]) {\r
1566       case 't':\r
1567       case 'T':\r
1568         *(Boolean *) ad->argLoc = TRUE;\r
1569         break;\r
1570       case 'f':\r
1571       case 'F':\r
1572         *(Boolean *) ad->argLoc = FALSE;\r
1573         break;\r
1574       default:\r
1575         ExitArgError("Unrecognized boolean argument value", argValue);\r
1576         break;\r
1577       }\r
1578       break;\r
1579 \r
1580     case ArgColor:\r
1581       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1582       break;\r
1583 \r
1584     case ArgAttribs: {\r
1585       ColorClass cc = (ColorClass)ad->argLoc;\r
1586       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1587       }\r
1588       break;\r
1589       \r
1590     case ArgBoardSize:\r
1591       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1592       break;\r
1593 \r
1594     case ArgFont:\r
1595       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1596       break;\r
1597 \r
1598     case ArgCommSettings:\r
1599       ParseCommSettings(argValue, &dcb);\r
1600       break;\r
1601 \r
1602     case ArgNone:\r
1603       ExitArgError("Unrecognized argument", argValue);\r
1604       break;\r
1605     }\r
1606   }\r
1607 }\r
1608 \r
1609 VOID\r
1610 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1611 {\r
1612   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1613   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1614   DeleteDC(hdc);\r
1615   lf->lfWidth = 0;\r
1616   lf->lfEscapement = 0;\r
1617   lf->lfOrientation = 0;\r
1618   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1619   lf->lfItalic = mfp->italic;\r
1620   lf->lfUnderline = mfp->underline;\r
1621   lf->lfStrikeOut = mfp->strikeout;\r
1622   lf->lfCharSet = DEFAULT_CHARSET;\r
1623   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1624   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1625   lf->lfQuality = DEFAULT_QUALITY;\r
1626   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1627   strcpy(lf->lfFaceName, mfp->faceName);\r
1628 }\r
1629 \r
1630 VOID\r
1631 CreateFontInMF(MyFont *mf)\r
1632 {\r
1633   LFfromMFP(&mf->lf, &mf->mfp);\r
1634   if (mf->hf) DeleteObject(mf->hf);\r
1635   mf->hf = CreateFontIndirect(&mf->lf);\r
1636 }\r
1637 \r
1638 VOID\r
1639 SetDefaultTextAttribs()\r
1640 {\r
1641   ColorClass cc;\r
1642   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1643     ParseAttribs(&textAttribs[cc].color, \r
1644                  &textAttribs[cc].effects, \r
1645                  defaultTextAttribs[cc]);\r
1646   }\r
1647 }\r
1648 \r
1649 VOID\r
1650 SetDefaultSounds()\r
1651 {\r
1652   ColorClass cc;\r
1653   SoundClass sc;\r
1654   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1655     textAttribs[cc].sound.name = strdup("");\r
1656     textAttribs[cc].sound.data = NULL;\r
1657   }\r
1658   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1659     sounds[sc].name = strdup("");\r
1660     sounds[sc].data = NULL;\r
1661   }\r
1662   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1663 }\r
1664 \r
1665 VOID\r
1666 LoadAllSounds()\r
1667 {\r
1668   ColorClass cc;\r
1669   SoundClass sc;\r
1670   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1671     MyLoadSound(&textAttribs[cc].sound);\r
1672   }\r
1673   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1674     MyLoadSound(&sounds[sc]);\r
1675   }\r
1676 }\r
1677 \r
1678 VOID\r
1679 InitAppData(LPSTR lpCmdLine)\r
1680 {\r
1681   int i, j;\r
1682   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1683   char *dummy, *p;\r
1684 \r
1685   programName = szAppName;\r
1686 \r
1687   /* Initialize to defaults */\r
1688   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1689   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1690   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1691   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1692   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1693   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1694   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1695   SetDefaultTextAttribs();\r
1696   SetDefaultSounds();\r
1697   appData.movesPerSession = MOVES_PER_SESSION;\r
1698   appData.initString = INIT_STRING;\r
1699   appData.secondInitString = INIT_STRING;\r
1700   appData.firstComputerString = COMPUTER_STRING;\r
1701   appData.secondComputerString = COMPUTER_STRING;\r
1702   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1703   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1704   appData.firstPlaysBlack = FALSE;\r
1705   appData.noChessProgram = FALSE;\r
1706   chessProgram = FALSE;\r
1707   appData.firstHost = FIRST_HOST;\r
1708   appData.secondHost = SECOND_HOST;\r
1709   appData.firstDirectory = FIRST_DIRECTORY;\r
1710   appData.secondDirectory = SECOND_DIRECTORY;\r
1711   appData.bitmapDirectory = "";\r
1712   appData.remoteShell = REMOTE_SHELL;\r
1713   appData.remoteUser = "";\r
1714   appData.timeDelay = TIME_DELAY;\r
1715   appData.timeControl = TIME_CONTROL;\r
1716   appData.timeIncrement = TIME_INCREMENT;\r
1717   appData.icsActive = FALSE;\r
1718   appData.icsHost = "";\r
1719   appData.icsPort = ICS_PORT;\r
1720   appData.icsCommPort = ICS_COMM_PORT;\r
1721   appData.icsLogon = ICS_LOGON;\r
1722   appData.icsHelper = "";\r
1723   appData.useTelnet = FALSE;\r
1724   appData.telnetProgram = TELNET_PROGRAM;\r
1725   appData.gateway = "";\r
1726   appData.loadGameFile = "";\r
1727   appData.loadGameIndex = 0;\r
1728   appData.saveGameFile = "";\r
1729   appData.autoSaveGames = FALSE;\r
1730   appData.loadPositionFile = "";\r
1731   appData.loadPositionIndex = 1;\r
1732   appData.savePositionFile = "";\r
1733   appData.matchMode = FALSE;\r
1734   appData.matchGames = 0;\r
1735   appData.monoMode = FALSE;\r
1736   appData.debugMode = FALSE;\r
1737   appData.clockMode = TRUE;\r
1738   boardSize = (BoardSize) -1; /* determine by screen size */\r
1739   appData.Iconic = FALSE; /*unused*/\r
1740   appData.searchTime = "";\r
1741   appData.searchDepth = 0;\r
1742   appData.showCoords = FALSE;\r
1743   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1744   appData.autoCallFlag = FALSE;\r
1745   appData.flipView = FALSE;\r
1746   appData.autoFlipView = TRUE;\r
1747   appData.cmailGameName = "";\r
1748   appData.alwaysPromoteToQueen = FALSE;\r
1749   appData.oldSaveStyle = FALSE;\r
1750   appData.quietPlay = FALSE;\r
1751   appData.showThinking = FALSE;\r
1752   appData.ponderNextMove = TRUE;\r
1753   appData.periodicUpdates = TRUE;\r
1754   appData.popupExitMessage = TRUE;\r
1755   appData.popupMoveErrors = FALSE;\r
1756   appData.autoObserve = FALSE;\r
1757   appData.autoComment = FALSE;\r
1758   appData.animate = TRUE;\r
1759   appData.animSpeed = 10;\r
1760   appData.animateDragging = TRUE;\r
1761   appData.highlightLastMove = TRUE;\r
1762   appData.getMoveList = TRUE;\r
1763   appData.testLegality = TRUE;\r
1764   appData.premove = TRUE;\r
1765   appData.premoveWhite = FALSE;\r
1766   appData.premoveWhiteText = "";\r
1767   appData.premoveBlack = FALSE;\r
1768   appData.premoveBlackText = "";\r
1769   appData.icsAlarm = TRUE;\r
1770   appData.icsAlarmTime = 5000;\r
1771   appData.autoRaiseBoard = TRUE;\r
1772   appData.localLineEditing = TRUE;\r
1773   appData.colorize = TRUE;\r
1774   appData.reuseFirst = TRUE;\r
1775   appData.reuseSecond = TRUE;\r
1776   appData.blindfold = FALSE;\r
1777   dcb.DCBlength = sizeof(DCB);\r
1778   dcb.BaudRate = 9600;\r
1779   dcb.fBinary = TRUE;\r
1780   dcb.fParity = FALSE;\r
1781   dcb.fOutxCtsFlow = FALSE;\r
1782   dcb.fOutxDsrFlow = FALSE;\r
1783   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1784   dcb.fDsrSensitivity = FALSE;\r
1785   dcb.fTXContinueOnXoff = TRUE;\r
1786   dcb.fOutX = FALSE;\r
1787   dcb.fInX = FALSE;\r
1788   dcb.fNull = FALSE;\r
1789   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1790   dcb.fAbortOnError = FALSE;\r
1791   dcb.wReserved = 0;\r
1792   dcb.ByteSize = 7;\r
1793   dcb.Parity = SPACEPARITY;\r
1794   dcb.StopBits = ONESTOPBIT;\r
1795   settingsFileName = SETTINGS_FILE;\r
1796   saveSettingsOnExit = TRUE;\r
1797   boardX = CW_USEDEFAULT;\r
1798   boardY = CW_USEDEFAULT;\r
1799   consoleX = CW_USEDEFAULT; \r
1800   consoleY = CW_USEDEFAULT; \r
1801   consoleW = CW_USEDEFAULT;\r
1802   consoleH = CW_USEDEFAULT;\r
1803   analysisX = CW_USEDEFAULT; \r
1804   analysisY = CW_USEDEFAULT; \r
1805   analysisW = CW_USEDEFAULT;\r
1806   analysisH = CW_USEDEFAULT;\r
1807   commentX = CW_USEDEFAULT; \r
1808   commentY = CW_USEDEFAULT; \r
1809   commentW = CW_USEDEFAULT;\r
1810   commentH = CW_USEDEFAULT;\r
1811   editTagsX = CW_USEDEFAULT; \r
1812   editTagsY = CW_USEDEFAULT; \r
1813   editTagsW = CW_USEDEFAULT;\r
1814   editTagsH = CW_USEDEFAULT;\r
1815   gameListX = CW_USEDEFAULT; \r
1816   gameListY = CW_USEDEFAULT; \r
1817   gameListW = CW_USEDEFAULT;\r
1818   gameListH = CW_USEDEFAULT;\r
1819   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1820   icsNames = ICS_NAMES;\r
1821   firstChessProgramNames = FCP_NAMES;\r
1822   secondChessProgramNames = SCP_NAMES;\r
1823   appData.initialMode = "";\r
1824   appData.variant = "normal";\r
1825   appData.firstProtocolVersion = PROTOVER;\r
1826   appData.secondProtocolVersion = PROTOVER;\r
1827   appData.showButtonBar = TRUE;\r
1828 \r
1829    /* [AS] New properties (see comments in header file) */\r
1830   appData.firstScoreIsAbsolute = FALSE;\r
1831   appData.secondScoreIsAbsolute = FALSE;\r
1832   appData.saveExtendedInfoInPGN = FALSE;\r
1833   appData.hideThinkingFromHuman = FALSE;\r
1834   appData.liteBackTextureFile = "";\r
1835   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1836   appData.darkBackTextureFile = "";\r
1837   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1838   appData.renderPiecesWithFont = "";\r
1839   appData.fontToPieceTable = "";\r
1840   appData.fontBackColorWhite = 0;\r
1841   appData.fontForeColorWhite = 0;\r
1842   appData.fontBackColorBlack = 0;\r
1843   appData.fontForeColorBlack = 0;\r
1844   appData.fontPieceSize = 80;\r
1845   appData.overrideLineGap = 1;\r
1846   appData.adjudicateLossThreshold = 0;\r
1847   appData.delayBeforeQuit = 0;\r
1848   appData.delayAfterQuit = 0;\r
1849   appData.nameOfDebugFile = "winboard.debug";\r
1850   appData.pgnEventHeader = "Computer Chess Game";\r
1851   appData.defaultFrcPosition = -1;\r
1852   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1853   appData.saveOutOfBookInfo = TRUE;\r
1854   appData.showEvalInMoveHistory = TRUE;\r
1855   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1856   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1857   appData.highlightMoveWithArrow = FALSE;\r
1858   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1859   appData.useStickyWindows = TRUE;\r
1860   appData.adjudicateDrawMoves = 0;\r
1861   appData.autoDisplayComment = TRUE;\r
1862   appData.autoDisplayTags = TRUE;\r
1863   appData.firstIsUCI = FALSE;\r
1864   appData.secondIsUCI = FALSE;\r
1865   appData.firstHasOwnBookUCI = TRUE;\r
1866   appData.secondHasOwnBookUCI = TRUE;\r
1867   appData.polyglotDir = "";\r
1868   appData.usePolyglotBook = FALSE;\r
1869   appData.polyglotBook = "";\r
1870   appData.defaultHashSize = 64;\r
1871   appData.defaultCacheSizeEGTB = 4;\r
1872   appData.defaultPathEGTB = "c:\\egtb";\r
1873 \r
1874   InitWindowPlacement( &wpMoveHistory );\r
1875   InitWindowPlacement( &wpEvalGraph );\r
1876   InitWindowPlacement( &wpEngineOutput );\r
1877 \r
1878   /* [HGM] User-selectable board size */\r
1879   appData.NrFiles = 8;\r
1880   appData.NrRanks = 8;\r
1881   appData.matchPause  = 10000;\r
1882   appData.testClaims  = FALSE;\r
1883   appData.ruleMoves   = 51;\r
1884   appData.drawRepeats = 6;\r
1885 \r
1886 #ifdef ZIPPY\r
1887   appData.zippyTalk = ZIPPY_TALK;\r
1888   appData.zippyPlay = ZIPPY_PLAY;\r
1889   appData.zippyLines = ZIPPY_LINES;\r
1890   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1891   appData.zippyPassword = ZIPPY_PASSWORD;\r
1892   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1893   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1894   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1895   appData.zippyUseI = ZIPPY_USE_I;\r
1896   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1897   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1898   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1899   appData.zippyGameStart = ZIPPY_GAME_START;\r
1900   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1901   appData.zippyAbort = ZIPPY_ABORT;\r
1902   appData.zippyVariants = ZIPPY_VARIANTS;\r
1903   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1904   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1905 #endif\r
1906 \r
1907   /* Point font array elements to structures and\r
1908      parse default font names */\r
1909   for (i=0; i<NUM_FONTS; i++) {\r
1910     for (j=0; j<NUM_SIZES; j++) {\r
1911       font[j][i] = &fontRec[j][i];\r
1912       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1913     }\r
1914   }\r
1915   \r
1916   /* Parse default settings file if any */\r
1917   if (ParseSettingsFile(settingsFileName, buf)) {\r
1918     settingsFileName = strdup(buf);\r
1919   }\r
1920 \r
1921   /* Parse command line */\r
1922   ParseArgs(StringGet, &lpCmdLine);\r
1923 \r
1924   /* [HGM] make sure board size is acceptable */\r
1925   if(appData.NrFiles > BOARD_SIZE ||\r
1926      appData.NrRanks > BOARD_SIZE   )\r
1927       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
1928 \r
1929   /* Propagate options that affect others */\r
1930   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1931   if (appData.icsActive || appData.noChessProgram) {\r
1932      chessProgram = FALSE;  /* not local chess program mode */\r
1933   }\r
1934 \r
1935   /* Open startup dialog if needed */\r
1936   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1937       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1938       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1939                         *appData.secondChessProgram == NULLCHAR))) {\r
1940     FARPROC lpProc;\r
1941     \r
1942     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1943     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1944     FreeProcInstance(lpProc);\r
1945   }\r
1946 \r
1947   /* Make sure save files land in the right (?) directory */\r
1948   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
1949     appData.saveGameFile = strdup(buf);\r
1950   }\r
1951   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1952     appData.savePositionFile = strdup(buf);\r
1953   }\r
1954 \r
1955   /* Finish initialization for fonts and sounds */\r
1956   for (i=0; i<NUM_FONTS; i++) {\r
1957     for (j=0; j<NUM_SIZES; j++) {\r
1958       CreateFontInMF(font[j][i]);\r
1959     }\r
1960   }\r
1961   /* xboard, and older WinBoards, controlled the move sound with the\r
1962      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1963      always turn the option on (so that the backend will call us),\r
1964      then let the user turn the sound off by setting it to silence if\r
1965      desired.  To accommodate old winboard.ini files saved by old\r
1966      versions of WinBoard, we also turn off the sound if the option\r
1967      was initially set to false. */\r
1968   if (!appData.ringBellAfterMoves) {\r
1969     sounds[(int)SoundMove].name = strdup("");\r
1970     appData.ringBellAfterMoves = TRUE;\r
1971   }\r
1972   GetCurrentDirectory(MSG_SIZ, currDir);\r
1973   SetCurrentDirectory(installDir);\r
1974   LoadAllSounds();\r
1975   SetCurrentDirectory(currDir);\r
1976 \r
1977   p = icsTextMenuString;\r
1978   if (p[0] == '@') {\r
1979     FILE* f = fopen(p + 1, "r");\r
1980     if (f == NULL) {\r
1981       DisplayFatalError(p + 1, errno, 2);\r
1982       return;\r
1983     }\r
1984     i = fread(buf, 1, sizeof(buf)-1, f);\r
1985     fclose(f);\r
1986     buf[i] = NULLCHAR;\r
1987     p = buf;\r
1988   }\r
1989   ParseIcsTextMenu(strdup(p));\r
1990 }\r
1991 \r
1992 \r
1993 VOID\r
1994 InitMenuChecks()\r
1995 {\r
1996   HMENU hmenu = GetMenu(hwndMain);\r
1997 \r
1998   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1999                         MF_BYCOMMAND|((appData.icsActive &&\r
2000                                        *appData.icsCommPort != NULLCHAR) ?\r
2001                                       MF_ENABLED : MF_GRAYED));\r
2002   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2003                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2004                                      MF_CHECKED : MF_UNCHECKED));\r
2005 }\r
2006 \r
2007 \r
2008 VOID\r
2009 SaveSettings(char* name)\r
2010 {\r
2011   FILE *f;\r
2012   ArgDescriptor *ad;\r
2013   WINDOWPLACEMENT wp;\r
2014   char dir[MSG_SIZ];\r
2015 \r
2016   if (!hwndMain) return;\r
2017 \r
2018   GetCurrentDirectory(MSG_SIZ, dir);\r
2019   SetCurrentDirectory(installDir);\r
2020   f = fopen(name, "w");\r
2021   SetCurrentDirectory(dir);\r
2022   if (f == NULL) {\r
2023     DisplayError(name, errno);\r
2024     return;\r
2025   }\r
2026   fprintf(f, ";\n");\r
2027   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2028   fprintf(f, ";\n");\r
2029   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2030   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2031   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2032   fprintf(f, ";\n");\r
2033 \r
2034   wp.length = sizeof(WINDOWPLACEMENT);\r
2035   GetWindowPlacement(hwndMain, &wp);\r
2036   boardX = wp.rcNormalPosition.left;\r
2037   boardY = wp.rcNormalPosition.top;\r
2038 \r
2039   if (hwndConsole) {\r
2040     GetWindowPlacement(hwndConsole, &wp);\r
2041     consoleX = wp.rcNormalPosition.left;\r
2042     consoleY = wp.rcNormalPosition.top;\r
2043     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2044     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2045   }\r
2046 \r
2047   if (analysisDialog) {\r
2048     GetWindowPlacement(analysisDialog, &wp);\r
2049     analysisX = wp.rcNormalPosition.left;\r
2050     analysisY = wp.rcNormalPosition.top;\r
2051     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2052     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2053   }\r
2054 \r
2055   if (commentDialog) {\r
2056     GetWindowPlacement(commentDialog, &wp);\r
2057     commentX = wp.rcNormalPosition.left;\r
2058     commentY = wp.rcNormalPosition.top;\r
2059     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2060     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2061   }\r
2062 \r
2063   if (editTagsDialog) {\r
2064     GetWindowPlacement(editTagsDialog, &wp);\r
2065     editTagsX = wp.rcNormalPosition.left;\r
2066     editTagsY = wp.rcNormalPosition.top;\r
2067     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2068     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2069   }\r
2070 \r
2071   if (gameListDialog) {\r
2072     GetWindowPlacement(gameListDialog, &wp);\r
2073     gameListX = wp.rcNormalPosition.left;\r
2074     gameListY = wp.rcNormalPosition.top;\r
2075     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2076     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2077   }\r
2078 \r
2079   /* [AS] Move history */\r
2080   wpMoveHistory.visible = MoveHistoryIsUp();\r
2081   \r
2082   if( moveHistoryDialog ) {\r
2083     GetWindowPlacement(moveHistoryDialog, &wp);\r
2084     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2085     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2086     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2087     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2088   }\r
2089 \r
2090   /* [AS] Eval graph */\r
2091   wpEvalGraph.visible = EvalGraphIsUp();\r
2092 \r
2093   if( evalGraphDialog ) {\r
2094     GetWindowPlacement(evalGraphDialog, &wp);\r
2095     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2096     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2097     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2098     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2099   }\r
2100 \r
2101   /* [AS] Engine output */\r
2102   wpEngineOutput.visible = EngineOutputIsUp();\r
2103 \r
2104   if( engineOutputDialog ) {\r
2105     GetWindowPlacement(engineOutputDialog, &wp);\r
2106     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2107     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2108     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2109     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2110   }\r
2111 \r
2112   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2113     if (!ad->save) continue;\r
2114     switch (ad->argType) {\r
2115     case ArgString:\r
2116       {\r
2117         char *p = *(char **)ad->argLoc;\r
2118         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2119           /* Quote multiline values or \-containing values\r
2120              with { } if possible */\r
2121           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2122         } else {\r
2123           /* Else quote with " " */\r
2124           fprintf(f, "/%s=\"", ad->argName);\r
2125           while (*p) {\r
2126             if (*p == '\n') fprintf(f, "\n");\r
2127             else if (*p == '\r') fprintf(f, "\\r");\r
2128             else if (*p == '\t') fprintf(f, "\\t");\r
2129             else if (*p == '\b') fprintf(f, "\\b");\r
2130             else if (*p == '\f') fprintf(f, "\\f");\r
2131             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2132             else if (*p == '\"') fprintf(f, "\\\"");\r
2133             else if (*p == '\\') fprintf(f, "\\\\");\r
2134             else putc(*p, f);\r
2135             p++;\r
2136           }\r
2137           fprintf(f, "\"\n");\r
2138         }\r
2139       }\r
2140       break;\r
2141     case ArgInt:\r
2142       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2143       break;\r
2144     case ArgFloat:\r
2145       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2146       break;\r
2147     case ArgBoolean:\r
2148       fprintf(f, "/%s=%s\n", ad->argName, \r
2149         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2150       break;\r
2151     case ArgTrue:\r
2152       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2153       break;\r
2154     case ArgFalse:\r
2155       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2156       break;\r
2157     case ArgColor:\r
2158       {\r
2159         COLORREF color = *(COLORREF *)ad->argLoc;\r
2160         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2161           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2162       }\r
2163       break;\r
2164     case ArgAttribs:\r
2165       {\r
2166         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2167         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2168           (ta->effects & CFE_BOLD) ? "b" : "",\r
2169           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2170           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2171           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2172           (ta->effects) ? " " : "",\r
2173           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2174       }\r
2175       break;\r
2176     case ArgFilename:\r
2177       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2178         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2179       } else {\r
2180         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2181       }\r
2182       break;\r
2183     case ArgBoardSize:\r
2184       fprintf(f, "/%s=%s\n", ad->argName,\r
2185               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2186       break;\r
2187     case ArgFont:\r
2188       {\r
2189         int bs;\r
2190         for (bs=0; bs<NUM_SIZES; bs++) {\r
2191           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2192           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2193           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2194             ad->argName, mfp->faceName, mfp->pointSize,\r
2195             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2196             mfp->bold ? "b" : "",\r
2197             mfp->italic ? "i" : "",\r
2198             mfp->underline ? "u" : "",\r
2199             mfp->strikeout ? "s" : "");\r
2200         }\r
2201       }\r
2202       break;\r
2203     case ArgCommSettings:\r
2204       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2205     }\r
2206   }\r
2207   fclose(f);\r
2208 }\r
2209 \r
2210 \r
2211 \r
2212 /*---------------------------------------------------------------------------*\\r
2213  *\r
2214  * GDI board drawing routines\r
2215  *\r
2216 \*---------------------------------------------------------------------------*/\r
2217 \r
2218 /* [AS] Draw square using background texture */\r
2219 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2220 {\r
2221     XFORM   x;\r
2222 \r
2223     if( mode == 0 ) {\r
2224         return; /* Should never happen! */\r
2225     }\r
2226 \r
2227     SetGraphicsMode( dst, GM_ADVANCED );\r
2228 \r
2229     switch( mode ) {\r
2230     case 1:\r
2231         /* Identity */\r
2232         break;\r
2233     case 2:\r
2234         /* X reflection */\r
2235         x.eM11 = -1.0;\r
2236         x.eM12 = 0;\r
2237         x.eM21 = 0;\r
2238         x.eM22 = 1.0;\r
2239         x.eDx = (FLOAT) dw + dx - 1;\r
2240         x.eDy = 0;\r
2241         dx = 0;\r
2242         SetWorldTransform( dst, &x );\r
2243         break;\r
2244     case 3:\r
2245         /* Y reflection */\r
2246         x.eM11 = 1.0;\r
2247         x.eM12 = 0;\r
2248         x.eM21 = 0;\r
2249         x.eM22 = -1.0;\r
2250         x.eDx = 0;\r
2251         x.eDy = (FLOAT) dh + dy - 1;\r
2252         dy = 0;\r
2253         SetWorldTransform( dst, &x );\r
2254         break;\r
2255     case 4:\r
2256         /* X/Y flip */\r
2257         x.eM11 = 0;\r
2258         x.eM12 = 1.0;\r
2259         x.eM21 = 1.0;\r
2260         x.eM22 = 0;\r
2261         x.eDx = (FLOAT) dx;\r
2262         x.eDy = (FLOAT) dy;\r
2263         dx = 0;\r
2264         dy = 0;\r
2265         SetWorldTransform( dst, &x );\r
2266         break;\r
2267     }\r
2268 \r
2269     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2270 \r
2271     x.eM11 = 1.0;\r
2272     x.eM12 = 0;\r
2273     x.eM21 = 0;\r
2274     x.eM22 = 1.0;\r
2275     x.eDx = 0;\r
2276     x.eDy = 0;\r
2277     SetWorldTransform( dst, &x );\r
2278 \r
2279     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2280 }\r
2281 \r
2282 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2283 enum {\r
2284     PM_WP = (int) WhitePawn, \r
2285     PM_WN = (int) WhiteKnight, \r
2286     PM_WB = (int) WhiteBishop, \r
2287     PM_WR = (int) WhiteRook, \r
2288 #ifdef FAIRY\r
2289     PM_WA  = (int) WhiteCardinal, \r
2290     PM_WC  = (int) WhiteMarshall, \r
2291     PM_WFP = (int) WhiteFairyPawn, \r
2292     PM_WFN = (int) WhiteFairyKnight, \r
2293     PM_WFB = (int) WhiteFairyBishop, \r
2294     PM_WFR = (int) WhiteFairyRook, \r
2295     PM_WFA = (int) WhiteFairyCardinal, \r
2296     PM_WFC = (int) WhiteFairyMarshall, \r
2297     PM_WFQ = (int) WhiteFairyQueen, \r
2298     PM_WFK = (int) WhiteFairyKing, \r
2299 #endif\r
2300     PM_WQ = (int) WhiteQueen, \r
2301     PM_WK = (int) WhiteKing,\r
2302     PM_BP = (int) BlackPawn, \r
2303     PM_BN = (int) BlackKnight, \r
2304     PM_BB = (int) BlackBishop, \r
2305     PM_BR = (int) BlackRook, \r
2306 #ifdef FAIRY\r
2307     PM_BA  = (int) BlackCardinal, \r
2308     PM_BC  = (int) BlackMarshall, \r
2309     PM_BFP = (int) BlackFairyPawn, \r
2310     PM_BFN = (int) BlackFairyKnight, \r
2311     PM_BFB = (int) BlackFairyBishop, \r
2312     PM_BFR = (int) BlackFairyRook, \r
2313     PM_BFA = (int) BlackFairyCardinal, \r
2314     PM_BFC = (int) BlackFairyMarshall, \r
2315     PM_BFQ = (int) BlackFairyQueen, \r
2316     PM_BFK = (int) BlackFairyKing,\r
2317 #endif\r
2318     PM_BQ = (int) BlackQueen, \r
2319     PM_BK = (int) BlackKing\r
2320 };\r
2321 \r
2322 static HFONT hPieceFont = NULL;\r
2323 static HBITMAP hPieceMask[(int) EmptySquare];\r
2324 static HBITMAP hPieceFace[(int) EmptySquare];\r
2325 static int fontBitmapSquareSize = 0;\r
2326 static char pieceToFontChar[(int) EmptySquare] =\r
2327                               { 'p', 'n', 'b', 'r',\r
2328 #ifdef FAIRY\r
2329                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2330 #endif\r
2331                       'q', 'k', 'o', 'm', 'v', 't', \r
2332 #ifdef FAIRY\r
2333                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2334 #endif\r
2335                                                               'w', 'l' };\r
2336 \r
2337 static BOOL SetPieceToFontCharTable( const char * map )\r
2338 {\r
2339     BOOL result = FALSE; int NrPieces;\r
2340 \r
2341     if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare \r
2342                     && NrPieces >= 12 && !(NrPieces&1)) {\r
2343         int i; /* [HGM] Accept even length from 12 to 32 */\r
2344 \r
2345         for( i=0; i<(int) EmptySquare; i++ ) pieceToFontChar[i] = 0;\r
2346         for( i=0; i<NrPieces/2-2; i++ ) {\r
2347             pieceToFontChar[i] = map[i];\r
2348             pieceToFontChar[i + (int)BlackPawn - (int) WhitePawn] = map[i+NrPieces/2];\r
2349         }\r
2350         pieceToFontChar[(int) WhiteQueen] = map[NrPieces/2-2];\r
2351         pieceToFontChar[(int) WhiteKing]  = map[NrPieces/2-1];\r
2352         pieceToFontChar[(int) BlackQueen] = map[NrPieces-2];\r
2353         pieceToFontChar[(int) BlackKing]  = map[NrPieces-1];\r
2354 \r
2355         result = TRUE;\r
2356     }\r
2357 \r
2358     return result;\r
2359 }\r
2360 \r
2361 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2362 {\r
2363     HBRUSH hbrush;\r
2364     BYTE r1 = GetRValue( color );\r
2365     BYTE g1 = GetGValue( color );\r
2366     BYTE b1 = GetBValue( color );\r
2367     BYTE r2 = r1 / 2;\r
2368     BYTE g2 = g1 / 2;\r
2369     BYTE b2 = b1 / 2;\r
2370     RECT rc;\r
2371 \r
2372     /* Create a uniform background first */\r
2373     hbrush = CreateSolidBrush( color );\r
2374     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2375     FillRect( hdc, &rc, hbrush );\r
2376     DeleteObject( hbrush );\r
2377     \r
2378     if( mode == 1 ) {\r
2379         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2380         int steps = squareSize / 2;\r
2381         int i;\r
2382 \r
2383         for( i=0; i<steps; i++ ) {\r
2384             BYTE r = r1 - (r1-r2) * i / steps;\r
2385             BYTE g = g1 - (g1-g2) * i / steps;\r
2386             BYTE b = b1 - (b1-b2) * i / steps;\r
2387 \r
2388             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2389             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2390             FillRect( hdc, &rc, hbrush );\r
2391             DeleteObject(hbrush);\r
2392         }\r
2393     }\r
2394     else if( mode == 2 ) {\r
2395         /* Diagonal gradient, good more or less for every piece */\r
2396         POINT triangle[3];\r
2397         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2398         HBRUSH hbrush_old;\r
2399         int steps = squareSize;\r
2400         int i;\r
2401 \r
2402         triangle[0].x = squareSize - steps;\r
2403         triangle[0].y = squareSize;\r
2404         triangle[1].x = squareSize;\r
2405         triangle[1].y = squareSize;\r
2406         triangle[2].x = squareSize;\r
2407         triangle[2].y = squareSize - steps;\r
2408 \r
2409         for( i=0; i<steps; i++ ) {\r
2410             BYTE r = r1 - (r1-r2) * i / steps;\r
2411             BYTE g = g1 - (g1-g2) * i / steps;\r
2412             BYTE b = b1 - (b1-b2) * i / steps;\r
2413 \r
2414             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2415             hbrush_old = SelectObject( hdc, hbrush );\r
2416             Polygon( hdc, triangle, 3 );\r
2417             SelectObject( hdc, hbrush_old );\r
2418             DeleteObject(hbrush);\r
2419             triangle[0].x++;\r
2420             triangle[2].y++;\r
2421         }\r
2422 \r
2423         SelectObject( hdc, hpen );\r
2424     }\r
2425 }\r
2426 \r
2427 /*\r
2428     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2429     seems to work ok. The main problem here is to find the "inside" of a chess\r
2430     piece: follow the steps as explained below.\r
2431 */\r
2432 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2433 {\r
2434     HBITMAP hbm;\r
2435     HBITMAP hbm_old;\r
2436     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2437     RECT rc;\r
2438     SIZE sz;\r
2439     POINT pt;\r
2440     int backColor = whitePieceColor; \r
2441     int foreColor = blackPieceColor;\r
2442     int shapeIndex = index < 6 ? index+6 : index;\r
2443     \r
2444     if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2445         backColor = appData.fontBackColorWhite;\r
2446         foreColor = appData.fontForeColorWhite;\r
2447     }\r
2448     else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2449         backColor = appData.fontBackColorBlack;\r
2450         foreColor = appData.fontForeColorBlack;\r
2451     }\r
2452 \r
2453     /* Mask */\r
2454     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2455 \r
2456     hbm_old = SelectObject( hdc, hbm );\r
2457 \r
2458     rc.left = 0;\r
2459     rc.top = 0;\r
2460     rc.right = squareSize;\r
2461     rc.bottom = squareSize;\r
2462 \r
2463     /* Step 1: background is now black */\r
2464     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2465 \r
2466     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2467 \r
2468     pt.x = (squareSize - sz.cx) / 2;\r
2469     pt.y = (squareSize - sz.cy) / 2;\r
2470 \r
2471     SetBkMode( hdc, TRANSPARENT );\r
2472     SetTextColor( hdc, chroma );\r
2473     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2474     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2475 \r
2476     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2477     /* Step 3: the area outside the piece is filled with white */\r
2478     FloodFill( hdc, 0, 0, chroma );\r
2479     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2480     /* \r
2481         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2482         but if the start point is not inside the piece we're lost!\r
2483         There should be a better way to do this... if we could create a region or path\r
2484         from the fill operation we would be fine for example.\r
2485     */\r
2486     FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2487 \r
2488     SetTextColor( hdc, 0 );\r
2489     /* \r
2490         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2491         draw the piece again in black for safety.\r
2492     */\r
2493     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2494 \r
2495     SelectObject( hdc, hbm_old );\r
2496 \r
2497     if( hPieceMask[index] != NULL ) {\r
2498         DeleteObject( hPieceMask[index] );\r
2499     }\r
2500 \r
2501     hPieceMask[index] = hbm;\r
2502 \r
2503     /* Face */\r
2504     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2505 \r
2506     SelectObject( hdc, hbm );\r
2507 \r
2508     {\r
2509         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2510         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2511         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2512 \r
2513         SelectObject( dc1, hPieceMask[index] );\r
2514         SelectObject( dc2, bm2 );\r
2515         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2516         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2517         \r
2518         /* \r
2519             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2520             the piece background and deletes (makes transparent) the rest.\r
2521             Thanks to that mask, we are free to paint the background with the greates\r
2522             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2523             We use this, to make gradients and give the pieces a "roundish" look.\r
2524         */\r
2525         SetPieceBackground( hdc, backColor, 2 );\r
2526         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2527 \r
2528         DeleteDC( dc2 );\r
2529         DeleteDC( dc1 );\r
2530         DeleteObject( bm2 );\r
2531     }\r
2532 \r
2533     SetTextColor( hdc, foreColor );\r
2534     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2535 \r
2536     SelectObject( hdc, hbm_old );\r
2537 \r
2538     if( hPieceFace[index] != NULL ) {\r
2539         DeleteObject( hPieceFace[index] );\r
2540     }\r
2541 \r
2542     hPieceFace[index] = hbm;\r
2543 }\r
2544 \r
2545 static int TranslatePieceToFontPiece( int piece )\r
2546 {\r
2547     switch( piece ) {\r
2548     case BlackPawn:\r
2549         return PM_BP;\r
2550     case BlackKnight:\r
2551         return PM_BN;\r
2552     case BlackBishop:\r
2553         return PM_BB;\r
2554     case BlackRook:\r
2555         return PM_BR;\r
2556     case BlackQueen:\r
2557         return PM_BQ;\r
2558     case BlackKing:\r
2559         return PM_BK;\r
2560     case WhitePawn:\r
2561         return PM_WP;\r
2562     case WhiteKnight:\r
2563         return PM_WN;\r
2564     case WhiteBishop:\r
2565         return PM_WB;\r
2566     case WhiteRook:\r
2567         return PM_WR;\r
2568     case WhiteQueen:\r
2569         return PM_WQ;\r
2570     case WhiteKing:\r
2571         return PM_WK;\r
2572 #ifdef FAIRY\r
2573     case BlackCardinal:\r
2574         return PM_BA;\r
2575     case BlackMarshall:\r
2576         return PM_BC;\r
2577     case BlackFairyPawn:\r
2578         return PM_BFP;\r
2579     case BlackFairyKnight:\r
2580         return PM_BFN;\r
2581     case BlackFairyBishop:\r
2582         return PM_BFB;\r
2583     case BlackFairyRook:\r
2584         return PM_BFR;\r
2585     case BlackFairyCardinal:\r
2586         return PM_BFA;\r
2587     case BlackFairyMarshall:\r
2588         return PM_BFC;\r
2589     case BlackFairyQueen:\r
2590         return PM_BFQ;\r
2591     case BlackFairyKing:\r
2592         return PM_BFK;\r
2593     case WhiteCardinal:\r
2594         return PM_WA;\r
2595     case WhiteMarshall:\r
2596         return PM_WC;\r
2597     case WhiteFairyPawn:\r
2598         return PM_WFP;\r
2599     case WhiteFairyKnight:\r
2600         return PM_WFN;\r
2601     case WhiteFairyBishop:\r
2602         return PM_WFB;\r
2603     case WhiteFairyRook:\r
2604         return PM_WFR;\r
2605     case WhiteFairyCardinal:\r
2606         return PM_WFA;\r
2607     case WhiteFairyMarshall:\r
2608         return PM_WFC;\r
2609     case WhiteFairyQueen:\r
2610         return PM_WFQ;\r
2611     case WhiteFairyKing:\r
2612         return PM_WFK;\r
2613 #endif\r
2614     }\r
2615 \r
2616     return 0;\r
2617 }\r
2618 \r
2619 void CreatePiecesFromFont()\r
2620 {\r
2621     LOGFONT lf;\r
2622     HDC hdc_window = NULL;\r
2623     HDC hdc = NULL;\r
2624     HFONT hfont_old;\r
2625     int fontHeight;\r
2626     int i;\r
2627 \r
2628     if( fontBitmapSquareSize < 0 ) {\r
2629         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2630         return;\r
2631     }\r
2632 \r
2633     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2634         fontBitmapSquareSize = -1;\r
2635         return;\r
2636     }\r
2637 \r
2638     if( fontBitmapSquareSize != squareSize ) {\r
2639         hdc_window = GetDC( hwndMain );\r
2640         hdc = CreateCompatibleDC( hdc_window );\r
2641 \r
2642         if( hPieceFont != NULL ) {\r
2643             DeleteObject( hPieceFont );\r
2644         }\r
2645         else {\r
2646             for( i=0; i<12; i++ ) {\r
2647                 hPieceMask[i] = NULL;\r
2648                 hPieceFace[i] = NULL;\r
2649             }\r
2650         }\r
2651 \r
2652         fontHeight = 75;\r
2653 \r
2654         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2655             fontHeight = appData.fontPieceSize;\r
2656         }\r
2657 \r
2658         fontHeight = (fontHeight * squareSize) / 100;\r
2659 \r
2660         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2661         lf.lfWidth = 0;\r
2662         lf.lfEscapement = 0;\r
2663         lf.lfOrientation = 0;\r
2664         lf.lfWeight = FW_NORMAL;\r
2665         lf.lfItalic = 0;\r
2666         lf.lfUnderline = 0;\r
2667         lf.lfStrikeOut = 0;\r
2668         lf.lfCharSet = DEFAULT_CHARSET;\r
2669         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2670         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2671         lf.lfQuality = PROOF_QUALITY;\r
2672         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2673         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2674         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2675 \r
2676         hPieceFont = CreateFontIndirect( &lf );\r
2677 \r
2678         if( hPieceFont == NULL ) {\r
2679             fontBitmapSquareSize = -2;\r
2680         }\r
2681         else {\r
2682             /* Setup font-to-piece character table */\r
2683             if( ! SetPieceToFontCharTable(appData.fontToPieceTable) ) {\r
2684                 /* No (or wrong) global settings, try to detect the font */\r
2685                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2686                     /* Alpha */\r
2687                     SetPieceToFontCharTable("phbrqkojntwl");\r
2688                 }\r
2689                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2690                     /* DiagramTT* family */\r
2691                     SetPieceToFontCharTable("PNLRQKpnlrqk");\r
2692                 }\r
2693 #ifdef FAIRY\r
2694                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2695                     /* Fairy symbols */\r
2696                         SetPieceToFontCharTable("PNBRACFHEWUOGMQKpnbracfewuogmqk");\r
2697                 }\r
2698 #endif\r
2699                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2700                     /* Good Companion (Some characters get warped as literal :-( */\r
2701                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2702                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2703                     SetPieceToFontCharTable(s);\r
2704                 }\r
2705                 else {\r
2706                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2707                     SetPieceToFontCharTable("pnbrqkomvtwl");\r
2708                 }\r
2709             }\r
2710 \r
2711             /* Create bitmaps */\r
2712             hfont_old = SelectObject( hdc, hPieceFont );\r
2713 \r
2714             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2715             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2716             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2717             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2718             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2719             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2720             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2721             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2722             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2723             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2724             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2725             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2726 #ifdef FAIRY\r
2727             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2728             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2729             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFP );\r
2730             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFN );\r
2731             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFB );\r
2732             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFR );\r
2733             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFA );\r
2734             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFC );\r
2735             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFQ );\r
2736             CreatePieceMaskFromFont( hdc_window, hdc, PM_WFK );\r
2737             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2738             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2739             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFP );\r
2740             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFN );\r
2741             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFB );\r
2742             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFR );\r
2743             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFA );\r
2744             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFC );\r
2745             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFQ );\r
2746             CreatePieceMaskFromFont( hdc_window, hdc, PM_BFK );\r
2747 #endif\r
2748 \r
2749             SelectObject( hdc, hfont_old );\r
2750 \r
2751             fontBitmapSquareSize = squareSize;\r
2752         }\r
2753     }\r
2754 \r
2755     if( hdc != NULL ) {\r
2756         DeleteDC( hdc );\r
2757     }\r
2758 \r
2759     if( hdc_window != NULL ) {\r
2760         ReleaseDC( hwndMain, hdc_window );\r
2761     }\r
2762 }\r
2763 \r
2764 HBITMAP\r
2765 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2766 {\r
2767   char name[128];\r
2768 \r
2769   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2770   if (gameInfo.event &&\r
2771       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2772       strcmp(name, "k80s") == 0) {\r
2773     strcpy(name, "tim");\r
2774   }\r
2775   return LoadBitmap(hinst, name);\r
2776 }\r
2777 \r
2778 \r
2779 /* Insert a color into the program's logical palette\r
2780    structure.  This code assumes the given color is\r
2781    the result of the RGB or PALETTERGB macro, and it\r
2782    knows how those macros work (which is documented).\r
2783 */\r
2784 VOID\r
2785 InsertInPalette(COLORREF color)\r
2786 {\r
2787   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2788 \r
2789   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2790     DisplayFatalError("Too many colors", 0, 1);\r
2791     pLogPal->palNumEntries--;\r
2792     return;\r
2793   }\r
2794 \r
2795   pe->peFlags = (char) 0;\r
2796   pe->peRed = (char) (0xFF & color);\r
2797   pe->peGreen = (char) (0xFF & (color >> 8));\r
2798   pe->peBlue = (char) (0xFF & (color >> 16));\r
2799   return;\r
2800 }\r
2801 \r
2802 \r
2803 VOID\r
2804 InitDrawingColors()\r
2805 {\r
2806   if (pLogPal == NULL) {\r
2807     /* Allocate enough memory for a logical palette with\r
2808      * PALETTESIZE entries and set the size and version fields\r
2809      * of the logical palette structure.\r
2810      */\r
2811     pLogPal = (NPLOGPALETTE)\r
2812       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2813                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2814     pLogPal->palVersion    = 0x300;\r
2815   }\r
2816   pLogPal->palNumEntries = 0;\r
2817 \r
2818   InsertInPalette(lightSquareColor);\r
2819   InsertInPalette(darkSquareColor);\r
2820   InsertInPalette(whitePieceColor);\r
2821   InsertInPalette(blackPieceColor);\r
2822   InsertInPalette(highlightSquareColor);\r
2823   InsertInPalette(premoveHighlightColor);\r
2824 \r
2825   /*  create a logical color palette according the information\r
2826    *  in the LOGPALETTE structure.\r
2827    */\r
2828   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2829 \r
2830   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2831   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2832   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2833   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2834   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2835 \r
2836   /* [AS] Force rendering of the font-based pieces */\r
2837   if( fontBitmapSquareSize > 0 ) {\r
2838     fontBitmapSquareSize = 0;\r
2839   }\r
2840 }\r
2841 \r
2842 \r
2843 int\r
2844 BoardWidth(int boardSize, int n)\r
2845 { /* [HGM] argument n added to allow different width and height */\r
2846   int lineGap = sizeInfo[boardSize].lineGap;\r
2847 \r
2848   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2849       lineGap = appData.overrideLineGap;\r
2850   }\r
2851 \r
2852   return (n + 1) * lineGap +\r
2853           n * sizeInfo[boardSize].squareSize;\r
2854 }\r
2855 \r
2856 /* Respond to board resize by dragging edge */\r
2857 VOID\r
2858 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2859 {\r
2860   BoardSize newSize = NUM_SIZES - 1;\r
2861   static int recurse = 0;\r
2862   if (IsIconic(hwndMain)) return;\r
2863   if (recurse > 0) return;\r
2864   recurse++;\r
2865   while (newSize > 0 &&\r
2866          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2867           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2868     newSize--;\r
2869   } \r
2870   boardSize = newSize;\r
2871   InitDrawingSizes(boardSize, flags);\r
2872   recurse--;\r
2873 }\r
2874 \r
2875 \r
2876 \r
2877 VOID\r
2878 InitDrawingSizes(BoardSize boardSize, int flags)\r
2879 {\r
2880   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2881   ChessSquare piece;\r
2882   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2883   HDC hdc;\r
2884   SIZE clockSize, messageSize;\r
2885   HFONT oldFont;\r
2886   char buf[MSG_SIZ];\r
2887   char *str;\r
2888   HMENU hmenu = GetMenu(hwndMain);\r
2889   RECT crect, wrect;\r
2890   int offby;\r
2891   LOGBRUSH logbrush;\r
2892 \r
2893   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2894   smallLayout = sizeInfo[boardSize].smallLayout;\r
2895   squareSize = sizeInfo[boardSize].squareSize;\r
2896   lineGap = sizeInfo[boardSize].lineGap;\r
2897 \r
2898   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2899       lineGap = appData.overrideLineGap;\r
2900   }\r
2901 \r
2902   if (tinyLayout != oldTinyLayout) {\r
2903     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2904     if (tinyLayout) {\r
2905       style &= ~WS_SYSMENU;\r
2906       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2907                  "&Minimize\tCtrl+F4");\r
2908     } else {\r
2909       style |= WS_SYSMENU;\r
2910       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2911     }\r
2912     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2913 \r
2914     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2915       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2916         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2917     }\r
2918     DrawMenuBar(hwndMain);\r
2919   }\r
2920 \r
2921   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2922   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2923 \r
2924   /* Get text area sizes */\r
2925   hdc = GetDC(hwndMain);\r
2926   if (appData.clockMode) {\r
2927     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2928   } else {\r
2929     sprintf(buf, "White");\r
2930   }\r
2931   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2932   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2933   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2934   str = "We only care about the height here";\r
2935   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2936   SelectObject(hdc, oldFont);\r
2937   ReleaseDC(hwndMain, hdc);\r
2938 \r
2939   /* Compute where everything goes */\r
2940   whiteRect.left = OUTER_MARGIN;\r
2941   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2942   whiteRect.top = OUTER_MARGIN;\r
2943   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2944 \r
2945   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2946   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2947   blackRect.top = whiteRect.top;\r
2948   blackRect.bottom = whiteRect.bottom;\r
2949 \r
2950   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2951   if (appData.showButtonBar) {\r
2952     messageRect.right = blackRect.right\r
2953       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2954   } else {\r
2955     messageRect.right = blackRect.right;\r
2956   }\r
2957   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2958   messageRect.bottom = messageRect.top + messageSize.cy;\r
2959 \r
2960   boardRect.left = whiteRect.left;\r
2961   boardRect.right = boardRect.left + boardWidth;\r
2962   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2963   boardRect.bottom = boardRect.top + boardHeight;\r
2964 \r
2965   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2966   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2967   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2968   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2969     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2970   GetWindowRect(hwndMain, &wrect);\r
2971   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2972                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2973   /* compensate if menu bar wrapped */\r
2974   GetClientRect(hwndMain, &crect);\r
2975   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2976   winHeight += offby;\r
2977   switch (flags) {\r
2978   case WMSZ_TOPLEFT:\r
2979     SetWindowPos(hwndMain, NULL, \r
2980                  wrect.right - winWidth, wrect.bottom - winHeight, \r
2981                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2982     break;\r
2983 \r
2984   case WMSZ_TOPRIGHT:\r
2985   case WMSZ_TOP:\r
2986     SetWindowPos(hwndMain, NULL, \r
2987                  wrect.left, wrect.bottom - winHeight, \r
2988                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2989     break;\r
2990 \r
2991   case WMSZ_BOTTOMLEFT:\r
2992   case WMSZ_LEFT:\r
2993     SetWindowPos(hwndMain, NULL, \r
2994                  wrect.right - winWidth, wrect.top, \r
2995                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2996     break;\r
2997 \r
2998   case WMSZ_BOTTOMRIGHT:\r
2999   case WMSZ_BOTTOM:\r
3000   case WMSZ_RIGHT:\r
3001   default:\r
3002     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3003                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3004     break;\r
3005   }\r
3006 \r
3007   hwndPause = NULL;\r
3008   for (i = 0; i < N_BUTTONS; i++) {\r
3009     if (buttonDesc[i].hwnd != NULL) {\r
3010       DestroyWindow(buttonDesc[i].hwnd);\r
3011       buttonDesc[i].hwnd = NULL;\r
3012     }\r
3013     if (appData.showButtonBar) {\r
3014       buttonDesc[i].hwnd =\r
3015         CreateWindow("BUTTON", buttonDesc[i].label,\r
3016                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3017                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3018                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3019                      (HMENU) buttonDesc[i].id,\r
3020                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3021       if (tinyLayout) {\r
3022         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3023                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3024                     MAKELPARAM(FALSE, 0));\r
3025       }\r
3026       if (buttonDesc[i].id == IDM_Pause)\r
3027         hwndPause = buttonDesc[i].hwnd;\r
3028       buttonDesc[i].wndproc = (WNDPROC)\r
3029         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3030     }\r
3031   }\r
3032   if (gridPen != NULL) DeleteObject(gridPen);\r
3033   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3034   if (premovePen != NULL) DeleteObject(premovePen);\r
3035   if (lineGap != 0) {\r
3036     logbrush.lbStyle = BS_SOLID;\r
3037     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3038     gridPen =\r
3039       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3040                    lineGap, &logbrush, 0, NULL);\r
3041     logbrush.lbColor = highlightSquareColor;\r
3042     highlightPen =\r
3043       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3044                    lineGap, &logbrush, 0, NULL);\r
3045 \r
3046     logbrush.lbColor = premoveHighlightColor; \r
3047     premovePen =\r
3048       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3049                    lineGap, &logbrush, 0, NULL);\r
3050 \r
3051     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3052     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3053       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3054       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3055         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3056       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3057         BOARD_WIDTH * (squareSize + lineGap);\r
3058         lineGap / 2 + (i * (squareSize + lineGap));\r
3059       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3060     }\r
3061     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3062       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3063       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3064         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3065         lineGap / 2 + (i * (squareSize + lineGap));\r
3066       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3067         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3068       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3069     }\r
3070   }\r
3071 \r
3072   if (boardSize == oldBoardSize) return;\r
3073   oldBoardSize = boardSize;\r
3074   oldTinyLayout = tinyLayout;\r
3075 \r
3076   /* Load piece bitmaps for this board size */\r
3077   for (i=0; i<=2; i++) {\r
3078     for (piece = WhitePawn;\r
3079          (int) piece < (int) BlackPawn;\r
3080          piece = (ChessSquare) ((int) piece + 1)) {\r
3081       if (pieceBitmap[i][piece] != NULL)\r
3082         DeleteObject(pieceBitmap[i][piece]);\r
3083     }\r
3084   }\r
3085 \r
3086   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3087   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3088   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3089   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3090   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3091   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3092   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3093   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3094   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3095   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3096   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3097   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3098   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3099   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3100   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3101   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3102   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3103   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3104 #ifdef FAIRY\r
3105   if(squareSize==72 || squareSize==49) { /* experiment with some home-made bitmaps */\r
3106   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3107   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3108   pieceBitmap[0][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3109   pieceBitmap[0][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3110   pieceBitmap[0][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3111   pieceBitmap[0][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3112   pieceBitmap[0][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3113   pieceBitmap[0][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3114   pieceBitmap[0][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3115   pieceBitmap[0][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3116   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3117   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3118   pieceBitmap[1][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3119   pieceBitmap[1][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3120   pieceBitmap[1][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3121   pieceBitmap[1][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3122   pieceBitmap[1][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3123   pieceBitmap[1][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3124   pieceBitmap[1][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3125   pieceBitmap[1][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3126   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3127   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3128   pieceBitmap[2][WhiteFairyPawn] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3129   pieceBitmap[2][WhiteFairyKnight] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3130   pieceBitmap[2][WhiteFairyBishop] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3131   pieceBitmap[2][WhiteFairyRook] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3132   pieceBitmap[2][WhiteFairyQueen] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3133   pieceBitmap[2][WhiteFairyCardinal] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3134   pieceBitmap[2][WhiteFairyMarshall] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3135   pieceBitmap[2][WhiteFairyKing] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3136   }\r
3137 #endif\r
3138 \r
3139 }\r
3140 \r
3141 HBITMAP\r
3142 PieceBitmap(ChessSquare p, int kind)\r
3143 {\r
3144   if ((int) p >= (int) BlackPawn)\r
3145     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3146 \r
3147   return pieceBitmap[kind][(int) p];\r
3148 }\r
3149 \r
3150 /***************************************************************/\r
3151 \r
3152 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3153 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3154 /*\r
3155 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3156 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3157 */\r
3158 \r
3159 VOID\r
3160 SquareToPos(int row, int column, int * x, int * y)\r
3161 {\r
3162   if (flipView) {\r
3163     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3164     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3165   } else {\r
3166     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3167     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3168   }\r
3169 }\r
3170 \r
3171 VOID\r
3172 DrawCoordsOnDC(HDC hdc)\r
3173 {\r
3174   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
3175   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
3176   char str[2] = { NULLCHAR, NULLCHAR };\r
3177   int oldMode, oldAlign, x, y, start, i;\r
3178   HFONT oldFont;\r
3179   HBRUSH oldBrush;\r
3180 \r
3181   if (!appData.showCoords)\r
3182     return;\r
3183 \r
3184   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3185 \r
3186   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3187   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3188   oldAlign = GetTextAlign(hdc);\r
3189   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3190 \r
3191   y = boardRect.top + lineGap;\r
3192   x = boardRect.left + lineGap;\r
3193 \r
3194   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3195   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3196     str[0] = files[start + i];\r
3197     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3198     y += squareSize + lineGap;\r
3199   }\r
3200 \r
3201   start = flipView ? 12-BOARD_WIDTH : 12;\r
3202 \r
3203   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3204   for (i = 0; i < BOARD_WIDTH; i++) {\r
3205     str[0] = ranks[start + i];\r
3206     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3207     x += squareSize + lineGap;\r
3208   }    \r
3209 \r
3210   SelectObject(hdc, oldBrush);\r
3211   SetBkMode(hdc, oldMode);\r
3212   SetTextAlign(hdc, oldAlign);\r
3213   SelectObject(hdc, oldFont);\r
3214 }\r
3215 \r
3216 VOID\r
3217 DrawGridOnDC(HDC hdc)\r
3218 {\r
3219   HPEN oldPen;\r
3220  \r
3221   if (lineGap != 0) {\r
3222     oldPen = SelectObject(hdc, gridPen);\r
3223     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3224     SelectObject(hdc, oldPen);\r
3225   }\r
3226 }\r
3227 \r
3228 #define HIGHLIGHT_PEN 0\r
3229 #define PREMOVE_PEN   1\r
3230 \r
3231 VOID\r
3232 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3233 {\r
3234   int x1, y1;\r
3235   HPEN oldPen, hPen;\r
3236   if (lineGap == 0) return;\r
3237   if (flipView) {\r
3238     x1 = boardRect.left +\r
3239       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3240     y1 = boardRect.top +\r
3241       lineGap/2 + y * (squareSize + lineGap);\r
3242   } else {\r
3243     x1 = boardRect.left +\r
3244       lineGap/2 + x * (squareSize + lineGap);\r
3245     y1 = boardRect.top +\r
3246       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3247   }\r
3248   hPen = pen ? premovePen : highlightPen;\r
3249   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3250   MoveToEx(hdc, x1, y1, NULL);\r
3251   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3252   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3253   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3254   LineTo(hdc, x1, y1);\r
3255   SelectObject(hdc, oldPen);\r
3256 }\r
3257 \r
3258 VOID\r
3259 DrawHighlightsOnDC(HDC hdc)\r
3260 {\r
3261   int i;\r
3262   for (i=0; i<2; i++) {\r
3263     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3264       DrawHighlightOnDC(hdc, TRUE,\r
3265                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3266                         HIGHLIGHT_PEN);\r
3267   }\r
3268   for (i=0; i<2; i++) {\r
3269     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3270         premoveHighlightInfo.sq[i].y >= 0) {\r
3271         DrawHighlightOnDC(hdc, TRUE,\r
3272                           premoveHighlightInfo.sq[i].x, \r
3273                           premoveHighlightInfo.sq[i].y,\r
3274                           PREMOVE_PEN);\r
3275     }\r
3276   }\r
3277 }\r
3278 \r
3279 /* Note: sqcolor is used only in monoMode */\r
3280 /* Note that this code is largely duplicated in woptions.c,\r
3281    function DrawSampleSquare, so that needs to be updated too */\r
3282 VOID\r
3283 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3284 {\r
3285   HBITMAP oldBitmap;\r
3286   HBRUSH oldBrush;\r
3287 \r
3288   if (appData.blindfold) return;\r
3289 \r
3290   /* [AS] Use font-based pieces if needed */\r
3291   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3292     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3293     CreatePiecesFromFont();\r
3294 \r
3295     if( fontBitmapSquareSize == squareSize ) {\r
3296         int index = TranslatePieceToFontPiece( piece );\r
3297 \r
3298         SelectObject( tmphdc, hPieceMask[ index ] );\r
3299 \r
3300         BitBlt( hdc,\r
3301             x, y,\r
3302             squareSize, squareSize,\r
3303             tmphdc,\r
3304             0, 0,\r
3305             SRCAND );\r
3306 \r
3307         SelectObject( tmphdc, hPieceFace[ index ] );\r
3308 \r
3309         BitBlt( hdc,\r
3310             x, y,\r
3311             squareSize, squareSize,\r
3312             tmphdc,\r
3313             0, 0,\r
3314             SRCPAINT );\r
3315 \r
3316         return;\r
3317     }\r
3318   }\r
3319 \r
3320   if (appData.monoMode) {\r
3321     SelectObject(tmphdc, PieceBitmap(piece, \r
3322       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3323     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3324            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3325   } else {\r
3326     if (color) {\r
3327       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3328       oldBrush = SelectObject(hdc, whitePieceBrush);\r
3329       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3330 #if 0\r
3331       /* Use black piece color for outline of white pieces */\r
3332       /* Not sure this looks really good (though xboard does it).\r
3333          Maybe better to have another selectable color, default black */\r
3334       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3335       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3336       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3337 #else\r
3338       /* Use black for outline of white pieces */\r
3339       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3340       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);\r
3341 #endif\r
3342     } else {\r
3343 #if 0\r
3344       /* Use white piece color for details of black pieces */\r
3345       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3346          WHITE_PIECE ones aren't always the right shape. */\r
3347       /* Not sure this looks really good (though xboard does it).\r
3348          Maybe better to have another selectable color, default medium gray? */\r
3349       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3350       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3351       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3352       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3353       SelectObject(hdc, blackPieceBrush);\r
3354       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3355 #else\r
3356       /* Use square color for details of black pieces */\r
3357       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3358       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3359       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
3360 #endif\r
3361     }\r
3362     SelectObject(hdc, oldBrush);\r
3363     SelectObject(tmphdc, oldBitmap);\r
3364   }\r
3365 }\r
3366 \r
3367 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3368 int GetBackTextureMode( int algo )\r
3369 {\r
3370     int result = BACK_TEXTURE_MODE_DISABLED;\r
3371 \r
3372     switch( algo ) \r
3373     {\r
3374         case BACK_TEXTURE_MODE_PLAIN:\r
3375             result = 1; /* Always use identity map */\r
3376             break;\r
3377         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3378             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3379             break;\r
3380     }\r
3381 \r
3382     return result;\r
3383 }\r
3384 \r
3385 /* \r
3386     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3387     to handle redraws cleanly (as random numbers would always be different).\r
3388 */\r
3389 VOID RebuildTextureSquareInfo()\r
3390 {\r
3391     BITMAP bi;\r
3392     int lite_w = 0;\r
3393     int lite_h = 0;\r
3394     int dark_w = 0;\r
3395     int dark_h = 0;\r
3396     int row;\r
3397     int col;\r
3398 \r
3399     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3400 \r
3401     if( liteBackTexture != NULL ) {\r
3402         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3403             lite_w = bi.bmWidth;\r
3404             lite_h = bi.bmHeight;\r
3405         }\r
3406     }\r
3407 \r
3408     if( darkBackTexture != NULL ) {\r
3409         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3410             dark_w = bi.bmWidth;\r
3411             dark_h = bi.bmHeight;\r
3412         }\r
3413     }\r
3414 \r
3415     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3416         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3417             if( (col + row) & 1 ) {\r
3418                 /* Lite square */\r
3419                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3420                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3421                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3422                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3423                 }\r
3424             }\r
3425             else {\r
3426                 /* Dark square */\r
3427                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3428                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3429                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3430                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3431                 }\r
3432             }\r
3433         }\r
3434     }\r
3435 }\r
3436 \r
3437 /* [AS] Arrow highlighting support */\r
3438 \r
3439 static int A_WIDTH = 5; /* Width of arrow body */\r
3440 \r
3441 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3442 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3443 \r
3444 static double Sqr( double x )\r
3445 {\r
3446     return x*x;\r
3447 }\r
3448 \r
3449 static int Round( double x )\r
3450 {\r
3451     return (int) (x + 0.5);\r
3452 }\r
3453 \r
3454 /* Draw an arrow between two points using current settings */\r
3455 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3456 {\r
3457     POINT arrow[7];\r
3458     double dx, dy, j, k, x, y;\r
3459 \r
3460     if( d_x == s_x ) {\r
3461         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3462 \r
3463         arrow[0].x = s_x + A_WIDTH;\r
3464         arrow[0].y = s_y;\r
3465 \r
3466         arrow[1].x = s_x + A_WIDTH;\r
3467         arrow[1].y = d_y - h;\r
3468 \r
3469         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3470         arrow[2].y = d_y - h;\r
3471 \r
3472         arrow[3].x = d_x;\r
3473         arrow[3].y = d_y;\r
3474 \r
3475         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3476         arrow[4].y = d_y - h;\r
3477 \r
3478         arrow[5].x = s_x - A_WIDTH;\r
3479         arrow[5].y = d_y - h;\r
3480 \r
3481         arrow[6].x = s_x - A_WIDTH;\r
3482         arrow[6].y = s_y;\r
3483     }\r
3484     else if( d_y == s_y ) {\r
3485         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3486 \r
3487         arrow[0].x = s_x;\r
3488         arrow[0].y = s_y + A_WIDTH;\r
3489 \r
3490         arrow[1].x = d_x - w;\r
3491         arrow[1].y = s_y + A_WIDTH;\r
3492 \r
3493         arrow[2].x = d_x - w;\r
3494         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3495 \r
3496         arrow[3].x = d_x;\r
3497         arrow[3].y = d_y;\r
3498 \r
3499         arrow[4].x = d_x - w;\r
3500         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3501 \r
3502         arrow[5].x = d_x - w;\r
3503         arrow[5].y = s_y - A_WIDTH;\r
3504 \r
3505         arrow[6].x = s_x;\r
3506         arrow[6].y = s_y - A_WIDTH;\r
3507     }\r
3508     else {\r
3509         /* [AS] Needed a lot of paper for this! :-) */\r
3510         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3511         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3512   \r
3513         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3514 \r
3515         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3516 \r
3517         x = s_x;\r
3518         y = s_y;\r
3519 \r
3520         arrow[0].x = Round(x - j);\r
3521         arrow[0].y = Round(y + j*dx);\r
3522 \r
3523         arrow[1].x = Round(x + j);\r
3524         arrow[1].y = Round(y - j*dx);\r
3525 \r
3526         if( d_x > s_x ) {\r
3527             x = (double) d_x - k;\r
3528             y = (double) d_y - k*dy;\r
3529         }\r
3530         else {\r
3531             x = (double) d_x + k;\r
3532             y = (double) d_y + k*dy;\r
3533         }\r
3534 \r
3535         arrow[2].x = Round(x + j);\r
3536         arrow[2].y = Round(y - j*dx);\r
3537 \r
3538         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3539         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3540 \r
3541         arrow[4].x = d_x;\r
3542         arrow[4].y = d_y;\r
3543 \r
3544         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3545         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3546 \r
3547         arrow[6].x = Round(x - j);\r
3548         arrow[6].y = Round(y + j*dx);\r
3549     }\r
3550 \r
3551     Polygon( hdc, arrow, 7 );\r
3552 }\r
3553 \r
3554 /* [AS] Draw an arrow between two squares */\r
3555 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3556 {\r
3557     int s_x, s_y, d_x, d_y;\r
3558     HPEN hpen;\r
3559     HPEN holdpen;\r
3560     HBRUSH hbrush;\r
3561     HBRUSH holdbrush;\r
3562     LOGBRUSH stLB;\r
3563 \r
3564     if( s_col == d_col && s_row == d_row ) {\r
3565         return;\r
3566     }\r
3567 \r
3568     /* Get source and destination points */\r
3569     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3570     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3571 \r
3572     if( d_y > s_y ) {\r
3573         d_y += squareSize / 4;\r
3574     }\r
3575     else if( d_y < s_y ) {\r
3576         d_y += 3 * squareSize / 4;\r
3577     }\r
3578     else {\r
3579         d_y += squareSize / 2;\r
3580     }\r
3581 \r
3582     if( d_x > s_x ) {\r
3583         d_x += squareSize / 4;\r
3584     }\r
3585     else if( d_x < s_x ) {\r
3586         d_x += 3 * squareSize / 4;\r
3587     }\r
3588     else {\r
3589         d_x += squareSize / 2;\r
3590     }\r
3591 \r
3592     s_x += squareSize / 2;\r
3593     s_y += squareSize / 2;\r
3594 \r
3595     /* Adjust width */\r
3596     A_WIDTH = squareSize / 14;\r
3597 \r
3598     /* Draw */\r
3599     stLB.lbStyle = BS_SOLID;\r
3600     stLB.lbColor = appData.highlightArrowColor;\r
3601     stLB.lbHatch = 0;\r
3602 \r
3603     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3604     holdpen = SelectObject( hdc, hpen );\r
3605     hbrush = CreateBrushIndirect( &stLB );\r
3606     holdbrush = SelectObject( hdc, hbrush );\r
3607 \r
3608     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3609 \r
3610     SelectObject( hdc, holdpen );\r
3611     SelectObject( hdc, holdbrush );\r
3612     DeleteObject( hpen );\r
3613     DeleteObject( hbrush );\r
3614 }\r
3615 \r
3616 BOOL HasHighlightInfo()\r
3617 {\r
3618     BOOL result = FALSE;\r
3619 \r
3620     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3621         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3622     {\r
3623         result = TRUE;\r
3624     }\r
3625 \r
3626     return result;\r
3627 }\r
3628 \r
3629 BOOL IsDrawArrowEnabled()\r
3630 {\r
3631     BOOL result = FALSE;\r
3632 \r
3633     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3634         result = TRUE;\r
3635     }\r
3636 \r
3637     return result;\r
3638 }\r
3639 \r
3640 VOID DrawArrowHighlight( HDC hdc )\r
3641 {\r
3642     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3643         DrawArrowBetweenSquares( hdc,\r
3644             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3645             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3646     }\r
3647 }\r
3648 \r
3649 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3650 {\r
3651     HRGN result = NULL;\r
3652 \r
3653     if( HasHighlightInfo() ) {\r
3654         int x1, y1, x2, y2;\r
3655         int sx, sy, dx, dy;\r
3656 \r
3657         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3658         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3659 \r
3660         sx = MIN( x1, x2 );\r
3661         sy = MIN( y1, y2 );\r
3662         dx = MAX( x1, x2 ) + squareSize;\r
3663         dy = MAX( y1, y2 ) + squareSize;\r
3664 \r
3665         result = CreateRectRgn( sx, sy, dx, dy );\r
3666     }\r
3667 \r
3668     return result;\r
3669 }\r
3670 \r
3671 /*\r
3672     Warning: this function modifies the behavior of several other functions. \r
3673     \r
3674     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3675     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3676     repaint is scattered all over the place, which is not good for features such as\r
3677     "arrow highlighting" that require a full repaint of the board.\r
3678 \r
3679     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3680     user interaction, when speed is not so important) but especially to avoid errors\r
3681     in the displayed graphics.\r
3682 \r
3683     In such patched places, I always try refer to this function so there is a single\r
3684     place to maintain knowledge.\r
3685     \r
3686     To restore the original behavior, just return FALSE unconditionally.\r
3687 */\r
3688 BOOL IsFullRepaintPreferrable()\r
3689 {\r
3690     BOOL result = FALSE;\r
3691 \r
3692     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3693         /* Arrow may appear on the board */\r
3694         result = TRUE;\r
3695     }\r
3696 \r
3697     return result;\r
3698 }\r
3699 \r
3700 /* \r
3701     This function is called by DrawPosition to know whether a full repaint must\r
3702     be forced or not.\r
3703 \r
3704     Only DrawPosition may directly call this function, which makes use of \r
3705     some state information. Other function should call DrawPosition specifying \r
3706     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3707 */\r
3708 BOOL DrawPositionNeedsFullRepaint()\r
3709 {\r
3710     BOOL result = FALSE;\r
3711 \r
3712     /* \r
3713         Probably a slightly better policy would be to trigger a full repaint\r
3714         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3715         but animation is fast enough that it's difficult to notice.\r
3716     */\r
3717     if( animInfo.piece == EmptySquare ) {\r
3718         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3719             result = TRUE;\r
3720         }\r
3721     }\r
3722 \r
3723     return result;\r
3724 }\r
3725 \r
3726 VOID\r
3727 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3728 {\r
3729   int row, column, x, y, square_color, piece_color;\r
3730   ChessSquare piece;\r
3731   HBRUSH oldBrush;\r
3732   HDC texture_hdc = NULL;\r
3733 \r
3734   /* [AS] Initialize background textures if needed */\r
3735   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3736       if( backTextureSquareSize != squareSize ) {\r
3737           backTextureSquareSize = squareSize;\r
3738           RebuildTextureSquareInfo();\r
3739       }\r
3740 \r
3741       texture_hdc = CreateCompatibleDC( hdc );\r
3742   }\r
3743 \r
3744   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3745     for (column = 0; column < BOARD_WIDTH; column++) {\r
3746   \r
3747       SquareToPos(row, column, &x, &y);\r
3748 \r
3749       piece = board[row][column];\r
3750 \r
3751       square_color = ((column + row) % 2) == 1;\r
3752       if(!strcmp(appData.variant, "xiangqi") ) {\r
3753           square_color = 1;\r
3754           if( (row < 3 || row > BOARD_HEIGHT-4) &&\r
3755               column < (BOARD_WIDTH + 4)/2 &&\r
3756               column > (BOARD_WIDTH - 5)/2 ) square_color = 0;\r
3757       }\r
3758       piece_color = (int) piece < (int) BlackPawn;\r
3759 \r
3760       if (appData.monoMode) {\r
3761         if (piece == EmptySquare) {\r
3762           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3763                  square_color ? WHITENESS : BLACKNESS);\r
3764         } else {\r
3765           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3766         }\r
3767       } \r
3768       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3769           /* [AS] Draw the square using a texture bitmap */\r
3770           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3771 \r
3772           DrawTile( x, y, \r
3773               squareSize, squareSize, \r
3774               hdc, \r
3775               texture_hdc,\r
3776               backTextureSquareInfo[row][column].mode,\r
3777               backTextureSquareInfo[row][column].x,\r
3778               backTextureSquareInfo[row][column].y );\r
3779 \r
3780           SelectObject( texture_hdc, hbm );\r
3781 \r
3782           if (piece != EmptySquare) {\r
3783               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3784           }\r
3785       }\r
3786       else {\r
3787         oldBrush = SelectObject(hdc, square_color ?\r
3788                                 lightSquareBrush : darkSquareBrush);\r
3789         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3790         SelectObject(hdc, oldBrush);\r
3791         if (piece != EmptySquare)\r
3792           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3793       }\r
3794     }\r
3795   }\r
3796 \r
3797   if( texture_hdc != NULL ) {\r
3798     DeleteDC( texture_hdc );\r
3799   }\r
3800 }\r
3801 \r
3802 #define MAX_CLIPS 200   /* more than enough */\r
3803 \r
3804 VOID\r
3805 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3806 {\r
3807   static Board lastReq, lastDrawn;\r
3808   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3809   static int lastDrawnFlipView = 0;\r
3810   static int lastReqValid = 0, lastDrawnValid = 0;\r
3811   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3812   HDC tmphdc;\r
3813   HDC hdcmem;\r
3814   HBITMAP bufferBitmap;\r
3815   HBITMAP oldBitmap;\r
3816   RECT Rect;\r
3817   HRGN clips[MAX_CLIPS];\r
3818   ChessSquare dragged_piece = EmptySquare;\r
3819 \r
3820   /* I'm undecided on this - this function figures out whether a full\r
3821    * repaint is necessary on its own, so there's no real reason to have the\r
3822    * caller tell it that.  I think this can safely be set to FALSE - but\r
3823    * if we trust the callers not to request full repaints unnessesarily, then\r
3824    * we could skip some clipping work.  In other words, only request a full\r
3825    * redraw when the majority of pieces have changed positions (ie. flip, \r
3826    * gamestart and similar)  --Hawk\r
3827    */\r
3828   Boolean fullrepaint = repaint;\r
3829 \r
3830   if( DrawPositionNeedsFullRepaint() ) {\r
3831       fullrepaint = TRUE;\r
3832   }\r
3833 \r
3834 #if 0\r
3835   if( fullrepaint ) {\r
3836       static int repaint_count = 0;\r
3837       char buf[128];\r
3838 \r
3839       repaint_count++;\r
3840       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
3841       OutputDebugString( buf );\r
3842   }\r
3843 #endif\r
3844 \r
3845   if (board == NULL) {\r
3846     if (!lastReqValid) {\r
3847       return;\r
3848     }\r
3849     board = lastReq;\r
3850   } else {\r
3851     CopyBoard(lastReq, board);\r
3852     lastReqValid = 1;\r
3853   }\r
3854 \r
3855   if (doingSizing) {\r
3856     return;\r
3857   }\r
3858 \r
3859   if (IsIconic(hwndMain)) {\r
3860     return;\r
3861   }\r
3862 \r
3863   if (hdc == NULL) {\r
3864     hdc = GetDC(hwndMain);\r
3865     if (!appData.monoMode) {\r
3866       SelectPalette(hdc, hPal, FALSE);\r
3867       RealizePalette(hdc);\r
3868     }\r
3869     releaseDC = TRUE;\r
3870   } else {\r
3871     releaseDC = FALSE;\r
3872   }\r
3873 \r
3874 #if 0\r
3875   fprintf(debugFP, "*******************************\n"\r
3876                    "repaint = %s\n"\r
3877                    "dragInfo.from (%d,%d)\n"\r
3878                    "dragInfo.start (%d,%d)\n"\r
3879                    "dragInfo.pos (%d,%d)\n"\r
3880                    "dragInfo.lastpos (%d,%d)\n", \r
3881                     repaint ? "TRUE" : "FALSE",\r
3882                     dragInfo.from.x, dragInfo.from.y, \r
3883                     dragInfo.start.x, dragInfo.start.y,\r
3884                     dragInfo.pos.x, dragInfo.pos.y,\r
3885                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
3886   fprintf(debugFP, "prev:  ");\r
3887   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3888     for (column = 0; column < BOARD_WIDTH; column++) {\r
3889       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
3890     }\r
3891   }\r
3892   fprintf(debugFP, "\n");\r
3893   fprintf(debugFP, "board: ");\r
3894   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3895     for (column = 0; column < BOARD_WIDTH; column++) {\r
3896       fprintf(debugFP, "%d ", board[row][column]);\r
3897     }\r
3898   }\r
3899   fprintf(debugFP, "\n");\r
3900   fflush(debugFP);\r
3901 #endif\r
3902 \r
3903   /* Create some work-DCs */\r
3904   hdcmem = CreateCompatibleDC(hdc);\r
3905   tmphdc = CreateCompatibleDC(hdc);\r
3906 \r
3907   /* Figure out which squares need updating by comparing the \r
3908    * newest board with the last drawn board and checking if\r
3909    * flipping has changed.\r
3910    */\r
3911   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
3912     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
3913       for (column = 0; column < BOARD_WIDTH; column++) {\r
3914         if (lastDrawn[row][column] != board[row][column]) {\r
3915           SquareToPos(row, column, &x, &y);\r
3916           clips[num_clips++] =\r
3917             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
3918         }\r
3919       }\r
3920     }\r
3921     for (i=0; i<2; i++) {\r
3922       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
3923           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
3924         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
3925             lastDrawnHighlight.sq[i].y >= 0) {\r
3926           SquareToPos(lastDrawnHighlight.sq[i].y,\r
3927                       lastDrawnHighlight.sq[i].x, &x, &y);\r
3928           clips[num_clips++] =\r
3929             CreateRectRgn(x - lineGap, y - lineGap, \r
3930                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3931         }\r
3932         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
3933           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
3934           clips[num_clips++] =\r
3935             CreateRectRgn(x - lineGap, y - lineGap, \r
3936                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3937         }\r
3938       }\r
3939     }\r
3940     for (i=0; i<2; i++) {\r
3941       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
3942           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
3943         if (lastDrawnPremove.sq[i].x >= 0 &&\r
3944             lastDrawnPremove.sq[i].y >= 0) {\r
3945           SquareToPos(lastDrawnPremove.sq[i].y,\r
3946                       lastDrawnPremove.sq[i].x, &x, &y);\r
3947           clips[num_clips++] =\r
3948             CreateRectRgn(x - lineGap, y - lineGap, \r
3949                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3950         }\r
3951         if (premoveHighlightInfo.sq[i].x >= 0 && \r
3952             premoveHighlightInfo.sq[i].y >= 0) {\r
3953           SquareToPos(premoveHighlightInfo.sq[i].y, \r
3954                       premoveHighlightInfo.sq[i].x, &x, &y);\r
3955           clips[num_clips++] =\r
3956             CreateRectRgn(x - lineGap, y - lineGap, \r
3957                           x + squareSize + lineGap, y + squareSize + lineGap);\r
3958         }\r
3959       }\r
3960     }\r
3961   } else {\r
3962     fullrepaint = TRUE;\r
3963   }\r
3964 \r
3965   /* Create a buffer bitmap - this is the actual bitmap\r
3966    * being written to.  When all the work is done, we can\r
3967    * copy it to the real DC (the screen).  This avoids\r
3968    * the problems with flickering.\r
3969    */\r
3970   GetClientRect(hwndMain, &Rect);\r
3971   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
3972                                         Rect.bottom-Rect.top+1);\r
3973   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
3974   if (!appData.monoMode) {\r
3975     SelectPalette(hdcmem, hPal, FALSE);\r
3976   }\r
3977 \r
3978   /* Create clips for dragging */\r
3979   if (!fullrepaint) {\r
3980     if (dragInfo.from.x >= 0) {\r
3981       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
3982       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3983     }\r
3984     if (dragInfo.start.x >= 0) {\r
3985       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
3986       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3987     }\r
3988     if (dragInfo.pos.x >= 0) {\r
3989       x = dragInfo.pos.x - squareSize / 2;\r
3990       y = dragInfo.pos.y - squareSize / 2;\r
3991       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3992     }\r
3993     if (dragInfo.lastpos.x >= 0) {\r
3994       x = dragInfo.lastpos.x - squareSize / 2;\r
3995       y = dragInfo.lastpos.y - squareSize / 2;\r
3996       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
3997     }\r
3998   }\r
3999 \r
4000   /* If dragging is in progress, we temporarely remove the piece */\r
4001   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4002     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4003     board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4004   }\r
4005 \r
4006   /* Are we animating a move?  \r
4007    * If so, \r
4008    *   - remove the piece from the board (temporarely)\r
4009    *   - calculate the clipping region\r
4010    */\r
4011   if (!fullrepaint) {\r
4012     if (animInfo.piece != EmptySquare) {\r
4013       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4014       x = boardRect.left + animInfo.lastpos.x;\r
4015       y = boardRect.top + animInfo.lastpos.y;\r
4016       x2 = boardRect.left + animInfo.pos.x;\r
4017       y2 = boardRect.top + animInfo.pos.y;\r
4018       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4019       /* Slight kludge.  The real problem is that after AnimateMove is\r
4020          done, the position on the screen does not match lastDrawn.\r
4021          This currently causes trouble only on e.p. captures in\r
4022          atomic, where the piece moves to an empty square and then\r
4023          explodes.  The old and new positions both had an empty square\r
4024          at the destination, but animation has drawn a piece there and\r
4025          we have to remember to erase it. */\r
4026       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4027     }\r
4028   }\r
4029 \r
4030   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4031   if (num_clips == 0)\r
4032     fullrepaint = TRUE;\r
4033 \r
4034   /* Set clipping on the memory DC */\r
4035   if (!fullrepaint) {\r
4036     SelectClipRgn(hdcmem, clips[0]);\r
4037     for (x = 1; x < num_clips; x++) {\r
4038       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4039         abort();  // this should never ever happen!\r
4040     }\r
4041   }\r
4042 \r
4043   /* Do all the drawing to the memory DC */\r
4044   DrawGridOnDC(hdcmem);\r
4045   DrawHighlightsOnDC(hdcmem);\r
4046   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4047 \r
4048   if( appData.highlightMoveWithArrow ) {\r
4049     DrawArrowHighlight(hdcmem);\r
4050   }\r
4051 \r
4052   DrawCoordsOnDC(hdcmem);\r
4053 \r
4054   /* Put the dragged piece back into place and draw it */\r
4055   if (dragged_piece != EmptySquare) {\r
4056     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4057     x = dragInfo.pos.x - squareSize / 2;\r
4058     y = dragInfo.pos.y - squareSize / 2;\r
4059     DrawPieceOnDC(hdcmem, dragged_piece,\r
4060                   ((int) dragged_piece < (int) BlackPawn), \r
4061                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4062   }   \r
4063   \r
4064   /* Put the animated piece back into place and draw it */\r
4065   if (animInfo.piece != EmptySquare) {\r
4066     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4067     x = boardRect.left + animInfo.pos.x;\r
4068     y = boardRect.top + animInfo.pos.y;\r
4069     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4070                   ((int) animInfo.piece < (int) BlackPawn),\r
4071                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4072   }\r
4073 \r
4074   /* Release the bufferBitmap by selecting in the old bitmap \r
4075    * and delete the memory DC\r
4076    */\r
4077   SelectObject(hdcmem, oldBitmap);\r
4078   DeleteDC(hdcmem);\r
4079 \r
4080   /* Set clipping on the target DC */\r
4081   if (!fullrepaint) {\r
4082     SelectClipRgn(hdc, clips[0]);\r
4083     for (x = 1; x < num_clips; x++) {\r
4084       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4085         abort();   // this should never ever happen!\r
4086     } \r
4087   }\r
4088 \r
4089   /* Copy the new bitmap onto the screen in one go.\r
4090    * This way we avoid any flickering\r
4091    */\r
4092   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4093   BitBlt(hdc, boardRect.left, boardRect.top,\r
4094          boardRect.right - boardRect.left,\r
4095          boardRect.bottom - boardRect.top,\r
4096          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4097   SelectObject(tmphdc, oldBitmap);\r
4098 \r
4099   /* Massive cleanup */\r
4100   for (x = 0; x < num_clips; x++)\r
4101     DeleteObject(clips[x]);\r
4102 \r
4103   DeleteDC(tmphdc);\r
4104   DeleteObject(bufferBitmap);\r
4105 \r
4106   if (releaseDC) \r
4107     ReleaseDC(hwndMain, hdc);\r
4108   \r
4109   if (lastDrawnFlipView != flipView) {\r
4110     if (flipView)\r
4111       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4112     else\r
4113       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4114   }\r
4115 \r
4116   CopyBoard(lastDrawn, board);\r
4117   lastDrawnHighlight = highlightInfo;\r
4118   lastDrawnPremove   = premoveHighlightInfo;\r
4119   lastDrawnFlipView = flipView;\r
4120   lastDrawnValid = 1;\r
4121 }\r
4122 \r
4123 \r
4124 /*---------------------------------------------------------------------------*\\r
4125 | CLIENT PAINT PROCEDURE\r
4126 |   This is the main event-handler for the WM_PAINT message.\r
4127 |\r
4128 \*---------------------------------------------------------------------------*/\r
4129 VOID\r
4130 PaintProc(HWND hwnd)\r
4131 {\r
4132   HDC         hdc;\r
4133   PAINTSTRUCT ps;\r
4134   HFONT       oldFont;\r
4135 \r
4136   if(hdc = BeginPaint(hwnd, &ps)) {\r
4137     if (IsIconic(hwnd)) {\r
4138       DrawIcon(hdc, 2, 2, iconCurrent);\r
4139     } else {\r
4140       if (!appData.monoMode) {\r
4141         SelectPalette(hdc, hPal, FALSE);\r
4142         RealizePalette(hdc);\r
4143       }\r
4144       HDCDrawPosition(hdc, 1, NULL);\r
4145       oldFont =\r
4146         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4147       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4148                  ETO_CLIPPED|ETO_OPAQUE,\r
4149                  &messageRect, messageText, strlen(messageText), NULL);\r
4150       SelectObject(hdc, oldFont);\r
4151       DisplayBothClocks();\r
4152     }\r
4153     EndPaint(hwnd,&ps);\r
4154   }\r
4155 \r
4156   return;\r
4157 }\r
4158 \r
4159 \r
4160 /*\r
4161  * If the user selects on a border boundary, return -1; if off the board,\r
4162  *   return -2.  Otherwise map the event coordinate to the square.\r
4163  * The offset boardRect.left or boardRect.top must already have been\r
4164  *   subtracted from x.\r
4165  */\r
4166 int\r
4167 EventToSquare(int x)\r
4168 {\r
4169   if (x <= 0)\r
4170     return -2;\r
4171   if (x < lineGap)\r
4172     return -1;\r
4173   x -= lineGap;\r
4174   if ((x % (squareSize + lineGap)) >= squareSize)\r
4175     return -1;\r
4176   x /= (squareSize + lineGap);\r
4177   if (x >= BOARD_SIZE)\r
4178     return -2;\r
4179   return x;\r
4180 }\r
4181 \r
4182 typedef struct {\r
4183   char piece;\r
4184   int command;\r
4185   char* name;\r
4186 } DropEnable;\r
4187 \r
4188 DropEnable dropEnables[] = {\r
4189   { 'P', DP_Pawn, "Pawn" },\r
4190   { 'N', DP_Knight, "Knight" },\r
4191   { 'B', DP_Bishop, "Bishop" },\r
4192   { 'R', DP_Rook, "Rook" },\r
4193   { 'Q', DP_Queen, "Queen" },\r
4194 };\r
4195 \r
4196 VOID\r
4197 SetupDropMenu(HMENU hmenu)\r
4198 {\r
4199   int i, count, enable;\r
4200   char *p;\r
4201   extern char white_holding[], black_holding[];\r
4202   char item[MSG_SIZ];\r
4203 \r
4204   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4205     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4206                dropEnables[i].piece);\r
4207     count = 0;\r
4208     while (p && *p++ == dropEnables[i].piece) count++;\r
4209     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4210     enable = count > 0 || !appData.testLegality\r
4211       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4212                       && !appData.icsActive);\r
4213     ModifyMenu(hmenu, dropEnables[i].command,\r
4214                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4215                dropEnables[i].command, item);\r
4216   }\r
4217 }\r
4218 \r
4219 static int fromX = -1, fromY = -1, toX, toY;\r
4220 \r
4221 /* Event handler for mouse messages */\r
4222 VOID\r
4223 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4224 {\r
4225   int x, y;\r
4226   POINT pt;\r
4227   static int recursive = 0;\r
4228   HMENU hmenu;\r
4229   BOOLEAN needsRedraw = FALSE;\r
4230   BOOLEAN saveAnimate;\r
4231   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4232   static BOOLEAN sameAgain = FALSE;\r
4233 \r
4234   if (recursive) {\r
4235     if (message == WM_MBUTTONUP) {\r
4236       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4237          to the middle button: we simulate pressing the left button too!\r
4238          */\r
4239       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4240       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4241     }\r
4242     return;\r
4243   }\r
4244   recursive++;\r
4245   \r
4246   pt.x = LOWORD(lParam);\r
4247   pt.y = HIWORD(lParam);\r
4248   x = EventToSquare(pt.x - boardRect.left);\r
4249   y = EventToSquare(pt.y - boardRect.top);\r
4250   if (!flipView && y >= 0) {\r
4251     y = BOARD_HEIGHT - 1 - y;\r
4252   }\r
4253   if (flipView && x >= 0) {\r
4254     x = BOARD_WIDTH - 1 - x;\r
4255   }\r
4256 \r
4257   switch (message) {\r
4258   case WM_LBUTTONDOWN:\r
4259     ErrorPopDown();\r
4260     sameAgain = FALSE;\r
4261     if (y == -2) {\r
4262       /* Downclick vertically off board; check if on clock */\r
4263       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4264         if (gameMode == EditPosition) {\r
4265           SetWhiteToPlayEvent();\r
4266         } else if (gameMode == IcsPlayingBlack ||\r
4267                    gameMode == MachinePlaysWhite) {\r
4268           CallFlagEvent();\r
4269         }\r
4270       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4271         if (gameMode == EditPosition) {\r
4272           SetBlackToPlayEvent();\r
4273         } else if (gameMode == IcsPlayingWhite ||\r
4274                    gameMode == MachinePlaysBlack) {\r
4275           CallFlagEvent();\r
4276         }\r
4277       }\r
4278       if (!appData.highlightLastMove) {\r
4279         ClearHighlights();\r
4280         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4281       }\r
4282       fromX = fromY = -1;\r
4283       dragInfo.start.x = dragInfo.start.y = -1;\r
4284       dragInfo.from = dragInfo.start;\r
4285       break;\r
4286     } else if (x < 0 || y < 0) {\r
4287       break;\r
4288     } else if (fromX == x && fromY == y) {\r
4289       /* Downclick on same square again */\r
4290       ClearHighlights();\r
4291       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4292       sameAgain = TRUE;  \r
4293     } else if (fromX != -1) {\r
4294       /* Downclick on different square */\r
4295       ChessSquare pdown, pup;\r
4296       pdown = boards[currentMove][fromY][fromX];\r
4297       pup = boards[currentMove][y][x];\r
4298       if (gameMode == EditPosition || /* [HGM] max piece > King! */\r
4299           !((WhitePawn <= pdown && pdown < BlackPawn &&\r
4300              WhitePawn <= pup && pup < BlackPawn) ||\r
4301             (BlackPawn <= pdown && pdown < EmptySquare &&\r
4302              BlackPawn <= pup && pup < EmptySquare))) {\r
4303         /* EditPosition, empty square, or different color piece;\r
4304            click-click move is possible */\r
4305         toX = x;\r
4306         toY = y;\r
4307         if (IsPromotion(fromX, fromY, toX, toY)) {\r
4308           if (appData.alwaysPromoteToQueen) {\r
4309             UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
4310             if (!appData.highlightLastMove) {\r
4311               ClearHighlights();\r
4312               DrawPosition(forceFullRepaint || FALSE, NULL);\r
4313             }\r
4314           } else {\r
4315             SetHighlights(fromX, fromY, toX, toY);\r
4316             DrawPosition(forceFullRepaint || FALSE, NULL);\r
4317             PromotionPopup(hwnd);\r
4318           }\r
4319         } else {        /* not a promotion */\r
4320           if (appData.animate || appData.highlightLastMove) {\r
4321             SetHighlights(fromX, fromY, toX, toY);\r
4322           } else {\r
4323             ClearHighlights();\r
4324           }\r
4325           UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
4326           if (appData.animate && !appData.highlightLastMove) {\r
4327             ClearHighlights();\r
4328             DrawPosition(forceFullRepaint || FALSE, NULL);\r
4329           }\r
4330         }\r
4331         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4332         fromX = fromY = -1;\r
4333         break;\r
4334       }\r
4335       ClearHighlights();\r
4336       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4337     }\r
4338     /* First downclick, or restart on a square with same color piece */\r
4339     if (!frozen && OKToStartUserMove(x, y)) {\r
4340       fromX = x;\r
4341       fromY = y;\r
4342       dragInfo.lastpos = pt;\r
4343       dragInfo.from.x = fromX;\r
4344       dragInfo.from.y = fromY;\r
4345       dragInfo.start = dragInfo.from;\r
4346       SetCapture(hwndMain);\r
4347     } else {\r
4348       fromX = fromY = -1;\r
4349       dragInfo.start.x = dragInfo.start.y = -1;\r
4350       dragInfo.from = dragInfo.start;\r
4351       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4352     }\r
4353     break;\r
4354 \r
4355   case WM_LBUTTONUP:\r
4356     ReleaseCapture();\r
4357     if (fromX == -1) break;\r
4358     if (x == fromX && y == fromY) {\r
4359       dragInfo.from.x = dragInfo.from.y = -1;\r
4360       /* Upclick on same square */\r
4361       if (sameAgain) {\r
4362         /* Clicked same square twice: abort click-click move */\r
4363         fromX = fromY = -1;\r
4364         gotPremove = 0;\r
4365         ClearPremoveHighlights();\r
4366       } else {\r
4367         /* First square clicked: start click-click move */\r
4368         SetHighlights(fromX, fromY, -1, -1);\r
4369       }\r
4370       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4371     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4372       /* Errant click; ignore */\r
4373       break;\r
4374     } else {\r
4375       /* Finish drag move */\r
4376       dragInfo.from.x = dragInfo.from.y = -1;\r
4377       toX = x;\r
4378       toY = y;\r
4379       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4380       appData.animate = appData.animate && !appData.animateDragging;\r
4381       if (IsPromotion(fromX, fromY, toX, toY)) {\r
4382         if (appData.alwaysPromoteToQueen) {\r
4383           UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
4384         } else {\r
4385           DrawPosition(forceFullRepaint || FALSE, NULL);\r
4386           PromotionPopup(hwnd);\r
4387         }\r
4388       } else {\r
4389         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
4390       }\r
4391       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4392       appData.animate = saveAnimate;\r
4393       fromX = fromY = -1;\r
4394       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4395         ClearHighlights();\r
4396       }\r
4397       if (appData.animate || appData.animateDragging ||\r
4398           appData.highlightDragging || gotPremove) {\r
4399         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4400       }\r
4401     }\r
4402     dragInfo.start.x = dragInfo.start.y = -1; \r
4403     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4404     break;\r
4405 \r
4406   case WM_MOUSEMOVE:\r
4407     if ((appData.animateDragging || appData.highlightDragging)\r
4408         && (wParam & MK_LBUTTON)\r
4409         && dragInfo.from.x >= 0) \r
4410     {\r
4411       BOOL full_repaint = FALSE;\r
4412 \r
4413       if (appData.animateDragging) {\r
4414         dragInfo.pos = pt;\r
4415       }\r
4416       if (appData.highlightDragging) {\r
4417         SetHighlights(fromX, fromY, x, y);\r
4418         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y > BOARD_WIDTH) ) {\r
4419             full_repaint = TRUE;\r
4420         }\r
4421       }\r
4422       \r
4423       DrawPosition( full_repaint, NULL);\r
4424       \r
4425       dragInfo.lastpos = dragInfo.pos;\r
4426     }\r
4427     break;\r
4428 \r
4429   case WM_MBUTTONDOWN:\r
4430   case WM_RBUTTONDOWN:\r
4431     ErrorPopDown();\r
4432     ReleaseCapture();\r
4433     fromX = fromY = -1;\r
4434     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4435     dragInfo.start.x = dragInfo.start.y = -1;\r
4436     dragInfo.from = dragInfo.start;\r
4437     dragInfo.lastpos = dragInfo.pos;\r
4438     if (appData.highlightDragging) {\r
4439       ClearHighlights();\r
4440     }\r
4441     DrawPosition(TRUE, NULL);\r
4442 \r
4443     switch (gameMode) {\r
4444     case EditPosition:\r
4445     case IcsExamining:\r
4446       if (x < 0 || y < 0) break;\r
4447       fromX = x;\r
4448       fromY = y;\r
4449       if (message == WM_MBUTTONDOWN) {\r
4450         buttonCount = 3;  /* even if system didn't think so */\r
4451         if (wParam & MK_SHIFT) \r
4452           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4453         else\r
4454           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4455       } else { /* message == WM_RBUTTONDOWN */\r
4456 #if 0\r
4457         if (buttonCount == 3) {\r
4458           if (wParam & MK_SHIFT) \r
4459             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4460           else\r
4461             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4462         } else {\r
4463           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4464         }\r
4465 #else\r
4466         /* Just have one menu, on the right button.  Windows users don't\r
4467            think to try the middle one, and sometimes other software steals\r
4468            it, or it doesn't really exist. */\r
4469         MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4470 #endif\r
4471       }\r
4472       break;\r
4473     case IcsPlayingWhite:\r
4474     case IcsPlayingBlack:\r
4475     case EditGame:\r
4476     case MachinePlaysWhite:\r
4477     case MachinePlaysBlack:\r
4478       if (appData.testLegality &&\r
4479           gameInfo.variant != VariantBughouse &&\r
4480           gameInfo.variant != VariantCrazyhouse) break;\r
4481       if (x < 0 || y < 0) break;\r
4482       fromX = x;\r
4483       fromY = y;\r
4484       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4485       SetupDropMenu(hmenu);\r
4486       MenuPopup(hwnd, pt, hmenu, -1);\r
4487       break;\r
4488     default:\r
4489       break;\r
4490     }\r
4491     break;\r
4492   }\r
4493 \r
4494   recursive--;\r
4495 }\r
4496 \r
4497 /* Preprocess messages for buttons in main window */\r
4498 LRESULT CALLBACK\r
4499 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4500 {\r
4501   int id = GetWindowLong(hwnd, GWL_ID);\r
4502   int i, dir;\r
4503 \r
4504   for (i=0; i<N_BUTTONS; i++) {\r
4505     if (buttonDesc[i].id == id) break;\r
4506   }\r
4507   if (i == N_BUTTONS) return 0;\r
4508   switch (message) {\r
4509   case WM_KEYDOWN:\r
4510     switch (wParam) {\r
4511     case VK_LEFT:\r
4512     case VK_RIGHT:\r
4513       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4514       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4515       return TRUE;\r
4516     }\r
4517     break;\r
4518   case WM_CHAR:\r
4519     switch (wParam) {\r
4520     case '\r':\r
4521       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4522       return TRUE;\r
4523     case '\t':\r
4524       if (appData.icsActive) {\r
4525         if (GetKeyState(VK_SHIFT) < 0) {\r
4526           /* shifted */\r
4527           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4528           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4529           SetFocus(h);\r
4530         } else {\r
4531           /* unshifted */\r
4532           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4533           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4534           SetFocus(h);\r
4535         }\r
4536         return TRUE;\r
4537       }\r
4538       break;\r
4539     default:\r
4540       if (appData.icsActive) {\r
4541         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4542         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4543         SetFocus(h);\r
4544         SendMessage(h, WM_CHAR, wParam, lParam);\r
4545         return TRUE;\r
4546       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4547         PopUpMoveDialog((char)wParam);\r
4548       }\r
4549       break;\r
4550     }\r
4551     break;\r
4552   }\r
4553   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4554 }\r
4555 \r
4556 /* Process messages for Promotion dialog box */\r
4557 LRESULT CALLBACK\r
4558 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4559 {\r
4560   char promoChar;\r
4561 \r
4562   switch (message) {\r
4563   case WM_INITDIALOG: /* message: initialize dialog box */\r
4564     /* Center the dialog over the application window */\r
4565     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4566     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4567       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4568        gameInfo.variant == VariantGiveaway) ?\r
4569                SW_SHOW : SW_HIDE);\r
4570 #ifdef FAIRY\r
4571     /* [HGM] Only allow C & A promotions in Capablanca Chess */\r
4572     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4573       (gameInfo.variant == VariantCapablanca || \r
4574        gameInfo.variant == VariantGothic) ?\r
4575                SW_SHOW : SW_HIDE);\r
4576     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4577       (gameInfo.variant == VariantCapablanca || \r
4578        gameInfo.variant == VariantGothic) ?\r
4579                SW_SHOW : SW_HIDE);\r
4580 #endif\r
4581     return TRUE;\r
4582 \r
4583   case WM_COMMAND: /* message: received a command */\r
4584     switch (LOWORD(wParam)) {\r
4585     case IDCANCEL:\r
4586       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4587       ClearHighlights();\r
4588       DrawPosition(FALSE, NULL);\r
4589       return TRUE;\r
4590     case PB_King:\r
4591       promoChar = 'k';\r
4592       break;\r
4593     case PB_Queen:\r
4594       promoChar = 'q';\r
4595       break;\r
4596     case PB_Rook:\r
4597       promoChar = 'r';\r
4598       break;\r
4599     case PB_Bishop:\r
4600       promoChar = 'b';\r
4601       break;\r
4602 #ifdef FAIRY\r
4603     case PB_Chancellor:\r
4604       promoChar = 'c';\r
4605       break;\r
4606     case PB_Archbishop:\r
4607       promoChar = 'a';\r
4608       break;\r
4609 #endif\r
4610     case PB_Knight:\r
4611       promoChar = 'n';\r
4612       break;\r
4613     default:\r
4614       return FALSE;\r
4615     }\r
4616     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4617     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
4618     if (!appData.highlightLastMove) {\r
4619       ClearHighlights();\r
4620       DrawPosition(FALSE, NULL);\r
4621     }\r
4622     return TRUE;\r
4623   }\r
4624   return FALSE;\r
4625 }\r
4626 \r
4627 /* Pop up promotion dialog */\r
4628 VOID\r
4629 PromotionPopup(HWND hwnd)\r
4630 {\r
4631   FARPROC lpProc;\r
4632 \r
4633   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4634   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4635     hwnd, (DLGPROC)lpProc);\r
4636   FreeProcInstance(lpProc);\r
4637 }\r
4638 \r
4639 /* Toggle ShowThinking */\r
4640 VOID\r
4641 ToggleShowThinking()\r
4642 {\r
4643   ShowThinkingEvent(!appData.showThinking);\r
4644 }\r
4645 \r
4646 VOID\r
4647 LoadGameDialog(HWND hwnd, char* title)\r
4648 {\r
4649   UINT number = 0;\r
4650   FILE *f;\r
4651   char fileTitle[MSG_SIZ];\r
4652   f = OpenFileDialog(hwnd, FALSE, "",\r
4653                      appData.oldSaveStyle ? "gam" : "pgn",\r
4654                      GAME_FILT,\r
4655                      title, &number, fileTitle, NULL);\r
4656   if (f != NULL) {\r
4657     cmailMsgLoaded = FALSE;\r
4658     if (number == 0) {\r
4659       int error = GameListBuild(f);\r
4660       if (error) {\r
4661         DisplayError("Cannot build game list", error);\r
4662       } else if (!ListEmpty(&gameList) &&\r
4663                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4664         GameListPopUp(f, fileTitle);\r
4665         return;\r
4666       }\r
4667       GameListDestroy();\r
4668       number = 1;\r
4669     }\r
4670     LoadGame(f, number, fileTitle, FALSE);\r
4671   }\r
4672 }\r
4673 \r
4674 VOID\r
4675 ChangedConsoleFont()\r
4676 {\r
4677   CHARFORMAT cfmt;\r
4678   CHARRANGE tmpsel, sel;\r
4679   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4680   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4681   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4682   PARAFORMAT paraf;\r
4683 \r
4684   cfmt.cbSize = sizeof(CHARFORMAT);\r
4685   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4686   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4687   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4688    * size.  This was undocumented in the version of MSVC++ that I had\r
4689    * when I wrote the code, but is apparently documented now.\r
4690    */\r
4691   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4692   cfmt.bCharSet = f->lf.lfCharSet;\r
4693   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4694   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4695   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4696   /* Why are the following seemingly needed too? */\r
4697   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4698   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4699   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4700   tmpsel.cpMin = 0;\r
4701   tmpsel.cpMax = -1; /*999999?*/\r
4702   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4703   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4704   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4705    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4706    */\r
4707   paraf.cbSize = sizeof(paraf);\r
4708   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4709   paraf.dxStartIndent = 0;\r
4710   paraf.dxOffset = WRAP_INDENT;\r
4711   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4712   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4713 }\r
4714 \r
4715 /*---------------------------------------------------------------------------*\\r
4716  *\r
4717  * Window Proc for main window\r
4718  *\r
4719 \*---------------------------------------------------------------------------*/\r
4720 \r
4721 /* Process messages for main window, etc. */\r
4722 LRESULT CALLBACK\r
4723 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4724 {\r
4725   FARPROC lpProc;\r
4726   int wmId, wmEvent;\r
4727   char *defName;\r
4728   FILE *f;\r
4729   UINT number;\r
4730   char fileTitle[MSG_SIZ];\r
4731   static SnapData sd;\r
4732 \r
4733   switch (message) {\r
4734 \r
4735   case WM_PAINT: /* message: repaint portion of window */\r
4736     PaintProc(hwnd);\r
4737     break;\r
4738 \r
4739   case WM_ERASEBKGND:\r
4740     if (IsIconic(hwnd)) {\r
4741       /* Cheat; change the message */\r
4742       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4743     } else {\r
4744       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4745     }\r
4746     break;\r
4747 \r
4748   case WM_LBUTTONDOWN:\r
4749   case WM_MBUTTONDOWN:\r
4750   case WM_RBUTTONDOWN:\r
4751   case WM_LBUTTONUP:\r
4752   case WM_MBUTTONUP:\r
4753   case WM_RBUTTONUP:\r
4754   case WM_MOUSEMOVE:\r
4755     MouseEvent(hwnd, message, wParam, lParam);\r
4756     break;\r
4757 \r
4758   case WM_CHAR:\r
4759     \r
4760     if (appData.icsActive) {\r
4761       if (wParam == '\t') {\r
4762         if (GetKeyState(VK_SHIFT) < 0) {\r
4763           /* shifted */\r
4764           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4765           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4766           SetFocus(h);\r
4767         } else {\r
4768           /* unshifted */\r
4769           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4770           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4771           SetFocus(h);\r
4772         }\r
4773       } else {\r
4774         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4775         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4776         SetFocus(h);\r
4777         SendMessage(h, message, wParam, lParam);\r
4778       }\r
4779     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4780       PopUpMoveDialog((char)wParam);\r
4781     }\r
4782     break;\r
4783 \r
4784   case WM_PALETTECHANGED:\r
4785     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4786       int nnew;\r
4787       HDC hdc = GetDC(hwndMain);\r
4788       SelectPalette(hdc, hPal, TRUE);\r
4789       nnew = RealizePalette(hdc);\r
4790       if (nnew > 0) {\r
4791         paletteChanged = TRUE;\r
4792 #if 0\r
4793         UpdateColors(hdc);\r
4794 #else\r
4795         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
4796 #endif\r
4797       }\r
4798       ReleaseDC(hwnd, hdc);\r
4799     }\r
4800     break;\r
4801 \r
4802   case WM_QUERYNEWPALETTE:\r
4803     if (!appData.monoMode /*&& paletteChanged*/) {\r
4804       int nnew;\r
4805       HDC hdc = GetDC(hwndMain);\r
4806       paletteChanged = FALSE;\r
4807       SelectPalette(hdc, hPal, FALSE);\r
4808       nnew = RealizePalette(hdc);\r
4809       if (nnew > 0) {\r
4810         InvalidateRect(hwnd, &boardRect, FALSE);\r
4811       }\r
4812       ReleaseDC(hwnd, hdc);\r
4813       return TRUE;\r
4814     }\r
4815     return FALSE;\r
4816 \r
4817   case WM_COMMAND: /* message: command from application menu */\r
4818     wmId    = LOWORD(wParam);\r
4819     wmEvent = HIWORD(wParam);\r
4820 \r
4821     switch (wmId) {\r
4822     case IDM_NewGame:\r
4823       ResetGameEvent();\r
4824       AnalysisPopDown();\r
4825       break;\r
4826 \r
4827     case IDM_NewGameFRC:\r
4828       if( NewGameFRC() == 0 ) {\r
4829         ResetGameEvent();\r
4830         AnalysisPopDown();\r
4831       }\r
4832       break;\r
4833 \r
4834     case IDM_LoadGame:\r
4835       LoadGameDialog(hwnd, "Load Game from File");\r
4836       break;\r
4837 \r
4838     case IDM_LoadNextGame:\r
4839       ReloadGame(1);\r
4840       break;\r
4841 \r
4842     case IDM_LoadPrevGame:\r
4843       ReloadGame(-1);\r
4844       break;\r
4845 \r
4846     case IDM_ReloadGame:\r
4847       ReloadGame(0);\r
4848       break;\r
4849 \r
4850     case IDM_LoadPosition:\r
4851       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
4852         Reset(FALSE, TRUE);\r
4853       }\r
4854       number = 1;\r
4855       f = OpenFileDialog(hwnd, FALSE, "",\r
4856                          appData.oldSaveStyle ? "pos" : "fen",\r
4857                          POSITION_FILT,\r
4858                          "Load Position from File", &number, fileTitle, NULL);\r
4859       if (f != NULL) {\r
4860         LoadPosition(f, number, fileTitle);\r
4861       }\r
4862       break;\r
4863 \r
4864     case IDM_LoadNextPosition:\r
4865       ReloadPosition(1);\r
4866       break;\r
4867 \r
4868     case IDM_LoadPrevPosition:\r
4869       ReloadPosition(-1);\r
4870       break;\r
4871 \r
4872     case IDM_ReloadPosition:\r
4873       ReloadPosition(0);\r
4874       break;\r
4875 \r
4876     case IDM_SaveGame:\r
4877       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
4878       f = OpenFileDialog(hwnd, TRUE, defName,\r
4879                          appData.oldSaveStyle ? "gam" : "pgn",\r
4880                          GAME_FILT,\r
4881                          "Save Game to File", NULL, fileTitle, NULL);\r
4882       if (f != NULL) {\r
4883         SaveGame(f, 0, "");\r
4884       }\r
4885       break;\r
4886 \r
4887     case IDM_SavePosition:\r
4888       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
4889       f = OpenFileDialog(hwnd, TRUE, defName,\r
4890                          appData.oldSaveStyle ? "pos" : "fen",\r
4891                          POSITION_FILT,\r
4892                          "Save Position to File", NULL, fileTitle, NULL);\r
4893       if (f != NULL) {\r
4894         SavePosition(f, 0, "");\r
4895       }\r
4896       break;\r
4897 \r
4898     case IDM_CopyGame:\r
4899       CopyGameToClipboard();\r
4900       break;\r
4901 \r
4902     case IDM_PasteGame:\r
4903       PasteGameFromClipboard();\r
4904       break;\r
4905 \r
4906     case IDM_CopyGameListToClipboard:\r
4907       CopyGameListToClipboard();\r
4908       break;\r
4909 \r
4910     /* [AS] Autodetect FEN or PGN data */\r
4911     case IDM_PasteAny:\r
4912       PasteGameOrFENFromClipboard();\r
4913       break;\r
4914 \r
4915     /* [AS] Move history */\r
4916     case IDM_ShowMoveHistory:\r
4917         if( MoveHistoryIsUp() ) {\r
4918             MoveHistoryPopDown();\r
4919         }\r
4920         else {\r
4921             MoveHistoryPopUp();\r
4922         }\r
4923         break;\r
4924 \r
4925     /* [AS] Eval graph */\r
4926     case IDM_ShowEvalGraph:\r
4927         if( EvalGraphIsUp() ) {\r
4928             EvalGraphPopDown();\r
4929         }\r
4930         else {\r
4931             EvalGraphPopUp();\r
4932         }\r
4933         break;\r
4934 \r
4935     /* [AS] Engine output */\r
4936     case IDM_ShowEngineOutput:\r
4937         if( EngineOutputIsUp() ) {\r
4938             EngineOutputPopDown();\r
4939         }\r
4940         else {\r
4941             EngineOutputPopUp();\r
4942         }\r
4943         break;\r
4944 \r
4945     /* [AS] User adjudication */\r
4946     case IDM_UserAdjudication_White:\r
4947         UserAdjudicationEvent( +1 );\r
4948         break;\r
4949 \r
4950     case IDM_UserAdjudication_Black:\r
4951         UserAdjudicationEvent( -1 );\r
4952         break;\r
4953 \r
4954     case IDM_UserAdjudication_Draw:\r
4955         UserAdjudicationEvent( 0 );\r
4956         break;\r
4957 \r
4958     /* [AS] Game list options dialog */\r
4959     case IDM_GameListOptions:\r
4960       GameListOptions();\r
4961       break;\r
4962 \r
4963     case IDM_CopyPosition:\r
4964       CopyFENToClipboard();\r
4965       break;\r
4966 \r
4967     case IDM_PastePosition:\r
4968       PasteFENFromClipboard();\r
4969       break;\r
4970 \r
4971     case IDM_MailMove:\r
4972       MailMoveEvent();\r
4973       break;\r
4974 \r
4975     case IDM_ReloadCMailMsg:\r
4976       Reset(TRUE, TRUE);\r
4977       ReloadCmailMsgEvent(FALSE);\r
4978       break;\r
4979 \r
4980     case IDM_Minimize:\r
4981       ShowWindow(hwnd, SW_MINIMIZE);\r
4982       break;\r
4983 \r
4984     case IDM_Exit:\r
4985       ExitEvent(0);\r
4986       break;\r
4987 \r
4988     case IDM_MachineWhite:\r
4989       MachineWhiteEvent();\r
4990       /*\r
4991        * refresh the tags dialog only if it's visible\r
4992        */\r
4993       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
4994           char *tags;\r
4995           tags = PGNTags(&gameInfo);\r
4996           TagsPopUp(tags, CmailMsg());\r
4997           free(tags);\r
4998       }\r
4999       break;\r
5000 \r
5001     case IDM_MachineBlack:\r
5002       MachineBlackEvent();\r
5003       /*\r
5004        * refresh the tags dialog only if it's visible\r
5005        */\r
5006       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5007           char *tags;\r
5008           tags = PGNTags(&gameInfo);\r
5009           TagsPopUp(tags, CmailMsg());\r
5010           free(tags);\r
5011       }\r
5012       break;\r
5013 \r
5014     case IDM_TwoMachines:\r
5015       TwoMachinesEvent();\r
5016       /*\r
5017        * refresh the tags dialog only if it's visible\r
5018        */\r
5019       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5020           char *tags;\r
5021           tags = PGNTags(&gameInfo);\r
5022           TagsPopUp(tags, CmailMsg());\r
5023           free(tags);\r
5024       }\r
5025       break;\r
5026 \r
5027     case IDM_AnalysisMode:\r
5028       if (!first.analysisSupport) {\r
5029         char buf[MSG_SIZ];\r
5030         sprintf(buf, "%s does not support analysis", first.tidy);\r
5031         DisplayError(buf, 0);\r
5032       } else {\r
5033         if (!appData.showThinking) ToggleShowThinking();\r
5034         AnalyzeModeEvent();\r
5035       }\r
5036       break;\r
5037 \r
5038     case IDM_AnalyzeFile:\r
5039       if (!first.analysisSupport) {\r
5040         char buf[MSG_SIZ];\r
5041         sprintf(buf, "%s does not support analysis", first.tidy);\r
5042         DisplayError(buf, 0);\r
5043       } else {\r
5044         if (!appData.showThinking) ToggleShowThinking();\r
5045         AnalyzeFileEvent();\r
5046         LoadGameDialog(hwnd, "Analyze Game from File");\r
5047         AnalysisPeriodicEvent(1);\r
5048       }\r
5049       break;\r
5050 \r
5051     case IDM_IcsClient:\r
5052       IcsClientEvent();\r
5053       break;\r
5054 \r
5055     case IDM_EditGame:\r
5056       EditGameEvent();\r
5057       break;\r
5058 \r
5059     case IDM_EditPosition:\r
5060       EditPositionEvent();\r
5061       break;\r
5062 \r
5063     case IDM_Training:\r
5064       TrainingEvent();\r
5065       break;\r
5066 \r
5067     case IDM_ShowGameList:\r
5068       ShowGameListProc();\r
5069       break;\r
5070 \r
5071     case IDM_EditTags:\r
5072       EditTagsProc();\r
5073       break;\r
5074 \r
5075     case IDM_EditComment:\r
5076       if (commentDialogUp && editComment) {\r
5077         CommentPopDown();\r
5078       } else {\r
5079         EditCommentEvent();\r
5080       }\r
5081       break;\r
5082 \r
5083     case IDM_Pause:\r
5084       PauseEvent();\r
5085       break;\r
5086 \r
5087     case IDM_Accept:\r
5088       AcceptEvent();\r
5089       break;\r
5090 \r
5091     case IDM_Decline:\r
5092       DeclineEvent();\r
5093       break;\r
5094 \r
5095     case IDM_Rematch:\r
5096       RematchEvent();\r
5097       break;\r
5098 \r
5099     case IDM_CallFlag:\r
5100       CallFlagEvent();\r
5101       break;\r
5102 \r
5103     case IDM_Draw:\r
5104       DrawEvent();\r
5105       break;\r
5106 \r
5107     case IDM_Adjourn:\r
5108       AdjournEvent();\r
5109       break;\r
5110 \r
5111     case IDM_Abort:\r
5112       AbortEvent();\r
5113       break;\r
5114 \r
5115     case IDM_Resign:\r
5116       ResignEvent();\r
5117       break;\r
5118 \r
5119     case IDM_StopObserving:\r
5120       StopObservingEvent();\r
5121       break;\r
5122 \r
5123     case IDM_StopExamining:\r
5124       StopExaminingEvent();\r
5125       break;\r
5126 \r
5127     case IDM_TypeInMove:\r
5128       PopUpMoveDialog('\000');\r
5129       break;\r
5130 \r
5131     case IDM_Backward:\r
5132       BackwardEvent();\r
5133       SetFocus(hwndMain);\r
5134       break;\r
5135 \r
5136     case IDM_Forward:\r
5137       ForwardEvent();\r
5138       SetFocus(hwndMain);\r
5139       break;\r
5140 \r
5141     case IDM_ToStart:\r
5142       ToStartEvent();\r
5143       SetFocus(hwndMain);\r
5144       break;\r
5145 \r
5146     case IDM_ToEnd:\r
5147       ToEndEvent();\r
5148       SetFocus(hwndMain);\r
5149       break;\r
5150 \r
5151     case IDM_Revert:\r
5152       RevertEvent();\r
5153       break;\r
5154 \r
5155     case IDM_TruncateGame:\r
5156       TruncateGameEvent();\r
5157       break;\r
5158 \r
5159     case IDM_MoveNow:\r
5160       MoveNowEvent();\r
5161       break;\r
5162 \r
5163     case IDM_RetractMove:\r
5164       RetractMoveEvent();\r
5165       break;\r
5166 \r
5167     case IDM_FlipView:\r
5168       flipView = !flipView;\r
5169       DrawPosition(FALSE, NULL);\r
5170       break;\r
5171 \r
5172     case IDM_GeneralOptions:\r
5173       GeneralOptionsPopup(hwnd);\r
5174       DrawPosition(TRUE, NULL);\r
5175       break;\r
5176 \r
5177     case IDM_BoardOptions:\r
5178       BoardOptionsPopup(hwnd);\r
5179       break;\r
5180 \r
5181     case IDM_EnginePlayOptions:\r
5182       EnginePlayOptionsPopup(hwnd);\r
5183       break;\r
5184 \r
5185     case IDM_OptionsUCI:\r
5186       UciOptionsPopup(hwnd);\r
5187       break;\r
5188 \r
5189     case IDM_IcsOptions:\r
5190       IcsOptionsPopup(hwnd);\r
5191       break;\r
5192 \r
5193     case IDM_Fonts:\r
5194       FontsOptionsPopup(hwnd);\r
5195       break;\r
5196 \r
5197     case IDM_Sounds:\r
5198       SoundOptionsPopup(hwnd);\r
5199       break;\r
5200 \r
5201     case IDM_CommPort:\r
5202       CommPortOptionsPopup(hwnd);\r
5203       break;\r
5204 \r
5205     case IDM_LoadOptions:\r
5206       LoadOptionsPopup(hwnd);\r
5207       break;\r
5208 \r
5209     case IDM_SaveOptions:\r
5210       SaveOptionsPopup(hwnd);\r
5211       break;\r
5212 \r
5213     case IDM_TimeControl:\r
5214       TimeControlOptionsPopup(hwnd);\r
5215       break;\r
5216 \r
5217     case IDM_SaveSettings:\r
5218       SaveSettings(settingsFileName);\r
5219       break;\r
5220 \r
5221     case IDM_SaveSettingsOnExit:\r
5222       saveSettingsOnExit = !saveSettingsOnExit;\r
5223       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5224                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5225                                          MF_CHECKED : MF_UNCHECKED));\r
5226       break;\r
5227 \r
5228     case IDM_Hint:\r
5229       HintEvent();\r
5230       break;\r
5231 \r
5232     case IDM_Book:\r
5233       BookEvent();\r
5234       break;\r
5235 \r
5236     case IDM_AboutGame:\r
5237       AboutGameEvent();\r
5238       break;\r
5239 \r
5240     case IDM_Debug:\r
5241       appData.debugMode = !appData.debugMode;\r
5242       if (appData.debugMode) {\r
5243         char dir[MSG_SIZ];\r
5244         GetCurrentDirectory(MSG_SIZ, dir);\r
5245         SetCurrentDirectory(installDir);\r
5246         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5247         SetCurrentDirectory(dir);\r
5248         setbuf(debugFP, NULL);\r
5249       } else {\r
5250         fclose(debugFP);\r
5251         debugFP = NULL;\r
5252       }\r
5253       break;\r
5254 \r
5255     case IDM_HELPCONTENTS:\r
5256       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5257         MessageBox (GetFocus(),\r
5258                     "Unable to activate help",\r
5259                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5260       }\r
5261       break;\r
5262 \r
5263     case IDM_HELPSEARCH:\r
5264       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5265         MessageBox (GetFocus(),\r
5266                     "Unable to activate help",\r
5267                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5268       }\r
5269       break;\r
5270 \r
5271     case IDM_HELPHELP:\r
5272       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5273         MessageBox (GetFocus(),\r
5274                     "Unable to activate help",\r
5275                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5276       }\r
5277       break;\r
5278 \r
5279     case IDM_ABOUT:\r
5280       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5281       DialogBox(hInst, \r
5282         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5283         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5284       FreeProcInstance(lpProc);\r
5285       break;\r
5286 \r
5287     case IDM_DirectCommand1:\r
5288       AskQuestionEvent("Direct Command",\r
5289                        "Send to chess program:", "", "1");\r
5290       break;\r
5291     case IDM_DirectCommand2:\r
5292       AskQuestionEvent("Direct Command",\r
5293                        "Send to second chess program:", "", "2");\r
5294       break;\r
5295 \r
5296     case EP_WhitePawn:\r
5297       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5298       fromX = fromY = -1;\r
5299       break;\r
5300 \r
5301     case EP_WhiteKnight:\r
5302       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5303       fromX = fromY = -1;\r
5304       break;\r
5305 \r
5306     case EP_WhiteBishop:\r
5307       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5308       fromX = fromY = -1;\r
5309       break;\r
5310 \r
5311     case EP_WhiteRook:\r
5312       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5313       fromX = fromY = -1;\r
5314       break;\r
5315 \r
5316     case EP_WhiteQueen:\r
5317       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5318       fromX = fromY = -1;\r
5319       break;\r
5320 \r
5321     case EP_WhiteKing:\r
5322       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5323       fromX = fromY = -1;\r
5324       break;\r
5325 \r
5326     case EP_BlackPawn:\r
5327       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5328       fromX = fromY = -1;\r
5329       break;\r
5330 \r
5331     case EP_BlackKnight:\r
5332       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5333       fromX = fromY = -1;\r
5334       break;\r
5335 \r
5336     case EP_BlackBishop:\r
5337       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5338       fromX = fromY = -1;\r
5339       break;\r
5340 \r
5341     case EP_BlackRook:\r
5342       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5343       fromX = fromY = -1;\r
5344       break;\r
5345 \r
5346     case EP_BlackQueen:\r
5347       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5348       fromX = fromY = -1;\r
5349       break;\r
5350 \r
5351     case EP_BlackKing:\r
5352       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5353       fromX = fromY = -1;\r
5354       break;\r
5355 \r
5356     case EP_EmptySquare:\r
5357       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5358       fromX = fromY = -1;\r
5359       break;\r
5360 \r
5361     case EP_ClearBoard:\r
5362       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5363       fromX = fromY = -1;\r
5364       break;\r
5365 \r
5366     case EP_White:\r
5367       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5368       fromX = fromY = -1;\r
5369       break;\r
5370 \r
5371     case EP_Black:\r
5372       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5373       fromX = fromY = -1;\r
5374       break;\r
5375 \r
5376     case DP_Pawn:\r
5377       DropMenuEvent(WhitePawn, fromX, fromY);\r
5378       fromX = fromY = -1;\r
5379       break;\r
5380 \r
5381     case DP_Knight:\r
5382       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5383       fromX = fromY = -1;\r
5384       break;\r
5385 \r
5386     case DP_Bishop:\r
5387       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5388       fromX = fromY = -1;\r
5389       break;\r
5390 \r
5391     case DP_Rook:\r
5392       DropMenuEvent(WhiteRook, fromX, fromY);\r
5393       fromX = fromY = -1;\r
5394       break;\r
5395 \r
5396     case DP_Queen:\r
5397       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5398       fromX = fromY = -1;\r
5399       break;\r
5400 \r
5401     default:\r
5402       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5403     }\r
5404     break;\r
5405 \r
5406   case WM_TIMER:\r
5407     switch (wParam) {\r
5408     case CLOCK_TIMER_ID:\r
5409       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5410       clockTimerEvent = 0;\r
5411       DecrementClocks(); /* call into back end */\r
5412       break;\r
5413     case LOAD_GAME_TIMER_ID:\r
5414       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5415       loadGameTimerEvent = 0;\r
5416       AutoPlayGameLoop(); /* call into back end */\r
5417       break;\r
5418     case ANALYSIS_TIMER_ID:\r
5419       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5420           appData.periodicUpdates) {\r
5421         AnalysisPeriodicEvent(0);\r
5422       } else {\r
5423         KillTimer(hwnd, analysisTimerEvent);\r
5424         analysisTimerEvent = 0;\r
5425       }\r
5426       break;\r
5427     case DELAYED_TIMER_ID:\r
5428       KillTimer(hwnd, delayedTimerEvent);\r
5429       delayedTimerEvent = 0;\r
5430       delayedTimerCallback();\r
5431       break;\r
5432     }\r
5433     break;\r
5434 \r
5435   case WM_USER_Input:\r
5436     InputEvent(hwnd, message, wParam, lParam);\r
5437     break;\r
5438 \r
5439   /* [AS] Also move "attached" child windows */\r
5440   case WM_WINDOWPOSCHANGING:\r
5441     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5442         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5443 \r
5444         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5445             /* Window is moving */\r
5446             RECT rcMain;\r
5447 \r
5448             GetWindowRect( hwnd, &rcMain );\r
5449             \r
5450             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5451             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5452             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5453         }\r
5454     }\r
5455     break;\r
5456 \r
5457   /* [AS] Snapping */\r
5458   case WM_ENTERSIZEMOVE:\r
5459     if (hwnd == hwndMain) {\r
5460       doingSizing = TRUE;\r
5461       lastSizing = 0;\r
5462     }\r
5463     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5464     break;\r
5465 \r
5466   case WM_SIZING:\r
5467     if (hwnd == hwndMain) {\r
5468       lastSizing = wParam;\r
5469     }\r
5470     break;\r
5471 \r
5472   case WM_MOVING:\r
5473       return OnMoving( &sd, hwnd, wParam, lParam );\r
5474 \r
5475   case WM_EXITSIZEMOVE:\r
5476     if (hwnd == hwndMain) {\r
5477       RECT client;\r
5478       doingSizing = FALSE;\r
5479       InvalidateRect(hwnd, &boardRect, FALSE);\r
5480       GetClientRect(hwnd, &client);\r
5481       ResizeBoard(client.right, client.bottom, lastSizing);\r
5482       lastSizing = 0;\r
5483     }\r
5484     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5485     break;\r
5486 \r
5487   case WM_DESTROY: /* message: window being destroyed */\r
5488     PostQuitMessage(0);\r
5489     break;\r
5490 \r
5491   case WM_CLOSE:\r
5492     if (hwnd == hwndMain) {\r
5493       ExitEvent(0);\r
5494     }\r
5495     break;\r
5496 \r
5497   default:      /* Passes it on if unprocessed */\r
5498     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5499   }\r
5500   return 0;\r
5501 }\r
5502 \r
5503 /*---------------------------------------------------------------------------*\\r
5504  *\r
5505  * Misc utility routines\r
5506  *\r
5507 \*---------------------------------------------------------------------------*/\r
5508 \r
5509 /*\r
5510  * Decent random number generator, at least not as bad as Windows\r
5511  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5512  */\r
5513 unsigned int randstate;\r
5514 \r
5515 int\r
5516 myrandom(void)\r
5517 {\r
5518   randstate = randstate * 1664525 + 1013904223;\r
5519   return (int) randstate & 0x7fffffff;\r
5520 }\r
5521 \r
5522 void\r
5523 mysrandom(unsigned int seed)\r
5524 {\r
5525   randstate = seed;\r
5526 }\r
5527 \r
5528 \r
5529 /* \r
5530  * returns TRUE if user selects a different color, FALSE otherwise \r
5531  */\r
5532 \r
5533 BOOL\r
5534 ChangeColor(HWND hwnd, COLORREF *which)\r
5535 {\r
5536   static BOOL firstTime = TRUE;\r
5537   static DWORD customColors[16];\r
5538   CHOOSECOLOR cc;\r
5539   COLORREF newcolor;\r
5540   int i;\r
5541   ColorClass ccl;\r
5542 \r
5543   if (firstTime) {\r
5544     /* Make initial colors in use available as custom colors */\r
5545     /* Should we put the compiled-in defaults here instead? */\r
5546     i = 0;\r
5547     customColors[i++] = lightSquareColor & 0xffffff;\r
5548     customColors[i++] = darkSquareColor & 0xffffff;\r
5549     customColors[i++] = whitePieceColor & 0xffffff;\r
5550     customColors[i++] = blackPieceColor & 0xffffff;\r
5551     customColors[i++] = highlightSquareColor & 0xffffff;\r
5552     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5553 \r
5554     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5555       customColors[i++] = textAttribs[ccl].color;\r
5556     }\r
5557     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5558     firstTime = FALSE;\r
5559   }\r
5560 \r
5561   cc.lStructSize = sizeof(cc);\r
5562   cc.hwndOwner = hwnd;\r
5563   cc.hInstance = NULL;\r
5564   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5565   cc.lpCustColors = (LPDWORD) customColors;\r
5566   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5567 \r
5568   if (!ChooseColor(&cc)) return FALSE;\r
5569 \r
5570   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5571   if (newcolor == *which) return FALSE;\r
5572   *which = newcolor;\r
5573   return TRUE;\r
5574 \r
5575   /*\r
5576   InitDrawingColors();\r
5577   InvalidateRect(hwnd, &boardRect, FALSE);\r
5578   */\r
5579 }\r
5580 \r
5581 BOOLEAN\r
5582 MyLoadSound(MySound *ms)\r
5583 {\r
5584   BOOL ok = FALSE;\r
5585   struct stat st;\r
5586   FILE *f;\r
5587 \r
5588   if (ms->data) free(ms->data);\r
5589   ms->data = NULL;\r
5590 \r
5591   switch (ms->name[0]) {\r
5592   case NULLCHAR:\r
5593     /* Silence */\r
5594     ok = TRUE;\r
5595     break;\r
5596   case '$':\r
5597     /* System sound from Control Panel.  Don't preload here. */\r
5598     ok = TRUE;\r
5599     break;\r
5600   case '!':\r
5601     if (ms->name[1] == NULLCHAR) {\r
5602       /* "!" alone = silence */\r
5603       ok = TRUE;\r
5604     } else {\r
5605       /* Builtin wave resource.  Error if not found. */\r
5606       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5607       if (h == NULL) break;\r
5608       ms->data = (void *)LoadResource(hInst, h);\r
5609       if (h == NULL) break;\r
5610       ok = TRUE;\r
5611     }\r
5612     break;\r
5613   default:\r
5614     /* .wav file.  Error if not found. */\r
5615     f = fopen(ms->name, "rb");\r
5616     if (f == NULL) break;\r
5617     if (fstat(fileno(f), &st) < 0) break;\r
5618     ms->data = malloc(st.st_size);\r
5619     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5620     fclose(f);\r
5621     ok = TRUE;\r
5622     break;\r
5623   }\r
5624   if (!ok) {\r
5625     char buf[MSG_SIZ];\r
5626     sprintf(buf, "Error loading sound %s", ms->name);\r
5627     DisplayError(buf, GetLastError());\r
5628   }\r
5629   return ok;\r
5630 }\r
5631 \r
5632 BOOLEAN\r
5633 MyPlaySound(MySound *ms)\r
5634 {\r
5635   BOOLEAN ok = FALSE;\r
5636   switch (ms->name[0]) {\r
5637   case NULLCHAR:\r
5638     /* Silence */\r
5639     ok = TRUE;\r
5640     break;\r
5641   case '$':\r
5642     /* System sound from Control Panel (deprecated feature).\r
5643        "$" alone or an unset sound name gets default beep (still in use). */\r
5644     if (ms->name[1]) {\r
5645       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5646     }\r
5647     if (!ok) ok = MessageBeep(MB_OK);\r
5648     break; \r
5649   case '!':\r
5650     /* Builtin wave resource, or "!" alone for silence */\r
5651     if (ms->name[1]) {\r
5652       if (ms->data == NULL) return FALSE;\r
5653       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5654     } else {\r
5655       ok = TRUE;\r
5656     }\r
5657     break;\r
5658   default:\r
5659     /* .wav file.  Error if not found. */\r
5660     if (ms->data == NULL) return FALSE;\r
5661     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5662     break;\r
5663   }\r
5664   /* Don't print an error: this can happen innocently if the sound driver\r
5665      is busy; for instance, if another instance of WinBoard is playing\r
5666      a sound at about the same time. */\r
5667 #if 0\r
5668   if (!ok) {\r
5669     char buf[MSG_SIZ];\r
5670     sprintf(buf, "Error playing sound %s", ms->name);\r
5671     DisplayError(buf, GetLastError());\r
5672   }\r
5673 #endif\r
5674   return ok;\r
5675 }\r
5676 \r
5677 \r
5678 LRESULT CALLBACK\r
5679 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5680 {\r
5681   BOOL ok;\r
5682   OPENFILENAME *ofn;\r
5683   static UINT *number; /* gross that this is static */\r
5684 \r
5685   switch (message) {\r
5686   case WM_INITDIALOG: /* message: initialize dialog box */\r
5687     /* Center the dialog over the application window */\r
5688     ofn = (OPENFILENAME *) lParam;\r
5689     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5690       number = (UINT *) ofn->lCustData;\r
5691       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5692     } else {\r
5693       number = NULL;\r
5694     }\r
5695     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5696     return FALSE;  /* Allow for further processing */\r
5697 \r
5698   case WM_COMMAND:\r
5699     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5700       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5701     }\r
5702     return FALSE;  /* Allow for further processing */\r
5703   }\r
5704   return FALSE;\r
5705 }\r
5706 \r
5707 UINT APIENTRY\r
5708 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5709 {\r
5710   static UINT *number;\r
5711   OPENFILENAME *ofname;\r
5712   OFNOTIFY *ofnot;\r
5713   switch (uiMsg) {\r
5714   case WM_INITDIALOG:\r
5715     ofname = (OPENFILENAME *)lParam;\r
5716     number = (UINT *)(ofname->lCustData);\r
5717     break;\r
5718   case WM_NOTIFY:\r
5719     ofnot = (OFNOTIFY *)lParam;\r
5720     if (ofnot->hdr.code == CDN_FILEOK) {\r
5721       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5722     }\r
5723     break;\r
5724   }\r
5725   return 0;\r
5726 }\r
5727 \r
5728 \r
5729 FILE *\r
5730 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
5731                char *nameFilt, char *dlgTitle, UINT *number,\r
5732                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5733 {\r
5734   OPENFILENAME openFileName;\r
5735   char buf1[MSG_SIZ];\r
5736   FILE *f;\r
5737 \r
5738   if (fileName == NULL) fileName = buf1;\r
5739   if (defName == NULL) {\r
5740     strcpy(fileName, "*.");\r
5741     strcat(fileName, defExt);\r
5742   } else {\r
5743     strcpy(fileName, defName);\r
5744   }\r
5745   if (fileTitle) strcpy(fileTitle, "");\r
5746   if (number) *number = 0;\r
5747 \r
5748   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5749   openFileName.hwndOwner         = hwnd;\r
5750   openFileName.hInstance         = (HANDLE) hInst;\r
5751   openFileName.lpstrFilter       = nameFilt;\r
5752   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5753   openFileName.nMaxCustFilter    = 0L;\r
5754   openFileName.nFilterIndex      = 1L;\r
5755   openFileName.lpstrFile         = fileName;\r
5756   openFileName.nMaxFile          = MSG_SIZ;\r
5757   openFileName.lpstrFileTitle    = fileTitle;\r
5758   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5759   openFileName.lpstrInitialDir   = NULL;\r
5760   openFileName.lpstrTitle        = dlgTitle;\r
5761   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5762     | (write ? 0 : OFN_FILEMUSTEXIST) \r
5763     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5764     | (oldDialog ? 0 : OFN_EXPLORER);\r
5765   openFileName.nFileOffset       = 0;\r
5766   openFileName.nFileExtension    = 0;\r
5767   openFileName.lpstrDefExt       = defExt;\r
5768   openFileName.lCustData         = (LONG) number;\r
5769   openFileName.lpfnHook          = oldDialog ?\r
5770     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5771   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5772 \r
5773   if (write ? GetSaveFileName(&openFileName) : \r
5774               GetOpenFileName(&openFileName)) {\r
5775     /* open the file */\r
5776     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");\r
5777     if (f == NULL) {\r
5778       MessageBox(hwnd, "File open failed", NULL,\r
5779                  MB_OK|MB_ICONEXCLAMATION);\r
5780       return NULL;\r
5781     }\r
5782   } else {\r
5783     int err = CommDlgExtendedError();\r
5784     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
5785     return FALSE;\r
5786   }\r
5787   return f;\r
5788 }\r
5789 \r
5790 \r
5791 \r
5792 VOID APIENTRY\r
5793 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
5794 {\r
5795   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
5796 \r
5797   /*\r
5798    * Get the first pop-up menu in the menu template. This is the\r
5799    * menu that TrackPopupMenu displays.\r
5800    */\r
5801   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
5802 \r
5803   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
5804 \r
5805   /*\r
5806    * TrackPopup uses screen coordinates, so convert the\r
5807    * coordinates of the mouse click to screen coordinates.\r
5808    */\r
5809   ClientToScreen(hwnd, (LPPOINT) &pt);\r
5810 \r
5811   /* Draw and track the floating pop-up menu. */\r
5812   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
5813                  pt.x, pt.y, 0, hwnd, NULL);\r
5814 \r
5815   /* Destroy the menu.*/\r
5816   DestroyMenu(hmenu);\r
5817 }\r
5818    \r
5819 typedef struct {\r
5820   HWND hDlg, hText;\r
5821   int sizeX, sizeY, newSizeX, newSizeY;\r
5822   HDWP hdwp;\r
5823 } ResizeEditPlusButtonsClosure;\r
5824 \r
5825 BOOL CALLBACK\r
5826 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
5827 {\r
5828   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
5829   RECT rect;\r
5830   POINT pt;\r
5831 \r
5832   if (hChild == cl->hText) return TRUE;\r
5833   GetWindowRect(hChild, &rect); /* gives screen coords */\r
5834   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
5835   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
5836   ScreenToClient(cl->hDlg, &pt);\r
5837   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
5838     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
5839   return TRUE;\r
5840 }\r
5841 \r
5842 /* Resize a dialog that has a (rich) edit field filling most of\r
5843    the top, with a row of buttons below */\r
5844 VOID\r
5845 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
5846 {\r
5847   RECT rectText;\r
5848   int newTextHeight, newTextWidth;\r
5849   ResizeEditPlusButtonsClosure cl;\r
5850   \r
5851   /*if (IsIconic(hDlg)) return;*/\r
5852   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
5853   \r
5854   cl.hdwp = BeginDeferWindowPos(8);\r
5855 \r
5856   GetWindowRect(hText, &rectText); /* gives screen coords */\r
5857   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5858   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5859   if (newTextHeight < 0) {\r
5860     newSizeY += -newTextHeight;\r
5861     newTextHeight = 0;\r
5862   }\r
5863   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
5864     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5865 \r
5866   cl.hDlg = hDlg;\r
5867   cl.hText = hText;\r
5868   cl.sizeX = sizeX;\r
5869   cl.sizeY = sizeY;\r
5870   cl.newSizeX = newSizeX;\r
5871   cl.newSizeY = newSizeY;\r
5872   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
5873 \r
5874   EndDeferWindowPos(cl.hdwp);\r
5875 }\r
5876 \r
5877 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
5878 {\r
5879     RECT    rChild, rParent;\r
5880     int     wChild, hChild, wParent, hParent;\r
5881     int     wScreen, hScreen, xNew, yNew;\r
5882     HDC     hdc;\r
5883 \r
5884     /* Get the Height and Width of the child window */\r
5885     GetWindowRect (hwndChild, &rChild);\r
5886     wChild = rChild.right - rChild.left;\r
5887     hChild = rChild.bottom - rChild.top;\r
5888 \r
5889     /* Get the Height and Width of the parent window */\r
5890     GetWindowRect (hwndParent, &rParent);\r
5891     wParent = rParent.right - rParent.left;\r
5892     hParent = rParent.bottom - rParent.top;\r
5893 \r
5894     /* Get the display limits */\r
5895     hdc = GetDC (hwndChild);\r
5896     wScreen = GetDeviceCaps (hdc, HORZRES);\r
5897     hScreen = GetDeviceCaps (hdc, VERTRES);\r
5898     ReleaseDC(hwndChild, hdc);\r
5899 \r
5900     /* Calculate new X position, then adjust for screen */\r
5901     xNew = rParent.left + ((wParent - wChild) /2);\r
5902     if (xNew < 0) {\r
5903         xNew = 0;\r
5904     } else if ((xNew+wChild) > wScreen) {\r
5905         xNew = wScreen - wChild;\r
5906     }\r
5907 \r
5908     /* Calculate new Y position, then adjust for screen */\r
5909     if( mode == 0 ) {\r
5910         yNew = rParent.top  + ((hParent - hChild) /2);\r
5911     }\r
5912     else {\r
5913         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
5914     }\r
5915 \r
5916     if (yNew < 0) {\r
5917         yNew = 0;\r
5918     } else if ((yNew+hChild) > hScreen) {\r
5919         yNew = hScreen - hChild;\r
5920     }\r
5921 \r
5922     /* Set it, and return */\r
5923     return SetWindowPos (hwndChild, NULL,\r
5924                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
5925 }\r
5926 \r
5927 /* Center one window over another */\r
5928 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
5929 {\r
5930     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
5931 }\r
5932 \r
5933 /*---------------------------------------------------------------------------*\\r
5934  *\r
5935  * Startup Dialog functions\r
5936  *\r
5937 \*---------------------------------------------------------------------------*/\r
5938 void\r
5939 InitComboStrings(HANDLE hwndCombo, char **cd)\r
5940 {\r
5941   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5942 \r
5943   while (*cd != NULL) {\r
5944     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
5945     cd++;\r
5946   }\r
5947 }\r
5948 \r
5949 void\r
5950 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
5951 {\r
5952   char buf1[ARG_MAX];\r
5953   int len;\r
5954 \r
5955   if (str[0] == '@') {\r
5956     FILE* f = fopen(str + 1, "r");\r
5957     if (f == NULL) {\r
5958       DisplayFatalError(str + 1, errno, 2);\r
5959       return;\r
5960     }\r
5961     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
5962     fclose(f);\r
5963     buf1[len] = NULLCHAR;\r
5964     str = buf1;\r
5965   }\r
5966 \r
5967   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
5968 \r
5969   for (;;) {\r
5970     char buf[MSG_SIZ];\r
5971     char *end = strchr(str, '\n');\r
5972     if (end == NULL) return;\r
5973     memcpy(buf, str, end - str);\r
5974     buf[end - str] = NULLCHAR;\r
5975     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
5976     str = end + 1;\r
5977   }\r
5978 }\r
5979 \r
5980 void\r
5981 SetStartupDialogEnables(HWND hDlg)\r
5982 {\r
5983   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
5984     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5985     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5986   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
5987     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
5988   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
5989     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
5990   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
5991     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
5992   EnableWindow(GetDlgItem(hDlg, IDOK),\r
5993     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
5994     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
5995     IsDlgButtonChecked(hDlg, OPT_View));\r
5996 }\r
5997 \r
5998 char *\r
5999 QuoteForFilename(char *filename)\r
6000 {\r
6001   int dquote, space;\r
6002   dquote = strchr(filename, '"') != NULL;\r
6003   space = strchr(filename, ' ') != NULL;\r
6004   if (dquote || space) {\r
6005     if (dquote) {\r
6006       return "'";\r
6007     } else {\r
6008       return "\"";\r
6009     }\r
6010   } else {\r
6011     return "";\r
6012   }\r
6013 }\r
6014 \r
6015 VOID\r
6016 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6017 {\r
6018   char buf[MSG_SIZ];\r
6019   char *q;\r
6020 \r
6021   InitComboStringsFromOption(hwndCombo, nthnames);\r
6022   q = QuoteForFilename(nthcp);\r
6023   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6024   if (*nthdir != NULLCHAR) {\r
6025     q = QuoteForFilename(nthdir);\r
6026     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6027   }\r
6028   if (*nthcp == NULLCHAR) {\r
6029     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6030   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6031     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6032     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6033   }\r
6034 }\r
6035 \r
6036 LRESULT CALLBACK\r
6037 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6038 {\r
6039   char buf[MSG_SIZ];\r
6040   HANDLE hwndCombo;\r
6041   char *p;\r
6042 \r
6043   switch (message) {\r
6044   case WM_INITDIALOG:\r
6045     /* Center the dialog */\r
6046     CenterWindow (hDlg, GetDesktopWindow());\r
6047     /* Initialize the dialog items */\r
6048     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6049                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6050                   firstChessProgramNames);\r
6051     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6052                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6053                   secondChessProgramNames);\r
6054     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6055     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6056     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6057     if (*appData.icsHelper != NULLCHAR) {\r
6058       char *q = QuoteForFilename(appData.icsHelper);\r
6059       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6060     }\r
6061     if (*appData.icsHost == NULLCHAR) {\r
6062       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6063       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6064     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6065       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6066       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6067     }\r
6068 \r
6069     if (appData.icsActive) {\r
6070       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6071     }\r
6072     else if (appData.noChessProgram) {\r
6073       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6074     }\r
6075     else {\r
6076       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6077     }\r
6078 \r
6079     SetStartupDialogEnables(hDlg);\r
6080     return TRUE;\r
6081 \r
6082   case WM_COMMAND:\r
6083     switch (LOWORD(wParam)) {\r
6084     case IDOK:\r
6085       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6086         strcpy(buf, "/fcp=");\r
6087         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6088         p = buf;\r
6089         ParseArgs(StringGet, &p);\r
6090         strcpy(buf, "/scp=");\r
6091         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6092         p = buf;\r
6093         ParseArgs(StringGet, &p);\r
6094         appData.noChessProgram = FALSE;\r
6095         appData.icsActive = FALSE;\r
6096       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6097         strcpy(buf, "/ics /icshost=");\r
6098         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6099         p = buf;\r
6100         ParseArgs(StringGet, &p);\r
6101         if (appData.zippyPlay) {\r
6102           strcpy(buf, "/fcp=");\r
6103           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6104           p = buf;\r
6105           ParseArgs(StringGet, &p);\r
6106         }\r
6107       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6108         appData.noChessProgram = TRUE;\r
6109         appData.icsActive = FALSE;\r
6110       } else {\r
6111         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6112                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6113         return TRUE;\r
6114       }\r
6115       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6116         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6117         p = buf;\r
6118         ParseArgs(StringGet, &p);\r
6119       }\r
6120       EndDialog(hDlg, TRUE);\r
6121       return TRUE;\r
6122 \r
6123     case IDCANCEL:\r
6124       ExitEvent(0);\r
6125       return TRUE;\r
6126 \r
6127     case IDM_HELPCONTENTS:\r
6128       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6129         MessageBox (GetFocus(),\r
6130                     "Unable to activate help",\r
6131                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6132       }\r
6133       break;\r
6134 \r
6135     default:\r
6136       SetStartupDialogEnables(hDlg);\r
6137       break;\r
6138     }\r
6139     break;\r
6140   }\r
6141   return FALSE;\r
6142 }\r
6143 \r
6144 /*---------------------------------------------------------------------------*\\r
6145  *\r
6146  * About box dialog functions\r
6147  *\r
6148 \*---------------------------------------------------------------------------*/\r
6149 \r
6150 /* Process messages for "About" dialog box */\r
6151 LRESULT CALLBACK\r
6152 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6153 {\r
6154   switch (message) {\r
6155   case WM_INITDIALOG: /* message: initialize dialog box */\r
6156     /* Center the dialog over the application window */\r
6157     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6158     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6159     return (TRUE);\r
6160 \r
6161   case WM_COMMAND: /* message: received a command */\r
6162     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6163         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6164       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6165       return (TRUE);\r
6166     }\r
6167     break;\r
6168   }\r
6169   return (FALSE);\r
6170 }\r
6171 \r
6172 /*---------------------------------------------------------------------------*\\r
6173  *\r
6174  * Comment Dialog functions\r
6175  *\r
6176 \*---------------------------------------------------------------------------*/\r
6177 \r
6178 LRESULT CALLBACK\r
6179 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6180 {\r
6181   static HANDLE hwndText = NULL;\r
6182   int len, newSizeX, newSizeY, flags;\r
6183   static int sizeX, sizeY;\r
6184   char *str;\r
6185   RECT rect;\r
6186   MINMAXINFO *mmi;\r
6187 \r
6188   switch (message) {\r
6189   case WM_INITDIALOG: /* message: initialize dialog box */\r
6190     /* Initialize the dialog items */\r
6191     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6192     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6193     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6194     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6195     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6196     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6197     SetWindowText(hDlg, commentTitle);\r
6198     if (editComment) {\r
6199       SetFocus(hwndText);\r
6200     } else {\r
6201       SetFocus(GetDlgItem(hDlg, IDOK));\r
6202     }\r
6203     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6204                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6205                 MAKELPARAM(FALSE, 0));\r
6206     /* Size and position the dialog */\r
6207     if (!commentDialog) {\r
6208       commentDialog = hDlg;\r
6209       flags = SWP_NOZORDER;\r
6210       GetClientRect(hDlg, &rect);\r
6211       sizeX = rect.right;\r
6212       sizeY = rect.bottom;\r
6213       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6214           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6215         WINDOWPLACEMENT wp;\r
6216         EnsureOnScreen(&commentX, &commentY);\r
6217         wp.length = sizeof(WINDOWPLACEMENT);\r
6218         wp.flags = 0;\r
6219         wp.showCmd = SW_SHOW;\r
6220         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6221         wp.rcNormalPosition.left = commentX;\r
6222         wp.rcNormalPosition.right = commentX + commentW;\r
6223         wp.rcNormalPosition.top = commentY;\r
6224         wp.rcNormalPosition.bottom = commentY + commentH;\r
6225         SetWindowPlacement(hDlg, &wp);\r
6226 \r
6227         GetClientRect(hDlg, &rect);\r
6228         newSizeX = rect.right;\r
6229         newSizeY = rect.bottom;\r
6230         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6231                               newSizeX, newSizeY);\r
6232         sizeX = newSizeX;\r
6233         sizeY = newSizeY;\r
6234       }\r
6235     }\r
6236     return FALSE;\r
6237 \r
6238   case WM_COMMAND: /* message: received a command */\r
6239     switch (LOWORD(wParam)) {\r
6240     case IDOK:\r
6241       if (editComment) {\r
6242         char *p, *q;\r
6243         /* Read changed options from the dialog box */\r
6244         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6245         len = GetWindowTextLength(hwndText);\r
6246         str = (char *) malloc(len + 1);\r
6247         GetWindowText(hwndText, str, len + 1);\r
6248         p = q = str;\r
6249         while (*q) {\r
6250           if (*q == '\r')\r
6251             q++;\r
6252           else\r
6253             *p++ = *q++;\r
6254         }\r
6255         *p = NULLCHAR;\r
6256         ReplaceComment(commentIndex, str);\r
6257         free(str);\r
6258       }\r
6259       CommentPopDown();\r
6260       return TRUE;\r
6261 \r
6262     case IDCANCEL:\r
6263     case OPT_CancelComment:\r
6264       CommentPopDown();\r
6265       return TRUE;\r
6266 \r
6267     case OPT_ClearComment:\r
6268       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6269       break;\r
6270 \r
6271     case OPT_EditComment:\r
6272       EditCommentEvent();\r
6273       return TRUE;\r
6274 \r
6275     default:\r
6276       break;\r
6277     }\r
6278     break;\r
6279 \r
6280   case WM_SIZE:\r
6281     newSizeX = LOWORD(lParam);\r
6282     newSizeY = HIWORD(lParam);\r
6283     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6284     sizeX = newSizeX;\r
6285     sizeY = newSizeY;\r
6286     break;\r
6287 \r
6288   case WM_GETMINMAXINFO:\r
6289     /* Prevent resizing window too small */\r
6290     mmi = (MINMAXINFO *) lParam;\r
6291     mmi->ptMinTrackSize.x = 100;\r
6292     mmi->ptMinTrackSize.y = 100;\r
6293     break;\r
6294   }\r
6295   return FALSE;\r
6296 }\r
6297 \r
6298 VOID\r
6299 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6300 {\r
6301   FARPROC lpProc;\r
6302   char *p, *q;\r
6303 \r
6304   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6305 \r
6306   if (str == NULL) str = "";\r
6307   p = (char *) malloc(2 * strlen(str) + 2);\r
6308   q = p;\r
6309   while (*str) {\r
6310     if (*str == '\n') *q++ = '\r';\r
6311     *q++ = *str++;\r
6312   }\r
6313   *q = NULLCHAR;\r
6314   if (commentText != NULL) free(commentText);\r
6315 \r
6316   commentIndex = index;\r
6317   commentTitle = title;\r
6318   commentText = p;\r
6319   editComment = edit;\r
6320 \r
6321   if (commentDialog) {\r
6322     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6323     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6324   } else {\r
6325     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6326     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6327                  hwndMain, (DLGPROC)lpProc);\r
6328     FreeProcInstance(lpProc);\r
6329   }\r
6330   commentDialogUp = TRUE;\r
6331 }\r
6332 \r
6333 \r
6334 /*---------------------------------------------------------------------------*\\r
6335  *\r
6336  * Type-in move dialog functions\r
6337  * \r
6338 \*---------------------------------------------------------------------------*/\r
6339 \r
6340 LRESULT CALLBACK\r
6341 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6342 {\r
6343   char move[MSG_SIZ];\r
6344   HWND hInput;\r
6345   ChessMove moveType;\r
6346   int fromX, fromY, toX, toY;\r
6347   char promoChar;\r
6348 \r
6349   switch (message) {\r
6350   case WM_INITDIALOG:\r
6351     move[0] = (char) lParam;\r
6352     move[1] = NULLCHAR;\r
6353     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6354     hInput = GetDlgItem(hDlg, OPT_Move);\r
6355     SetWindowText(hInput, move);\r
6356     SetFocus(hInput);\r
6357     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6358     return FALSE;\r
6359 \r
6360   case WM_COMMAND:\r
6361     switch (LOWORD(wParam)) {\r
6362     case IDOK:\r
6363       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6364         gameMode != Training) {\r
6365         DisplayMoveError("Displayed move is not current");\r
6366       } else {\r
6367         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6368         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6369           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6370           if (gameMode != Training)\r
6371               forwardMostMove = currentMove;\r
6372           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6373         } else {\r
6374           DisplayMoveError("Could not parse move");\r
6375         }\r
6376       }\r
6377       EndDialog(hDlg, TRUE);\r
6378       return TRUE;\r
6379     case IDCANCEL:\r
6380       EndDialog(hDlg, FALSE);\r
6381       return TRUE;\r
6382     default:\r
6383       break;\r
6384     }\r
6385     break;\r
6386   }\r
6387   return FALSE;\r
6388 }\r
6389 \r
6390 VOID\r
6391 PopUpMoveDialog(char firstchar)\r
6392 {\r
6393     FARPROC lpProc;\r
6394     \r
6395     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6396         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6397         gameMode == AnalyzeMode || gameMode == EditGame || \r
6398         gameMode == EditPosition || gameMode == IcsExamining ||\r
6399         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6400         gameMode == Training) {\r
6401       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6402       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6403         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6404       FreeProcInstance(lpProc);\r
6405     }\r
6406 }\r
6407 \r
6408 /*---------------------------------------------------------------------------*\\r
6409  *\r
6410  *  Error dialogs\r
6411  * \r
6412 \*---------------------------------------------------------------------------*/\r
6413 \r
6414 /* Nonmodal error box */\r
6415 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6416                              WPARAM wParam, LPARAM lParam);\r
6417 \r
6418 VOID\r
6419 ErrorPopUp(char *title, char *content)\r
6420 {\r
6421   FARPROC lpProc;\r
6422   char *p, *q;\r
6423   BOOLEAN modal = hwndMain == NULL;\r
6424 \r
6425   p = content;\r
6426   q = errorMessage;\r
6427   while (*p) {\r
6428     if (*p == '\n') {\r
6429       if (modal) {\r
6430         *q++ = ' ';\r
6431         p++;\r
6432       } else {\r
6433         *q++ = '\r';\r
6434         *q++ = *p++;\r
6435       }\r
6436     } else {\r
6437       *q++ = *p++;\r
6438     }\r
6439   }\r
6440   *q = NULLCHAR;\r
6441   strncpy(errorTitle, title, sizeof(errorTitle));\r
6442   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6443   \r
6444   if (modal) {\r
6445     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6446   } else {\r
6447     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6448     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6449                  hwndMain, (DLGPROC)lpProc);\r
6450     FreeProcInstance(lpProc);\r
6451   }\r
6452 }\r
6453 \r
6454 VOID\r
6455 ErrorPopDown()\r
6456 {\r
6457   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6458   if (errorDialog == NULL) return;\r
6459   DestroyWindow(errorDialog);\r
6460   errorDialog = NULL;\r
6461 }\r
6462 \r
6463 LRESULT CALLBACK\r
6464 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6465 {\r
6466   HANDLE hwndText;\r
6467   RECT rChild;\r
6468 \r
6469   switch (message) {\r
6470   case WM_INITDIALOG:\r
6471     GetWindowRect(hDlg, &rChild);\r
6472 \r
6473     /*\r
6474     SetWindowPos(hDlg, NULL, rChild.left,\r
6475       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6476       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6477     */\r
6478 \r
6479     /* \r
6480         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6481         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6482         and it doesn't work when you resize the dialog.\r
6483         For now, just give it a default position.\r
6484     */\r
6485     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6486 \r
6487     errorDialog = hDlg;\r
6488     SetWindowText(hDlg, errorTitle);\r
6489     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6490     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6491     return FALSE;\r
6492 \r
6493   case WM_COMMAND:\r
6494     switch (LOWORD(wParam)) {\r
6495     case IDOK:\r
6496     case IDCANCEL:\r
6497       if (errorDialog == hDlg) errorDialog = NULL;\r
6498       DestroyWindow(hDlg);\r
6499       return TRUE;\r
6500 \r
6501     default:\r
6502       break;\r
6503     }\r
6504     break;\r
6505   }\r
6506   return FALSE;\r
6507 }\r
6508 \r
6509 #ifdef GOTHIC\r
6510 LRESULT CALLBACK\r
6511 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6512 {\r
6513   HANDLE hwndText;\r
6514   RECT rChild;\r
6515   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6516 \r
6517   switch (message) {\r
6518   case WM_INITDIALOG:\r
6519     GetWindowRect(hDlg, &rChild);\r
6520 \r
6521     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
6522                                                              SWP_NOZORDER);\r
6523 \r
6524     /* \r
6525         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6526         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6527         and it doesn't work when you resize the dialog.\r
6528         For now, just give it a default position.\r
6529     */\r
6530 \r
6531     SetWindowText(hDlg, errorTitle);\r
6532     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6533     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6534     return FALSE;\r
6535 \r
6536   case WM_COMMAND:\r
6537     switch (LOWORD(wParam)) {\r
6538     case IDOK:\r
6539     case IDCANCEL:\r
6540       if (errorDialog == hDlg) errorDialog = NULL;\r
6541       DestroyWindow(hDlg);\r
6542       return TRUE;\r
6543 \r
6544     default:\r
6545       break;\r
6546     }\r
6547     break;\r
6548   }\r
6549   return FALSE;\r
6550 }\r
6551 \r
6552 VOID\r
6553 GothicPopUp(char *title)\r
6554 {\r
6555   FARPROC lpProc;\r
6556   char *p, *q;\r
6557   BOOLEAN modal = hwndMain == NULL;\r
6558 \r
6559   strncpy(errorTitle, title, sizeof(errorTitle));\r
6560   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6561   \r
6562     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6563     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6564                  hwndMain, (DLGPROC)lpProc);\r
6565     FreeProcInstance(lpProc);\r
6566 }\r
6567 #endif\r
6568 \r
6569 /*---------------------------------------------------------------------------*\\r
6570  *\r
6571  *  Ics Interaction console functions\r
6572  *\r
6573 \*---------------------------------------------------------------------------*/\r
6574 \r
6575 #define HISTORY_SIZE 64\r
6576 static char *history[HISTORY_SIZE];\r
6577 int histIn = 0, histP = 0;\r
6578 \r
6579 VOID\r
6580 SaveInHistory(char *cmd)\r
6581 {\r
6582   if (history[histIn] != NULL) {\r
6583     free(history[histIn]);\r
6584     history[histIn] = NULL;\r
6585   }\r
6586   if (*cmd == NULLCHAR) return;\r
6587   history[histIn] = StrSave(cmd);\r
6588   histIn = (histIn + 1) % HISTORY_SIZE;\r
6589   if (history[histIn] != NULL) {\r
6590     free(history[histIn]);\r
6591     history[histIn] = NULL;\r
6592   }\r
6593   histP = histIn;\r
6594 }\r
6595 \r
6596 char *\r
6597 PrevInHistory(char *cmd)\r
6598 {\r
6599   int newhp;\r
6600   if (histP == histIn) {\r
6601     if (history[histIn] != NULL) free(history[histIn]);\r
6602     history[histIn] = StrSave(cmd);\r
6603   }\r
6604   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6605   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6606   histP = newhp;\r
6607   return history[histP];\r
6608 }\r
6609 \r
6610 char *\r
6611 NextInHistory()\r
6612 {\r
6613   if (histP == histIn) return NULL;\r
6614   histP = (histP + 1) % HISTORY_SIZE;\r
6615   return history[histP];\r
6616 }\r
6617 \r
6618 typedef struct {\r
6619   char *item;\r
6620   char *command;\r
6621   BOOLEAN getname;\r
6622   BOOLEAN immediate;\r
6623 } IcsTextMenuEntry;\r
6624 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
6625 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
6626 \r
6627 void\r
6628 ParseIcsTextMenu(char *icsTextMenuString)\r
6629 {\r
6630   int flags = 0;\r
6631   IcsTextMenuEntry *e = icsTextMenuEntry;\r
6632   char *p = icsTextMenuString;\r
6633   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6634     free(e->item);\r
6635     e->item = NULL;\r
6636     if (e->command != NULL) {\r
6637       free(e->command);\r
6638       e->command = NULL;\r
6639     }\r
6640     e++;\r
6641   }\r
6642   e = icsTextMenuEntry;\r
6643   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6644     if (*p == ';' || *p == '\n') {\r
6645       e->item = strdup("-");\r
6646       e->command = NULL;\r
6647       p++;\r
6648     } else if (*p == '-') {\r
6649       e->item = strdup("-");\r
6650       e->command = NULL;\r
6651       p++;\r
6652       if (*p) p++;\r
6653     } else {\r
6654       char *q, *r, *s, *t;\r
6655       char c;\r
6656       q = strchr(p, ',');\r
6657       if (q == NULL) break;\r
6658       *q = NULLCHAR;\r
6659       r = strchr(q + 1, ',');\r
6660       if (r == NULL) break;\r
6661       *r = NULLCHAR;\r
6662       s = strchr(r + 1, ',');\r
6663       if (s == NULL) break;\r
6664       *s = NULLCHAR;\r
6665       c = ';';\r
6666       t = strchr(s + 1, c);\r
6667       if (t == NULL) {\r
6668         c = '\n';\r
6669         t = strchr(s + 1, c);\r
6670       }\r
6671       if (t != NULL) *t = NULLCHAR;\r
6672       e->item = strdup(p);\r
6673       e->command = strdup(q + 1);\r
6674       e->getname = *(r + 1) != '0';\r
6675       e->immediate = *(s + 1) != '0';\r
6676       *q = ',';\r
6677       *r = ',';\r
6678       *s = ',';\r
6679       if (t == NULL) break;\r
6680       *t = c;\r
6681       p = t + 1;\r
6682     }\r
6683     e++;\r
6684   } \r
6685 }\r
6686 \r
6687 HMENU\r
6688 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6689 {\r
6690   HMENU hmenu, h;\r
6691   int i = 0;\r
6692   hmenu = LoadMenu(hInst, "TextMenu");\r
6693   h = GetSubMenu(hmenu, 0);\r
6694   while (e->item) {\r
6695     if (strcmp(e->item, "-") == 0) {\r
6696       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6697     } else {\r
6698       if (e->item[0] == '|') {\r
6699         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6700                    IDM_CommandX + i, &e->item[1]);\r
6701       } else {\r
6702         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6703       }\r
6704     }\r
6705     e++;\r
6706     i++;\r
6707   } \r
6708   return hmenu;\r
6709 }\r
6710 \r
6711 WNDPROC consoleTextWindowProc;\r
6712 \r
6713 void\r
6714 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6715 {\r
6716   char buf[MSG_SIZ], name[MSG_SIZ];\r
6717   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6718   CHARRANGE sel;\r
6719 \r
6720   if (!getname) {\r
6721     SetWindowText(hInput, command);\r
6722     if (immediate) {\r
6723       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6724     } else {\r
6725       sel.cpMin = 999999;\r
6726       sel.cpMax = 999999;\r
6727       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6728       SetFocus(hInput);\r
6729     }\r
6730     return;\r
6731   }    \r
6732   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6733   if (sel.cpMin == sel.cpMax) {\r
6734     /* Expand to surrounding word */\r
6735     TEXTRANGE tr;\r
6736     do {\r
6737       tr.chrg.cpMax = sel.cpMin;\r
6738       tr.chrg.cpMin = --sel.cpMin;\r
6739       if (sel.cpMin < 0) break;\r
6740       tr.lpstrText = name;\r
6741       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6742     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6743     sel.cpMin++;\r
6744 \r
6745     do {\r
6746       tr.chrg.cpMin = sel.cpMax;\r
6747       tr.chrg.cpMax = ++sel.cpMax;\r
6748       tr.lpstrText = name;\r
6749       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6750     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6751     sel.cpMax--;\r
6752 \r
6753     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6754       MessageBeep(MB_ICONEXCLAMATION);\r
6755       return;\r
6756     }\r
6757     tr.chrg = sel;\r
6758     tr.lpstrText = name;\r
6759     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6760   } else {\r
6761     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6762       MessageBeep(MB_ICONEXCLAMATION);\r
6763       return;\r
6764     }\r
6765     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
6766   }\r
6767   if (immediate) {\r
6768     sprintf(buf, "%s %s", command, name);\r
6769     SetWindowText(hInput, buf);\r
6770     SendMessage(hInput, WM_CHAR, '\r', 0);\r
6771   } else {\r
6772     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
6773     SetWindowText(hInput, buf);\r
6774     sel.cpMin = 999999;\r
6775     sel.cpMax = 999999;\r
6776     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6777     SetFocus(hInput);\r
6778   }\r
6779 }\r
6780 \r
6781 LRESULT CALLBACK \r
6782 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6783 {\r
6784   HWND hInput;\r
6785   CHARRANGE sel;\r
6786 \r
6787   switch (message) {\r
6788   case WM_KEYDOWN:\r
6789     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
6790     switch (wParam) {\r
6791     case VK_PRIOR:\r
6792       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
6793       return 0;\r
6794     case VK_NEXT:\r
6795       sel.cpMin = 999999;\r
6796       sel.cpMax = 999999;\r
6797       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6798       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
6799       return 0;\r
6800     }\r
6801     break;\r
6802   case WM_CHAR:\r
6803     if (wParam == '\t') {\r
6804       if (GetKeyState(VK_SHIFT) < 0) {\r
6805         /* shifted */\r
6806         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6807         if (buttonDesc[0].hwnd) {\r
6808           SetFocus(buttonDesc[0].hwnd);\r
6809         } else {\r
6810           SetFocus(hwndMain);\r
6811         }\r
6812       } else {\r
6813         /* unshifted */\r
6814         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
6815       }\r
6816     } else {\r
6817       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6818       SetFocus(hInput);\r
6819       SendMessage(hInput, message, wParam, lParam);\r
6820     }\r
6821     return 0;\r
6822   case WM_PASTE:\r
6823     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6824     SetFocus(hInput);\r
6825     return SendMessage(hInput, message, wParam, lParam);\r
6826   case WM_MBUTTONDOWN:\r
6827     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6828   case WM_RBUTTONDOWN:\r
6829     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
6830       /* Move selection here if it was empty */\r
6831       POINT pt;\r
6832       pt.x = LOWORD(lParam);\r
6833       pt.y = HIWORD(lParam);\r
6834       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6835       if (sel.cpMin == sel.cpMax) {\r
6836         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
6837         sel.cpMax = sel.cpMin;\r
6838         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6839       }\r
6840       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
6841     }\r
6842     return 0;\r
6843   case WM_RBUTTONUP:\r
6844     if (GetKeyState(VK_SHIFT) & ~1) {\r
6845       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
6846         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
6847     } else {\r
6848       POINT pt;\r
6849       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
6850       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6851       if (sel.cpMin == sel.cpMax) {\r
6852         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
6853         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
6854       }\r
6855       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
6856         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
6857       }\r
6858       pt.x = LOWORD(lParam);\r
6859       pt.y = HIWORD(lParam);\r
6860       MenuPopup(hwnd, pt, hmenu, -1);\r
6861     }\r
6862     return 0;\r
6863   case WM_COMMAND:\r
6864     switch (LOWORD(wParam)) {\r
6865     case IDM_QuickPaste:\r
6866       {\r
6867         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6868         if (sel.cpMin == sel.cpMax) {\r
6869           MessageBeep(MB_ICONEXCLAMATION);\r
6870           return 0;\r
6871         }\r
6872         SendMessage(hwnd, WM_COPY, 0, 0);\r
6873         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6874         SendMessage(hInput, WM_PASTE, 0, 0);\r
6875         SetFocus(hInput);\r
6876         return 0;\r
6877       }\r
6878     case IDM_Cut:\r
6879       SendMessage(hwnd, WM_CUT, 0, 0);\r
6880       return 0;\r
6881     case IDM_Paste:\r
6882       SendMessage(hwnd, WM_PASTE, 0, 0);\r
6883       return 0;\r
6884     case IDM_Copy:\r
6885       SendMessage(hwnd, WM_COPY, 0, 0);\r
6886       return 0;\r
6887     default:\r
6888       {\r
6889         int i = LOWORD(wParam) - IDM_CommandX;\r
6890         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
6891             icsTextMenuEntry[i].command != NULL) {\r
6892           CommandX(hwnd, icsTextMenuEntry[i].command,\r
6893                    icsTextMenuEntry[i].getname,\r
6894                    icsTextMenuEntry[i].immediate);\r
6895           return 0;\r
6896         }\r
6897       }\r
6898       break;\r
6899     }\r
6900     break;\r
6901   }\r
6902   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
6903 }\r
6904 \r
6905 WNDPROC consoleInputWindowProc;\r
6906 \r
6907 LRESULT CALLBACK\r
6908 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
6909 {\r
6910   char buf[MSG_SIZ];\r
6911   char *p;\r
6912   static BOOL sendNextChar = FALSE;\r
6913   static BOOL quoteNextChar = FALSE;\r
6914   InputSource *is = consoleInputSource;\r
6915   CHARFORMAT cf;\r
6916   CHARRANGE sel;\r
6917 \r
6918   switch (message) {\r
6919   case WM_CHAR:\r
6920     if (!appData.localLineEditing || sendNextChar) {\r
6921       is->buf[0] = (CHAR) wParam;\r
6922       is->count = 1;\r
6923       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6924       sendNextChar = FALSE;\r
6925       return 0;\r
6926     }\r
6927     if (quoteNextChar) {\r
6928       buf[0] = (char) wParam;\r
6929       buf[1] = NULLCHAR;\r
6930       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
6931       quoteNextChar = FALSE;\r
6932       return 0;\r
6933     }\r
6934     switch (wParam) {\r
6935     case '\r':   /* Enter key */\r
6936       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
6937       if (consoleEcho) SaveInHistory(is->buf);\r
6938       is->buf[is->count++] = '\n';\r
6939       is->buf[is->count] = NULLCHAR;\r
6940       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
6941       if (consoleEcho) {\r
6942         ConsoleOutput(is->buf, is->count, TRUE);\r
6943       } else if (appData.localLineEditing) {\r
6944         ConsoleOutput("\n", 1, TRUE);\r
6945       }\r
6946       /* fall thru */\r
6947     case '\033': /* Escape key */\r
6948       SetWindowText(hwnd, "");\r
6949       cf.cbSize = sizeof(CHARFORMAT);\r
6950       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6951       if (consoleEcho) {\r
6952         cf.crTextColor = textAttribs[ColorNormal].color;\r
6953       } else {\r
6954         cf.crTextColor = COLOR_ECHOOFF;\r
6955       }\r
6956       cf.dwEffects = textAttribs[ColorNormal].effects;\r
6957       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6958       return 0;\r
6959     case '\t':   /* Tab key */\r
6960       if (GetKeyState(VK_SHIFT) < 0) {\r
6961         /* shifted */\r
6962         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
6963       } else {\r
6964         /* unshifted */\r
6965         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
6966         if (buttonDesc[0].hwnd) {\r
6967           SetFocus(buttonDesc[0].hwnd);\r
6968         } else {\r
6969           SetFocus(hwndMain);\r
6970         }\r
6971       }\r
6972       return 0;\r
6973     case '\023': /* Ctrl+S */\r
6974       sendNextChar = TRUE;\r
6975       return 0;\r
6976     case '\021': /* Ctrl+Q */\r
6977       quoteNextChar = TRUE;\r
6978       return 0;\r
6979     default:\r
6980       break;\r
6981     }\r
6982     break;\r
6983   case WM_KEYDOWN:\r
6984     switch (wParam) {\r
6985     case VK_UP:\r
6986       GetWindowText(hwnd, buf, MSG_SIZ);\r
6987       p = PrevInHistory(buf);\r
6988       if (p != NULL) {\r
6989         SetWindowText(hwnd, p);\r
6990         sel.cpMin = 999999;\r
6991         sel.cpMax = 999999;\r
6992         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6993         return 0;\r
6994       }\r
6995       break;\r
6996     case VK_DOWN:\r
6997       p = NextInHistory();\r
6998       if (p != NULL) {\r
6999         SetWindowText(hwnd, p);\r
7000         sel.cpMin = 999999;\r
7001         sel.cpMax = 999999;\r
7002         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7003         return 0;\r
7004       }\r
7005       break;\r
7006     case VK_HOME:\r
7007     case VK_END:\r
7008       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7009       /* fall thru */\r
7010     case VK_PRIOR:\r
7011     case VK_NEXT:\r
7012       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7013       return 0;\r
7014     }\r
7015     break;\r
7016   case WM_MBUTTONDOWN:\r
7017     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7018       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7019     break;\r
7020   case WM_RBUTTONUP:\r
7021     if (GetKeyState(VK_SHIFT) & ~1) {\r
7022       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7023         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7024     } else {\r
7025       POINT pt;\r
7026       HMENU hmenu;\r
7027       hmenu = LoadMenu(hInst, "InputMenu");\r
7028       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7029       if (sel.cpMin == sel.cpMax) {\r
7030         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7031         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7032       }\r
7033       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7034         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7035       }\r
7036       pt.x = LOWORD(lParam);\r
7037       pt.y = HIWORD(lParam);\r
7038       MenuPopup(hwnd, pt, hmenu, -1);\r
7039     }\r
7040     return 0;\r
7041   case WM_COMMAND:\r
7042     switch (LOWORD(wParam)) { \r
7043     case IDM_Undo:\r
7044       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7045       return 0;\r
7046     case IDM_SelectAll:\r
7047       sel.cpMin = 0;\r
7048       sel.cpMax = -1; /*999999?*/\r
7049       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7050       return 0;\r
7051     case IDM_Cut:\r
7052       SendMessage(hwnd, WM_CUT, 0, 0);\r
7053       return 0;\r
7054     case IDM_Paste:\r
7055       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7056       return 0;\r
7057     case IDM_Copy:\r
7058       SendMessage(hwnd, WM_COPY, 0, 0);\r
7059       return 0;\r
7060     }\r
7061     break;\r
7062   }\r
7063   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7064 }\r
7065 \r
7066 #define CO_MAX  100000\r
7067 #define CO_TRIM   1000\r
7068 \r
7069 LRESULT CALLBACK\r
7070 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7071 {\r
7072   static SnapData sd;\r
7073   static HWND hText, hInput, hFocus;\r
7074   InputSource *is = consoleInputSource;\r
7075   RECT rect;\r
7076   static int sizeX, sizeY;\r
7077   int newSizeX, newSizeY;\r
7078   MINMAXINFO *mmi;\r
7079 \r
7080   switch (message) {\r
7081   case WM_INITDIALOG: /* message: initialize dialog box */\r
7082     hwndConsole = hDlg;\r
7083     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7084     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7085     SetFocus(hInput);\r
7086     consoleTextWindowProc = (WNDPROC)\r
7087       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7088     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7089     consoleInputWindowProc = (WNDPROC)\r
7090       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7091     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7092     Colorize(ColorNormal, TRUE);\r
7093     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7094     ChangedConsoleFont();\r
7095     GetClientRect(hDlg, &rect);\r
7096     sizeX = rect.right;\r
7097     sizeY = rect.bottom;\r
7098     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7099         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7100       WINDOWPLACEMENT wp;\r
7101       EnsureOnScreen(&consoleX, &consoleY);\r
7102       wp.length = sizeof(WINDOWPLACEMENT);\r
7103       wp.flags = 0;\r
7104       wp.showCmd = SW_SHOW;\r
7105       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7106       wp.rcNormalPosition.left = consoleX;\r
7107       wp.rcNormalPosition.right = consoleX + consoleW;\r
7108       wp.rcNormalPosition.top = consoleY;\r
7109       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7110       SetWindowPlacement(hDlg, &wp);\r
7111     }\r
7112     return FALSE;\r
7113 \r
7114   case WM_SETFOCUS:\r
7115     SetFocus(hInput);\r
7116     return 0;\r
7117 \r
7118   case WM_CLOSE:\r
7119     ExitEvent(0);\r
7120     /* not reached */\r
7121     break;\r
7122 \r
7123   case WM_SIZE:\r
7124     if (IsIconic(hDlg)) break;\r
7125     newSizeX = LOWORD(lParam);\r
7126     newSizeY = HIWORD(lParam);\r
7127     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7128       RECT rectText, rectInput;\r
7129       POINT pt;\r
7130       int newTextHeight, newTextWidth;\r
7131       GetWindowRect(hText, &rectText);\r
7132       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7133       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7134       if (newTextHeight < 0) {\r
7135         newSizeY += -newTextHeight;\r
7136         newTextHeight = 0;\r
7137       }\r
7138       SetWindowPos(hText, NULL, 0, 0,\r
7139         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7140       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7141       pt.x = rectInput.left;\r
7142       pt.y = rectInput.top + newSizeY - sizeY;\r
7143       ScreenToClient(hDlg, &pt);\r
7144       SetWindowPos(hInput, NULL, \r
7145         pt.x, pt.y, /* needs client coords */   \r
7146         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7147         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7148     }\r
7149     sizeX = newSizeX;\r
7150     sizeY = newSizeY;\r
7151     break;\r
7152 \r
7153   case WM_GETMINMAXINFO:\r
7154     /* Prevent resizing window too small */\r
7155     mmi = (MINMAXINFO *) lParam;\r
7156     mmi->ptMinTrackSize.x = 100;\r
7157     mmi->ptMinTrackSize.y = 100;\r
7158     break;\r
7159 \r
7160   /* [AS] Snapping */\r
7161   case WM_ENTERSIZEMOVE:\r
7162     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7163 \r
7164   case WM_SIZING:\r
7165     return OnSizing( &sd, hDlg, wParam, lParam );\r
7166 \r
7167   case WM_MOVING:\r
7168     return OnMoving( &sd, hDlg, wParam, lParam );\r
7169 \r
7170   case WM_EXITSIZEMOVE:\r
7171     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7172   }\r
7173 \r
7174   return DefWindowProc(hDlg, message, wParam, lParam);\r
7175 }\r
7176 \r
7177 \r
7178 VOID\r
7179 ConsoleCreate()\r
7180 {\r
7181   HWND hCons;\r
7182   if (hwndConsole) return;\r
7183   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7184   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7185 }\r
7186 \r
7187 \r
7188 VOID\r
7189 ConsoleOutput(char* data, int length, int forceVisible)\r
7190 {\r
7191   HWND hText;\r
7192   int trim, exlen;\r
7193   char *p, *q;\r
7194   char buf[CO_MAX+1];\r
7195   POINT pEnd;\r
7196   RECT rect;\r
7197   static int delayLF = 0;\r
7198   CHARRANGE savesel, sel;\r
7199 \r
7200   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7201   p = data;\r
7202   q = buf;\r
7203   if (delayLF) {\r
7204     *q++ = '\r';\r
7205     *q++ = '\n';\r
7206     delayLF = 0;\r
7207   }\r
7208   while (length--) {\r
7209     if (*p == '\n') {\r
7210       if (*++p) {\r
7211         *q++ = '\r';\r
7212         *q++ = '\n';\r
7213       } else {\r
7214         delayLF = 1;\r
7215       }\r
7216     } else if (*p == '\007') {\r
7217        MyPlaySound(&sounds[(int)SoundBell]);\r
7218        p++;\r
7219     } else {\r
7220       *q++ = *p++;\r
7221     }\r
7222   }\r
7223   *q = NULLCHAR;\r
7224   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7225   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7226   /* Save current selection */\r
7227   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7228   exlen = GetWindowTextLength(hText);\r
7229   /* Find out whether current end of text is visible */\r
7230   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7231   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7232   /* Trim existing text if it's too long */\r
7233   if (exlen + (q - buf) > CO_MAX) {\r
7234     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7235     sel.cpMin = 0;\r
7236     sel.cpMax = trim;\r
7237     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7238     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7239     exlen -= trim;\r
7240     savesel.cpMin -= trim;\r
7241     savesel.cpMax -= trim;\r
7242     if (exlen < 0) exlen = 0;\r
7243     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7244     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7245   }\r
7246   /* Append the new text */\r
7247   sel.cpMin = exlen;\r
7248   sel.cpMax = exlen;\r
7249   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7250   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7251   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7252   if (forceVisible || exlen == 0 ||\r
7253       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7254        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7255     /* Scroll to make new end of text visible if old end of text\r
7256        was visible or new text is an echo of user typein */\r
7257     sel.cpMin = 9999999;\r
7258     sel.cpMax = 9999999;\r
7259     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7260     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7261     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7262     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7263   }\r
7264   if (savesel.cpMax == exlen || forceVisible) {\r
7265     /* Move insert point to new end of text if it was at the old\r
7266        end of text or if the new text is an echo of user typein */\r
7267     sel.cpMin = 9999999;\r
7268     sel.cpMax = 9999999;\r
7269     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7270   } else {\r
7271     /* Restore previous selection */\r
7272     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7273   }\r
7274   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7275 }\r
7276 \r
7277 /*---------*/\r
7278 \r
7279 \r
7280 void\r
7281 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7282               RECT *rect, char *color, char *flagFell)\r
7283 {\r
7284   char buf[100];\r
7285   char *str;\r
7286   COLORREF oldFg, oldBg;\r
7287   HFONT oldFont;\r
7288 \r
7289   if (appData.clockMode) {\r
7290     if (tinyLayout)\r
7291       sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7292     else\r
7293       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
7294     str = buf;\r
7295   } else {\r
7296     str = color;\r
7297   }\r
7298 \r
7299   if (highlight) {\r
7300     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7301     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7302   } else {\r
7303     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7304     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7305   }\r
7306   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7307 \r
7308   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7309              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7310              rect, str, strlen(str), NULL);\r
7311 \r
7312   (void) SetTextColor(hdc, oldFg);\r
7313   (void) SetBkColor(hdc, oldBg);\r
7314   (void) SelectObject(hdc, oldFont);\r
7315 }\r
7316 \r
7317 \r
7318 int\r
7319 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7320            OVERLAPPED *ovl)\r
7321 {\r
7322   int ok, err;\r
7323 \r
7324   /* [AS]  */\r
7325   if( count <= 0 ) {\r
7326     if (appData.debugMode) {\r
7327       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7328     }\r
7329 \r
7330     return ERROR_INVALID_USER_BUFFER;\r
7331   }\r
7332 \r
7333   ResetEvent(ovl->hEvent);\r
7334   ovl->Offset = ovl->OffsetHigh = 0;\r
7335   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7336   if (ok) {\r
7337     err = NO_ERROR;\r
7338   } else {\r
7339     err = GetLastError();\r
7340     if (err == ERROR_IO_PENDING) {\r
7341       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7342       if (ok)\r
7343         err = NO_ERROR;\r
7344       else\r
7345         err = GetLastError();\r
7346     }\r
7347   }\r
7348   return err;\r
7349 }\r
7350 \r
7351 int\r
7352 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7353             OVERLAPPED *ovl)\r
7354 {\r
7355   int ok, err;\r
7356 \r
7357   ResetEvent(ovl->hEvent);\r
7358   ovl->Offset = ovl->OffsetHigh = 0;\r
7359   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7360   if (ok) {\r
7361     err = NO_ERROR;\r
7362   } else {\r
7363     err = GetLastError();\r
7364     if (err == ERROR_IO_PENDING) {\r
7365       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7366       if (ok)\r
7367         err = NO_ERROR;\r
7368       else\r
7369         err = GetLastError();\r
7370     }\r
7371   }\r
7372   return err;\r
7373 }\r
7374 \r
7375 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7376 void CheckForInputBufferFull( InputSource * is )\r
7377 {\r
7378     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7379         /* Look for end of line */\r
7380         char * p = is->buf;\r
7381         \r
7382         while( p < is->next && *p != '\n' ) {\r
7383             p++;\r
7384         }\r
7385 \r
7386         if( p >= is->next ) {\r
7387             if (appData.debugMode) {\r
7388                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
7389             }\r
7390 \r
7391             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7392             is->count = (DWORD) -1;\r
7393             is->next = is->buf;\r
7394         }\r
7395     }\r
7396 }\r
7397 \r
7398 DWORD\r
7399 InputThread(LPVOID arg)\r
7400 {\r
7401   InputSource *is;\r
7402   OVERLAPPED ovl;\r
7403 \r
7404   is = (InputSource *) arg;\r
7405   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7406   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7407   while (is->hThread != NULL) {\r
7408     is->error = DoReadFile(is->hFile, is->next,\r
7409                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7410                            &is->count, &ovl);\r
7411     if (is->error == NO_ERROR) {\r
7412       is->next += is->count;\r
7413     } else {\r
7414       if (is->error == ERROR_BROKEN_PIPE) {\r
7415         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7416         is->count = 0;\r
7417       } else {\r
7418         is->count = (DWORD) -1;\r
7419         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7420         break; \r
7421       }\r
7422     }\r
7423 \r
7424     CheckForInputBufferFull( is );\r
7425 \r
7426     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7427 \r
7428     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7429 \r
7430     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7431   }\r
7432 \r
7433   CloseHandle(ovl.hEvent);\r
7434   CloseHandle(is->hFile);\r
7435 \r
7436   if (appData.debugMode) {\r
7437     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
7438   }\r
7439 \r
7440   return 0;\r
7441 }\r
7442 \r
7443 \r
7444 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7445 DWORD\r
7446 NonOvlInputThread(LPVOID arg)\r
7447 {\r
7448   InputSource *is;\r
7449   char *p, *q;\r
7450   int i;\r
7451   char prev;\r
7452 \r
7453   is = (InputSource *) arg;\r
7454   while (is->hThread != NULL) {\r
7455     is->error = ReadFile(is->hFile, is->next,\r
7456                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7457                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7458     if (is->error == NO_ERROR) {\r
7459       /* Change CRLF to LF */\r
7460       if (is->next > is->buf) {\r
7461         p = is->next - 1;\r
7462         i = is->count + 1;\r
7463       } else {\r
7464         p = is->next;\r
7465         i = is->count;\r
7466       }\r
7467       q = p;\r
7468       prev = NULLCHAR;\r
7469       while (i > 0) {\r
7470         if (prev == '\r' && *p == '\n') {\r
7471           *(q-1) = '\n';\r
7472           is->count--;\r
7473         } else { \r
7474           *q++ = *p;\r
7475         }\r
7476         prev = *p++;\r
7477         i--;\r
7478       }\r
7479       *q = NULLCHAR;\r
7480       is->next = q;\r
7481     } else {\r
7482       if (is->error == ERROR_BROKEN_PIPE) {\r
7483         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7484         is->count = 0; \r
7485       } else {\r
7486         is->count = (DWORD) -1;\r
7487       }\r
7488     }\r
7489 \r
7490     CheckForInputBufferFull( is );\r
7491 \r
7492     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7493 \r
7494     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7495 \r
7496     if (is->count < 0) break;  /* Quit on error */\r
7497   }\r
7498   CloseHandle(is->hFile);\r
7499   return 0;\r
7500 }\r
7501 \r
7502 DWORD\r
7503 SocketInputThread(LPVOID arg)\r
7504 {\r
7505   InputSource *is;\r
7506 \r
7507   is = (InputSource *) arg;\r
7508   while (is->hThread != NULL) {\r
7509     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7510     if ((int)is->count == SOCKET_ERROR) {\r
7511       is->count = (DWORD) -1;\r
7512       is->error = WSAGetLastError();\r
7513     } else {\r
7514       is->error = NO_ERROR;\r
7515       is->next += is->count;\r
7516       if (is->count == 0 && is->second == is) {\r
7517         /* End of file on stderr; quit with no message */\r
7518         break;\r
7519       }\r
7520     }\r
7521     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7522 \r
7523     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7524 \r
7525     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7526   }\r
7527   return 0;\r
7528 }\r
7529 \r
7530 VOID\r
7531 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7532 {\r
7533   InputSource *is;\r
7534 \r
7535   is = (InputSource *) lParam;\r
7536   if (is->lineByLine) {\r
7537     /* Feed in lines one by one */\r
7538     char *p = is->buf;\r
7539     char *q = p;\r
7540     while (q < is->next) {\r
7541       if (*q++ == '\n') {\r
7542         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7543         p = q;\r
7544       }\r
7545     }\r
7546     \r
7547     /* Move any partial line to the start of the buffer */\r
7548     q = is->buf;\r
7549     while (p < is->next) {\r
7550       *q++ = *p++;\r
7551     }\r
7552     is->next = q;\r
7553 \r
7554     if (is->error != NO_ERROR || is->count == 0) {\r
7555       /* Notify backend of the error.  Note: If there was a partial\r
7556          line at the end, it is not flushed through. */\r
7557       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7558     }\r
7559   } else {\r
7560     /* Feed in the whole chunk of input at once */\r
7561     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7562     is->next = is->buf;\r
7563   }\r
7564 }\r
7565 \r
7566 /*---------------------------------------------------------------------------*\\r
7567  *\r
7568  *  Menu enables. Used when setting various modes.\r
7569  *\r
7570 \*---------------------------------------------------------------------------*/\r
7571 \r
7572 typedef struct {\r
7573   int item;\r
7574   int flags;\r
7575 } Enables;\r
7576 \r
7577 VOID\r
7578 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7579 {\r
7580   while (enab->item > 0) {\r
7581     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7582     enab++;\r
7583   }\r
7584 }\r
7585 \r
7586 Enables gnuEnables[] = {\r
7587   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7588   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7589   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7590   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7591   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7592   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7593   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7594   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7595   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7596   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7597   { -1, -1 }\r
7598 };\r
7599 \r
7600 Enables icsEnables[] = {\r
7601   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7602   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7603   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7604   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7605   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7606   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7607   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7608   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7609   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7610   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7611   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7612   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7613   { -1, -1 }\r
7614 };\r
7615 \r
7616 #ifdef ZIPPY\r
7617 Enables zippyEnables[] = {\r
7618   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7619   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7620   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7621   { -1, -1 }\r
7622 };\r
7623 #endif\r
7624 \r
7625 Enables ncpEnables[] = {\r
7626   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7627   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7628   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7629   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7630   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7631   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7632   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7633   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7634   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7635   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7636   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7637   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7638   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7639   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7640   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7641   { -1, -1 }\r
7642 };\r
7643 \r
7644 Enables trainingOnEnables[] = {\r
7645   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7646   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7647   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7648   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7649   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7650   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7651   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7652   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7653   { -1, -1 }\r
7654 };\r
7655 \r
7656 Enables trainingOffEnables[] = {\r
7657   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7658   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7659   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7660   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7661   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7662   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7663   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7664   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7665   { -1, -1 }\r
7666 };\r
7667 \r
7668 /* These modify either ncpEnables or gnuEnables */\r
7669 Enables cmailEnables[] = {\r
7670   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7671   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7672   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7673   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7674   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7675   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7676   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7677   { -1, -1 }\r
7678 };\r
7679 \r
7680 Enables machineThinkingEnables[] = {\r
7681   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7682   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7683   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7684   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7685   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7686   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7687   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7688   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7689   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7690   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7691   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7692   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7693   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7694   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7695   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7696   { -1, -1 }\r
7697 };\r
7698 \r
7699 Enables userThinkingEnables[] = {\r
7700   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7701   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7702   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7703   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7704   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7705   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7706   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7707   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7708   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7709   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7710   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7711   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7712   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7713   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7714   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7715   { -1, -1 }\r
7716 };\r
7717 \r
7718 /*---------------------------------------------------------------------------*\\r
7719  *\r
7720  *  Front-end interface functions exported by XBoard.\r
7721  *  Functions appear in same order as prototypes in frontend.h.\r
7722  * \r
7723 \*---------------------------------------------------------------------------*/\r
7724 VOID\r
7725 ModeHighlight()\r
7726 {\r
7727   static UINT prevChecked = 0;\r
7728   static int prevPausing = 0;\r
7729   UINT nowChecked;\r
7730 \r
7731   if (pausing != prevPausing) {\r
7732     prevPausing = pausing;\r
7733     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7734                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
7735     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
7736   }\r
7737 \r
7738   switch (gameMode) {\r
7739   case BeginningOfGame:\r
7740     if (appData.icsActive)\r
7741       nowChecked = IDM_IcsClient;\r
7742     else if (appData.noChessProgram)\r
7743       nowChecked = IDM_EditGame;\r
7744     else\r
7745       nowChecked = IDM_MachineBlack;\r
7746     break;\r
7747   case MachinePlaysBlack:\r
7748     nowChecked = IDM_MachineBlack;\r
7749     break;\r
7750   case MachinePlaysWhite:\r
7751     nowChecked = IDM_MachineWhite;\r
7752     break;\r
7753   case TwoMachinesPlay:\r
7754     nowChecked = IDM_TwoMachines;\r
7755     break;\r
7756   case AnalyzeMode:\r
7757     nowChecked = IDM_AnalysisMode;\r
7758     break;\r
7759   case AnalyzeFile:\r
7760     nowChecked = IDM_AnalyzeFile;\r
7761     break;\r
7762   case EditGame:\r
7763     nowChecked = IDM_EditGame;\r
7764     break;\r
7765   case PlayFromGameFile:\r
7766     nowChecked = IDM_LoadGame;\r
7767     break;\r
7768   case EditPosition:\r
7769     nowChecked = IDM_EditPosition;\r
7770     break;\r
7771   case Training:\r
7772     nowChecked = IDM_Training;\r
7773     break;\r
7774   case IcsPlayingWhite:\r
7775   case IcsPlayingBlack:\r
7776   case IcsObserving:\r
7777   case IcsIdle:\r
7778     nowChecked = IDM_IcsClient;\r
7779     break;\r
7780   default:\r
7781   case EndOfGame:\r
7782     nowChecked = 0;\r
7783     break;\r
7784   }\r
7785   if (prevChecked != 0)\r
7786     (void) CheckMenuItem(GetMenu(hwndMain),\r
7787                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
7788   if (nowChecked != 0)\r
7789     (void) CheckMenuItem(GetMenu(hwndMain),\r
7790                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
7791 \r
7792   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
7793     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
7794                           MF_BYCOMMAND|MF_ENABLED);\r
7795   } else {\r
7796     (void) EnableMenuItem(GetMenu(hwndMain), \r
7797                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
7798   }\r
7799 \r
7800   prevChecked = nowChecked;\r
7801 }\r
7802 \r
7803 VOID\r
7804 SetICSMode()\r
7805 {\r
7806   HMENU hmenu = GetMenu(hwndMain);\r
7807   SetMenuEnables(hmenu, icsEnables);\r
7808   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
7809     MF_BYPOSITION|MF_ENABLED);\r
7810 #ifdef ZIPPY\r
7811   if (appData.zippyPlay) {\r
7812     SetMenuEnables(hmenu, zippyEnables);\r
7813   }\r
7814 #endif\r
7815 }\r
7816 \r
7817 VOID\r
7818 SetGNUMode()\r
7819 {\r
7820   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
7821 }\r
7822 \r
7823 VOID\r
7824 SetNCPMode()\r
7825 {\r
7826   HMENU hmenu = GetMenu(hwndMain);\r
7827   SetMenuEnables(hmenu, ncpEnables);\r
7828   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
7829     MF_BYPOSITION|MF_GRAYED);\r
7830     DrawMenuBar(hwndMain);\r
7831 }\r
7832 \r
7833 VOID\r
7834 SetCmailMode()\r
7835 {\r
7836   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
7837 }\r
7838 \r
7839 VOID \r
7840 SetTrainingModeOn()\r
7841 {\r
7842   int i;\r
7843   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
7844   for (i = 0; i < N_BUTTONS; i++) {\r
7845     if (buttonDesc[i].hwnd != NULL)\r
7846       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
7847   }\r
7848   CommentPopDown();\r
7849 }\r
7850 \r
7851 VOID SetTrainingModeOff()\r
7852 {\r
7853   int i;\r
7854   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
7855   for (i = 0; i < N_BUTTONS; i++) {\r
7856     if (buttonDesc[i].hwnd != NULL)\r
7857       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
7858   }\r
7859 }\r
7860 \r
7861 \r
7862 VOID\r
7863 SetUserThinkingEnables()\r
7864 {\r
7865   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
7866 }\r
7867 \r
7868 VOID\r
7869 SetMachineThinkingEnables()\r
7870 {\r
7871   HMENU hMenu = GetMenu(hwndMain);\r
7872   int flags = MF_BYCOMMAND|MF_ENABLED;\r
7873 \r
7874   SetMenuEnables(hMenu, machineThinkingEnables);\r
7875 \r
7876   if (gameMode == MachinePlaysBlack) {\r
7877     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
7878   } else if (gameMode == MachinePlaysWhite) {\r
7879     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
7880   } else if (gameMode == TwoMachinesPlay) {\r
7881     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
7882   }\r
7883 }\r
7884 \r
7885 \r
7886 VOID\r
7887 DisplayTitle(char *str)\r
7888 {\r
7889   char title[MSG_SIZ], *host;\r
7890   if (str[0] != NULLCHAR) {\r
7891     strcpy(title, str);\r
7892   } else if (appData.icsActive) {\r
7893     if (appData.icsCommPort[0] != NULLCHAR)\r
7894       host = "ICS";\r
7895     else \r
7896       host = appData.icsHost;\r
7897     sprintf(title, "%s: %s", szTitle, host);\r
7898   } else if (appData.noChessProgram) {\r
7899     strcpy(title, szTitle);\r
7900   } else {\r
7901     strcpy(title, szTitle);\r
7902     strcat(title, ": ");\r
7903     strcat(title, first.tidy);\r
7904   }\r
7905   SetWindowText(hwndMain, title);\r
7906 }\r
7907 \r
7908 \r
7909 VOID\r
7910 DisplayMessage(char *str1, char *str2)\r
7911 {\r
7912   HDC hdc;\r
7913   HFONT oldFont;\r
7914   int remain = MESSAGE_TEXT_MAX - 1;\r
7915   int len;\r
7916 \r
7917   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
7918   messageText[0] = NULLCHAR;\r
7919   if (*str1) {\r
7920     len = strlen(str1);\r
7921     if (len > remain) len = remain;\r
7922     strncpy(messageText, str1, len);\r
7923     messageText[len] = NULLCHAR;\r
7924     remain -= len;\r
7925   }\r
7926   if (*str2 && remain >= 2) {\r
7927     if (*str1) {\r
7928       strcat(messageText, "  ");\r
7929       remain -= 2;\r
7930     }\r
7931     len = strlen(str2);\r
7932     if (len > remain) len = remain;\r
7933     strncat(messageText, str2, len);\r
7934   }\r
7935   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
7936 \r
7937   if (IsIconic(hwndMain)) return;\r
7938   hdc = GetDC(hwndMain);\r
7939   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
7940   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
7941              &messageRect, messageText, strlen(messageText), NULL);\r
7942   (void) SelectObject(hdc, oldFont);\r
7943   (void) ReleaseDC(hwndMain, hdc);\r
7944 }\r
7945 \r
7946 VOID\r
7947 DisplayError(char *str, int error)\r
7948 {\r
7949   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
7950   int len;\r
7951 \r
7952   if (error == 0) {\r
7953     strcpy(buf, str);\r
7954   } else {\r
7955     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7956                         NULL, error, LANG_NEUTRAL,\r
7957                         (LPSTR) buf2, MSG_SIZ, NULL);\r
7958     if (len > 0) {\r
7959       sprintf(buf, "%s:\n%s", str, buf2);\r
7960     } else {\r
7961       ErrorMap *em = errmap;\r
7962       while (em->err != 0 && em->err != error) em++;\r
7963       if (em->err != 0) {\r
7964         sprintf(buf, "%s:\n%s", str, em->msg);\r
7965       } else {\r
7966         sprintf(buf, "%s:\nError code %d", str, error);\r
7967       }\r
7968     }\r
7969   }\r
7970   \r
7971   ErrorPopUp("Error", buf);\r
7972 }\r
7973 \r
7974 \r
7975 VOID\r
7976 DisplayMoveError(char *str)\r
7977 {\r
7978   fromX = fromY = -1;\r
7979   ClearHighlights();\r
7980   DrawPosition(FALSE, NULL);\r
7981   if (appData.popupMoveErrors) {\r
7982     ErrorPopUp("Error", str);\r
7983   } else {\r
7984     DisplayMessage(str, "");\r
7985     moveErrorMessageUp = TRUE;\r
7986   }\r
7987 }\r
7988 \r
7989 VOID\r
7990 DisplayFatalError(char *str, int error, int exitStatus)\r
7991 {\r
7992   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
7993   int len;\r
7994   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
7995 \r
7996   if (error != 0) {\r
7997     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
7998                         NULL, error, LANG_NEUTRAL,\r
7999                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8000     if (len > 0) {\r
8001       sprintf(buf, "%s:\n%s", str, buf2);\r
8002     } else {\r
8003       ErrorMap *em = errmap;\r
8004       while (em->err != 0 && em->err != error) em++;\r
8005       if (em->err != 0) {\r
8006         sprintf(buf, "%s:\n%s", str, em->msg);\r
8007       } else {\r
8008         sprintf(buf, "%s:\nError code %d", str, error);\r
8009       }\r
8010     }\r
8011     str = buf;\r
8012   }\r
8013   if (appData.debugMode) {\r
8014     fprintf(debugFP, "%s: %s\n", label, str);\r
8015   }\r
8016   if (appData.popupExitMessage) {\r
8017     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8018                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8019   }\r
8020   ExitEvent(exitStatus);\r
8021 }\r
8022 \r
8023 \r
8024 VOID\r
8025 DisplayInformation(char *str)\r
8026 {\r
8027   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8028 }\r
8029 \r
8030 \r
8031 VOID\r
8032 DisplayNote(char *str)\r
8033 {\r
8034   ErrorPopUp("Note", str);\r
8035 }\r
8036 \r
8037 \r
8038 typedef struct {\r
8039   char *title, *question, *replyPrefix;\r
8040   ProcRef pr;\r
8041 } QuestionParams;\r
8042 \r
8043 LRESULT CALLBACK\r
8044 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8045 {\r
8046   static QuestionParams *qp;\r
8047   char reply[MSG_SIZ];\r
8048   int len, err;\r
8049 \r
8050   switch (message) {\r
8051   case WM_INITDIALOG:\r
8052     qp = (QuestionParams *) lParam;\r
8053     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8054     SetWindowText(hDlg, qp->title);\r
8055     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8056     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8057     return FALSE;\r
8058 \r
8059   case WM_COMMAND:\r
8060     switch (LOWORD(wParam)) {\r
8061     case IDOK:\r
8062       strcpy(reply, qp->replyPrefix);\r
8063       if (*reply) strcat(reply, " ");\r
8064       len = strlen(reply);\r
8065       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8066       strcat(reply, "\n");\r
8067       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8068       EndDialog(hDlg, TRUE);\r
8069       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8070       return TRUE;\r
8071     case IDCANCEL:\r
8072       EndDialog(hDlg, FALSE);\r
8073       return TRUE;\r
8074     default:\r
8075       break;\r
8076     }\r
8077     break;\r
8078   }\r
8079   return FALSE;\r
8080 }\r
8081 \r
8082 VOID\r
8083 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8084 {\r
8085     QuestionParams qp;\r
8086     FARPROC lpProc;\r
8087     \r
8088     qp.title = title;\r
8089     qp.question = question;\r
8090     qp.replyPrefix = replyPrefix;\r
8091     qp.pr = pr;\r
8092     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8093     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8094       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8095     FreeProcInstance(lpProc);\r
8096 }\r
8097 \r
8098 /* [AS] Pick FRC position */\r
8099 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8100 {\r
8101     static int * lpIndexFRC;\r
8102     BOOL index_is_ok;\r
8103     char buf[16];\r
8104 \r
8105     switch( message )\r
8106     {\r
8107     case WM_INITDIALOG:\r
8108         lpIndexFRC = (int *) lParam;\r
8109 \r
8110         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8111 \r
8112         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8113         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8114         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8115         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8116 \r
8117         break;\r
8118 \r
8119     case WM_COMMAND:\r
8120         switch( LOWORD(wParam) ) {\r
8121         case IDOK:\r
8122             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8123             EndDialog( hDlg, 0 );\r
8124             return TRUE;\r
8125         case IDCANCEL:\r
8126             EndDialog( hDlg, 1 );   \r
8127             return TRUE;\r
8128         case IDC_NFG_Edit:\r
8129             if( HIWORD(wParam) == EN_CHANGE ) {\r
8130                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8131 \r
8132                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8133             }\r
8134             return TRUE;\r
8135         case IDC_NFG_Random:\r
8136             sprintf( buf, "%d", myrandom() % 960 );\r
8137             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8138             return TRUE;\r
8139         }\r
8140 \r
8141         break;\r
8142     }\r
8143 \r
8144     return FALSE;\r
8145 }\r
8146 \r
8147 int NewGameFRC()\r
8148 {\r
8149     int result;\r
8150     int index = appData.defaultFrcPosition;\r
8151     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8152 \r
8153     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8154 \r
8155     if( result == 0 ) {\r
8156         appData.defaultFrcPosition = index;\r
8157     }\r
8158 \r
8159     return result;\r
8160 }\r
8161 \r
8162 /* [AS] Game list options */\r
8163 typedef struct {\r
8164     char id;\r
8165     char * name;\r
8166 } GLT_Item;\r
8167 \r
8168 static GLT_Item GLT_ItemInfo[] = {\r
8169     { GLT_EVENT,      "Event" },\r
8170     { GLT_SITE,       "Site" },\r
8171     { GLT_DATE,       "Date" },\r
8172     { GLT_ROUND,      "Round" },\r
8173     { GLT_PLAYERS,    "Players" },\r
8174     { GLT_RESULT,     "Result" },\r
8175     { GLT_WHITE_ELO,  "White Rating" },\r
8176     { GLT_BLACK_ELO,  "Black Rating" },\r
8177     { GLT_TIME_CONTROL,"Time Control" },\r
8178     { GLT_VARIANT,    "Variant" },\r
8179     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8180     { 0, 0 }\r
8181 };\r
8182 \r
8183 const char * GLT_FindItem( char id )\r
8184 {\r
8185     const char * result = 0;\r
8186 \r
8187     GLT_Item * list = GLT_ItemInfo;\r
8188 \r
8189     while( list->id != 0 ) {\r
8190         if( list->id == id ) {\r
8191             result = list->name;\r
8192             break;\r
8193         }\r
8194 \r
8195         list++;\r
8196     }\r
8197 \r
8198     return result;\r
8199 }\r
8200 \r
8201 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8202 {\r
8203     const char * name = GLT_FindItem( id );\r
8204 \r
8205     if( name != 0 ) {\r
8206         if( index >= 0 ) {\r
8207             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8208         }\r
8209         else {\r
8210             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8211         }\r
8212     }\r
8213 }\r
8214 \r
8215 void GLT_TagsToList( HWND hDlg, char * tags )\r
8216 {\r
8217     char * pc = tags;\r
8218 \r
8219     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8220 \r
8221     while( *pc ) {\r
8222         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8223         pc++;\r
8224     }\r
8225 \r
8226     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8227 \r
8228     pc = GLT_ALL_TAGS;\r
8229 \r
8230     while( *pc ) {\r
8231         if( strchr( tags, *pc ) == 0 ) {\r
8232             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8233         }\r
8234         pc++;\r
8235     }\r
8236 \r
8237     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8238 }\r
8239 \r
8240 char GLT_ListItemToTag( HWND hDlg, int index )\r
8241 {\r
8242     char result = '\0';\r
8243     char name[128];\r
8244 \r
8245     GLT_Item * list = GLT_ItemInfo;\r
8246 \r
8247     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8248         while( list->id != 0 ) {\r
8249             if( strcmp( list->name, name ) == 0 ) {\r
8250                 result = list->id;\r
8251                 break;\r
8252             }\r
8253 \r
8254             list++;\r
8255         }\r
8256     }\r
8257 \r
8258     return result;\r
8259 }\r
8260 \r
8261 void GLT_MoveSelection( HWND hDlg, int delta )\r
8262 {\r
8263     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8264     int idx2 = idx1 + delta;\r
8265     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8266 \r
8267     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8268         char buf[128];\r
8269 \r
8270         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8271         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8272         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8273         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8274     }\r
8275 }\r
8276 \r
8277 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8278 {\r
8279     static char glt[64];\r
8280     static char * lpUserGLT;\r
8281 \r
8282     switch( message )\r
8283     {\r
8284     case WM_INITDIALOG:\r
8285         lpUserGLT = (char *) lParam;\r
8286         \r
8287         strcpy( glt, lpUserGLT );\r
8288 \r
8289         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8290 \r
8291         /* Initialize list */\r
8292         GLT_TagsToList( hDlg, glt );\r
8293 \r
8294         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8295 \r
8296         break;\r
8297 \r
8298     case WM_COMMAND:\r
8299         switch( LOWORD(wParam) ) {\r
8300         case IDOK:\r
8301             {\r
8302                 char * pc = lpUserGLT;\r
8303                 int idx = 0;\r
8304                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8305                 char id;\r
8306 \r
8307                 do {\r
8308                     id = GLT_ListItemToTag( hDlg, idx );\r
8309 \r
8310                     *pc++ = id;\r
8311                     idx++;\r
8312                 } while( id != '\0' );\r
8313             }\r
8314             EndDialog( hDlg, 0 );\r
8315             return TRUE;\r
8316         case IDCANCEL:\r
8317             EndDialog( hDlg, 1 );\r
8318             return TRUE;\r
8319 \r
8320         case IDC_GLT_Default:\r
8321             strcpy( glt, GLT_DEFAULT_TAGS );\r
8322             GLT_TagsToList( hDlg, glt );\r
8323             return TRUE;\r
8324 \r
8325         case IDC_GLT_Restore:\r
8326             strcpy( glt, lpUserGLT );\r
8327             GLT_TagsToList( hDlg, glt );\r
8328             return TRUE;\r
8329 \r
8330         case IDC_GLT_Up:\r
8331             GLT_MoveSelection( hDlg, -1 );\r
8332             return TRUE;\r
8333 \r
8334         case IDC_GLT_Down:\r
8335             GLT_MoveSelection( hDlg, +1 );\r
8336             return TRUE;\r
8337         }\r
8338 \r
8339         break;\r
8340     }\r
8341 \r
8342     return FALSE;\r
8343 }\r
8344 \r
8345 int GameListOptions()\r
8346 {\r
8347     char glt[64];\r
8348     int result;\r
8349     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8350 \r
8351     strcpy( glt, appData.gameListTags );\r
8352 \r
8353     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8354 \r
8355     if( result == 0 ) {\r
8356         /* [AS] Memory leak here! */\r
8357         appData.gameListTags = strdup( glt ); \r
8358     }\r
8359 \r
8360     return result;\r
8361 }\r
8362 \r
8363 \r
8364 VOID\r
8365 DisplayIcsInteractionTitle(char *str)\r
8366 {\r
8367   char consoleTitle[MSG_SIZ];\r
8368 \r
8369   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8370   SetWindowText(hwndConsole, consoleTitle);\r
8371 }\r
8372 \r
8373 void\r
8374 DrawPosition(int fullRedraw, Board board)\r
8375 {\r
8376   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8377 }\r
8378 \r
8379 \r
8380 VOID\r
8381 ResetFrontEnd()\r
8382 {\r
8383   fromX = fromY = -1;\r
8384   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8385     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8386     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8387     dragInfo.lastpos = dragInfo.pos;\r
8388     dragInfo.start.x = dragInfo.start.y = -1;\r
8389     dragInfo.from = dragInfo.start;\r
8390     ReleaseCapture();\r
8391     DrawPosition(TRUE, NULL);\r
8392   }\r
8393 }\r
8394 \r
8395 \r
8396 VOID\r
8397 CommentPopUp(char *title, char *str)\r
8398 {\r
8399   HWND hwnd = GetActiveWindow();\r
8400   EitherCommentPopUp(0, title, str, FALSE);\r
8401   SetActiveWindow(hwnd);\r
8402 }\r
8403 \r
8404 VOID\r
8405 CommentPopDown(void)\r
8406 {\r
8407   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8408   if (commentDialog) {\r
8409     ShowWindow(commentDialog, SW_HIDE);\r
8410   }\r
8411   commentDialogUp = FALSE;\r
8412 }\r
8413 \r
8414 VOID\r
8415 EditCommentPopUp(int index, char *title, char *str)\r
8416 {\r
8417   EitherCommentPopUp(index, title, str, TRUE);\r
8418 }\r
8419 \r
8420 \r
8421 VOID\r
8422 RingBell()\r
8423 {\r
8424   MyPlaySound(&sounds[(int)SoundMove]);\r
8425 }\r
8426 \r
8427 VOID PlayIcsWinSound()\r
8428 {\r
8429   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8430 }\r
8431 \r
8432 VOID PlayIcsLossSound()\r
8433 {\r
8434   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8435 }\r
8436 \r
8437 VOID PlayIcsDrawSound()\r
8438 {\r
8439   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8440 }\r
8441 \r
8442 VOID PlayIcsUnfinishedSound()\r
8443 {\r
8444   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8445 }\r
8446 \r
8447 VOID\r
8448 PlayAlarmSound()\r
8449 {\r
8450   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8451 }\r
8452 \r
8453 \r
8454 VOID\r
8455 EchoOn()\r
8456 {\r
8457   HWND hInput;\r
8458   consoleEcho = TRUE;\r
8459   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8460   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8461   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8462 }\r
8463 \r
8464 \r
8465 VOID\r
8466 EchoOff()\r
8467 {\r
8468   CHARFORMAT cf;\r
8469   HWND hInput;\r
8470   consoleEcho = FALSE;\r
8471   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8472   /* This works OK: set text and background both to the same color */\r
8473   cf = consoleCF;\r
8474   cf.crTextColor = COLOR_ECHOOFF;\r
8475   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8476   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8477 }\r
8478 \r
8479 /* No Raw()...? */\r
8480 \r
8481 void Colorize(ColorClass cc, int continuation)\r
8482 {\r
8483   currentColorClass = cc;\r
8484   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8485   consoleCF.crTextColor = textAttribs[cc].color;\r
8486   consoleCF.dwEffects = textAttribs[cc].effects;\r
8487   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8488 }\r
8489 \r
8490 char *\r
8491 UserName()\r
8492 {\r
8493   static char buf[MSG_SIZ];\r
8494   DWORD bufsiz = MSG_SIZ;\r
8495 \r
8496   if (!GetUserName(buf, &bufsiz)) {\r
8497     /*DisplayError("Error getting user name", GetLastError());*/\r
8498     strcpy(buf, "User");\r
8499   }\r
8500   return buf;\r
8501 }\r
8502 \r
8503 char *\r
8504 HostName()\r
8505 {\r
8506   static char buf[MSG_SIZ];\r
8507   DWORD bufsiz = MSG_SIZ;\r
8508 \r
8509   if (!GetComputerName(buf, &bufsiz)) {\r
8510     /*DisplayError("Error getting host name", GetLastError());*/\r
8511     strcpy(buf, "Unknown");\r
8512   }\r
8513   return buf;\r
8514 }\r
8515 \r
8516 \r
8517 int\r
8518 ClockTimerRunning()\r
8519 {\r
8520   return clockTimerEvent != 0;\r
8521 }\r
8522 \r
8523 int\r
8524 StopClockTimer()\r
8525 {\r
8526   if (clockTimerEvent == 0) return FALSE;\r
8527   KillTimer(hwndMain, clockTimerEvent);\r
8528   clockTimerEvent = 0;\r
8529   return TRUE;\r
8530 }\r
8531 \r
8532 void\r
8533 StartClockTimer(long millisec)\r
8534 {\r
8535   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8536                              (UINT) millisec, NULL);\r
8537 }\r
8538 \r
8539 void\r
8540 DisplayWhiteClock(long timeRemaining, int highlight)\r
8541 {\r
8542   HDC hdc;\r
8543   hdc = GetDC(hwndMain);\r
8544   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8545 \r
8546   if (!IsIconic(hwndMain)) {\r
8547     DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White", flag);\r
8548   }\r
8549   if (highlight && iconCurrent == iconBlack) {\r
8550     iconCurrent = iconWhite;\r
8551     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8552     if (IsIconic(hwndMain)) {\r
8553       DrawIcon(hdc, 2, 2, iconCurrent);\r
8554     }\r
8555   }\r
8556   (void) ReleaseDC(hwndMain, hdc);\r
8557   if (hwndConsole)\r
8558     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8559 }\r
8560 \r
8561 void\r
8562 DisplayBlackClock(long timeRemaining, int highlight)\r
8563 {\r
8564   HDC hdc;\r
8565   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8566 \r
8567   hdc = GetDC(hwndMain);\r
8568   if (!IsIconic(hwndMain)) {\r
8569     DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black", flag);\r
8570   }\r
8571   if (highlight && iconCurrent == iconWhite) {\r
8572     iconCurrent = iconBlack;\r
8573     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8574     if (IsIconic(hwndMain)) {\r
8575       DrawIcon(hdc, 2, 2, iconCurrent);\r
8576     }\r
8577   }\r
8578   (void) ReleaseDC(hwndMain, hdc);\r
8579   if (hwndConsole)\r
8580     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8581 }\r
8582 \r
8583 \r
8584 int\r
8585 LoadGameTimerRunning()\r
8586 {\r
8587   return loadGameTimerEvent != 0;\r
8588 }\r
8589 \r
8590 int\r
8591 StopLoadGameTimer()\r
8592 {\r
8593   if (loadGameTimerEvent == 0) return FALSE;\r
8594   KillTimer(hwndMain, loadGameTimerEvent);\r
8595   loadGameTimerEvent = 0;\r
8596   return TRUE;\r
8597 }\r
8598 \r
8599 void\r
8600 StartLoadGameTimer(long millisec)\r
8601 {\r
8602   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8603                                 (UINT) millisec, NULL);\r
8604 }\r
8605 \r
8606 void\r
8607 AutoSaveGame()\r
8608 {\r
8609   char *defName;\r
8610   FILE *f;\r
8611   char fileTitle[MSG_SIZ];\r
8612 \r
8613   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8614   f = OpenFileDialog(hwndMain, TRUE, defName,\r
8615                      appData.oldSaveStyle ? "gam" : "pgn",\r
8616                      GAME_FILT, \r
8617                      "Save Game to File", NULL, fileTitle, NULL);\r
8618   if (f != NULL) {\r
8619     SaveGame(f, 0, "");\r
8620     fclose(f);\r
8621   }\r
8622 }\r
8623 \r
8624 \r
8625 void\r
8626 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8627 {\r
8628   if (delayedTimerEvent != 0) {\r
8629     if (appData.debugMode) {\r
8630       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8631     }\r
8632     KillTimer(hwndMain, delayedTimerEvent);\r
8633     delayedTimerEvent = 0;\r
8634     delayedTimerCallback();\r
8635   }\r
8636   delayedTimerCallback = cb;\r
8637   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8638                                 (UINT) millisec, NULL);\r
8639 }\r
8640 \r
8641 DelayedEventCallback\r
8642 GetDelayedEvent()\r
8643 {\r
8644   if (delayedTimerEvent) {\r
8645     return delayedTimerCallback;\r
8646   } else {\r
8647     return NULL;\r
8648   }\r
8649 }\r
8650 \r
8651 void\r
8652 CancelDelayedEvent()\r
8653 {\r
8654   if (delayedTimerEvent) {\r
8655     KillTimer(hwndMain, delayedTimerEvent);\r
8656     delayedTimerEvent = 0;\r
8657   }\r
8658 }\r
8659 \r
8660 /* Start a child process running the given program.\r
8661    The process's standard output can be read from "from", and its\r
8662    standard input can be written to "to".\r
8663    Exit with fatal error if anything goes wrong.\r
8664    Returns an opaque pointer that can be used to destroy the process\r
8665    later.\r
8666 */\r
8667 int\r
8668 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8669 {\r
8670 #define BUFSIZE 4096\r
8671 \r
8672   HANDLE hChildStdinRd, hChildStdinWr,\r
8673     hChildStdoutRd, hChildStdoutWr;\r
8674   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8675   SECURITY_ATTRIBUTES saAttr;\r
8676   BOOL fSuccess;\r
8677   PROCESS_INFORMATION piProcInfo;\r
8678   STARTUPINFO siStartInfo;\r
8679   ChildProc *cp;\r
8680   char buf[MSG_SIZ];\r
8681   DWORD err;\r
8682 \r
8683   if (appData.debugMode) {\r
8684     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8685   }\r
8686 \r
8687   *pr = NoProc;\r
8688 \r
8689   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8690   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8691   saAttr.bInheritHandle = TRUE;\r
8692   saAttr.lpSecurityDescriptor = NULL;\r
8693 \r
8694   /*\r
8695    * The steps for redirecting child's STDOUT:\r
8696    *     1. Create anonymous pipe to be STDOUT for child.\r
8697    *     2. Create a noninheritable duplicate of read handle,\r
8698    *         and close the inheritable read handle.\r
8699    */\r
8700 \r
8701   /* Create a pipe for the child's STDOUT. */\r
8702   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8703     return GetLastError();\r
8704   }\r
8705 \r
8706   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8707   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8708                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8709                              FALSE,     /* not inherited */\r
8710                              DUPLICATE_SAME_ACCESS);\r
8711   if (! fSuccess) {\r
8712     return GetLastError();\r
8713   }\r
8714   CloseHandle(hChildStdoutRd);\r
8715 \r
8716   /*\r
8717    * The steps for redirecting child's STDIN:\r
8718    *     1. Create anonymous pipe to be STDIN for child.\r
8719    *     2. Create a noninheritable duplicate of write handle,\r
8720    *         and close the inheritable write handle.\r
8721    */\r
8722 \r
8723   /* Create a pipe for the child's STDIN. */\r
8724   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8725     return GetLastError();\r
8726   }\r
8727 \r
8728   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8729   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8730                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8731                              FALSE,     /* not inherited */\r
8732                              DUPLICATE_SAME_ACCESS);\r
8733   if (! fSuccess) {\r
8734     return GetLastError();\r
8735   }\r
8736   CloseHandle(hChildStdinWr);\r
8737 \r
8738   /* Arrange to (1) look in dir for the child .exe file, and\r
8739    * (2) have dir be the child's working directory.  Interpret\r
8740    * dir relative to the directory WinBoard loaded from. */\r
8741   GetCurrentDirectory(MSG_SIZ, buf);\r
8742   SetCurrentDirectory(installDir);\r
8743   SetCurrentDirectory(dir);\r
8744 \r
8745   /* Now create the child process. */\r
8746 \r
8747   siStartInfo.cb = sizeof(STARTUPINFO);\r
8748   siStartInfo.lpReserved = NULL;\r
8749   siStartInfo.lpDesktop = NULL;\r
8750   siStartInfo.lpTitle = NULL;\r
8751   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
8752   siStartInfo.cbReserved2 = 0;\r
8753   siStartInfo.lpReserved2 = NULL;\r
8754   siStartInfo.hStdInput = hChildStdinRd;\r
8755   siStartInfo.hStdOutput = hChildStdoutWr;\r
8756   siStartInfo.hStdError = hChildStdoutWr;\r
8757 \r
8758   fSuccess = CreateProcess(NULL,\r
8759                            cmdLine,        /* command line */\r
8760                            NULL,           /* process security attributes */\r
8761                            NULL,           /* primary thread security attrs */\r
8762                            TRUE,           /* handles are inherited */\r
8763                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
8764                            NULL,           /* use parent's environment */\r
8765                            NULL,\r
8766                            &siStartInfo, /* STARTUPINFO pointer */\r
8767                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
8768 \r
8769   err = GetLastError();\r
8770   SetCurrentDirectory(buf); /* return to prev directory */\r
8771   if (! fSuccess) {\r
8772     return err;\r
8773   }\r
8774 \r
8775   /* Close the handles we don't need in the parent */\r
8776   CloseHandle(piProcInfo.hThread);\r
8777   CloseHandle(hChildStdinRd);\r
8778   CloseHandle(hChildStdoutWr);\r
8779 \r
8780   /* Prepare return value */\r
8781   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8782   cp->kind = CPReal;\r
8783   cp->hProcess = piProcInfo.hProcess;\r
8784   cp->pid = piProcInfo.dwProcessId;\r
8785   cp->hFrom = hChildStdoutRdDup;\r
8786   cp->hTo = hChildStdinWrDup;\r
8787 \r
8788   *pr = (void *) cp;\r
8789 \r
8790   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
8791      2000 where engines sometimes don't see the initial command(s)\r
8792      from WinBoard and hang.  I don't understand how that can happen,\r
8793      but the Sleep is harmless, so I've put it in.  Others have also\r
8794      reported what may be the same problem, so hopefully this will fix\r
8795      it for them too.  */\r
8796   Sleep(500);\r
8797 \r
8798   return NO_ERROR;\r
8799 }\r
8800 \r
8801 \r
8802 void\r
8803 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
8804 {\r
8805   ChildProc *cp;\r
8806 \r
8807   cp = (ChildProc *) pr;\r
8808   if (cp == NULL) return;\r
8809 \r
8810   switch (cp->kind) {\r
8811   case CPReal:\r
8812     /* TerminateProcess is considered harmful, so... */\r
8813     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
8814     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
8815     /* The following doesn't work because the chess program\r
8816        doesn't "have the same console" as WinBoard.  Maybe\r
8817        we could arrange for this even though neither WinBoard\r
8818        nor the chess program uses a console for stdio? */\r
8819     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
8820 \r
8821     /* [AS] Special termination modes for misbehaving programs... */\r
8822     if( signal == 9 ) {\r
8823         if ( appData.debugMode) {\r
8824             fprintf( debugFP, "Terminating process %u\n", cp->pid );\r
8825         }\r
8826 \r
8827         TerminateProcess( cp->hProcess, 0 );\r
8828     }\r
8829     else if( signal == 10 ) {\r
8830         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
8831 \r
8832         if( dw != WAIT_OBJECT_0 ) {\r
8833             if ( appData.debugMode) {\r
8834                 fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );\r
8835             }\r
8836 \r
8837             TerminateProcess( cp->hProcess, 0 );\r
8838         }\r
8839     }\r
8840 \r
8841     CloseHandle(cp->hProcess);\r
8842     break;\r
8843 \r
8844   case CPComm:\r
8845     if (cp->hFrom) CloseHandle(cp->hFrom);\r
8846     break;\r
8847 \r
8848   case CPSock:\r
8849     closesocket(cp->sock);\r
8850     WSACleanup();\r
8851     break;\r
8852 \r
8853   case CPRcmd:\r
8854     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
8855     closesocket(cp->sock);\r
8856     closesocket(cp->sock2);\r
8857     WSACleanup();\r
8858     break;\r
8859   }\r
8860   free(cp);\r
8861 }\r
8862 \r
8863 void\r
8864 InterruptChildProcess(ProcRef pr)\r
8865 {\r
8866   ChildProc *cp;\r
8867 \r
8868   cp = (ChildProc *) pr;\r
8869   if (cp == NULL) return;\r
8870   switch (cp->kind) {\r
8871   case CPReal:\r
8872     /* The following doesn't work because the chess program\r
8873        doesn't "have the same console" as WinBoard.  Maybe\r
8874        we could arrange for this even though neither WinBoard\r
8875        nor the chess program uses a console for stdio */\r
8876     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
8877     break;\r
8878 \r
8879   case CPComm:\r
8880   case CPSock:\r
8881     /* Can't interrupt */\r
8882     break;\r
8883 \r
8884   case CPRcmd:\r
8885     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
8886     break;\r
8887   }\r
8888 }\r
8889 \r
8890 \r
8891 int\r
8892 OpenTelnet(char *host, char *port, ProcRef *pr)\r
8893 {\r
8894   char cmdLine[MSG_SIZ];\r
8895 \r
8896   if (port[0] == NULLCHAR) {\r
8897     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
8898   } else {\r
8899     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
8900   }\r
8901   return StartChildProcess(cmdLine, "", pr);\r
8902 }\r
8903 \r
8904 \r
8905 /* Code to open TCP sockets */\r
8906 \r
8907 int\r
8908 OpenTCP(char *host, char *port, ProcRef *pr)\r
8909 {\r
8910   ChildProc *cp;\r
8911   int err;\r
8912   SOCKET s;\r
8913   struct sockaddr_in sa, mysa;\r
8914   struct hostent FAR *hp;\r
8915   unsigned short uport;\r
8916   WORD wVersionRequested;\r
8917   WSADATA wsaData;\r
8918 \r
8919   /* Initialize socket DLL */\r
8920   wVersionRequested = MAKEWORD(1, 1);\r
8921   err = WSAStartup(wVersionRequested, &wsaData);\r
8922   if (err != 0) return err;\r
8923 \r
8924   /* Make socket */\r
8925   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
8926     err = WSAGetLastError();\r
8927     WSACleanup();\r
8928     return err;\r
8929   }\r
8930 \r
8931   /* Bind local address using (mostly) don't-care values.\r
8932    */\r
8933   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
8934   mysa.sin_family = AF_INET;\r
8935   mysa.sin_addr.s_addr = INADDR_ANY;\r
8936   uport = (unsigned short) 0;\r
8937   mysa.sin_port = htons(uport);\r
8938   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
8939       == SOCKET_ERROR) {\r
8940     err = WSAGetLastError();\r
8941     WSACleanup();\r
8942     return err;\r
8943   }\r
8944 \r
8945   /* Resolve remote host name */\r
8946   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
8947   if (!(hp = gethostbyname(host))) {\r
8948     unsigned int b0, b1, b2, b3;\r
8949 \r
8950     err = WSAGetLastError();\r
8951 \r
8952     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
8953       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
8954       hp->h_addrtype = AF_INET;\r
8955       hp->h_length = 4;\r
8956       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
8957       hp->h_addr_list[0] = (char *) malloc(4);\r
8958       hp->h_addr_list[0][0] = (char) b0;\r
8959       hp->h_addr_list[0][1] = (char) b1;\r
8960       hp->h_addr_list[0][2] = (char) b2;\r
8961       hp->h_addr_list[0][3] = (char) b3;\r
8962     } else {\r
8963       WSACleanup();\r
8964       return err;\r
8965     }\r
8966   }\r
8967   sa.sin_family = hp->h_addrtype;\r
8968   uport = (unsigned short) atoi(port);\r
8969   sa.sin_port = htons(uport);\r
8970   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
8971 \r
8972   /* Make connection */\r
8973   if (connect(s, (struct sockaddr *) &sa,\r
8974               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
8975     err = WSAGetLastError();\r
8976     WSACleanup();\r
8977     return err;\r
8978   }\r
8979 \r
8980   /* Prepare return value */\r
8981   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
8982   cp->kind = CPSock;\r
8983   cp->sock = s;\r
8984   *pr = (ProcRef *) cp;\r
8985 \r
8986   return NO_ERROR;\r
8987 }\r
8988 \r
8989 int\r
8990 OpenCommPort(char *name, ProcRef *pr)\r
8991 {\r
8992   HANDLE h;\r
8993   COMMTIMEOUTS ct;\r
8994   ChildProc *cp;\r
8995   char fullname[MSG_SIZ];\r
8996 \r
8997   if (*name != '\\')\r
8998     sprintf(fullname, "\\\\.\\%s", name);\r
8999   else\r
9000     strcpy(fullname, name);\r
9001 \r
9002   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9003                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9004   if (h == (HANDLE) -1) {\r
9005     return GetLastError();\r
9006   }\r
9007   hCommPort = h;\r
9008 \r
9009   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9010 \r
9011   /* Accumulate characters until a 100ms pause, then parse */\r
9012   ct.ReadIntervalTimeout = 100;\r
9013   ct.ReadTotalTimeoutMultiplier = 0;\r
9014   ct.ReadTotalTimeoutConstant = 0;\r
9015   ct.WriteTotalTimeoutMultiplier = 0;\r
9016   ct.WriteTotalTimeoutConstant = 0;\r
9017   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9018 \r
9019   /* Prepare return value */\r
9020   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9021   cp->kind = CPComm;\r
9022   cp->hFrom = h;\r
9023   cp->hTo = h;\r
9024   *pr = (ProcRef *) cp;\r
9025 \r
9026   return NO_ERROR;\r
9027 }\r
9028 \r
9029 int\r
9030 OpenLoopback(ProcRef *pr)\r
9031 {\r
9032   DisplayFatalError("Not implemented", 0, 1);\r
9033   return NO_ERROR;\r
9034 }\r
9035 \r
9036 \r
9037 int\r
9038 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9039 {\r
9040   ChildProc *cp;\r
9041   int err;\r
9042   SOCKET s, s2, s3;\r
9043   struct sockaddr_in sa, mysa;\r
9044   struct hostent FAR *hp;\r
9045   unsigned short uport;\r
9046   WORD wVersionRequested;\r
9047   WSADATA wsaData;\r
9048   int fromPort;\r
9049   char stderrPortStr[MSG_SIZ];\r
9050 \r
9051   /* Initialize socket DLL */\r
9052   wVersionRequested = MAKEWORD(1, 1);\r
9053   err = WSAStartup(wVersionRequested, &wsaData);\r
9054   if (err != 0) return err;\r
9055 \r
9056   /* Resolve remote host name */\r
9057   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9058   if (!(hp = gethostbyname(host))) {\r
9059     unsigned int b0, b1, b2, b3;\r
9060 \r
9061     err = WSAGetLastError();\r
9062 \r
9063     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9064       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9065       hp->h_addrtype = AF_INET;\r
9066       hp->h_length = 4;\r
9067       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9068       hp->h_addr_list[0] = (char *) malloc(4);\r
9069       hp->h_addr_list[0][0] = (char) b0;\r
9070       hp->h_addr_list[0][1] = (char) b1;\r
9071       hp->h_addr_list[0][2] = (char) b2;\r
9072       hp->h_addr_list[0][3] = (char) b3;\r
9073     } else {\r
9074       WSACleanup();\r
9075       return err;\r
9076     }\r
9077   }\r
9078   sa.sin_family = hp->h_addrtype;\r
9079   uport = (unsigned short) 514;\r
9080   sa.sin_port = htons(uport);\r
9081   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9082 \r
9083   /* Bind local socket to unused "privileged" port address\r
9084    */\r
9085   s = INVALID_SOCKET;\r
9086   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9087   mysa.sin_family = AF_INET;\r
9088   mysa.sin_addr.s_addr = INADDR_ANY;\r
9089   for (fromPort = 1023;; fromPort--) {\r
9090     if (fromPort < 0) {\r
9091       WSACleanup();\r
9092       return WSAEADDRINUSE;\r
9093     }\r
9094     if (s == INVALID_SOCKET) {\r
9095       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9096         err = WSAGetLastError();\r
9097         WSACleanup();\r
9098         return err;\r
9099       }\r
9100     }\r
9101     uport = (unsigned short) fromPort;\r
9102     mysa.sin_port = htons(uport);\r
9103     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9104         == SOCKET_ERROR) {\r
9105       err = WSAGetLastError();\r
9106       if (err == WSAEADDRINUSE) continue;\r
9107       WSACleanup();\r
9108       return err;\r
9109     }\r
9110     if (connect(s, (struct sockaddr *) &sa,\r
9111       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9112       err = WSAGetLastError();\r
9113       if (err == WSAEADDRINUSE) {\r
9114         closesocket(s);\r
9115         s = -1;\r
9116         continue;\r
9117       }\r
9118       WSACleanup();\r
9119       return err;\r
9120     }\r
9121     break;\r
9122   }\r
9123 \r
9124   /* Bind stderr local socket to unused "privileged" port address\r
9125    */\r
9126   s2 = INVALID_SOCKET;\r
9127   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9128   mysa.sin_family = AF_INET;\r
9129   mysa.sin_addr.s_addr = INADDR_ANY;\r
9130   for (fromPort = 1023;; fromPort--) {\r
9131     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9132     if (fromPort < 0) {\r
9133       (void) closesocket(s);\r
9134       WSACleanup();\r
9135       return WSAEADDRINUSE;\r
9136     }\r
9137     if (s2 == INVALID_SOCKET) {\r
9138       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9139         err = WSAGetLastError();\r
9140         closesocket(s);\r
9141         WSACleanup();\r
9142         return err;\r
9143       }\r
9144     }\r
9145     uport = (unsigned short) fromPort;\r
9146     mysa.sin_port = htons(uport);\r
9147     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9148         == SOCKET_ERROR) {\r
9149       err = WSAGetLastError();\r
9150       if (err == WSAEADDRINUSE) continue;\r
9151       (void) closesocket(s);\r
9152       WSACleanup();\r
9153       return err;\r
9154     }\r
9155     if (listen(s2, 1) == SOCKET_ERROR) {\r
9156       err = WSAGetLastError();\r
9157       if (err == WSAEADDRINUSE) {\r
9158         closesocket(s2);\r
9159         s2 = INVALID_SOCKET;\r
9160         continue;\r
9161       }\r
9162       (void) closesocket(s);\r
9163       (void) closesocket(s2);\r
9164       WSACleanup();\r
9165       return err;\r
9166     }\r
9167     break;\r
9168   }\r
9169   prevStderrPort = fromPort; // remember port used\r
9170   sprintf(stderrPortStr, "%d", fromPort);\r
9171 \r
9172   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9173     err = WSAGetLastError();\r
9174     (void) closesocket(s);\r
9175     (void) closesocket(s2);\r
9176     WSACleanup();\r
9177     return err;\r
9178   }\r
9179 \r
9180   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9181     err = WSAGetLastError();\r
9182     (void) closesocket(s);\r
9183     (void) closesocket(s2);\r
9184     WSACleanup();\r
9185     return err;\r
9186   }\r
9187   if (*user == NULLCHAR) user = UserName();\r
9188   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9189     err = WSAGetLastError();\r
9190     (void) closesocket(s);\r
9191     (void) closesocket(s2);\r
9192     WSACleanup();\r
9193     return err;\r
9194   }\r
9195   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9196     err = WSAGetLastError();\r
9197     (void) closesocket(s);\r
9198     (void) closesocket(s2);\r
9199     WSACleanup();\r
9200     return err;\r
9201   }\r
9202 \r
9203   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9204     err = WSAGetLastError();\r
9205     (void) closesocket(s);\r
9206     (void) closesocket(s2);\r
9207     WSACleanup();\r
9208     return err;\r
9209   }\r
9210   (void) closesocket(s2);  /* Stop listening */\r
9211 \r
9212   /* Prepare return value */\r
9213   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9214   cp->kind = CPRcmd;\r
9215   cp->sock = s;\r
9216   cp->sock2 = s3;\r
9217   *pr = (ProcRef *) cp;\r
9218 \r
9219   return NO_ERROR;\r
9220 }\r
9221 \r
9222 \r
9223 InputSourceRef\r
9224 AddInputSource(ProcRef pr, int lineByLine,\r
9225                InputCallback func, VOIDSTAR closure)\r
9226 {\r
9227   InputSource *is, *is2 = NULL;\r
9228   ChildProc *cp = (ChildProc *) pr;\r
9229 \r
9230   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9231   is->lineByLine = lineByLine;\r
9232   is->func = func;\r
9233   is->closure = closure;\r
9234   is->second = NULL;\r
9235   is->next = is->buf;\r
9236   if (pr == NoProc) {\r
9237     is->kind = CPReal;\r
9238     consoleInputSource = is;\r
9239   } else {\r
9240     is->kind = cp->kind;\r
9241     /* \r
9242         [AS] Try to avoid a race condition if the thread is given control too early:\r
9243         we create all threads suspended so that the is->hThread variable can be\r
9244         safely assigned, then let the threads start with ResumeThread.\r
9245     */\r
9246     switch (cp->kind) {\r
9247     case CPReal:\r
9248       is->hFile = cp->hFrom;\r
9249       cp->hFrom = NULL; /* now owned by InputThread */\r
9250       is->hThread =\r
9251         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9252                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9253       break;\r
9254 \r
9255     case CPComm:\r
9256       is->hFile = cp->hFrom;\r
9257       cp->hFrom = NULL; /* now owned by InputThread */\r
9258       is->hThread =\r
9259         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9260                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9261       break;\r
9262 \r
9263     case CPSock:\r
9264       is->sock = cp->sock;\r
9265       is->hThread =\r
9266         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9267                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9268       break;\r
9269 \r
9270     case CPRcmd:\r
9271       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9272       *is2 = *is;\r
9273       is->sock = cp->sock;\r
9274       is->second = is2;\r
9275       is2->sock = cp->sock2;\r
9276       is2->second = is2;\r
9277       is->hThread =\r
9278         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9279                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9280       is2->hThread =\r
9281         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9282                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9283       break;\r
9284     }\r
9285 \r
9286     if( is->hThread != NULL ) {\r
9287         ResumeThread( is->hThread );\r
9288     }\r
9289 \r
9290     if( is2 != NULL && is2->hThread != NULL ) {\r
9291         ResumeThread( is2->hThread );\r
9292     }\r
9293   }\r
9294 \r
9295   return (InputSourceRef) is;\r
9296 }\r
9297 \r
9298 void\r
9299 RemoveInputSource(InputSourceRef isr)\r
9300 {\r
9301   InputSource *is;\r
9302 \r
9303   is = (InputSource *) isr;\r
9304   is->hThread = NULL;  /* tell thread to stop */\r
9305   CloseHandle(is->hThread);\r
9306   if (is->second != NULL) {\r
9307     is->second->hThread = NULL;\r
9308     CloseHandle(is->second->hThread);\r
9309   }\r
9310 }\r
9311 \r
9312 \r
9313 int\r
9314 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9315 {\r
9316   DWORD dOutCount;\r
9317   int outCount = SOCKET_ERROR;\r
9318   ChildProc *cp = (ChildProc *) pr;\r
9319   static OVERLAPPED ovl;\r
9320 \r
9321   if (pr == NoProc) {\r
9322     ConsoleOutput(message, count, FALSE);\r
9323     return count;\r
9324   } \r
9325 \r
9326   if (ovl.hEvent == NULL) {\r
9327     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9328   }\r
9329   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9330 \r
9331   switch (cp->kind) {\r
9332   case CPSock:\r
9333   case CPRcmd:\r
9334     outCount = send(cp->sock, message, count, 0);\r
9335     if (outCount == SOCKET_ERROR) {\r
9336       *outError = WSAGetLastError();\r
9337     } else {\r
9338       *outError = NO_ERROR;\r
9339     }\r
9340     break;\r
9341 \r
9342   case CPReal:\r
9343     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9344                   &dOutCount, NULL)) {\r
9345       *outError = NO_ERROR;\r
9346       outCount = (int) dOutCount;\r
9347     } else {\r
9348       *outError = GetLastError();\r
9349     }\r
9350     break;\r
9351 \r
9352   case CPComm:\r
9353     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9354                             &dOutCount, &ovl);\r
9355     if (*outError == NO_ERROR) {\r
9356       outCount = (int) dOutCount;\r
9357     }\r
9358     break;\r
9359   }\r
9360   return outCount;\r
9361 }\r
9362 \r
9363 int\r
9364 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9365                        long msdelay)\r
9366 {\r
9367   /* Ignore delay, not implemented for WinBoard */\r
9368   return OutputToProcess(pr, message, count, outError);\r
9369 }\r
9370 \r
9371 \r
9372 void\r
9373 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9374                         char *buf, int count, int error)\r
9375 {\r
9376   DisplayFatalError("Not implemented", 0, 1);\r
9377 }\r
9378 \r
9379 /* see wgamelist.c for Game List functions */\r
9380 /* see wedittags.c for Edit Tags functions */\r
9381 \r
9382 \r
9383 VOID\r
9384 ICSInitScript()\r
9385 {\r
9386   FILE *f;\r
9387   char buf[MSG_SIZ];\r
9388   char *dummy;\r
9389 \r
9390   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9391     f = fopen(buf, "r");\r
9392     if (f != NULL) {\r
9393       ProcessICSInitScript(f);\r
9394       fclose(f);\r
9395     }\r
9396   }\r
9397 }\r
9398 \r
9399 \r
9400 VOID\r
9401 StartAnalysisClock()\r
9402 {\r
9403   if (analysisTimerEvent) return;\r
9404   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9405                                         (UINT) 2000, NULL);\r
9406 }\r
9407 \r
9408 LRESULT CALLBACK\r
9409 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9410 {\r
9411   static HANDLE hwndText;\r
9412   RECT rect;\r
9413   static int sizeX, sizeY;\r
9414   int newSizeX, newSizeY, flags;\r
9415   MINMAXINFO *mmi;\r
9416 \r
9417   switch (message) {\r
9418   case WM_INITDIALOG: /* message: initialize dialog box */\r
9419     /* Initialize the dialog items */\r
9420     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
9421     SetWindowText(hDlg, analysisTitle);\r
9422     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
9423     /* Size and position the dialog */\r
9424     if (!analysisDialog) {\r
9425       analysisDialog = hDlg;\r
9426       flags = SWP_NOZORDER;\r
9427       GetClientRect(hDlg, &rect);\r
9428       sizeX = rect.right;\r
9429       sizeY = rect.bottom;\r
9430       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
9431           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
9432         WINDOWPLACEMENT wp;\r
9433         EnsureOnScreen(&analysisX, &analysisY);\r
9434         wp.length = sizeof(WINDOWPLACEMENT);\r
9435         wp.flags = 0;\r
9436         wp.showCmd = SW_SHOW;\r
9437         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
9438         wp.rcNormalPosition.left = analysisX;\r
9439         wp.rcNormalPosition.right = analysisX + analysisW;\r
9440         wp.rcNormalPosition.top = analysisY;\r
9441         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
9442         SetWindowPlacement(hDlg, &wp);\r
9443 \r
9444         GetClientRect(hDlg, &rect);\r
9445         newSizeX = rect.right;\r
9446         newSizeY = rect.bottom;\r
9447         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
9448                               newSizeX, newSizeY);\r
9449         sizeX = newSizeX;\r
9450         sizeY = newSizeY;\r
9451       }\r
9452     }\r
9453     return FALSE;\r
9454 \r
9455   case WM_COMMAND: /* message: received a command */\r
9456     switch (LOWORD(wParam)) {\r
9457     case IDCANCEL:\r
9458       EditGameEvent();\r
9459       return TRUE;\r
9460     default:\r
9461       break;\r
9462     }\r
9463     break;\r
9464 \r
9465   case WM_SIZE:\r
9466     newSizeX = LOWORD(lParam);\r
9467     newSizeY = HIWORD(lParam);\r
9468     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
9469     sizeX = newSizeX;\r
9470     sizeY = newSizeY;\r
9471     break;\r
9472 \r
9473   case WM_GETMINMAXINFO:\r
9474     /* Prevent resizing window too small */\r
9475     mmi = (MINMAXINFO *) lParam;\r
9476     mmi->ptMinTrackSize.x = 100;\r
9477     mmi->ptMinTrackSize.y = 100;\r
9478     break;\r
9479   }\r
9480   return FALSE;\r
9481 }\r
9482 \r
9483 VOID\r
9484 AnalysisPopUp(char* title, char* str)\r
9485 {\r
9486   FARPROC lpProc;\r
9487   char *p, *q;\r
9488 \r
9489   /* [AS] */\r
9490   EngineOutputPopUp();\r
9491   return;\r
9492 \r
9493   if (str == NULL) str = "";\r
9494   p = (char *) malloc(2 * strlen(str) + 2);\r
9495   q = p;\r
9496   while (*str) {\r
9497     if (*str == '\n') *q++ = '\r';\r
9498     *q++ = *str++;\r
9499   }\r
9500   *q = NULLCHAR;\r
9501   if (analysisText != NULL) free(analysisText);\r
9502   analysisText = p;\r
9503 \r
9504   if (analysisDialog) {\r
9505     SetWindowText(analysisDialog, title);\r
9506     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
9507     ShowWindow(analysisDialog, SW_SHOW);\r
9508   } else {\r
9509     analysisTitle = title;\r
9510     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
9511     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
9512                  hwndMain, (DLGPROC)lpProc);\r
9513     FreeProcInstance(lpProc);\r
9514   }\r
9515   analysisDialogUp = TRUE;  \r
9516 }\r
9517 \r
9518 VOID\r
9519 AnalysisPopDown()\r
9520 {\r
9521   if (analysisDialog) {\r
9522     ShowWindow(analysisDialog, SW_HIDE);\r
9523   }\r
9524   analysisDialogUp = FALSE;  \r
9525 }\r
9526 \r
9527 \r
9528 VOID\r
9529 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9530 {\r
9531   highlightInfo.sq[0].x = fromX;\r
9532   highlightInfo.sq[0].y = fromY;\r
9533   highlightInfo.sq[1].x = toX;\r
9534   highlightInfo.sq[1].y = toY;\r
9535 }\r
9536 \r
9537 VOID\r
9538 ClearHighlights()\r
9539 {\r
9540   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9541     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9542 }\r
9543 \r
9544 VOID\r
9545 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9546 {\r
9547   premoveHighlightInfo.sq[0].x = fromX;\r
9548   premoveHighlightInfo.sq[0].y = fromY;\r
9549   premoveHighlightInfo.sq[1].x = toX;\r
9550   premoveHighlightInfo.sq[1].y = toY;\r
9551 }\r
9552 \r
9553 VOID\r
9554 ClearPremoveHighlights()\r
9555 {\r
9556   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9557     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9558 }\r
9559 \r
9560 VOID\r
9561 ShutDownFrontEnd()\r
9562 {\r
9563   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9564   DeleteClipboardTempFiles();\r
9565 }\r
9566 \r
9567 void\r
9568 BoardToTop()\r
9569 {\r
9570     if (IsIconic(hwndMain))\r
9571       ShowWindow(hwndMain, SW_RESTORE);\r
9572 \r
9573     SetActiveWindow(hwndMain);\r
9574 }\r
9575 \r
9576 /*\r
9577  * Prototypes for animation support routines\r
9578  */\r
9579 static void ScreenSquare(int column, int row, POINT * pt);\r
9580 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9581      POINT frames[], int * nFrames);\r
9582 \r
9583 \r
9584 #define kFactor 4\r
9585 \r
9586 void\r
9587 AnimateMove(board, fromX, fromY, toX, toY)\r
9588      Board board;\r
9589      int fromX;\r
9590      int fromY;\r
9591      int toX;\r
9592      int toY;\r
9593 {\r
9594   ChessSquare piece;\r
9595   POINT start, finish, mid;\r
9596   POINT frames[kFactor * 2 + 1];\r
9597   int nFrames, n;\r
9598 \r
9599   if (!appData.animate) return;\r
9600   if (doingSizing) return;\r
9601   if (fromY < 0 || fromX < 0) return;\r
9602   piece = board[fromY][fromX];\r
9603   if (piece >= EmptySquare) return;\r
9604 \r
9605   ScreenSquare(fromX, fromY, &start);\r
9606   ScreenSquare(toX, toY, &finish);\r
9607 \r
9608   /* All pieces except knights move in straight line */\r
9609   if (piece != WhiteKnight && piece != BlackKnight) {\r
9610     mid.x = start.x + (finish.x - start.x) / 2;\r
9611     mid.y = start.y + (finish.y - start.y) / 2;\r
9612   } else {\r
9613     /* Knight: make diagonal movement then straight */\r
9614     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9615        mid.x = start.x + (finish.x - start.x) / 2;\r
9616        mid.y = finish.y;\r
9617      } else {\r
9618        mid.x = finish.x;\r
9619        mid.y = start.y + (finish.y - start.y) / 2;\r
9620      }\r
9621   }\r
9622   \r
9623   /* Don't use as many frames for very short moves */\r
9624   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9625     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9626   else\r
9627     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9628 \r
9629   animInfo.from.x = fromX;\r
9630   animInfo.from.y = fromY;\r
9631   animInfo.to.x = toX;\r
9632   animInfo.to.y = toY;\r
9633   animInfo.lastpos = start;\r
9634   animInfo.piece = piece;\r
9635   for (n = 0; n < nFrames; n++) {\r
9636     animInfo.pos = frames[n];\r
9637     DrawPosition(FALSE, NULL);\r
9638     animInfo.lastpos = animInfo.pos;\r
9639     Sleep(appData.animSpeed);\r
9640   }\r
9641   animInfo.pos = finish;\r
9642   DrawPosition(FALSE, NULL);\r
9643   animInfo.piece = EmptySquare;\r
9644 }\r
9645 \r
9646 /*      Convert board position to corner of screen rect and color       */\r
9647 \r
9648 static void\r
9649 ScreenSquare(column, row, pt)\r
9650      int column; int row; POINT * pt;\r
9651 {\r
9652   if (flipView) {\r
9653     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9654     pt->y = lineGap + row * (squareSize + lineGap);\r
9655   } else {\r
9656     pt->x = lineGap + column * (squareSize + lineGap);\r
9657     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9658   }\r
9659 }\r
9660 \r
9661 /*      Generate a series of frame coords from start->mid->finish.\r
9662         The movement rate doubles until the half way point is\r
9663         reached, then halves back down to the final destination,\r
9664         which gives a nice slow in/out effect. The algorithmn\r
9665         may seem to generate too many intermediates for short\r
9666         moves, but remember that the purpose is to attract the\r
9667         viewers attention to the piece about to be moved and\r
9668         then to where it ends up. Too few frames would be less\r
9669         noticeable.                                             */\r
9670 \r
9671 static void\r
9672 Tween(start, mid, finish, factor, frames, nFrames)\r
9673      POINT * start; POINT * mid;\r
9674      POINT * finish; int factor;\r
9675      POINT frames[]; int * nFrames;\r
9676 {\r
9677   int n, fraction = 1, count = 0;\r
9678 \r
9679   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9680   for (n = 0; n < factor; n++)\r
9681     fraction *= 2;\r
9682   for (n = 0; n < factor; n++) {\r
9683     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9684     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9685     count ++;\r
9686     fraction = fraction / 2;\r
9687   }\r
9688   \r
9689   /* Midpoint */\r
9690   frames[count] = *mid;\r
9691   count ++;\r
9692   \r
9693   /* Slow out, stepping 1/2, then 1/4, ... */\r
9694   fraction = 2;\r
9695   for (n = 0; n < factor; n++) {\r
9696     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9697     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9698     count ++;\r
9699     fraction = fraction * 2;\r
9700   }\r
9701   *nFrames = count;\r
9702 }\r
9703 \r
9704 void\r
9705 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9706 {\r
9707 #if 0\r
9708     char buf[256];\r
9709 \r
9710     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
9711         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
9712 \r
9713     OutputDebugString( buf );\r
9714 #endif\r
9715 \r
9716     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9717 \r
9718     EvalGraphSet( first, last, current, pvInfoList );\r
9719 }\r
9720 \r
9721 void SetProgramStats( FrontEndProgramStats * stats )\r
9722 {\r
9723 #if 0\r
9724     char buf[1024];\r
9725 \r
9726     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
9727         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
9728 \r
9729     OutputDebugString( buf );\r
9730 #endif\r
9731 \r
9732     EngineOutputUpdate( stats );\r
9733 }\r