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