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