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