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