changes from H.G. Muller; version 4.3.8
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 Boolean flipClock = FALSE;\r
96 \r
97 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
98 \r
99 typedef struct {\r
100   ChessSquare piece;  \r
101   POINT pos;      /* window coordinates of current pos */\r
102   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
103   POINT from;     /* board coordinates of the piece's orig pos */\r
104   POINT to;       /* board coordinates of the piece's new pos */\r
105 } AnimInfo;\r
106 \r
107 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
108 \r
109 typedef struct {\r
110   POINT start;    /* window coordinates of start pos */\r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114 } DragInfo;\r
115 \r
116 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT sq[2];    /* board coordinates of from, to squares */\r
120 } HighlightInfo;\r
121 \r
122 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
123 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
124 \r
125 /* Window class names */\r
126 char szAppName[] = "WinBoard";\r
127 char szConsoleName[] = "WBConsole";\r
128 \r
129 /* Title bar text */\r
130 char szTitle[] = "WinBoard";\r
131 char szConsoleTitle[] = "ICS Interaction";\r
132 \r
133 char *programName;\r
134 char *settingsFileName;\r
135 BOOLEAN saveSettingsOnExit;\r
136 char installDir[MSG_SIZ];\r
137 \r
138 BoardSize boardSize;\r
139 BOOLEAN chessProgram;\r
140 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
141 static int squareSize, lineGap, minorSize;\r
142 static int winWidth, winHeight;\r
143 static RECT messageRect, whiteRect, blackRect;\r
144 static char messageText[MESSAGE_TEXT_MAX];\r
145 static int clockTimerEvent = 0;\r
146 static int loadGameTimerEvent = 0;\r
147 static int analysisTimerEvent = 0;\r
148 static DelayedEventCallback delayedTimerCallback;\r
149 static int delayedTimerEvent = 0;\r
150 static int buttonCount = 2;\r
151 char *icsTextMenuString;\r
152 char *icsNames;\r
153 char *firstChessProgramNames;\r
154 char *secondChessProgramNames;\r
155 \r
156 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
157 \r
158 #define PALETTESIZE 256\r
159 \r
160 HINSTANCE hInst;          /* current instance */\r
161 HWND hwndMain = NULL;        /* root window*/\r
162 HWND hwndConsole = NULL;\r
163 BOOLEAN alwaysOnTop = FALSE;\r
164 RECT boardRect;\r
165 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
166   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
167 HPALETTE hPal;\r
168 ColorClass currentColorClass;\r
169 \r
170 HWND hCommPort = NULL;    /* currently open comm port */\r
171 static HWND hwndPause;    /* pause button */\r
172 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
173 static HBRUSH lightSquareBrush, darkSquareBrush,\r
174   blackSquareBrush, /* [HGM] for band between board and holdings */\r
175   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
176 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
177 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
178 static HPEN gridPen = NULL;\r
179 static HPEN highlightPen = NULL;\r
180 static HPEN premovePen = NULL;\r
181 static NPLOGPALETTE pLogPal;\r
182 static BOOL paletteChanged = FALSE;\r
183 static HICON iconWhite, iconBlack, iconCurrent;\r
184 static int doingSizing = FALSE;\r
185 static int lastSizing = 0;\r
186 static int prevStderrPort;\r
187 \r
188 /* [AS] Support for background textures */\r
189 #define BACK_TEXTURE_MODE_DISABLED      0\r
190 #define BACK_TEXTURE_MODE_PLAIN         1\r
191 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
192 \r
193 static HBITMAP liteBackTexture = NULL;\r
194 static HBITMAP darkBackTexture = NULL;\r
195 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
196 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
197 static int backTextureSquareSize = 0;\r
198 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
199 \r
200 #if __GNUC__ && !defined(_winmajor)\r
201 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
202 #else\r
203 #define oldDialog (_winmajor < 4)\r
204 #endif\r
205 \r
206 char *defaultTextAttribs[] = \r
207 {\r
208   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
209   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
210   COLOR_NONE\r
211 };\r
212 \r
213 typedef struct {\r
214   char *name;\r
215   int squareSize;\r
216   int lineGap;\r
217   int smallLayout;\r
218   int tinyLayout;\r
219   int cliWidth, cliHeight;\r
220 } SizeInfo;\r
221 \r
222 SizeInfo sizeInfo[] = \r
223 {\r
224   { "tiny",     21, 0, 1, 1, 0, 0 },\r
225   { "teeny",    25, 1, 1, 1, 0, 0 },\r
226   { "dinky",    29, 1, 1, 1, 0, 0 },\r
227   { "petite",   33, 1, 1, 1, 0, 0 },\r
228   { "slim",     37, 2, 1, 0, 0, 0 },\r
229   { "small",    40, 2, 1, 0, 0, 0 },\r
230   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
231   { "middling", 49, 2, 0, 0, 0, 0 },\r
232   { "average",  54, 2, 0, 0, 0, 0 },\r
233   { "moderate", 58, 3, 0, 0, 0, 0 },\r
234   { "medium",   64, 3, 0, 0, 0, 0 },\r
235   { "bulky",    72, 3, 0, 0, 0, 0 },\r
236   { "large",    80, 3, 0, 0, 0, 0 },\r
237   { "big",      87, 3, 0, 0, 0, 0 },\r
238   { "huge",     95, 3, 0, 0, 0, 0 },\r
239   { "giant",    108, 3, 0, 0, 0, 0 },\r
240   { "colossal", 116, 4, 0, 0, 0, 0 },\r
241   { "titanic",  129, 4, 0, 0, 0, 0 },\r
242   { NULL, 0, 0, 0, 0, 0, 0 }\r
243 };\r
244 \r
245 #define MF(x) {x, {0, }, {0, }, 0}\r
246 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
247 {\r
248   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
249   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
250   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
251   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
252   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
253   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
254   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
255   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
256   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
257   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
258   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
259   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
260   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
261   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
262   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
263   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
264   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
265   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
266 };\r
267 \r
268 MyFont *font[NUM_SIZES][NUM_FONTS];\r
269 \r
270 typedef struct {\r
271   char *label;\r
272   int id;\r
273   HWND hwnd;\r
274   WNDPROC wndproc;\r
275 } MyButtonDesc;\r
276 \r
277 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
278 #define N_BUTTONS 5\r
279 \r
280 MyButtonDesc buttonDesc[N_BUTTONS] =\r
281 {\r
282   {"<<", IDM_ToStart, NULL, NULL},\r
283   {"<", IDM_Backward, NULL, NULL},\r
284   {"P", IDM_Pause, NULL, NULL},\r
285   {">", IDM_Forward, NULL, NULL},\r
286   {">>", IDM_ToEnd, NULL, NULL},\r
287 };\r
288 \r
289 int tinyLayout = 0, smallLayout = 0;\r
290 #define MENU_BAR_ITEMS 6\r
291 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
292   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
293   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
294 };\r
295 \r
296 \r
297 MySound sounds[(int)NSoundClasses];\r
298 MyTextAttribs textAttribs[(int)NColorClasses];\r
299 \r
300 MyColorizeAttribs colorizeAttribs[] = {\r
301   { (COLORREF)0, 0, "Shout Text" },\r
302   { (COLORREF)0, 0, "SShout/CShout" },\r
303   { (COLORREF)0, 0, "Channel 1 Text" },\r
304   { (COLORREF)0, 0, "Channel Text" },\r
305   { (COLORREF)0, 0, "Kibitz Text" },\r
306   { (COLORREF)0, 0, "Tell Text" },\r
307   { (COLORREF)0, 0, "Challenge Text" },\r
308   { (COLORREF)0, 0, "Request Text" },\r
309   { (COLORREF)0, 0, "Seek Text" },\r
310   { (COLORREF)0, 0, "Normal Text" },\r
311   { (COLORREF)0, 0, "None" }\r
312 };\r
313 \r
314 \r
315 \r
316 static char *commentTitle;\r
317 static char *commentText;\r
318 static int commentIndex;\r
319 static Boolean editComment = FALSE;\r
320 HWND commentDialog = NULL;\r
321 BOOLEAN commentDialogUp = FALSE;\r
322 static int commentX, commentY, commentH, commentW;\r
323 \r
324 static char *analysisTitle;\r
325 static char *analysisText;\r
326 HWND analysisDialog = NULL;\r
327 BOOLEAN analysisDialogUp = FALSE;\r
328 static int analysisX, analysisY, analysisH, analysisW;\r
329 \r
330 char errorTitle[MSG_SIZ];\r
331 char errorMessage[2*MSG_SIZ];\r
332 HWND errorDialog = NULL;\r
333 BOOLEAN moveErrorMessageUp = FALSE;\r
334 BOOLEAN consoleEcho = TRUE;\r
335 CHARFORMAT consoleCF;\r
336 COLORREF consoleBackgroundColor;\r
337 \r
338 char *programVersion;\r
339 \r
340 #define CPReal 1\r
341 #define CPComm 2\r
342 #define CPSock 3\r
343 #define CPRcmd 4\r
344 typedef int CPKind;\r
345 \r
346 typedef struct {\r
347   CPKind kind;\r
348   HANDLE hProcess;\r
349   DWORD pid;\r
350   HANDLE hTo;\r
351   HANDLE hFrom;\r
352   SOCKET sock;\r
353   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
354 } ChildProc;\r
355 \r
356 #define INPUT_SOURCE_BUF_SIZE 4096\r
357 \r
358 typedef struct _InputSource {\r
359   CPKind kind;\r
360   HANDLE hFile;\r
361   SOCKET sock;\r
362   int lineByLine;\r
363   HANDLE hThread;\r
364   DWORD id;\r
365   char buf[INPUT_SOURCE_BUF_SIZE];\r
366   char *next;\r
367   DWORD count;\r
368   int error;\r
369   InputCallback func;\r
370   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
371   VOIDSTAR closure;\r
372 } InputSource;\r
373 \r
374 InputSource *consoleInputSource;\r
375 \r
376 DCB dcb;\r
377 \r
378 /* forward */\r
379 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
380 VOID ConsoleCreate();\r
381 LRESULT CALLBACK\r
382   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
383 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
384 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
385 VOID ParseCommSettings(char *arg, DCB *dcb);\r
386 LRESULT CALLBACK\r
387   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
388 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
389 void ParseIcsTextMenu(char *icsTextMenuString);\r
390 VOID PopUpMoveDialog(char firstchar);\r
391 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
392 \r
393 /* [AS] */\r
394 int NewGameFRC();\r
395 int GameListOptions();\r
396 \r
397 HWND moveHistoryDialog = NULL;\r
398 BOOLEAN moveHistoryDialogUp = FALSE;\r
399 \r
400 WindowPlacement wpMoveHistory;\r
401 \r
402 HWND evalGraphDialog = NULL;\r
403 BOOLEAN evalGraphDialogUp = FALSE;\r
404 \r
405 WindowPlacement wpEvalGraph;\r
406 \r
407 HWND engineOutputDialog = NULL;\r
408 BOOLEAN engineOutputDialogUp = FALSE;\r
409 \r
410 WindowPlacement wpEngineOutput;\r
411 \r
412 VOID MoveHistoryPopUp();\r
413 VOID MoveHistoryPopDown();\r
414 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
415 BOOL MoveHistoryIsUp();\r
416 \r
417 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
418 VOID EvalGraphPopUp();\r
419 VOID EvalGraphPopDown();\r
420 BOOL EvalGraphIsUp();\r
421 \r
422 VOID EngineOutputPopUp();\r
423 VOID EngineOutputPopDown();\r
424 BOOL EngineOutputIsUp();\r
425 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
426 \r
427 VOID GothicPopUp(char *title, char up);\r
428 /*\r
429  * Setting "frozen" should disable all user input other than deleting\r
430  * the window.  We do this while engines are initializing themselves.\r
431  */\r
432 static int frozen = 0;\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];\r
434 void FreezeUI()\r
435 {\r
436   HMENU hmenu;\r
437   int i;\r
438 \r
439   if (frozen) return;\r
440   frozen = 1;\r
441   hmenu = GetMenu(hwndMain);\r
442   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
443     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
444   }\r
445   DrawMenuBar(hwndMain);\r
446 }\r
447 \r
448 /* Undo a FreezeUI */\r
449 void ThawUI()\r
450 {\r
451   HMENU hmenu;\r
452   int i;\r
453 \r
454   if (!frozen) return;\r
455   frozen = 0;\r
456   hmenu = GetMenu(hwndMain);\r
457   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
458     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
459   }\r
460   DrawMenuBar(hwndMain);\r
461 }\r
462 \r
463 /*---------------------------------------------------------------------------*\\r
464  *\r
465  * WinMain\r
466  *\r
467 \*---------------------------------------------------------------------------*/\r
468 \r
469 int APIENTRY\r
470 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
471         LPSTR lpCmdLine, int nCmdShow)\r
472 {\r
473   MSG msg;\r
474   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
475 \r
476   debugFP = stderr;\r
477 \r
478   LoadLibrary("RICHED32.DLL");\r
479   consoleCF.cbSize = sizeof(CHARFORMAT);\r
480 \r
481   if (!InitApplication(hInstance)) {\r
482     return (FALSE);\r
483   }\r
484   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
485     return (FALSE);\r
486   }\r
487 \r
488   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
489   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
490   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
491 \r
492   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
493 \r
494   while (GetMessage(&msg, /* message structure */\r
495                     NULL, /* handle of window receiving the message */\r
496                     0,    /* lowest message to examine */\r
497                     0))   /* highest message to examine */\r
498     {\r
499       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
500           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
501           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
502           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
503           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
504           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
505           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
506           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
507           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
508           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
509         TranslateMessage(&msg); /* Translates virtual key codes */\r
510         DispatchMessage(&msg);  /* Dispatches message to window */\r
511       }\r
512     }\r
513 \r
514 \r
515   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
516 }\r
517 \r
518 /*---------------------------------------------------------------------------*\\r
519  *\r
520  * Initialization functions\r
521  *\r
522 \*---------------------------------------------------------------------------*/\r
523 \r
524 BOOL\r
525 InitApplication(HINSTANCE hInstance)\r
526 {\r
527   WNDCLASS wc;\r
528 \r
529   /* Fill in window class structure with parameters that describe the */\r
530   /* main window. */\r
531 \r
532   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
533   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
534   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
535   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
536   wc.hInstance     = hInstance;         /* Owner of this class */\r
537   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
538   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
539   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
540   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
541   wc.lpszClassName = szAppName;                 /* Name to register as */\r
542 \r
543   /* Register the window class and return success/failure code. */\r
544   if (!RegisterClass(&wc)) return FALSE;\r
545 \r
546   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
547   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
548   wc.cbClsExtra    = 0;\r
549   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
550   wc.hInstance     = hInstance;\r
551   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
552   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
553   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
554   wc.lpszMenuName  = NULL;\r
555   wc.lpszClassName = szConsoleName;\r
556 \r
557   if (!RegisterClass(&wc)) return FALSE;\r
558   return TRUE;\r
559 }\r
560 \r
561 \r
562 /* Set by InitInstance, used by EnsureOnScreen */\r
563 int screenHeight, screenWidth;\r
564 \r
565 void\r
566 EnsureOnScreen(int *x, int *y)\r
567 {\r
568   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
569   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
570   if (*x > screenWidth - 32) *x = 0;\r
571   if (*y > screenHeight - 32) *y = 0;\r
572   if (*x < 10) *x = 10;\r
573   if (*y < gap) *y = gap;\r
574 }\r
575 \r
576 BOOL\r
577 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
578 {\r
579   HWND hwnd; /* Main window handle. */\r
580   int ibs;\r
581   WINDOWPLACEMENT wp;\r
582   char *filepart;\r
583 \r
584   hInst = hInstance;    /* Store instance handle in our global variable */\r
585 \r
586   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
587     *filepart = NULLCHAR;\r
588   } else {\r
589     GetCurrentDirectory(MSG_SIZ, installDir);\r
590   }\r
591   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
592   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
593   if (appData.debugMode) {\r
594     debugFP = fopen(appData.nameOfDebugFile, "w");\r
595     setbuf(debugFP, NULL);\r
596   }\r
597 \r
598   InitBackEnd1();\r
599 \r
600   InitEngineUCI( installDir, &first );\r
601   InitEngineUCI( installDir, &second );\r
602 \r
603   /* Create a main window for this application instance. */\r
604   hwnd = CreateWindow(szAppName, szTitle,\r
605                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
606                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
607                       NULL, NULL, hInstance, NULL);\r
608   hwndMain = hwnd;\r
609 \r
610   /* If window could not be created, return "failure" */\r
611   if (!hwnd) {\r
612     return (FALSE);\r
613   }\r
614 \r
615   iconWhite = LoadIcon(hInstance, "icon_white");\r
616   iconBlack = LoadIcon(hInstance, "icon_black");\r
617   iconCurrent = iconWhite;\r
618   InitDrawingColors();\r
619   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
620   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
621   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
622     /* Compute window size for each board size, and use the largest\r
623        size that fits on this screen as the default. */\r
624     InitDrawingSizes((BoardSize)ibs, 0);\r
625     if (boardSize == (BoardSize)-1 &&\r
626         winHeight <= screenHeight\r
627            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
628         && winWidth <= screenWidth) {\r
629       boardSize = (BoardSize)ibs;\r
630     }\r
631   }\r
632   InitDrawingSizes(boardSize, 0);\r
633   InitMenuChecks();\r
634   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
635 \r
636   /* [AS] Load textures if specified */\r
637   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
638   \r
639   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
640       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
641       liteBackTextureMode = appData.liteBackTextureMode;\r
642 \r
643       if (liteBackTexture == NULL && appData.debugMode) {\r
644           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
645       }\r
646   }\r
647   \r
648   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
649       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
650       darkBackTextureMode = appData.darkBackTextureMode;\r
651 \r
652       if (darkBackTexture == NULL && appData.debugMode) {\r
653           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
654       }\r
655   }\r
656 \r
657   mysrandom( (unsigned) time(NULL) );\r
658 \r
659   /* Make a console window if needed */\r
660   if (appData.icsActive) {\r
661     ConsoleCreate();\r
662   }\r
663 \r
664   /* [AS] Restore layout */\r
665   if( wpMoveHistory.visible ) {\r
666       MoveHistoryPopUp();\r
667   }\r
668 \r
669   if( wpEvalGraph.visible ) {\r
670       EvalGraphPopUp();\r
671   }\r
672 \r
673   if( wpEngineOutput.visible ) {\r
674       EngineOutputPopUp();\r
675   }\r
676 \r
677   InitBackEnd2();\r
678 \r
679   /* Make the window visible; update its client area; and return "success" */\r
680   EnsureOnScreen(&boardX, &boardY);\r
681   wp.length = sizeof(WINDOWPLACEMENT);\r
682   wp.flags = 0;\r
683   wp.showCmd = nCmdShow;\r
684   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
685   wp.rcNormalPosition.left = boardX;\r
686   wp.rcNormalPosition.right = boardX + winWidth;\r
687   wp.rcNormalPosition.top = boardY;\r
688   wp.rcNormalPosition.bottom = boardY + winHeight;\r
689   SetWindowPlacement(hwndMain, &wp);\r
690 \r
691   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
692                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
693 \r
694   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
695   if( gameInfo.variant != VariantFischeRandom ) {\r
696       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
697   }\r
698 \r
699   if (hwndConsole) {\r
700 #if AOT_CONSOLE\r
701     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
702                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
703 #endif\r
704     ShowWindow(hwndConsole, nCmdShow);\r
705   }\r
706   UpdateWindow(hwnd);\r
707 \r
708   return TRUE;\r
709 \r
710 }\r
711 \r
712 \r
713 typedef enum {\r
714   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
715   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
716   ArgSettingsFilename\r
717 } ArgType;\r
718 \r
719 typedef struct {\r
720   char *argName;\r
721   ArgType argType;\r
722   /***\r
723   union {\r
724     String *pString;       // ArgString\r
725     int *pInt;             // ArgInt\r
726     float *pFloat;         // ArgFloat\r
727     Boolean *pBoolean;     // ArgBoolean\r
728     COLORREF *pColor;      // ArgColor\r
729     ColorClass cc;         // ArgAttribs\r
730     String *pFilename;     // ArgFilename\r
731     BoardSize *pBoardSize; // ArgBoardSize\r
732     int whichFont;         // ArgFont\r
733     DCB *pDCB;             // ArgCommSettings\r
734     String *pFilename;     // ArgSettingsFilename\r
735   } argLoc;\r
736   ***/\r
737   LPVOID argLoc;\r
738   BOOL save;\r
739 } ArgDescriptor;\r
740 \r
741 int junk;\r
742 ArgDescriptor argDescriptors[] = {\r
743   /* positional arguments */\r
744   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
745   { "", ArgNone, NULL },\r
746   /* keyword arguments */\r
747   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
748   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
749   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
750   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
751   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
752   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
753   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
754   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
755   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
756   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
757   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
758   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
759   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
760   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
761   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
762   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
763   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
764   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
765     FALSE },\r
766   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
767     FALSE },\r
768   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
769     FALSE },\r
770   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
771   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
772     FALSE },\r
773   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
774   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
775   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
776   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
777   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
778   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
779   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
780   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
781   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
782   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
783   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
784   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
785   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
786   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
787   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
788   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
789   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
790   /*!!bitmapDirectory?*/\r
791   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
792   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
793   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
794   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
795   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
796   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
797   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
798   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
799   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
800   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
801   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
802   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
803   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
804   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
805   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
806   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
807   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
808   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
809   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
810   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
811   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
812   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
813   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
814   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
815   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
816   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
817   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
818   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
819   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
820   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
821   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
822   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
823   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
824   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
825   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
826   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
827   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
828   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
829   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
830   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
831   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
832   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
833   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
834   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
835   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
836   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
837   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
838   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
839   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
840   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
841   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
842   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
843   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
844   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
845   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
846   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
847   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
848   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
849   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
850   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
851   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
852   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
853   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
854   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
855   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
856   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
857   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
858   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
859   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
860   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
861   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
862   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
863   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
864   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
865   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
866   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
867   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
868   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
869   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
870   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
871   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
872   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
873   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
874   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
875   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
876   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
877   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
878   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
879   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
880   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
881   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
882   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
883   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
884   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
885     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
886   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
887   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
888   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
889   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
890   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
891   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
892   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
893   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
894     TRUE }, /* must come after all fonts */\r
895   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
896   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
897     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
898   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
899   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
900   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
901   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
902   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
903   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
904   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
905   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
906   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
907   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
908   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
909   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
910   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
911   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
912   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
913   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
914   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
915   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
916   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
917   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
918   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
919   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
920   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
921   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
922   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
923   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
924   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
925   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
926 #if 0\r
927   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
928   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
929 #endif\r
930   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
931   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
932   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
933   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
934   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
935   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
936   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
937   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
938   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
939   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
940   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
941   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
942   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
943   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
944   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
945   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
946   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
947   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
948   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
949   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
950   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
951   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
952   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
953   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
954   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
955   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
956   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
957   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
958   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
959   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
960   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
961   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
962   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
963   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
964   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
965   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
966   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
967   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
968   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
969   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
970   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
971   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
972   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
973   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
974   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
975   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
976   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
977   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
978   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
979   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
980   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
981   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
982   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
983   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
984   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
985   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
986   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
987   { "highlightLastMove", ArgBoolean,\r
988     (LPVOID) &appData.highlightLastMove, TRUE },\r
989   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
990   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
991   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
992   { "highlightDragging", ArgBoolean,\r
993     (LPVOID) &appData.highlightDragging, TRUE },\r
994   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
995   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
996   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
997   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
998   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
999   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1000   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1001   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1002   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1003   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1004   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1005   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1006   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1007   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1008   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1009   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1010   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1011   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1012   { "soundShout", ArgFilename,\r
1013     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1014   { "soundSShout", ArgFilename,\r
1015     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1016   { "soundChannel1", ArgFilename,\r
1017     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1018   { "soundChannel", ArgFilename,\r
1019     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1020   { "soundKibitz", ArgFilename,\r
1021     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1022   { "soundTell", ArgFilename,\r
1023     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1024   { "soundChallenge", ArgFilename,\r
1025     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1026   { "soundRequest", ArgFilename,\r
1027     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1028   { "soundSeek", ArgFilename,\r
1029     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1030   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1031   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1032   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1033   { "soundIcsLoss", ArgFilename, \r
1034     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1035   { "soundIcsDraw", ArgFilename, \r
1036     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1037   { "soundIcsUnfinished", ArgFilename, \r
1038     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1039   { "soundIcsAlarm", ArgFilename, \r
1040     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1041   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1042   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1043   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1044   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1045   { "reuseChessPrograms", ArgBoolean,\r
1046     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1047   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1048   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1049   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1050   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1051   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1052   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1053   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1054   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1055   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1056   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1057   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1058   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1059   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1060   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1061   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1062   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1063   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1064   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1065   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1066   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1067   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1068   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1069   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1070   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1071   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1072   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1073   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1074   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1075   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1076   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1077   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1078   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1079   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1080   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1081   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1082   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1083   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1084     TRUE },\r
1085   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1086     TRUE },\r
1087   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1088   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1089   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1090   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1091   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1092   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1093   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1094   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1095   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1096   /* [AS] New features */\r
1097   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1098   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1099   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1100   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1101   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1102   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1103   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1104   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1105   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1106   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1107   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1108   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1109   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1110   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1111   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1112   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1113   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1114   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1115   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1116   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1117   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1118   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1119   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1120   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1121   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1122   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1123   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1124   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1125   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1126   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1127   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1128   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1129   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1130   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1131   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1132   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1133   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1134   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1135   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1136   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1137   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1138   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1139   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1140   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1141   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1142   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1143   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1144   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1145 \r
1146   /* [AS] Layout stuff */\r
1147   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1148   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1149   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1150   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1151   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1152 \r
1153   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1154   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1155   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1156   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1157   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1158 \r
1159   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1160   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1161   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1162   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1163   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1164 \r
1165   /* [HGM] board-size, adjudication and misc. options */\r
1166   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1167   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1168   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1169   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1170   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1171   { "flipBlack", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1172   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1173   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1174   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1175   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1176   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1177   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1178   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1179   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1180 \r
1181 #ifdef ZIPPY\r
1182   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1183   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1184   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1185   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1186   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1187   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1188   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1189   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1190   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1191   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1192   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1193   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1194   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1195     FALSE },\r
1196   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1197   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1198   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1199   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1200   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1201   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1202   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1203     FALSE },\r
1204   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1205   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1206   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1207   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1208   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1209   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1210   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1211   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1212   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1213   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1214   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1215   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1216   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1217   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1218   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1219   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1220   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1221   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1222 #endif\r
1223   { NULL, ArgNone, NULL, FALSE }\r
1224 };\r
1225 \r
1226 \r
1227 /* Kludge for indirection files on command line */\r
1228 char* lastIndirectionFilename;\r
1229 ArgDescriptor argDescriptorIndirection =\r
1230 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1231 \r
1232 \r
1233 VOID\r
1234 ExitArgError(char *msg, char *badArg)\r
1235 {\r
1236   char buf[MSG_SIZ];\r
1237 \r
1238   sprintf(buf, "%s %s", msg, badArg);\r
1239   DisplayFatalError(buf, 0, 2);\r
1240   exit(2);\r
1241 }\r
1242 \r
1243 /* Command line font name parser.  NULL name means do nothing.\r
1244    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1245    For backward compatibility, syntax without the colon is also\r
1246    accepted, but font names with digits in them won't work in that case.\r
1247 */\r
1248 VOID\r
1249 ParseFontName(char *name, MyFontParams *mfp)\r
1250 {\r
1251   char *p, *q;\r
1252   if (name == NULL) return;\r
1253   p = name;\r
1254   q = strchr(p, ':');\r
1255   if (q) {\r
1256     if (q - p >= sizeof(mfp->faceName))\r
1257       ExitArgError("Font name too long:", name);\r
1258     memcpy(mfp->faceName, p, q - p);\r
1259     mfp->faceName[q - p] = NULLCHAR;\r
1260     p = q + 1;\r
1261   } else {\r
1262     q = mfp->faceName;\r
1263     while (*p && !isdigit(*p)) {\r
1264       *q++ = *p++;\r
1265       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1266         ExitArgError("Font name too long:", name);\r
1267     }\r
1268     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1269     *q = NULLCHAR;\r
1270   }\r
1271   if (!*p) ExitArgError("Font point size missing:", name);\r
1272   mfp->pointSize = (float) atof(p);\r
1273   mfp->bold = (strchr(p, 'b') != NULL);\r
1274   mfp->italic = (strchr(p, 'i') != NULL);\r
1275   mfp->underline = (strchr(p, 'u') != NULL);\r
1276   mfp->strikeout = (strchr(p, 's') != NULL);\r
1277 }\r
1278 \r
1279 /* Color name parser.\r
1280    X version accepts X color names, but this one\r
1281    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1282 COLORREF\r
1283 ParseColorName(char *name)\r
1284 {\r
1285   int red, green, blue, count;\r
1286   char buf[MSG_SIZ];\r
1287 \r
1288   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1289   if (count != 3) {\r
1290     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1291       &red, &green, &blue);\r
1292   }\r
1293   if (count != 3) {\r
1294     sprintf(buf, "Can't parse color name %s", name);\r
1295     DisplayError(buf, 0);\r
1296     return RGB(0, 0, 0);\r
1297   }\r
1298   return PALETTERGB(red, green, blue);\r
1299 }\r
1300 \r
1301 \r
1302 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1303 {\r
1304   char *e = argValue;\r
1305   int eff = 0;\r
1306 \r
1307   while (*e) {\r
1308     if (*e == 'b')      eff |= CFE_BOLD;\r
1309     else if (*e == 'i') eff |= CFE_ITALIC;\r
1310     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1311     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1312     else if (*e == '#' || isdigit(*e)) break;\r
1313     e++;\r
1314   }\r
1315   *effects = eff;\r
1316   *color   = ParseColorName(e);\r
1317 }\r
1318 \r
1319 \r
1320 BoardSize\r
1321 ParseBoardSize(char *name)\r
1322 {\r
1323   BoardSize bs = SizeTiny;\r
1324   while (sizeInfo[bs].name != NULL) {\r
1325     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1326     bs++;\r
1327   }\r
1328   ExitArgError("Unrecognized board size value", name);\r
1329   return bs; /* not reached */\r
1330 }\r
1331 \r
1332 \r
1333 char\r
1334 StringGet(void *getClosure)\r
1335 {\r
1336   char **p = (char **) getClosure;\r
1337   return *((*p)++);\r
1338 }\r
1339 \r
1340 char\r
1341 FileGet(void *getClosure)\r
1342 {\r
1343   int c;\r
1344   FILE* f = (FILE*) getClosure;\r
1345 \r
1346   c = getc(f);\r
1347   if (c == EOF)\r
1348     return NULLCHAR;\r
1349   else\r
1350     return (char) c;\r
1351 }\r
1352 \r
1353 /* Parse settings file named "name". If file found, return the\r
1354    full name in fullname and return TRUE; else return FALSE */\r
1355 BOOLEAN\r
1356 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1357 {\r
1358   char *dummy;\r
1359   FILE *f;\r
1360 \r
1361   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1362     f = fopen(fullname, "r");\r
1363     if (f != NULL) {\r
1364       ParseArgs(FileGet, f);\r
1365       fclose(f);\r
1366       return TRUE;\r
1367     }\r
1368   }\r
1369   return FALSE;\r
1370 }\r
1371 \r
1372 VOID\r
1373 ParseArgs(GetFunc get, void *cl)\r
1374 {\r
1375   char argName[ARG_MAX];\r
1376   char argValue[ARG_MAX];\r
1377   ArgDescriptor *ad;\r
1378   char start;\r
1379   char *q;\r
1380   int i, octval;\r
1381   char ch;\r
1382   int posarg = 0;\r
1383 \r
1384   ch = get(cl);\r
1385   for (;;) {\r
1386     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1387     if (ch == NULLCHAR) break;\r
1388     if (ch == ';') {\r
1389       /* Comment to end of line */\r
1390       ch = get(cl);\r
1391       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1392       continue;\r
1393     } else if (ch == '/' || ch == '-') {\r
1394       /* Switch */\r
1395       q = argName;\r
1396       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1397              ch != '\n' && ch != '\t') {\r
1398         *q++ = ch;\r
1399         ch = get(cl);\r
1400       }\r
1401       *q = NULLCHAR;\r
1402 \r
1403       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1404         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1405 \r
1406       if (ad->argName == NULL)\r
1407         ExitArgError("Unrecognized argument", argName);\r
1408 \r
1409     } else if (ch == '@') {\r
1410       /* Indirection file */\r
1411       ad = &argDescriptorIndirection;\r
1412       ch = get(cl);\r
1413     } else {\r
1414       /* Positional argument */\r
1415       ad = &argDescriptors[posarg++];\r
1416       strcpy(argName, ad->argName);\r
1417     }\r
1418 \r
1419     if (ad->argType == ArgTrue) {\r
1420       *(Boolean *) ad->argLoc = TRUE;\r
1421       continue;\r
1422     }\r
1423     if (ad->argType == ArgFalse) {\r
1424       *(Boolean *) ad->argLoc = FALSE;\r
1425       continue;\r
1426     }\r
1427 \r
1428     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1429     if (ch == NULLCHAR || ch == '\n') {\r
1430       ExitArgError("No value provided for argument", argName);\r
1431     }\r
1432     q = argValue;\r
1433     if (ch == '{') {\r
1434       // Quoting with { }.  No characters have to (or can) be escaped.\r
1435       // Thus the string cannot contain a '}' character.\r
1436       start = ch;\r
1437       ch = get(cl);\r
1438       while (start) {\r
1439         switch (ch) {\r
1440         case NULLCHAR:\r
1441           start = NULLCHAR;\r
1442           break;\r
1443           \r
1444         case '}':\r
1445           ch = get(cl);\r
1446           start = NULLCHAR;\r
1447           break;\r
1448 \r
1449         default:\r
1450           *q++ = ch;\r
1451           ch = get(cl);\r
1452           break;\r
1453         }\r
1454       }   \r
1455     } else if (ch == '\'' || ch == '"') {\r
1456       // Quoting with ' ' or " ", with \ as escape character.\r
1457       // Inconvenient for long strings that may contain Windows filenames.\r
1458       start = ch;\r
1459       ch = get(cl);\r
1460       while (start) {\r
1461         switch (ch) {\r
1462         case NULLCHAR:\r
1463           start = NULLCHAR;\r
1464           break;\r
1465 \r
1466         default:\r
1467         not_special:\r
1468           *q++ = ch;\r
1469           ch = get(cl);\r
1470           break;\r
1471 \r
1472         case '\'':\r
1473         case '\"':\r
1474           if (ch == start) {\r
1475             ch = get(cl);\r
1476             start = NULLCHAR;\r
1477             break;\r
1478           } else {\r
1479             goto not_special;\r
1480           }\r
1481 \r
1482         case '\\':\r
1483           if (ad->argType == ArgFilename\r
1484               || ad->argType == ArgSettingsFilename) {\r
1485               goto not_special;\r
1486           }\r
1487           ch = get(cl);\r
1488           switch (ch) {\r
1489           case NULLCHAR:\r
1490             ExitArgError("Incomplete \\ escape in value for", argName);\r
1491             break;\r
1492           case 'n':\r
1493             *q++ = '\n';\r
1494             ch = get(cl);\r
1495             break;\r
1496           case 'r':\r
1497             *q++ = '\r';\r
1498             ch = get(cl);\r
1499             break;\r
1500           case 't':\r
1501             *q++ = '\t';\r
1502             ch = get(cl);\r
1503             break;\r
1504           case 'b':\r
1505             *q++ = '\b';\r
1506             ch = get(cl);\r
1507             break;\r
1508           case 'f':\r
1509             *q++ = '\f';\r
1510             ch = get(cl);\r
1511             break;\r
1512           default:\r
1513             octval = 0;\r
1514             for (i = 0; i < 3; i++) {\r
1515               if (ch >= '0' && ch <= '7') {\r
1516                 octval = octval*8 + (ch - '0');\r
1517                 ch = get(cl);\r
1518               } else {\r
1519                 break;\r
1520               }\r
1521             }\r
1522             if (i > 0) {\r
1523               *q++ = (char) octval;\r
1524             } else {\r
1525               *q++ = ch;\r
1526               ch = get(cl);\r
1527             }\r
1528             break;\r
1529           }\r
1530           break;\r
1531         }\r
1532       }\r
1533     } else {\r
1534       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1535         *q++ = ch;\r
1536         ch = get(cl);\r
1537       }\r
1538     }\r
1539     *q = NULLCHAR;\r
1540 \r
1541     switch (ad->argType) {\r
1542     case ArgInt:\r
1543       *(int *) ad->argLoc = atoi(argValue);\r
1544       break;\r
1545 \r
1546     case ArgFloat:\r
1547       *(float *) ad->argLoc = (float) atof(argValue);\r
1548       break;\r
1549 \r
1550     case ArgString:\r
1551     case ArgFilename:\r
1552       *(char **) ad->argLoc = strdup(argValue);\r
1553       break;\r
1554 \r
1555     case ArgSettingsFilename:\r
1556       {\r
1557         char fullname[MSG_SIZ];\r
1558         if (ParseSettingsFile(argValue, fullname)) {\r
1559           if (ad->argLoc != NULL) {\r
1560             *(char **) ad->argLoc = strdup(fullname);\r
1561           }\r
1562         } else {\r
1563           if (ad->argLoc != NULL) {\r
1564           } else {\r
1565             ExitArgError("Failed to open indirection file", argValue);\r
1566           }\r
1567         }\r
1568       }\r
1569       break;\r
1570 \r
1571     case ArgBoolean:\r
1572       switch (argValue[0]) {\r
1573       case 't':\r
1574       case 'T':\r
1575         *(Boolean *) ad->argLoc = TRUE;\r
1576         break;\r
1577       case 'f':\r
1578       case 'F':\r
1579         *(Boolean *) ad->argLoc = FALSE;\r
1580         break;\r
1581       default:\r
1582         ExitArgError("Unrecognized boolean argument value", argValue);\r
1583         break;\r
1584       }\r
1585       break;\r
1586 \r
1587     case ArgColor:\r
1588       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1589       break;\r
1590 \r
1591     case ArgAttribs: {\r
1592       ColorClass cc = (ColorClass)ad->argLoc;\r
1593       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1594       }\r
1595       break;\r
1596       \r
1597     case ArgBoardSize:\r
1598       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1599       break;\r
1600 \r
1601     case ArgFont:\r
1602       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1603       break;\r
1604 \r
1605     case ArgCommSettings:\r
1606       ParseCommSettings(argValue, &dcb);\r
1607       break;\r
1608 \r
1609     case ArgNone:\r
1610       ExitArgError("Unrecognized argument", argValue);\r
1611       break;\r
1612     }\r
1613   }\r
1614 }\r
1615 \r
1616 VOID\r
1617 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1618 {\r
1619   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1620   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1621   DeleteDC(hdc);\r
1622   lf->lfWidth = 0;\r
1623   lf->lfEscapement = 0;\r
1624   lf->lfOrientation = 0;\r
1625   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1626   lf->lfItalic = mfp->italic;\r
1627   lf->lfUnderline = mfp->underline;\r
1628   lf->lfStrikeOut = mfp->strikeout;\r
1629   lf->lfCharSet = DEFAULT_CHARSET;\r
1630   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1631   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1632   lf->lfQuality = DEFAULT_QUALITY;\r
1633   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1634   strcpy(lf->lfFaceName, mfp->faceName);\r
1635 }\r
1636 \r
1637 VOID\r
1638 CreateFontInMF(MyFont *mf)\r
1639 {\r
1640   LFfromMFP(&mf->lf, &mf->mfp);\r
1641   if (mf->hf) DeleteObject(mf->hf);\r
1642   mf->hf = CreateFontIndirect(&mf->lf);\r
1643 }\r
1644 \r
1645 VOID\r
1646 SetDefaultTextAttribs()\r
1647 {\r
1648   ColorClass cc;\r
1649   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1650     ParseAttribs(&textAttribs[cc].color, \r
1651                  &textAttribs[cc].effects, \r
1652                  defaultTextAttribs[cc]);\r
1653   }\r
1654 }\r
1655 \r
1656 VOID\r
1657 SetDefaultSounds()\r
1658 {\r
1659   ColorClass cc;\r
1660   SoundClass sc;\r
1661   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1662     textAttribs[cc].sound.name = strdup("");\r
1663     textAttribs[cc].sound.data = NULL;\r
1664   }\r
1665   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1666     sounds[sc].name = strdup("");\r
1667     sounds[sc].data = NULL;\r
1668   }\r
1669   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1670 }\r
1671 \r
1672 VOID\r
1673 LoadAllSounds()\r
1674 {\r
1675   ColorClass cc;\r
1676   SoundClass sc;\r
1677   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1678     MyLoadSound(&textAttribs[cc].sound);\r
1679   }\r
1680   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1681     MyLoadSound(&sounds[sc]);\r
1682   }\r
1683 }\r
1684 \r
1685 VOID\r
1686 InitAppData(LPSTR lpCmdLine)\r
1687 {\r
1688   int i, j;\r
1689   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1690   char *dummy, *p;\r
1691 \r
1692   programName = szAppName;\r
1693 \r
1694   /* Initialize to defaults */\r
1695   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1696   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1697   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1698   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1699   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1700   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1701   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1702   SetDefaultTextAttribs();\r
1703   SetDefaultSounds();\r
1704   appData.movesPerSession = MOVES_PER_SESSION;\r
1705   appData.initString = INIT_STRING;\r
1706   appData.secondInitString = INIT_STRING;\r
1707   appData.firstComputerString = COMPUTER_STRING;\r
1708   appData.secondComputerString = COMPUTER_STRING;\r
1709   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1710   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1711   appData.firstPlaysBlack = FALSE;\r
1712   appData.noChessProgram = FALSE;\r
1713   chessProgram = FALSE;\r
1714   appData.firstHost = FIRST_HOST;\r
1715   appData.secondHost = SECOND_HOST;\r
1716   appData.firstDirectory = FIRST_DIRECTORY;\r
1717   appData.secondDirectory = SECOND_DIRECTORY;\r
1718   appData.bitmapDirectory = "";\r
1719   appData.remoteShell = REMOTE_SHELL;\r
1720   appData.remoteUser = "";\r
1721   appData.timeDelay = TIME_DELAY;\r
1722   appData.timeControl = TIME_CONTROL;\r
1723   appData.timeIncrement = TIME_INCREMENT;\r
1724   appData.icsActive = FALSE;\r
1725   appData.icsHost = "";\r
1726   appData.icsPort = ICS_PORT;\r
1727   appData.icsCommPort = ICS_COMM_PORT;\r
1728   appData.icsLogon = ICS_LOGON;\r
1729   appData.icsHelper = "";\r
1730   appData.useTelnet = FALSE;\r
1731   appData.telnetProgram = TELNET_PROGRAM;\r
1732   appData.gateway = "";\r
1733   appData.loadGameFile = "";\r
1734   appData.loadGameIndex = 0;\r
1735   appData.saveGameFile = "";\r
1736   appData.autoSaveGames = FALSE;\r
1737   appData.loadPositionFile = "";\r
1738   appData.loadPositionIndex = 1;\r
1739   appData.savePositionFile = "";\r
1740   appData.matchMode = FALSE;\r
1741   appData.matchGames = 0;\r
1742   appData.monoMode = FALSE;\r
1743   appData.debugMode = FALSE;\r
1744   appData.clockMode = TRUE;\r
1745   boardSize = (BoardSize) -1; /* determine by screen size */\r
1746   appData.Iconic = FALSE; /*unused*/\r
1747   appData.searchTime = "";\r
1748   appData.searchDepth = 0;\r
1749   appData.showCoords = FALSE;\r
1750   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1751   appData.autoCallFlag = FALSE;\r
1752   appData.flipView = FALSE;\r
1753   appData.autoFlipView = TRUE;\r
1754   appData.cmailGameName = "";\r
1755   appData.alwaysPromoteToQueen = FALSE;\r
1756   appData.oldSaveStyle = FALSE;\r
1757   appData.quietPlay = FALSE;\r
1758   appData.showThinking = FALSE;\r
1759   appData.ponderNextMove = TRUE;\r
1760   appData.periodicUpdates = TRUE;\r
1761   appData.popupExitMessage = TRUE;\r
1762   appData.popupMoveErrors = FALSE;\r
1763   appData.autoObserve = FALSE;\r
1764   appData.autoComment = FALSE;\r
1765   appData.animate = TRUE;\r
1766   appData.animSpeed = 10;\r
1767   appData.animateDragging = TRUE;\r
1768   appData.highlightLastMove = TRUE;\r
1769   appData.getMoveList = TRUE;\r
1770   appData.testLegality = TRUE;\r
1771   appData.premove = TRUE;\r
1772   appData.premoveWhite = FALSE;\r
1773   appData.premoveWhiteText = "";\r
1774   appData.premoveBlack = FALSE;\r
1775   appData.premoveBlackText = "";\r
1776   appData.icsAlarm = TRUE;\r
1777   appData.icsAlarmTime = 5000;\r
1778   appData.autoRaiseBoard = TRUE;\r
1779   appData.localLineEditing = TRUE;\r
1780   appData.colorize = TRUE;\r
1781   appData.reuseFirst = TRUE;\r
1782   appData.reuseSecond = TRUE;\r
1783   appData.blindfold = FALSE;\r
1784   dcb.DCBlength = sizeof(DCB);\r
1785   dcb.BaudRate = 9600;\r
1786   dcb.fBinary = TRUE;\r
1787   dcb.fParity = FALSE;\r
1788   dcb.fOutxCtsFlow = FALSE;\r
1789   dcb.fOutxDsrFlow = FALSE;\r
1790   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1791   dcb.fDsrSensitivity = FALSE;\r
1792   dcb.fTXContinueOnXoff = TRUE;\r
1793   dcb.fOutX = FALSE;\r
1794   dcb.fInX = FALSE;\r
1795   dcb.fNull = FALSE;\r
1796   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1797   dcb.fAbortOnError = FALSE;\r
1798   dcb.wReserved = 0;\r
1799   dcb.ByteSize = 7;\r
1800   dcb.Parity = SPACEPARITY;\r
1801   dcb.StopBits = ONESTOPBIT;\r
1802   settingsFileName = SETTINGS_FILE;\r
1803   saveSettingsOnExit = TRUE;\r
1804   boardX = CW_USEDEFAULT;\r
1805   boardY = CW_USEDEFAULT;\r
1806   consoleX = CW_USEDEFAULT; \r
1807   consoleY = CW_USEDEFAULT; \r
1808   consoleW = CW_USEDEFAULT;\r
1809   consoleH = CW_USEDEFAULT;\r
1810   analysisX = CW_USEDEFAULT; \r
1811   analysisY = CW_USEDEFAULT; \r
1812   analysisW = CW_USEDEFAULT;\r
1813   analysisH = CW_USEDEFAULT;\r
1814   commentX = CW_USEDEFAULT; \r
1815   commentY = CW_USEDEFAULT; \r
1816   commentW = CW_USEDEFAULT;\r
1817   commentH = CW_USEDEFAULT;\r
1818   editTagsX = CW_USEDEFAULT; \r
1819   editTagsY = CW_USEDEFAULT; \r
1820   editTagsW = CW_USEDEFAULT;\r
1821   editTagsH = CW_USEDEFAULT;\r
1822   gameListX = CW_USEDEFAULT; \r
1823   gameListY = CW_USEDEFAULT; \r
1824   gameListW = CW_USEDEFAULT;\r
1825   gameListH = CW_USEDEFAULT;\r
1826   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1827   icsNames = ICS_NAMES;\r
1828   firstChessProgramNames = FCP_NAMES;\r
1829   secondChessProgramNames = SCP_NAMES;\r
1830   appData.initialMode = "";\r
1831   appData.variant = "normal";\r
1832   appData.firstProtocolVersion = PROTOVER;\r
1833   appData.secondProtocolVersion = PROTOVER;\r
1834   appData.showButtonBar = TRUE;\r
1835 \r
1836    /* [AS] New properties (see comments in header file) */\r
1837   appData.firstScoreIsAbsolute = FALSE;\r
1838   appData.secondScoreIsAbsolute = FALSE;\r
1839   appData.saveExtendedInfoInPGN = FALSE;\r
1840   appData.hideThinkingFromHuman = FALSE;\r
1841   appData.liteBackTextureFile = "";\r
1842   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1843   appData.darkBackTextureFile = "";\r
1844   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1845   appData.renderPiecesWithFont = "";\r
1846   appData.fontToPieceTable = "";\r
1847   appData.fontBackColorWhite = 0;\r
1848   appData.fontForeColorWhite = 0;\r
1849   appData.fontBackColorBlack = 0;\r
1850   appData.fontForeColorBlack = 0;\r
1851   appData.fontPieceSize = 80;\r
1852   appData.overrideLineGap = 1;\r
1853   appData.adjudicateLossThreshold = 0;\r
1854   appData.delayBeforeQuit = 0;\r
1855   appData.delayAfterQuit = 0;\r
1856   appData.nameOfDebugFile = "winboard.debug";\r
1857   appData.pgnEventHeader = "Computer Chess Game";\r
1858   appData.defaultFrcPosition = -1;\r
1859   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1860   appData.saveOutOfBookInfo = TRUE;\r
1861   appData.showEvalInMoveHistory = TRUE;\r
1862   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1863   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1864   appData.highlightMoveWithArrow = FALSE;\r
1865   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1866   appData.useStickyWindows = TRUE;\r
1867   appData.adjudicateDrawMoves = 0;\r
1868   appData.autoDisplayComment = TRUE;\r
1869   appData.autoDisplayTags = TRUE;\r
1870   appData.firstIsUCI = FALSE;\r
1871   appData.secondIsUCI = FALSE;\r
1872   appData.firstHasOwnBookUCI = TRUE;\r
1873   appData.secondHasOwnBookUCI = TRUE;\r
1874   appData.polyglotDir = "";\r
1875   appData.usePolyglotBook = FALSE;\r
1876   appData.polyglotBook = "";\r
1877   appData.defaultHashSize = 64;\r
1878   appData.defaultCacheSizeEGTB = 4;\r
1879   appData.defaultPathEGTB = "c:\\egtb";\r
1880 \r
1881   InitWindowPlacement( &wpMoveHistory );\r
1882   InitWindowPlacement( &wpEvalGraph );\r
1883   InitWindowPlacement( &wpEngineOutput );\r
1884 \r
1885   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1886   appData.NrFiles      = -1;\r
1887   appData.NrRanks      = -1;\r
1888   appData.holdingsSize = -1;\r
1889   appData.testClaims   = FALSE;\r
1890   appData.checkMates   = FALSE;\r
1891   appData.materialDraws= FALSE;\r
1892   appData.trivialDraws = FALSE;\r
1893   appData.ruleMoves    = 51;\r
1894   appData.drawRepeats  = 6;\r
1895   appData.matchPause   = 10000;\r
1896   appData.alphaRank    = FALSE;\r
1897   appData.allWhite     = FALSE;\r
1898   appData.upsideDown   = FALSE;\r
1899 \r
1900 #ifdef ZIPPY\r
1901   appData.zippyTalk = ZIPPY_TALK;\r
1902   appData.zippyPlay = ZIPPY_PLAY;\r
1903   appData.zippyLines = ZIPPY_LINES;\r
1904   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1905   appData.zippyPassword = ZIPPY_PASSWORD;\r
1906   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1907   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1908   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1909   appData.zippyUseI = ZIPPY_USE_I;\r
1910   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1911   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1912   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1913   appData.zippyGameStart = ZIPPY_GAME_START;\r
1914   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1915   appData.zippyAbort = ZIPPY_ABORT;\r
1916   appData.zippyVariants = ZIPPY_VARIANTS;\r
1917   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1918   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1919 #endif\r
1920 \r
1921   /* Point font array elements to structures and\r
1922      parse default font names */\r
1923   for (i=0; i<NUM_FONTS; i++) {\r
1924     for (j=0; j<NUM_SIZES; j++) {\r
1925       font[j][i] = &fontRec[j][i];\r
1926       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1927     }\r
1928   }\r
1929   \r
1930   /* Parse default settings file if any */\r
1931   if (ParseSettingsFile(settingsFileName, buf)) {\r
1932     settingsFileName = strdup(buf);\r
1933   }\r
1934 \r
1935   /* Parse command line */\r
1936   ParseArgs(StringGet, &lpCmdLine);\r
1937 \r
1938   /* [HGM] make sure board size is acceptable */\r
1939   if(appData.NrFiles > BOARD_SIZE ||\r
1940      appData.NrRanks > BOARD_SIZE   )\r
1941       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
1942 \r
1943   /* Propagate options that affect others */\r
1944   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1945   if (appData.icsActive || appData.noChessProgram) {\r
1946      chessProgram = FALSE;  /* not local chess program mode */\r
1947   }\r
1948 \r
1949   /* Open startup dialog if needed */\r
1950   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1951       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1952       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1953                         *appData.secondChessProgram == NULLCHAR))) {\r
1954     FARPROC lpProc;\r
1955     \r
1956     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1957     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1958     FreeProcInstance(lpProc);\r
1959   }\r
1960 \r
1961   /* Make sure save files land in the right (?) directory */\r
1962   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
1963     appData.saveGameFile = strdup(buf);\r
1964   }\r
1965   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1966     appData.savePositionFile = strdup(buf);\r
1967   }\r
1968 \r
1969   /* Finish initialization for fonts and sounds */\r
1970   for (i=0; i<NUM_FONTS; i++) {\r
1971     for (j=0; j<NUM_SIZES; j++) {\r
1972       CreateFontInMF(font[j][i]);\r
1973     }\r
1974   }\r
1975   /* xboard, and older WinBoards, controlled the move sound with the\r
1976      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1977      always turn the option on (so that the backend will call us),\r
1978      then let the user turn the sound off by setting it to silence if\r
1979      desired.  To accommodate old winboard.ini files saved by old\r
1980      versions of WinBoard, we also turn off the sound if the option\r
1981      was initially set to false. */\r
1982   if (!appData.ringBellAfterMoves) {\r
1983     sounds[(int)SoundMove].name = strdup("");\r
1984     appData.ringBellAfterMoves = TRUE;\r
1985   }\r
1986   GetCurrentDirectory(MSG_SIZ, currDir);\r
1987   SetCurrentDirectory(installDir);\r
1988   LoadAllSounds();\r
1989   SetCurrentDirectory(currDir);\r
1990 \r
1991   p = icsTextMenuString;\r
1992   if (p[0] == '@') {\r
1993     FILE* f = fopen(p + 1, "r");\r
1994     if (f == NULL) {\r
1995       DisplayFatalError(p + 1, errno, 2);\r
1996       return;\r
1997     }\r
1998     i = fread(buf, 1, sizeof(buf)-1, f);\r
1999     fclose(f);\r
2000     buf[i] = NULLCHAR;\r
2001     p = buf;\r
2002   }\r
2003   ParseIcsTextMenu(strdup(p));\r
2004 }\r
2005 \r
2006 \r
2007 VOID\r
2008 InitMenuChecks()\r
2009 {\r
2010   HMENU hmenu = GetMenu(hwndMain);\r
2011 \r
2012   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2013                         MF_BYCOMMAND|((appData.icsActive &&\r
2014                                        *appData.icsCommPort != NULLCHAR) ?\r
2015                                       MF_ENABLED : MF_GRAYED));\r
2016   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2017                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2018                                      MF_CHECKED : MF_UNCHECKED));\r
2019 }\r
2020 \r
2021 \r
2022 VOID\r
2023 SaveSettings(char* name)\r
2024 {\r
2025   FILE *f;\r
2026   ArgDescriptor *ad;\r
2027   WINDOWPLACEMENT wp;\r
2028   char dir[MSG_SIZ];\r
2029 \r
2030   if (!hwndMain) return;\r
2031 \r
2032   GetCurrentDirectory(MSG_SIZ, dir);\r
2033   SetCurrentDirectory(installDir);\r
2034   f = fopen(name, "w");\r
2035   SetCurrentDirectory(dir);\r
2036   if (f == NULL) {\r
2037     DisplayError(name, errno);\r
2038     return;\r
2039   }\r
2040   fprintf(f, ";\n");\r
2041   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2042   fprintf(f, ";\n");\r
2043   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2044   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2045   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2046   fprintf(f, ";\n");\r
2047 \r
2048   wp.length = sizeof(WINDOWPLACEMENT);\r
2049   GetWindowPlacement(hwndMain, &wp);\r
2050   boardX = wp.rcNormalPosition.left;\r
2051   boardY = wp.rcNormalPosition.top;\r
2052 \r
2053   if (hwndConsole) {\r
2054     GetWindowPlacement(hwndConsole, &wp);\r
2055     consoleX = wp.rcNormalPosition.left;\r
2056     consoleY = wp.rcNormalPosition.top;\r
2057     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2058     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2059   }\r
2060 \r
2061   if (analysisDialog) {\r
2062     GetWindowPlacement(analysisDialog, &wp);\r
2063     analysisX = wp.rcNormalPosition.left;\r
2064     analysisY = wp.rcNormalPosition.top;\r
2065     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2066     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2067   }\r
2068 \r
2069   if (commentDialog) {\r
2070     GetWindowPlacement(commentDialog, &wp);\r
2071     commentX = wp.rcNormalPosition.left;\r
2072     commentY = wp.rcNormalPosition.top;\r
2073     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2074     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2075   }\r
2076 \r
2077   if (editTagsDialog) {\r
2078     GetWindowPlacement(editTagsDialog, &wp);\r
2079     editTagsX = wp.rcNormalPosition.left;\r
2080     editTagsY = wp.rcNormalPosition.top;\r
2081     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2082     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2083   }\r
2084 \r
2085   if (gameListDialog) {\r
2086     GetWindowPlacement(gameListDialog, &wp);\r
2087     gameListX = wp.rcNormalPosition.left;\r
2088     gameListY = wp.rcNormalPosition.top;\r
2089     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2090     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2091   }\r
2092 \r
2093   /* [AS] Move history */\r
2094   wpMoveHistory.visible = MoveHistoryIsUp();\r
2095   \r
2096   if( moveHistoryDialog ) {\r
2097     GetWindowPlacement(moveHistoryDialog, &wp);\r
2098     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2099     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2100     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2101     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2102   }\r
2103 \r
2104   /* [AS] Eval graph */\r
2105   wpEvalGraph.visible = EvalGraphIsUp();\r
2106 \r
2107   if( evalGraphDialog ) {\r
2108     GetWindowPlacement(evalGraphDialog, &wp);\r
2109     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2110     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2111     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2112     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2113   }\r
2114 \r
2115   /* [AS] Engine output */\r
2116   wpEngineOutput.visible = EngineOutputIsUp();\r
2117 \r
2118   if( engineOutputDialog ) {\r
2119     GetWindowPlacement(engineOutputDialog, &wp);\r
2120     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2121     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2122     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2123     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2124   }\r
2125 \r
2126   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2127     if (!ad->save) continue;\r
2128     switch (ad->argType) {\r
2129     case ArgString:\r
2130       {\r
2131         char *p = *(char **)ad->argLoc;\r
2132         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2133           /* Quote multiline values or \-containing values\r
2134              with { } if possible */\r
2135           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2136         } else {\r
2137           /* Else quote with " " */\r
2138           fprintf(f, "/%s=\"", ad->argName);\r
2139           while (*p) {\r
2140             if (*p == '\n') fprintf(f, "\n");\r
2141             else if (*p == '\r') fprintf(f, "\\r");\r
2142             else if (*p == '\t') fprintf(f, "\\t");\r
2143             else if (*p == '\b') fprintf(f, "\\b");\r
2144             else if (*p == '\f') fprintf(f, "\\f");\r
2145             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2146             else if (*p == '\"') fprintf(f, "\\\"");\r
2147             else if (*p == '\\') fprintf(f, "\\\\");\r
2148             else putc(*p, f);\r
2149             p++;\r
2150           }\r
2151           fprintf(f, "\"\n");\r
2152         }\r
2153       }\r
2154       break;\r
2155     case ArgInt:\r
2156       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2157       break;\r
2158     case ArgFloat:\r
2159       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2160       break;\r
2161     case ArgBoolean:\r
2162       fprintf(f, "/%s=%s\n", ad->argName, \r
2163         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2164       break;\r
2165     case ArgTrue:\r
2166       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2167       break;\r
2168     case ArgFalse:\r
2169       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2170       break;\r
2171     case ArgColor:\r
2172       {\r
2173         COLORREF color = *(COLORREF *)ad->argLoc;\r
2174         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2175           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2176       }\r
2177       break;\r
2178     case ArgAttribs:\r
2179       {\r
2180         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2181         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2182           (ta->effects & CFE_BOLD) ? "b" : "",\r
2183           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2184           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2185           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2186           (ta->effects) ? " " : "",\r
2187           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2188       }\r
2189       break;\r
2190     case ArgFilename:\r
2191       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2192         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2193       } else {\r
2194         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2195       }\r
2196       break;\r
2197     case ArgBoardSize:\r
2198       fprintf(f, "/%s=%s\n", ad->argName,\r
2199               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2200       break;\r
2201     case ArgFont:\r
2202       {\r
2203         int bs;\r
2204         for (bs=0; bs<NUM_SIZES; bs++) {\r
2205           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2206           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2207           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2208             ad->argName, mfp->faceName, mfp->pointSize,\r
2209             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2210             mfp->bold ? "b" : "",\r
2211             mfp->italic ? "i" : "",\r
2212             mfp->underline ? "u" : "",\r
2213             mfp->strikeout ? "s" : "");\r
2214         }\r
2215       }\r
2216       break;\r
2217     case ArgCommSettings:\r
2218       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2219     }\r
2220   }\r
2221   fclose(f);\r
2222 }\r
2223 \r
2224 \r
2225 \r
2226 /*---------------------------------------------------------------------------*\\r
2227  *\r
2228  * GDI board drawing routines\r
2229  *\r
2230 \*---------------------------------------------------------------------------*/\r
2231 \r
2232 /* [AS] Draw square using background texture */\r
2233 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2234 {\r
2235     XFORM   x;\r
2236 \r
2237     if( mode == 0 ) {\r
2238         return; /* Should never happen! */\r
2239     }\r
2240 \r
2241     SetGraphicsMode( dst, GM_ADVANCED );\r
2242 \r
2243     switch( mode ) {\r
2244     case 1:\r
2245         /* Identity */\r
2246         break;\r
2247     case 2:\r
2248         /* X reflection */\r
2249         x.eM11 = -1.0;\r
2250         x.eM12 = 0;\r
2251         x.eM21 = 0;\r
2252         x.eM22 = 1.0;\r
2253         x.eDx = (FLOAT) dw + dx - 1;\r
2254         x.eDy = 0;\r
2255         dx = 0;\r
2256         SetWorldTransform( dst, &x );\r
2257         break;\r
2258     case 3:\r
2259         /* Y reflection */\r
2260         x.eM11 = 1.0;\r
2261         x.eM12 = 0;\r
2262         x.eM21 = 0;\r
2263         x.eM22 = -1.0;\r
2264         x.eDx = 0;\r
2265         x.eDy = (FLOAT) dh + dy - 1;\r
2266         dy = 0;\r
2267         SetWorldTransform( dst, &x );\r
2268         break;\r
2269     case 4:\r
2270         /* X/Y flip */\r
2271         x.eM11 = 0;\r
2272         x.eM12 = 1.0;\r
2273         x.eM21 = 1.0;\r
2274         x.eM22 = 0;\r
2275         x.eDx = (FLOAT) dx;\r
2276         x.eDy = (FLOAT) dy;\r
2277         dx = 0;\r
2278         dy = 0;\r
2279         SetWorldTransform( dst, &x );\r
2280         break;\r
2281     }\r
2282 \r
2283     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2284 \r
2285     x.eM11 = 1.0;\r
2286     x.eM12 = 0;\r
2287     x.eM21 = 0;\r
2288     x.eM22 = 1.0;\r
2289     x.eDx = 0;\r
2290     x.eDy = 0;\r
2291     SetWorldTransform( dst, &x );\r
2292 \r
2293     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2294 }\r
2295 \r
2296 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2297 enum {\r
2298     PM_WP = (int) WhitePawn, \r
2299     PM_WN = (int) WhiteKnight, \r
2300     PM_WB = (int) WhiteBishop, \r
2301     PM_WR = (int) WhiteRook, \r
2302     PM_WQ = (int) WhiteQueen, \r
2303     PM_WF = (int) WhiteFerz, \r
2304     PM_WW = (int) WhiteWazir, \r
2305     PM_WE = (int) WhiteAlfil, \r
2306     PM_WM = (int) WhiteMan, \r
2307     PM_WO = (int) WhiteCannon, \r
2308     PM_WU = (int) WhiteUnicorn, \r
2309     PM_WH = (int) WhiteNightrider, \r
2310     PM_WA = (int) WhiteCardinal, \r
2311     PM_WC = (int) WhiteMarshall, \r
2312     PM_WG = (int) WhiteGrasshopper, \r
2313     PM_WK = (int) WhiteKing,\r
2314     PM_BP = (int) BlackPawn, \r
2315     PM_BN = (int) BlackKnight, \r
2316     PM_BB = (int) BlackBishop, \r
2317     PM_BR = (int) BlackRook, \r
2318     PM_BQ = (int) BlackQueen, \r
2319     PM_BF = (int) BlackFerz, \r
2320     PM_BW = (int) BlackWazir, \r
2321     PM_BE = (int) BlackAlfil, \r
2322     PM_BM = (int) BlackMan,\r
2323     PM_BO = (int) BlackCannon, \r
2324     PM_BU = (int) BlackUnicorn, \r
2325     PM_BH = (int) BlackNightrider, \r
2326     PM_BA = (int) BlackCardinal, \r
2327     PM_BC = (int) BlackMarshall, \r
2328     PM_BG = (int) BlackGrasshopper, \r
2329     PM_BK = (int) BlackKing\r
2330 };\r
2331 \r
2332 static HFONT hPieceFont = NULL;\r
2333 static HBITMAP hPieceMask[(int) EmptySquare];\r
2334 static HBITMAP hPieceFace[(int) EmptySquare];\r
2335 static int fontBitmapSquareSize = 0;\r
2336 static char pieceToFontChar[(int) EmptySquare] =\r
2337                               { 'p', 'n', 'b', 'r', 'q', \r
2338                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2339                       'k', 'o', 'm', 'v', 't', 'w', \r
2340                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2341                                                               'l' };\r
2342 \r
2343 extern BOOL SetCharTable( char *table, const char * map );\r
2344 /* [HGM] moved to backend.c */\r
2345 \r
2346 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2347 {\r
2348     HBRUSH hbrush;\r
2349     BYTE r1 = GetRValue( color );\r
2350     BYTE g1 = GetGValue( color );\r
2351     BYTE b1 = GetBValue( color );\r
2352     BYTE r2 = r1 / 2;\r
2353     BYTE g2 = g1 / 2;\r
2354     BYTE b2 = b1 / 2;\r
2355     RECT rc;\r
2356 \r
2357     /* Create a uniform background first */\r
2358     hbrush = CreateSolidBrush( color );\r
2359     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2360     FillRect( hdc, &rc, hbrush );\r
2361     DeleteObject( hbrush );\r
2362     \r
2363     if( mode == 1 ) {\r
2364         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2365         int steps = squareSize / 2;\r
2366         int i;\r
2367 \r
2368         for( i=0; i<steps; i++ ) {\r
2369             BYTE r = r1 - (r1-r2) * i / steps;\r
2370             BYTE g = g1 - (g1-g2) * i / steps;\r
2371             BYTE b = b1 - (b1-b2) * i / steps;\r
2372 \r
2373             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2374             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2375             FillRect( hdc, &rc, hbrush );\r
2376             DeleteObject(hbrush);\r
2377         }\r
2378     }\r
2379     else if( mode == 2 ) {\r
2380         /* Diagonal gradient, good more or less for every piece */\r
2381         POINT triangle[3];\r
2382         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2383         HBRUSH hbrush_old;\r
2384         int steps = squareSize;\r
2385         int i;\r
2386 \r
2387         triangle[0].x = squareSize - steps;\r
2388         triangle[0].y = squareSize;\r
2389         triangle[1].x = squareSize;\r
2390         triangle[1].y = squareSize;\r
2391         triangle[2].x = squareSize;\r
2392         triangle[2].y = squareSize - steps;\r
2393 \r
2394         for( i=0; i<steps; i++ ) {\r
2395             BYTE r = r1 - (r1-r2) * i / steps;\r
2396             BYTE g = g1 - (g1-g2) * i / steps;\r
2397             BYTE b = b1 - (b1-b2) * i / steps;\r
2398 \r
2399             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2400             hbrush_old = SelectObject( hdc, hbrush );\r
2401             Polygon( hdc, triangle, 3 );\r
2402             SelectObject( hdc, hbrush_old );\r
2403             DeleteObject(hbrush);\r
2404             triangle[0].x++;\r
2405             triangle[2].y++;\r
2406         }\r
2407 \r
2408         SelectObject( hdc, hpen );\r
2409     }\r
2410 }\r
2411 \r
2412 /*\r
2413     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2414     seems to work ok. The main problem here is to find the "inside" of a chess\r
2415     piece: follow the steps as explained below.\r
2416 */\r
2417 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2418 {\r
2419     HBITMAP hbm;\r
2420     HBITMAP hbm_old;\r
2421     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2422     RECT rc;\r
2423     SIZE sz;\r
2424     POINT pt;\r
2425     int backColor = whitePieceColor; \r
2426     int foreColor = blackPieceColor;\r
2427     int shapeIndex = index < 6 ? index+6 : index;\r
2428     \r
2429     if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2430         backColor = appData.fontBackColorWhite;\r
2431         foreColor = appData.fontForeColorWhite;\r
2432     }\r
2433     else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2434         backColor = appData.fontBackColorBlack;\r
2435         foreColor = appData.fontForeColorBlack;\r
2436     }\r
2437 \r
2438     /* Mask */\r
2439     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2440 \r
2441     hbm_old = SelectObject( hdc, hbm );\r
2442 \r
2443     rc.left = 0;\r
2444     rc.top = 0;\r
2445     rc.right = squareSize;\r
2446     rc.bottom = squareSize;\r
2447 \r
2448     /* Step 1: background is now black */\r
2449     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2450 \r
2451     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2452 \r
2453     pt.x = (squareSize - sz.cx) / 2;\r
2454     pt.y = (squareSize - sz.cy) / 2;\r
2455 \r
2456     SetBkMode( hdc, TRANSPARENT );\r
2457     SetTextColor( hdc, chroma );\r
2458     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2459     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2460 \r
2461     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2462     /* Step 3: the area outside the piece is filled with white */\r
2463     FloodFill( hdc, 0, 0, chroma );\r
2464     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2465     /* \r
2466         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2467         but if the start point is not inside the piece we're lost!\r
2468         There should be a better way to do this... if we could create a region or path\r
2469         from the fill operation we would be fine for example.\r
2470     */\r
2471     FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2472 \r
2473     SetTextColor( hdc, 0 );\r
2474     /* \r
2475         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2476         draw the piece again in black for safety.\r
2477     */\r
2478     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2479 \r
2480     SelectObject( hdc, hbm_old );\r
2481 \r
2482     if( hPieceMask[index] != NULL ) {\r
2483         DeleteObject( hPieceMask[index] );\r
2484     }\r
2485 \r
2486     hPieceMask[index] = hbm;\r
2487 \r
2488     /* Face */\r
2489     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2490 \r
2491     SelectObject( hdc, hbm );\r
2492 \r
2493     {\r
2494         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2495         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2496         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2497 \r
2498         SelectObject( dc1, hPieceMask[index] );\r
2499         SelectObject( dc2, bm2 );\r
2500         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2501         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2502         \r
2503         /* \r
2504             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2505             the piece background and deletes (makes transparent) the rest.\r
2506             Thanks to that mask, we are free to paint the background with the greates\r
2507             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2508             We use this, to make gradients and give the pieces a "roundish" look.\r
2509         */\r
2510         SetPieceBackground( hdc, backColor, 2 );\r
2511         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2512 \r
2513         DeleteDC( dc2 );\r
2514         DeleteDC( dc1 );\r
2515         DeleteObject( bm2 );\r
2516     }\r
2517 \r
2518     SetTextColor( hdc, foreColor );\r
2519     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2520 \r
2521     SelectObject( hdc, hbm_old );\r
2522 \r
2523     if( hPieceFace[index] != NULL ) {\r
2524         DeleteObject( hPieceFace[index] );\r
2525     }\r
2526 \r
2527     hPieceFace[index] = hbm;\r
2528 }\r
2529 \r
2530 static int TranslatePieceToFontPiece( int piece )\r
2531 {\r
2532     switch( piece ) {\r
2533     case BlackPawn:\r
2534         return PM_BP;\r
2535     case BlackKnight:\r
2536         return PM_BN;\r
2537     case BlackBishop:\r
2538         return PM_BB;\r
2539     case BlackRook:\r
2540         return PM_BR;\r
2541     case BlackQueen:\r
2542         return PM_BQ;\r
2543     case BlackKing:\r
2544         return PM_BK;\r
2545     case WhitePawn:\r
2546         return PM_WP;\r
2547     case WhiteKnight:\r
2548         return PM_WN;\r
2549     case WhiteBishop:\r
2550         return PM_WB;\r
2551     case WhiteRook:\r
2552         return PM_WR;\r
2553     case WhiteQueen:\r
2554         return PM_WQ;\r
2555     case WhiteKing:\r
2556         return PM_WK;\r
2557 #ifdef FAIRY\r
2558     case BlackCardinal:\r
2559         return PM_BA;\r
2560     case BlackMarshall:\r
2561         return PM_BC;\r
2562     case BlackFerz:\r
2563         return PM_BF;\r
2564     case BlackNightrider:\r
2565         return PM_BH;\r
2566     case BlackAlfil:\r
2567         return PM_BE;\r
2568     case BlackWazir:\r
2569         return PM_BW;\r
2570     case BlackUnicorn:\r
2571         return PM_BU;\r
2572     case BlackCannon:\r
2573         return PM_BO;\r
2574     case BlackGrasshopper:\r
2575         return PM_BG;\r
2576     case BlackMan:\r
2577         return PM_BM;\r
2578     case WhiteCardinal:\r
2579         return PM_WA;\r
2580     case WhiteMarshall:\r
2581         return PM_WC;\r
2582     case WhiteFerz:\r
2583         return PM_WF;\r
2584     case WhiteNightrider:\r
2585         return PM_WH;\r
2586     case WhiteAlfil:\r
2587         return PM_WE;\r
2588     case WhiteWazir:\r
2589         return PM_WW;\r
2590     case WhiteUnicorn:\r
2591         return PM_WU;\r
2592     case WhiteCannon:\r
2593         return PM_WO;\r
2594     case WhiteGrasshopper:\r
2595         return PM_WG;\r
2596     case WhiteMan:\r
2597         return PM_WM;\r
2598 #endif\r
2599     }\r
2600 \r
2601     return 0;\r
2602 }\r
2603 \r
2604 void CreatePiecesFromFont()\r
2605 {\r
2606     LOGFONT lf;\r
2607     HDC hdc_window = NULL;\r
2608     HDC hdc = NULL;\r
2609     HFONT hfont_old;\r
2610     int fontHeight;\r
2611     int i;\r
2612 \r
2613     if( fontBitmapSquareSize < 0 ) {\r
2614         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2615         return;\r
2616     }\r
2617 \r
2618     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2619         fontBitmapSquareSize = -1;\r
2620         return;\r
2621     }\r
2622 \r
2623     if( fontBitmapSquareSize != squareSize ) {\r
2624         hdc_window = GetDC( hwndMain );\r
2625         hdc = CreateCompatibleDC( hdc_window );\r
2626 \r
2627         if( hPieceFont != NULL ) {\r
2628             DeleteObject( hPieceFont );\r
2629         }\r
2630         else {\r
2631             for( i=0; i<12; i++ ) {\r
2632                 hPieceMask[i] = NULL;\r
2633                 hPieceFace[i] = NULL;\r
2634             }\r
2635         }\r
2636 \r
2637         fontHeight = 75;\r
2638 \r
2639         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2640             fontHeight = appData.fontPieceSize;\r
2641         }\r
2642 \r
2643         fontHeight = (fontHeight * squareSize) / 100;\r
2644 \r
2645         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2646         lf.lfWidth = 0;\r
2647         lf.lfEscapement = 0;\r
2648         lf.lfOrientation = 0;\r
2649         lf.lfWeight = FW_NORMAL;\r
2650         lf.lfItalic = 0;\r
2651         lf.lfUnderline = 0;\r
2652         lf.lfStrikeOut = 0;\r
2653         lf.lfCharSet = DEFAULT_CHARSET;\r
2654         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2655         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2656         lf.lfQuality = PROOF_QUALITY;\r
2657         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2658         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2659         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2660 \r
2661         hPieceFont = CreateFontIndirect( &lf );\r
2662 \r
2663         if( hPieceFont == NULL ) {\r
2664             fontBitmapSquareSize = -2;\r
2665         }\r
2666         else {\r
2667             /* Setup font-to-piece character table */\r
2668             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2669                 /* No (or wrong) global settings, try to detect the font */\r
2670                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2671                     /* Alpha */\r
2672                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2673                 }\r
2674                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2675                     /* DiagramTT* family */\r
2676                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2677                 }\r
2678                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2679                     /* Fairy symbols */\r
2680                      SetCharTable(pieceToFontChar, "PNBRQFWEMOUHACGSKpnbrqfwemouhacgsk");\r
2681                 }\r
2682                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2683                     /* Good Companion (Some characters get warped as literal :-( */\r
2684                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2685                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2686                     SetCharTable(pieceToFontChar, s);\r
2687                 }\r
2688                 else {\r
2689                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2690                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2691                 }\r
2692             }\r
2693 \r
2694             /* Create bitmaps */\r
2695             hfont_old = SelectObject( hdc, hPieceFont );\r
2696 \r
2697             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2698             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2699             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2700             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2701             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2702             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2703             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2704             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2705             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2706             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2707             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2708             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2709 #ifdef FAIRY\r
2710             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2711             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2712             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2713             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2714             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2715             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2716             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2717             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2718             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2719             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2720             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2721             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2722             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2723             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2724             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2725             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2726             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2727             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2728             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2729             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2730 #endif\r
2731 \r
2732             SelectObject( hdc, hfont_old );\r
2733 \r
2734             fontBitmapSquareSize = squareSize;\r
2735         }\r
2736     }\r
2737 \r
2738     if( hdc != NULL ) {\r
2739         DeleteDC( hdc );\r
2740     }\r
2741 \r
2742     if( hdc_window != NULL ) {\r
2743         ReleaseDC( hwndMain, hdc_window );\r
2744     }\r
2745 }\r
2746 \r
2747 HBITMAP\r
2748 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2749 {\r
2750   char name[128];\r
2751 \r
2752   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2753   if (gameInfo.event &&\r
2754       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2755       strcmp(name, "k80s") == 0) {\r
2756     strcpy(name, "tim");\r
2757   }\r
2758   return LoadBitmap(hinst, name);\r
2759 }\r
2760 \r
2761 \r
2762 /* Insert a color into the program's logical palette\r
2763    structure.  This code assumes the given color is\r
2764    the result of the RGB or PALETTERGB macro, and it\r
2765    knows how those macros work (which is documented).\r
2766 */\r
2767 VOID\r
2768 InsertInPalette(COLORREF color)\r
2769 {\r
2770   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2771 \r
2772   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2773     DisplayFatalError("Too many colors", 0, 1);\r
2774     pLogPal->palNumEntries--;\r
2775     return;\r
2776   }\r
2777 \r
2778   pe->peFlags = (char) 0;\r
2779   pe->peRed = (char) (0xFF & color);\r
2780   pe->peGreen = (char) (0xFF & (color >> 8));\r
2781   pe->peBlue = (char) (0xFF & (color >> 16));\r
2782   return;\r
2783 }\r
2784 \r
2785 \r
2786 VOID\r
2787 InitDrawingColors()\r
2788 {\r
2789   if (pLogPal == NULL) {\r
2790     /* Allocate enough memory for a logical palette with\r
2791      * PALETTESIZE entries and set the size and version fields\r
2792      * of the logical palette structure.\r
2793      */\r
2794     pLogPal = (NPLOGPALETTE)\r
2795       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2796                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2797     pLogPal->palVersion    = 0x300;\r
2798   }\r
2799   pLogPal->palNumEntries = 0;\r
2800 \r
2801   InsertInPalette(lightSquareColor);\r
2802   InsertInPalette(darkSquareColor);\r
2803   InsertInPalette(whitePieceColor);\r
2804   InsertInPalette(blackPieceColor);\r
2805   InsertInPalette(highlightSquareColor);\r
2806   InsertInPalette(premoveHighlightColor);\r
2807 \r
2808   /*  create a logical color palette according the information\r
2809    *  in the LOGPALETTE structure.\r
2810    */\r
2811   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2812 \r
2813   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2814   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2815   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2816   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2817   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2818   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2819 \r
2820   /* [AS] Force rendering of the font-based pieces */\r
2821   if( fontBitmapSquareSize > 0 ) {\r
2822     fontBitmapSquareSize = 0;\r
2823   }\r
2824 }\r
2825 \r
2826 \r
2827 int\r
2828 BoardWidth(int boardSize, int n)\r
2829 { /* [HGM] argument n added to allow different width and height */\r
2830   int lineGap = sizeInfo[boardSize].lineGap;\r
2831 \r
2832   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2833       lineGap = appData.overrideLineGap;\r
2834   }\r
2835 \r
2836   return (n + 1) * lineGap +\r
2837           n * sizeInfo[boardSize].squareSize;\r
2838 }\r
2839 \r
2840 /* Respond to board resize by dragging edge */\r
2841 VOID\r
2842 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2843 {\r
2844   BoardSize newSize = NUM_SIZES - 1;\r
2845   static int recurse = 0;\r
2846   if (IsIconic(hwndMain)) return;\r
2847   if (recurse > 0) return;\r
2848   recurse++;\r
2849   while (newSize > 0 &&\r
2850          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2851           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2852     newSize--;\r
2853   } \r
2854   boardSize = newSize;\r
2855   InitDrawingSizes(boardSize, flags);\r
2856   recurse--;\r
2857 }\r
2858 \r
2859 \r
2860 \r
2861 VOID\r
2862 InitDrawingSizes(BoardSize boardSize, int flags)\r
2863 {\r
2864   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2865   ChessSquare piece;\r
2866   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2867   HDC hdc;\r
2868   SIZE clockSize, messageSize;\r
2869   HFONT oldFont;\r
2870   char buf[MSG_SIZ];\r
2871   char *str;\r
2872   HMENU hmenu = GetMenu(hwndMain);\r
2873   RECT crect, wrect;\r
2874   int offby;\r
2875   LOGBRUSH logbrush;\r
2876 \r
2877   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
2878   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2879 \r
2880   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2881   smallLayout = sizeInfo[boardSize].smallLayout;\r
2882   squareSize = sizeInfo[boardSize].squareSize;\r
2883   lineGap = sizeInfo[boardSize].lineGap;\r
2884   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2885 \r
2886   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2887       lineGap = appData.overrideLineGap;\r
2888   }\r
2889 \r
2890   if (tinyLayout != oldTinyLayout) {\r
2891     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2892     if (tinyLayout) {\r
2893       style &= ~WS_SYSMENU;\r
2894       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2895                  "&Minimize\tCtrl+F4");\r
2896     } else {\r
2897       style |= WS_SYSMENU;\r
2898       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2899     }\r
2900     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2901 \r
2902     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2903       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2904         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2905     }\r
2906     DrawMenuBar(hwndMain);\r
2907   }\r
2908 \r
2909   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
2910   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
2911 \r
2912   /* Get text area sizes */\r
2913   hdc = GetDC(hwndMain);\r
2914   if (appData.clockMode) {\r
2915     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2916   } else {\r
2917     sprintf(buf, "White");\r
2918   }\r
2919   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2920   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2921   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2922   str = "We only care about the height here";\r
2923   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2924   SelectObject(hdc, oldFont);\r
2925   ReleaseDC(hwndMain, hdc);\r
2926 \r
2927   /* Compute where everything goes */\r
2928   whiteRect.left = OUTER_MARGIN;\r
2929   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2930   whiteRect.top = OUTER_MARGIN;\r
2931   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2932 \r
2933   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2934   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2935   blackRect.top = whiteRect.top;\r
2936   blackRect.bottom = whiteRect.bottom;\r
2937 \r
2938   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2939   if (appData.showButtonBar) {\r
2940     messageRect.right = blackRect.right\r
2941       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2942   } else {\r
2943     messageRect.right = blackRect.right;\r
2944   }\r
2945   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2946   messageRect.bottom = messageRect.top + messageSize.cy;\r
2947 \r
2948   boardRect.left = whiteRect.left;\r
2949   boardRect.right = boardRect.left + boardWidth;\r
2950   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2951   boardRect.bottom = boardRect.top + boardHeight;\r
2952 \r
2953   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2954   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2955   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2956   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2957     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2958   GetWindowRect(hwndMain, &wrect);\r
2959   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2960                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2961   /* compensate if menu bar wrapped */\r
2962   GetClientRect(hwndMain, &crect);\r
2963   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2964   winHeight += offby;\r
2965   switch (flags) {\r
2966   case WMSZ_TOPLEFT:\r
2967     SetWindowPos(hwndMain, NULL, \r
2968                  wrect.right - winWidth, wrect.bottom - winHeight, \r
2969                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2970     break;\r
2971 \r
2972   case WMSZ_TOPRIGHT:\r
2973   case WMSZ_TOP:\r
2974     SetWindowPos(hwndMain, NULL, \r
2975                  wrect.left, wrect.bottom - winHeight, \r
2976                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2977     break;\r
2978 \r
2979   case WMSZ_BOTTOMLEFT:\r
2980   case WMSZ_LEFT:\r
2981     SetWindowPos(hwndMain, NULL, \r
2982                  wrect.right - winWidth, wrect.top, \r
2983                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2984     break;\r
2985 \r
2986   case WMSZ_BOTTOMRIGHT:\r
2987   case WMSZ_BOTTOM:\r
2988   case WMSZ_RIGHT:\r
2989   default:\r
2990     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2991                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2992     break;\r
2993   }\r
2994 \r
2995   hwndPause = NULL;\r
2996   for (i = 0; i < N_BUTTONS; i++) {\r
2997     if (buttonDesc[i].hwnd != NULL) {\r
2998       DestroyWindow(buttonDesc[i].hwnd);\r
2999       buttonDesc[i].hwnd = NULL;\r
3000     }\r
3001     if (appData.showButtonBar) {\r
3002       buttonDesc[i].hwnd =\r
3003         CreateWindow("BUTTON", buttonDesc[i].label,\r
3004                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3005                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3006                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3007                      (HMENU) buttonDesc[i].id,\r
3008                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3009       if (tinyLayout) {\r
3010         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3011                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3012                     MAKELPARAM(FALSE, 0));\r
3013       }\r
3014       if (buttonDesc[i].id == IDM_Pause)\r
3015         hwndPause = buttonDesc[i].hwnd;\r
3016       buttonDesc[i].wndproc = (WNDPROC)\r
3017         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3018     }\r
3019   }\r
3020   if (gridPen != NULL) DeleteObject(gridPen);\r
3021   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3022   if (premovePen != NULL) DeleteObject(premovePen);\r
3023   if (lineGap != 0) {\r
3024     logbrush.lbStyle = BS_SOLID;\r
3025     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3026     gridPen =\r
3027       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3028                    lineGap, &logbrush, 0, NULL);\r
3029     logbrush.lbColor = highlightSquareColor;\r
3030     highlightPen =\r
3031       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3032                    lineGap, &logbrush, 0, NULL);\r
3033 \r
3034     logbrush.lbColor = premoveHighlightColor; \r
3035     premovePen =\r
3036       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3037                    lineGap, &logbrush, 0, NULL);\r
3038 \r
3039     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3040     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3041       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3042       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3043         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3044       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3045         BOARD_WIDTH * (squareSize + lineGap);\r
3046         lineGap / 2 + (i * (squareSize + lineGap));\r
3047       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3048     }\r
3049     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3050       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3051       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3052         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3053         lineGap / 2 + (i * (squareSize + lineGap));\r
3054       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3055         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3056       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3057     }\r
3058   }\r
3059 \r
3060 #ifdef GOTHIC\r
3061   /* [HGM] Gothic licensing requirement */\r
3062   GothicPopUp( GOTHIC, gameInfo.variant == VariantGothic );\r
3063 #endif\r
3064 \r
3065 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3066   oldBoardSize = boardSize;\r
3067   oldTinyLayout = tinyLayout;\r
3068 \r
3069   /* Load piece bitmaps for this board size */\r
3070   for (i=0; i<=2; i++) {\r
3071     for (piece = WhitePawn;\r
3072          (int) piece < (int) BlackPawn;\r
3073          piece = (ChessSquare) ((int) piece + 1)) {\r
3074       if (pieceBitmap[i][piece] != NULL)\r
3075         DeleteObject(pieceBitmap[i][piece]);\r
3076     }\r
3077   }\r
3078 \r
3079   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3080   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3081   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3082   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3083   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3084   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3085   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3086   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3087   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3088   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3089   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3090   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3091   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3092   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3093   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3094   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3095   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3096   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3097   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3098   } else {\r
3099   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3100   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3101   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3102   }\r
3103   if(squareSize==72 || squareSize==49) { /* experiment with some home-made bitmaps */\r
3104   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3105   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3106   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3107   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3108   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3109   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3110   pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3111   pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3112   pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3113   pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3114   pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3115   pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3116   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3117   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3118   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3119   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3120   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3121   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3122   if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3123   pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3124   pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3125   pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3126   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3127   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3128   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3129   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3130   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3131   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3132   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3133   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3134   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3135   } else {\r
3136   pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3137   pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3138   pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3139   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3140   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3141   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3142   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3143   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3144   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3145   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3146   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3147   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3148   }\r
3149   if(gameInfo.variant != VariantCrazyhouse && gameInfo.variant != VariantShogi) {\r
3150   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3151   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3152   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3153   } else {\r
3154   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3155   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3156   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3157   }\r
3158   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3159       if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3160       else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3161   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3162   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3163   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3164   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3165   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3166   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3167   pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3168   pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3169   pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3170   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3171   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3172   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3173   }\r
3174 }\r
3175 \r
3176 HBITMAP\r
3177 PieceBitmap(ChessSquare p, int kind)\r
3178 {\r
3179   if ((int) p >= (int) BlackPawn)\r
3180     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3181 \r
3182   return pieceBitmap[kind][(int) p];\r
3183 }\r
3184 \r
3185 /***************************************************************/\r
3186 \r
3187 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3188 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3189 /*\r
3190 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3191 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3192 */\r
3193 \r
3194 VOID\r
3195 SquareToPos(int row, int column, int * x, int * y)\r
3196 {\r
3197   if (flipView) {\r
3198     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3199     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3200   } else {\r
3201     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3202     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3203   }\r
3204 }\r
3205 \r
3206 VOID\r
3207 DrawCoordsOnDC(HDC hdc)\r
3208 {\r
3209   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
3210   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
3211   char str[2] = { NULLCHAR, NULLCHAR };\r
3212   int oldMode, oldAlign, x, y, start, i;\r
3213   HFONT oldFont;\r
3214   HBRUSH oldBrush;\r
3215 \r
3216   if (!appData.showCoords)\r
3217     return;\r
3218 \r
3219   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3220 \r
3221   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3222   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3223   oldAlign = GetTextAlign(hdc);\r
3224   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3225 \r
3226   y = boardRect.top + lineGap;\r
3227   x = boardRect.left + lineGap;\r
3228 \r
3229   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3230   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3231     str[0] = files[start + i];\r
3232     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3233     y += squareSize + lineGap;\r
3234   }\r
3235 \r
3236   start = flipView ? 12-BOARD_WIDTH : 12;\r
3237 \r
3238   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3239   for (i = 0; i < BOARD_WIDTH; i++) {\r
3240     str[0] = ranks[start + i];\r
3241     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3242     x += squareSize + lineGap;\r
3243   }    \r
3244 \r
3245   SelectObject(hdc, oldBrush);\r
3246   SetBkMode(hdc, oldMode);\r
3247   SetTextAlign(hdc, oldAlign);\r
3248   SelectObject(hdc, oldFont);\r
3249 }\r
3250 \r
3251 VOID\r
3252 DrawGridOnDC(HDC hdc)\r
3253 {\r
3254   HPEN oldPen;\r
3255  \r
3256   if (lineGap != 0) {\r
3257     oldPen = SelectObject(hdc, gridPen);\r
3258     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3259     SelectObject(hdc, oldPen);\r
3260   }\r
3261 }\r
3262 \r
3263 #define HIGHLIGHT_PEN 0\r
3264 #define PREMOVE_PEN   1\r
3265 \r
3266 VOID\r
3267 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3268 {\r
3269   int x1, y1;\r
3270   HPEN oldPen, hPen;\r
3271   if (lineGap == 0) return;\r
3272   if (flipView) {\r
3273     x1 = boardRect.left +\r
3274       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3275     y1 = boardRect.top +\r
3276       lineGap/2 + y * (squareSize + lineGap);\r
3277   } else {\r
3278     x1 = boardRect.left +\r
3279       lineGap/2 + x * (squareSize + lineGap);\r
3280     y1 = boardRect.top +\r
3281       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3282   }\r
3283   hPen = pen ? premovePen : highlightPen;\r
3284   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3285   MoveToEx(hdc, x1, y1, NULL);\r
3286   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3287   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3288   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3289   LineTo(hdc, x1, y1);\r
3290   SelectObject(hdc, oldPen);\r
3291 }\r
3292 \r
3293 VOID\r
3294 DrawHighlightsOnDC(HDC hdc)\r
3295 {\r
3296   int i;\r
3297   for (i=0; i<2; i++) {\r
3298     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3299       DrawHighlightOnDC(hdc, TRUE,\r
3300                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3301                         HIGHLIGHT_PEN);\r
3302   }\r
3303   for (i=0; i<2; i++) {\r
3304     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3305         premoveHighlightInfo.sq[i].y >= 0) {\r
3306         DrawHighlightOnDC(hdc, TRUE,\r
3307                           premoveHighlightInfo.sq[i].x, \r
3308                           premoveHighlightInfo.sq[i].y,\r
3309                           PREMOVE_PEN);\r
3310     }\r
3311   }\r
3312 }\r
3313 \r
3314 /* Note: sqcolor is used only in monoMode */\r
3315 /* Note that this code is largely duplicated in woptions.c,\r
3316    function DrawSampleSquare, so that needs to be updated too */\r
3317 VOID\r
3318 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3319 {\r
3320   HBITMAP oldBitmap;\r
3321   HBRUSH oldBrush;\r
3322   int tmpSize;\r
3323 \r
3324   if (appData.blindfold) return;\r
3325 \r
3326   /* [AS] Use font-based pieces if needed */\r
3327   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3328     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3329     CreatePiecesFromFont();\r
3330 \r
3331     if( fontBitmapSquareSize == squareSize ) {\r
3332         int index = TranslatePieceToFontPiece( piece );\r
3333 \r
3334         SelectObject( tmphdc, hPieceMask[ index ] );\r
3335 \r
3336         BitBlt( hdc,\r
3337             x, y,\r
3338             squareSize, squareSize,\r
3339             tmphdc,\r
3340             0, 0,\r
3341             SRCAND );\r
3342 \r
3343         SelectObject( tmphdc, hPieceFace[ index ] );\r
3344 \r
3345         BitBlt( hdc,\r
3346             x, y,\r
3347             squareSize, squareSize,\r
3348             tmphdc,\r
3349             0, 0,\r
3350             SRCPAINT );\r
3351 \r
3352         return;\r
3353     }\r
3354   }\r
3355 \r
3356   if (appData.monoMode) {\r
3357     SelectObject(tmphdc, PieceBitmap(piece, \r
3358       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3359     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3360            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3361   } else {\r
3362     tmpSize = squareSize;\r
3363     if(minorSize &&\r
3364         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3365          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3366       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3367       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3368       x += (squareSize - minorSize)>>1;\r
3369       y += squareSize - minorSize - 2;\r
3370       tmpSize = minorSize;\r
3371     }\r
3372     if (color || appData.allWhite ) {\r
3373       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3374       if( color )\r
3375               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3376       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3377       if(appData.upsideDown && !color)\r
3378         StretchBlt(hdc, x, y+tmpSize, tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3379       else\r
3380         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3381 #if 0\r
3382       /* Use black piece color for outline of white pieces */\r
3383       /* Not sure this looks really good (though xboard does it).\r
3384          Maybe better to have another selectable color, default black */\r
3385       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3386       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3387       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3388 #else\r
3389       /* Use black for outline of white pieces */\r
3390       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3391       if(appData.upsideDown && !color)\r
3392         StretchBlt(hdc, x, y+tmpSize, tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3393       else\r
3394         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3395 #endif\r
3396     } else {\r
3397 #if 0\r
3398       /* Use white piece color for details of black pieces */\r
3399       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3400          WHITE_PIECE ones aren't always the right shape. */\r
3401       /* Not sure this looks really good (though xboard does it).\r
3402          Maybe better to have another selectable color, default medium gray? */\r
3403       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3404       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3405       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3406       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3407       SelectObject(hdc, blackPieceBrush);\r
3408       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3409 #else\r
3410       /* Use square color for details of black pieces */\r
3411       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3412       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3413       if(appData.upsideDown)\r
3414         StretchBlt(hdc, x, y+tmpSize, tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3415       else\r
3416         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3417 #endif\r
3418     }\r
3419     SelectObject(hdc, oldBrush);\r
3420     SelectObject(tmphdc, oldBitmap);\r
3421   }\r
3422 }\r
3423 \r
3424 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3425 int GetBackTextureMode( int algo )\r
3426 {\r
3427     int result = BACK_TEXTURE_MODE_DISABLED;\r
3428 \r
3429     switch( algo ) \r
3430     {\r
3431         case BACK_TEXTURE_MODE_PLAIN:\r
3432             result = 1; /* Always use identity map */\r
3433             break;\r
3434         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3435             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3436             break;\r
3437     }\r
3438 \r
3439     return result;\r
3440 }\r
3441 \r
3442 /* \r
3443     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3444     to handle redraws cleanly (as random numbers would always be different).\r
3445 */\r
3446 VOID RebuildTextureSquareInfo()\r
3447 {\r
3448     BITMAP bi;\r
3449     int lite_w = 0;\r
3450     int lite_h = 0;\r
3451     int dark_w = 0;\r
3452     int dark_h = 0;\r
3453     int row;\r
3454     int col;\r
3455 \r
3456     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3457 \r
3458     if( liteBackTexture != NULL ) {\r
3459         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3460             lite_w = bi.bmWidth;\r
3461             lite_h = bi.bmHeight;\r
3462         }\r
3463     }\r
3464 \r
3465     if( darkBackTexture != NULL ) {\r
3466         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3467             dark_w = bi.bmWidth;\r
3468             dark_h = bi.bmHeight;\r
3469         }\r
3470     }\r
3471 \r
3472     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3473         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3474             if( (col + row) & 1 ) {\r
3475                 /* Lite square */\r
3476                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3477                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3478                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3479                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3480                 }\r
3481             }\r
3482             else {\r
3483                 /* Dark square */\r
3484                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3485                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3486                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3487                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3488                 }\r
3489             }\r
3490         }\r
3491     }\r
3492 }\r
3493 \r
3494 /* [AS] Arrow highlighting support */\r
3495 \r
3496 static int A_WIDTH = 5; /* Width of arrow body */\r
3497 \r
3498 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3499 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3500 \r
3501 static double Sqr( double x )\r
3502 {\r
3503     return x*x;\r
3504 }\r
3505 \r
3506 static int Round( double x )\r
3507 {\r
3508     return (int) (x + 0.5);\r
3509 }\r
3510 \r
3511 /* Draw an arrow between two points using current settings */\r
3512 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3513 {\r
3514     POINT arrow[7];\r
3515     double dx, dy, j, k, x, y;\r
3516 \r
3517     if( d_x == s_x ) {\r
3518         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3519 \r
3520         arrow[0].x = s_x + A_WIDTH;\r
3521         arrow[0].y = s_y;\r
3522 \r
3523         arrow[1].x = s_x + A_WIDTH;\r
3524         arrow[1].y = d_y - h;\r
3525 \r
3526         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3527         arrow[2].y = d_y - h;\r
3528 \r
3529         arrow[3].x = d_x;\r
3530         arrow[3].y = d_y;\r
3531 \r
3532         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3533         arrow[4].y = d_y - h;\r
3534 \r
3535         arrow[5].x = s_x - A_WIDTH;\r
3536         arrow[5].y = d_y - h;\r
3537 \r
3538         arrow[6].x = s_x - A_WIDTH;\r
3539         arrow[6].y = s_y;\r
3540     }\r
3541     else if( d_y == s_y ) {\r
3542         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3543 \r
3544         arrow[0].x = s_x;\r
3545         arrow[0].y = s_y + A_WIDTH;\r
3546 \r
3547         arrow[1].x = d_x - w;\r
3548         arrow[1].y = s_y + A_WIDTH;\r
3549 \r
3550         arrow[2].x = d_x - w;\r
3551         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3552 \r
3553         arrow[3].x = d_x;\r
3554         arrow[3].y = d_y;\r
3555 \r
3556         arrow[4].x = d_x - w;\r
3557         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3558 \r
3559         arrow[5].x = d_x - w;\r
3560         arrow[5].y = s_y - A_WIDTH;\r
3561 \r
3562         arrow[6].x = s_x;\r
3563         arrow[6].y = s_y - A_WIDTH;\r
3564     }\r
3565     else {\r
3566         /* [AS] Needed a lot of paper for this! :-) */\r
3567         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3568         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3569   \r
3570         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3571 \r
3572         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3573 \r
3574         x = s_x;\r
3575         y = s_y;\r
3576 \r
3577         arrow[0].x = Round(x - j);\r
3578         arrow[0].y = Round(y + j*dx);\r
3579 \r
3580         arrow[1].x = Round(x + j);\r
3581         arrow[1].y = Round(y - j*dx);\r
3582 \r
3583         if( d_x > s_x ) {\r
3584             x = (double) d_x - k;\r
3585             y = (double) d_y - k*dy;\r
3586         }\r
3587         else {\r
3588             x = (double) d_x + k;\r
3589             y = (double) d_y + k*dy;\r
3590         }\r
3591 \r
3592         arrow[2].x = Round(x + j);\r
3593         arrow[2].y = Round(y - j*dx);\r
3594 \r
3595         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3596         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3597 \r
3598         arrow[4].x = d_x;\r
3599         arrow[4].y = d_y;\r
3600 \r
3601         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3602         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3603 \r
3604         arrow[6].x = Round(x - j);\r
3605         arrow[6].y = Round(y + j*dx);\r
3606     }\r
3607 \r
3608     Polygon( hdc, arrow, 7 );\r
3609 }\r
3610 \r
3611 /* [AS] Draw an arrow between two squares */\r
3612 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3613 {\r
3614     int s_x, s_y, d_x, d_y;\r
3615     HPEN hpen;\r
3616     HPEN holdpen;\r
3617     HBRUSH hbrush;\r
3618     HBRUSH holdbrush;\r
3619     LOGBRUSH stLB;\r
3620 \r
3621     if( s_col == d_col && s_row == d_row ) {\r
3622         return;\r
3623     }\r
3624 \r
3625     /* Get source and destination points */\r
3626     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3627     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3628 \r
3629     if( d_y > s_y ) {\r
3630         d_y += squareSize / 4;\r
3631     }\r
3632     else if( d_y < s_y ) {\r
3633         d_y += 3 * squareSize / 4;\r
3634     }\r
3635     else {\r
3636         d_y += squareSize / 2;\r
3637     }\r
3638 \r
3639     if( d_x > s_x ) {\r
3640         d_x += squareSize / 4;\r
3641     }\r
3642     else if( d_x < s_x ) {\r
3643         d_x += 3 * squareSize / 4;\r
3644     }\r
3645     else {\r
3646         d_x += squareSize / 2;\r
3647     }\r
3648 \r
3649     s_x += squareSize / 2;\r
3650     s_y += squareSize / 2;\r
3651 \r
3652     /* Adjust width */\r
3653     A_WIDTH = squareSize / 14;\r
3654 \r
3655     /* Draw */\r
3656     stLB.lbStyle = BS_SOLID;\r
3657     stLB.lbColor = appData.highlightArrowColor;\r
3658     stLB.lbHatch = 0;\r
3659 \r
3660     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3661     holdpen = SelectObject( hdc, hpen );\r
3662     hbrush = CreateBrushIndirect( &stLB );\r
3663     holdbrush = SelectObject( hdc, hbrush );\r
3664 \r
3665     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3666 \r
3667     SelectObject( hdc, holdpen );\r
3668     SelectObject( hdc, holdbrush );\r
3669     DeleteObject( hpen );\r
3670     DeleteObject( hbrush );\r
3671 }\r
3672 \r
3673 BOOL HasHighlightInfo()\r
3674 {\r
3675     BOOL result = FALSE;\r
3676 \r
3677     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3678         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3679     {\r
3680         result = TRUE;\r
3681     }\r
3682 \r
3683     return result;\r
3684 }\r
3685 \r
3686 BOOL IsDrawArrowEnabled()\r
3687 {\r
3688     BOOL result = FALSE;\r
3689 \r
3690     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3691         result = TRUE;\r
3692     }\r
3693 \r
3694     return result;\r
3695 }\r
3696 \r
3697 VOID DrawArrowHighlight( HDC hdc )\r
3698 {\r
3699     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3700         DrawArrowBetweenSquares( hdc,\r
3701             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3702             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3703     }\r
3704 }\r
3705 \r
3706 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3707 {\r
3708     HRGN result = NULL;\r
3709 \r
3710     if( HasHighlightInfo() ) {\r
3711         int x1, y1, x2, y2;\r
3712         int sx, sy, dx, dy;\r
3713 \r
3714         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3715         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3716 \r
3717         sx = MIN( x1, x2 );\r
3718         sy = MIN( y1, y2 );\r
3719         dx = MAX( x1, x2 ) + squareSize;\r
3720         dy = MAX( y1, y2 ) + squareSize;\r
3721 \r
3722         result = CreateRectRgn( sx, sy, dx, dy );\r
3723     }\r
3724 \r
3725     return result;\r
3726 }\r
3727 \r
3728 /*\r
3729     Warning: this function modifies the behavior of several other functions. \r
3730     \r
3731     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3732     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3733     repaint is scattered all over the place, which is not good for features such as\r
3734     "arrow highlighting" that require a full repaint of the board.\r
3735 \r
3736     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3737     user interaction, when speed is not so important) but especially to avoid errors\r
3738     in the displayed graphics.\r
3739 \r
3740     In such patched places, I always try refer to this function so there is a single\r
3741     place to maintain knowledge.\r
3742     \r
3743     To restore the original behavior, just return FALSE unconditionally.\r
3744 */\r
3745 BOOL IsFullRepaintPreferrable()\r
3746 {\r
3747     BOOL result = FALSE;\r
3748 \r
3749     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3750         /* Arrow may appear on the board */\r
3751         result = TRUE;\r
3752     }\r
3753 \r
3754     return result;\r
3755 }\r
3756 \r
3757 /* \r
3758     This function is called by DrawPosition to know whether a full repaint must\r
3759     be forced or not.\r
3760 \r
3761     Only DrawPosition may directly call this function, which makes use of \r
3762     some state information. Other function should call DrawPosition specifying \r
3763     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3764 */\r
3765 BOOL DrawPositionNeedsFullRepaint()\r
3766 {\r
3767     BOOL result = FALSE;\r
3768 \r
3769     /* \r
3770         Probably a slightly better policy would be to trigger a full repaint\r
3771         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
3772         but animation is fast enough that it's difficult to notice.\r
3773     */\r
3774     if( animInfo.piece == EmptySquare ) {\r
3775         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3776             result = TRUE;\r
3777         }\r
3778     }\r
3779 \r
3780     return result;\r
3781 }\r
3782 \r
3783 VOID\r
3784 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
3785 {\r
3786   int row, column, x, y, square_color, piece_color;\r
3787   ChessSquare piece;\r
3788   HBRUSH oldBrush;\r
3789   HDC texture_hdc = NULL;\r
3790 \r
3791   /* [AS] Initialize background textures if needed */\r
3792   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
3793       if( backTextureSquareSize != squareSize ) {\r
3794           backTextureSquareSize = squareSize;\r
3795           RebuildTextureSquareInfo();\r
3796       }\r
3797 \r
3798       texture_hdc = CreateCompatibleDC( hdc );\r
3799   }\r
3800 \r
3801   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3802     for (column = 0; column < BOARD_WIDTH; column++) {\r
3803   \r
3804       SquareToPos(row, column, &x, &y);\r
3805 \r
3806       piece = board[row][column];\r
3807 \r
3808       square_color = ((column + row) % 2) == 1;\r
3809       if(!strcmp(appData.variant, "xiangqi") ) {\r
3810           square_color = !InPalace(row, column);\r
3811           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
3812           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
3813       }\r
3814       piece_color = (int) piece < (int) BlackPawn;\r
3815 \r
3816 \r
3817 #ifdef FAIRY\r
3818       /* [HGM] holdings file: light square or black */\r
3819       if(column == BOARD_LEFT-2) {\r
3820             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
3821                 square_color = 1;\r
3822             else {\r
3823                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
3824                 continue;\r
3825             }\r
3826       } else\r
3827       if(column == BOARD_RGHT + 1 ) {\r
3828             if( row < gameInfo.holdingsSize )\r
3829                 square_color = 1;\r
3830             else {\r
3831                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
3832                 continue;\r
3833             }\r
3834       }\r
3835       if(column == BOARD_LEFT-1 ) /* left align */\r
3836             DisplayHoldingsCount(hdc, x, y, 0, (int) board[row][column]);\r
3837       else if( column == BOARD_RGHT) /* right align */\r
3838             DisplayHoldingsCount(hdc, x, y, 1, (int) board[row][column]);\r
3839       else\r
3840 #endif\r
3841       if (appData.monoMode) {\r
3842         if (piece == EmptySquare) {\r
3843           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
3844                  square_color ? WHITENESS : BLACKNESS);\r
3845         } else {\r
3846           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
3847         }\r
3848       } \r
3849       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
3850           /* [AS] Draw the square using a texture bitmap */\r
3851           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
3852 \r
3853           DrawTile( x, y, \r
3854               squareSize, squareSize, \r
3855               hdc, \r
3856               texture_hdc,\r
3857               backTextureSquareInfo[row][column].mode,\r
3858               backTextureSquareInfo[row][column].x,\r
3859               backTextureSquareInfo[row][column].y );\r
3860 \r
3861           SelectObject( texture_hdc, hbm );\r
3862 \r
3863           if (piece != EmptySquare) {\r
3864               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3865           }\r
3866       }\r
3867       else {\r
3868         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
3869 \r
3870         oldBrush = SelectObject(hdc, brush );\r
3871         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
3872         SelectObject(hdc, oldBrush);\r
3873         if (piece != EmptySquare)\r
3874           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
3875       }\r
3876     }\r
3877   }\r
3878 \r
3879   if( texture_hdc != NULL ) {\r
3880     DeleteDC( texture_hdc );\r
3881   }\r
3882 }\r
3883 \r
3884 #define MAX_CLIPS 200   /* more than enough */\r
3885 \r
3886 VOID\r
3887 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
3888 {\r
3889   static Board lastReq, lastDrawn;\r
3890   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
3891   static int lastDrawnFlipView = 0;\r
3892   static int lastReqValid = 0, lastDrawnValid = 0;\r
3893   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
3894   HDC tmphdc;\r
3895   HDC hdcmem;\r
3896   HBITMAP bufferBitmap;\r
3897   HBITMAP oldBitmap;\r
3898   RECT Rect;\r
3899   HRGN clips[MAX_CLIPS];\r
3900   ChessSquare dragged_piece = EmptySquare;\r
3901 \r
3902   /* I'm undecided on this - this function figures out whether a full\r
3903    * repaint is necessary on its own, so there's no real reason to have the\r
3904    * caller tell it that.  I think this can safely be set to FALSE - but\r
3905    * if we trust the callers not to request full repaints unnessesarily, then\r
3906    * we could skip some clipping work.  In other words, only request a full\r
3907    * redraw when the majority of pieces have changed positions (ie. flip, \r
3908    * gamestart and similar)  --Hawk\r
3909    */\r
3910   Boolean fullrepaint = repaint;\r
3911 \r
3912   if( DrawPositionNeedsFullRepaint() ) {\r
3913       fullrepaint = TRUE;\r
3914   }\r
3915 \r
3916 #if 0\r
3917   if( fullrepaint ) {\r
3918       static int repaint_count = 0;\r
3919       char buf[128];\r
3920 \r
3921       repaint_count++;\r
3922       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
3923       OutputDebugString( buf );\r
3924   }\r
3925 #endif\r
3926 \r
3927   if (board == NULL) {\r
3928     if (!lastReqValid) {\r
3929       return;\r
3930     }\r
3931     board = lastReq;\r
3932   } else {\r
3933     CopyBoard(lastReq, board);\r
3934     lastReqValid = 1;\r
3935   }\r
3936 \r
3937   if (doingSizing) {\r
3938     return;\r
3939   }\r
3940 \r
3941   if (IsIconic(hwndMain)) {\r
3942     return;\r
3943   }\r
3944 \r
3945   if (hdc == NULL) {\r
3946     hdc = GetDC(hwndMain);\r
3947     if (!appData.monoMode) {\r
3948       SelectPalette(hdc, hPal, FALSE);\r
3949       RealizePalette(hdc);\r
3950     }\r
3951     releaseDC = TRUE;\r
3952   } else {\r
3953     releaseDC = FALSE;\r
3954   }\r
3955 \r
3956 #if 0\r
3957   fprintf(debugFP, "*******************************\n"\r
3958                    "repaint = %s\n"\r
3959                    "dragInfo.from (%d,%d)\n"\r
3960                    "dragInfo.start (%d,%d)\n"\r
3961                    "dragInfo.pos (%d,%d)\n"\r
3962                    "dragInfo.lastpos (%d,%d)\n", \r
3963                     repaint ? "TRUE" : "FALSE",\r
3964                     dragInfo.from.x, dragInfo.from.y, \r
3965                     dragInfo.start.x, dragInfo.start.y,\r
3966                     dragInfo.pos.x, dragInfo.pos.y,\r
3967                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
3968   fprintf(debugFP, "prev:  ");\r
3969   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3970     for (column = 0; column < BOARD_WIDTH; column++) {\r
3971       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
3972     }\r
3973   }\r
3974   fprintf(debugFP, "\n");\r
3975   fprintf(debugFP, "board: ");\r
3976   for (row = 0; row < BOARD_HEIGHT; row++) {\r
3977     for (column = 0; column < BOARD_WIDTH; column++) {\r
3978       fprintf(debugFP, "%d ", board[row][column]);\r
3979     }\r
3980   }\r
3981   fprintf(debugFP, "\n");\r
3982   fflush(debugFP);\r
3983 #endif\r
3984 \r
3985   /* Create some work-DCs */\r
3986   hdcmem = CreateCompatibleDC(hdc);\r
3987   tmphdc = CreateCompatibleDC(hdc);\r
3988 \r
3989   /* If dragging is in progress, we temporarely remove the piece */\r
3990   /* [HGM] or temporarily decrease count if stacked              */\r
3991   /*       !! Moved to before board compare !!                   */\r
3992   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
3993     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
3994     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
3995             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
3996         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
3997     } else \r
3998     if(dragInfo.from.x == BOARD_RGHT+1) {\r
3999             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4000         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4001     } else \r
4002         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4003   }\r
4004 \r
4005   /* Figure out which squares need updating by comparing the \r
4006    * newest board with the last drawn board and checking if\r
4007    * flipping has changed.\r
4008    */\r
4009   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4010     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4011       for (column = 0; column < BOARD_WIDTH; column++) {\r
4012         if (lastDrawn[row][column] != board[row][column]) {\r
4013           SquareToPos(row, column, &x, &y);\r
4014           clips[num_clips++] =\r
4015             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4016         }\r
4017       }\r
4018     }\r
4019     for (i=0; i<2; i++) {\r
4020       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4021           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4022         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4023             lastDrawnHighlight.sq[i].y >= 0) {\r
4024           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4025                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4026           clips[num_clips++] =\r
4027             CreateRectRgn(x - lineGap, y - lineGap, \r
4028                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4029         }\r
4030         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4031           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4032           clips[num_clips++] =\r
4033             CreateRectRgn(x - lineGap, y - lineGap, \r
4034                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4035         }\r
4036       }\r
4037     }\r
4038     for (i=0; i<2; i++) {\r
4039       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4040           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4041         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4042             lastDrawnPremove.sq[i].y >= 0) {\r
4043           SquareToPos(lastDrawnPremove.sq[i].y,\r
4044                       lastDrawnPremove.sq[i].x, &x, &y);\r
4045           clips[num_clips++] =\r
4046             CreateRectRgn(x - lineGap, y - lineGap, \r
4047                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4048         }\r
4049         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4050             premoveHighlightInfo.sq[i].y >= 0) {\r
4051           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4052                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4053           clips[num_clips++] =\r
4054             CreateRectRgn(x - lineGap, y - lineGap, \r
4055                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4056         }\r
4057       }\r
4058     }\r
4059   } else {\r
4060     fullrepaint = TRUE;\r
4061   }\r
4062 \r
4063   /* Create a buffer bitmap - this is the actual bitmap\r
4064    * being written to.  When all the work is done, we can\r
4065    * copy it to the real DC (the screen).  This avoids\r
4066    * the problems with flickering.\r
4067    */\r
4068   GetClientRect(hwndMain, &Rect);\r
4069   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4070                                         Rect.bottom-Rect.top+1);\r
4071   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4072   if (!appData.monoMode) {\r
4073     SelectPalette(hdcmem, hPal, FALSE);\r
4074   }\r
4075 \r
4076   /* Create clips for dragging */\r
4077   if (!fullrepaint) {\r
4078     if (dragInfo.from.x >= 0) {\r
4079       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4080       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4081     }\r
4082     if (dragInfo.start.x >= 0) {\r
4083       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4084       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4085     }\r
4086     if (dragInfo.pos.x >= 0) {\r
4087       x = dragInfo.pos.x - squareSize / 2;\r
4088       y = dragInfo.pos.y - squareSize / 2;\r
4089       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4090     }\r
4091     if (dragInfo.lastpos.x >= 0) {\r
4092       x = dragInfo.lastpos.x - squareSize / 2;\r
4093       y = dragInfo.lastpos.y - squareSize / 2;\r
4094       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4095     }\r
4096   }\r
4097 \r
4098   /* Are we animating a move?  \r
4099    * If so, \r
4100    *   - remove the piece from the board (temporarely)\r
4101    *   - calculate the clipping region\r
4102    */\r
4103   if (!fullrepaint) {\r
4104     if (animInfo.piece != EmptySquare) {\r
4105       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4106       x = boardRect.left + animInfo.lastpos.x;\r
4107       y = boardRect.top + animInfo.lastpos.y;\r
4108       x2 = boardRect.left + animInfo.pos.x;\r
4109       y2 = boardRect.top + animInfo.pos.y;\r
4110       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4111       /* Slight kludge.  The real problem is that after AnimateMove is\r
4112          done, the position on the screen does not match lastDrawn.\r
4113          This currently causes trouble only on e.p. captures in\r
4114          atomic, where the piece moves to an empty square and then\r
4115          explodes.  The old and new positions both had an empty square\r
4116          at the destination, but animation has drawn a piece there and\r
4117          we have to remember to erase it. */\r
4118       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4119     }\r
4120   }\r
4121 \r
4122   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4123   if (num_clips == 0)\r
4124     fullrepaint = TRUE;\r
4125 \r
4126   /* Set clipping on the memory DC */\r
4127   if (!fullrepaint) {\r
4128     SelectClipRgn(hdcmem, clips[0]);\r
4129     for (x = 1; x < num_clips; x++) {\r
4130       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4131         abort();  // this should never ever happen!\r
4132     }\r
4133   }\r
4134 \r
4135   /* Do all the drawing to the memory DC */\r
4136   DrawGridOnDC(hdcmem);\r
4137   DrawHighlightsOnDC(hdcmem);\r
4138   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4139 \r
4140   if( appData.highlightMoveWithArrow ) {\r
4141     DrawArrowHighlight(hdcmem);\r
4142   }\r
4143 \r
4144   DrawCoordsOnDC(hdcmem);\r
4145 \r
4146   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4147                  /* to make sure lastDrawn contains what is actually drawn */\r
4148 \r
4149   /* Put the dragged piece back into place and draw it (out of place!) */\r
4150     if (dragged_piece != EmptySquare) {\r
4151     /* [HGM] or restack */\r
4152     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4153                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4154     else\r
4155     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4156                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4157     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4158     x = dragInfo.pos.x - squareSize / 2;\r
4159     y = dragInfo.pos.y - squareSize / 2;\r
4160     DrawPieceOnDC(hdcmem, dragged_piece,\r
4161                   ((int) dragged_piece < (int) BlackPawn), \r
4162                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4163   }   \r
4164   \r
4165   /* Put the animated piece back into place and draw it */\r
4166   if (animInfo.piece != EmptySquare) {\r
4167     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4168     x = boardRect.left + animInfo.pos.x;\r
4169     y = boardRect.top + animInfo.pos.y;\r
4170     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4171                   ((int) animInfo.piece < (int) BlackPawn),\r
4172                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4173   }\r
4174 \r
4175   /* Release the bufferBitmap by selecting in the old bitmap \r
4176    * and delete the memory DC\r
4177    */\r
4178   SelectObject(hdcmem, oldBitmap);\r
4179   DeleteDC(hdcmem);\r
4180 \r
4181   /* Set clipping on the target DC */\r
4182   if (!fullrepaint) {\r
4183     SelectClipRgn(hdc, clips[0]);\r
4184     for (x = 1; x < num_clips; x++) {\r
4185       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4186         abort();   // this should never ever happen!\r
4187     } \r
4188   }\r
4189 \r
4190   /* Copy the new bitmap onto the screen in one go.\r
4191    * This way we avoid any flickering\r
4192    */\r
4193   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4194   BitBlt(hdc, boardRect.left, boardRect.top,\r
4195          boardRect.right - boardRect.left,\r
4196          boardRect.bottom - boardRect.top,\r
4197          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4198   SelectObject(tmphdc, oldBitmap);\r
4199 \r
4200   /* Massive cleanup */\r
4201   for (x = 0; x < num_clips; x++)\r
4202     DeleteObject(clips[x]);\r
4203 \r
4204   DeleteDC(tmphdc);\r
4205   DeleteObject(bufferBitmap);\r
4206 \r
4207   if (releaseDC) \r
4208     ReleaseDC(hwndMain, hdc);\r
4209   \r
4210   if (lastDrawnFlipView != flipView) {\r
4211     if (flipView)\r
4212       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4213     else\r
4214       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4215   }\r
4216 \r
4217 /*  CopyBoard(lastDrawn, board);*/\r
4218   lastDrawnHighlight = highlightInfo;\r
4219   lastDrawnPremove   = premoveHighlightInfo;\r
4220   lastDrawnFlipView = flipView;\r
4221   lastDrawnValid = 1;\r
4222 }\r
4223 \r
4224 \r
4225 /*---------------------------------------------------------------------------*\\r
4226 | CLIENT PAINT PROCEDURE\r
4227 |   This is the main event-handler for the WM_PAINT message.\r
4228 |\r
4229 \*---------------------------------------------------------------------------*/\r
4230 VOID\r
4231 PaintProc(HWND hwnd)\r
4232 {\r
4233   HDC         hdc;\r
4234   PAINTSTRUCT ps;\r
4235   HFONT       oldFont;\r
4236 \r
4237   if(hdc = BeginPaint(hwnd, &ps)) {\r
4238     if (IsIconic(hwnd)) {\r
4239       DrawIcon(hdc, 2, 2, iconCurrent);\r
4240     } else {\r
4241       if (!appData.monoMode) {\r
4242         SelectPalette(hdc, hPal, FALSE);\r
4243         RealizePalette(hdc);\r
4244       }\r
4245       HDCDrawPosition(hdc, 1, NULL);\r
4246       oldFont =\r
4247         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4248       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4249                  ETO_CLIPPED|ETO_OPAQUE,\r
4250                  &messageRect, messageText, strlen(messageText), NULL);\r
4251       SelectObject(hdc, oldFont);\r
4252       DisplayBothClocks();\r
4253     }\r
4254     EndPaint(hwnd,&ps);\r
4255   }\r
4256 \r
4257   return;\r
4258 }\r
4259 \r
4260 \r
4261 /*\r
4262  * If the user selects on a border boundary, return -1; if off the board,\r
4263  *   return -2.  Otherwise map the event coordinate to the square.\r
4264  * The offset boardRect.left or boardRect.top must already have been\r
4265  *   subtracted from x.\r
4266  */\r
4267 int\r
4268 EventToSquare(int x)\r
4269 {\r
4270   if (x <= 0)\r
4271     return -2;\r
4272   if (x < lineGap)\r
4273     return -1;\r
4274   x -= lineGap;\r
4275   if ((x % (squareSize + lineGap)) >= squareSize)\r
4276     return -1;\r
4277   x /= (squareSize + lineGap);\r
4278   if (x >= BOARD_SIZE)\r
4279     return -2;\r
4280   return x;\r
4281 }\r
4282 \r
4283 typedef struct {\r
4284   char piece;\r
4285   int command;\r
4286   char* name;\r
4287 } DropEnable;\r
4288 \r
4289 DropEnable dropEnables[] = {\r
4290   { 'P', DP_Pawn, "Pawn" },\r
4291   { 'N', DP_Knight, "Knight" },\r
4292   { 'B', DP_Bishop, "Bishop" },\r
4293   { 'R', DP_Rook, "Rook" },\r
4294   { 'Q', DP_Queen, "Queen" },\r
4295 };\r
4296 \r
4297 VOID\r
4298 SetupDropMenu(HMENU hmenu)\r
4299 {\r
4300   int i, count, enable;\r
4301   char *p;\r
4302   extern char white_holding[], black_holding[];\r
4303   char item[MSG_SIZ];\r
4304 \r
4305   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4306     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4307                dropEnables[i].piece);\r
4308     count = 0;\r
4309     while (p && *p++ == dropEnables[i].piece) count++;\r
4310     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4311     enable = count > 0 || !appData.testLegality\r
4312       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4313                       && !appData.icsActive);\r
4314     ModifyMenu(hmenu, dropEnables[i].command,\r
4315                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4316                dropEnables[i].command, item);\r
4317   }\r
4318 }\r
4319 \r
4320 static int fromX = -1, fromY = -1, toX, toY;\r
4321 \r
4322 /* Event handler for mouse messages */\r
4323 VOID\r
4324 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4325 {\r
4326   int x, y;\r
4327   POINT pt;\r
4328   static int recursive = 0;\r
4329   HMENU hmenu;\r
4330   BOOLEAN needsRedraw = FALSE;\r
4331   BOOLEAN saveAnimate;\r
4332   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4333   static BOOLEAN sameAgain = FALSE;\r
4334   ChessMove moveType;\r
4335 \r
4336   if (recursive) {\r
4337     if (message == WM_MBUTTONUP) {\r
4338       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4339          to the middle button: we simulate pressing the left button too!\r
4340          */\r
4341       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4342       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4343     }\r
4344     return;\r
4345   }\r
4346   recursive++;\r
4347   \r
4348   pt.x = LOWORD(lParam);\r
4349   pt.y = HIWORD(lParam);\r
4350   x = EventToSquare(pt.x - boardRect.left);\r
4351   y = EventToSquare(pt.y - boardRect.top);\r
4352   if (!flipView && y >= 0) {\r
4353     y = BOARD_HEIGHT - 1 - y;\r
4354   }\r
4355   if (flipView && x >= 0) {\r
4356     x = BOARD_WIDTH - 1 - x;\r
4357   }\r
4358 \r
4359   switch (message) {\r
4360   case WM_LBUTTONDOWN:\r
4361     ErrorPopDown();\r
4362     sameAgain = FALSE;\r
4363     if (y == -2) {\r
4364       /* Downclick vertically off board; check if on clock */\r
4365       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4366         if (gameMode == EditPosition) {\r
4367           SetWhiteToPlayEvent();\r
4368         } else if (gameMode == IcsPlayingBlack ||\r
4369                    gameMode == MachinePlaysWhite) {\r
4370           CallFlagEvent();\r
4371         } else if (gameMode == EditGame) {\r
4372           AdjustClock(flipClock, -1);\r
4373         }\r
4374       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4375         if (gameMode == EditPosition) {\r
4376           SetBlackToPlayEvent();\r
4377         } else if (gameMode == IcsPlayingWhite ||\r
4378                    gameMode == MachinePlaysBlack) {\r
4379           CallFlagEvent();\r
4380         } else if (gameMode == EditGame) {\r
4381           AdjustClock(!flipClock, -1);\r
4382         }\r
4383       }\r
4384       if (!appData.highlightLastMove) {\r
4385         ClearHighlights();\r
4386         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4387       }\r
4388       fromX = fromY = -1;\r
4389       dragInfo.start.x = dragInfo.start.y = -1;\r
4390       dragInfo.from = dragInfo.start;\r
4391       break;\r
4392     } else if (x < 0 || y < 0\r
4393       /* [HGM] block clicks between board and holdings */\r
4394               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4395               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4396               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4397         /* EditPosition, empty square, or different color piece;\r
4398            click-click move is possible */\r
4399                                ) {\r
4400       break;\r
4401     } else if (fromX == x && fromY == y) {\r
4402       /* Downclick on same square again */\r
4403       ClearHighlights();\r
4404       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4405       sameAgain = TRUE;  \r
4406     } else if (fromX != -1 &&\r
4407                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4408                                                                         ) {\r
4409       /* Downclick on different square. */\r
4410       /* [HGM] if on holdings file, should count as new first click ! */\r
4411       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4412         toX = x;\r
4413         toY = y;\r
4414         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4415            to make sure move is legal before showing promotion popup */\r
4416         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4417         if(moveType != ImpossibleMove) {\r
4418           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4419           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4420              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4421               appData.alwaysPromoteToQueen) {\r
4422                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4423                   if (!appData.highlightLastMove) {\r
4424                       ClearHighlights();\r
4425                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4426                   }\r
4427           } else\r
4428           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4429                   SetHighlights(fromX, fromY, toX, toY);\r
4430                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4431                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4432                      If promotion to Q is legal, all are legal! */\r
4433                   PromotionPopup(hwnd);\r
4434           } else {       /* not a promotion */\r
4435              if (appData.animate || appData.highlightLastMove) {\r
4436                  SetHighlights(fromX, fromY, toX, toY);\r
4437              } else {\r
4438                  ClearHighlights();\r
4439              }\r
4440              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4441              if (appData.animate && !appData.highlightLastMove) {\r
4442                   ClearHighlights();\r
4443                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4444              }\r
4445           }\r
4446         }\r
4447         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4448         fromX = fromY = -1;\r
4449         break;\r
4450       }\r
4451       ClearHighlights();\r
4452       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4453     }\r
4454     /* First downclick, or restart on a square with same color piece */\r
4455     if (!frozen && OKToStartUserMove(x, y)) {\r
4456       fromX = x;\r
4457       fromY = y;\r
4458       dragInfo.lastpos = pt;\r
4459       dragInfo.from.x = fromX;\r
4460       dragInfo.from.y = fromY;\r
4461       dragInfo.start = dragInfo.from;\r
4462       SetCapture(hwndMain);\r
4463     } else {\r
4464       fromX = fromY = -1;\r
4465       dragInfo.start.x = dragInfo.start.y = -1;\r
4466       dragInfo.from = dragInfo.start;\r
4467       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4468     }\r
4469     break;\r
4470 \r
4471   case WM_LBUTTONUP:\r
4472     ReleaseCapture();\r
4473     if (fromX == -1) break;\r
4474     if (x == fromX && y == fromY) {\r
4475       /* Upclick on same square */\r
4476       if (sameAgain) {\r
4477         /* Clicked same square twice: abort click-click move */\r
4478         fromX = fromY = -1;\r
4479         gotPremove = 0;\r
4480         ClearPremoveHighlights();\r
4481       } else {\r
4482         /* First square clicked: start click-click move */\r
4483         SetHighlights(fromX, fromY, -1, -1);\r
4484       }\r
4485       dragInfo.from.x = dragInfo.from.y = -1;\r
4486       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4487     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4488       /* Errant click; ignore */\r
4489       break;\r
4490     } else {\r
4491       /* Finish drag move. */\r
4492     if (appData.debugMode) {\r
4493         fprintf(debugFP, "release\n");\r
4494     }\r
4495       dragInfo.from.x = dragInfo.from.y = -1;\r
4496       toX = x;\r
4497       toY = y;\r
4498       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4499       appData.animate = appData.animate && !appData.animateDragging;\r
4500       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4501       if(moveType != ImpossibleMove) {\r
4502           /* [HGM] use move type to determine if move is promotion.\r
4503              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4504           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4505              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4506               appData.alwaysPromoteToQueen) \r
4507                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4508           else \r
4509           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4510                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4511                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
4512         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4513       }\r
4514       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4515       appData.animate = saveAnimate;\r
4516       fromX = fromY = -1;\r
4517       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4518         ClearHighlights();\r
4519       }\r
4520       if (appData.animate || appData.animateDragging ||\r
4521           appData.highlightDragging || gotPremove) {\r
4522         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4523       }\r
4524     }\r
4525     dragInfo.start.x = dragInfo.start.y = -1; \r
4526     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4527     break;\r
4528 \r
4529   case WM_MOUSEMOVE:\r
4530     if ((appData.animateDragging || appData.highlightDragging)\r
4531         && (wParam & MK_LBUTTON)\r
4532         && dragInfo.from.x >= 0) \r
4533     {\r
4534       BOOL full_repaint = FALSE;\r
4535 \r
4536       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
4537       if (appData.animateDragging) {\r
4538         dragInfo.pos = pt;\r
4539       }\r
4540       if (appData.highlightDragging) {\r
4541         SetHighlights(fromX, fromY, x, y);\r
4542         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4543             full_repaint = TRUE;\r
4544         }\r
4545       }\r
4546       \r
4547       DrawPosition( full_repaint, NULL);\r
4548       \r
4549       dragInfo.lastpos = dragInfo.pos;\r
4550     }\r
4551     break;\r
4552 \r
4553   case WM_MBUTTONDOWN:\r
4554   case WM_RBUTTONDOWN:\r
4555     ErrorPopDown();\r
4556     ReleaseCapture();\r
4557     fromX = fromY = -1;\r
4558     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4559     dragInfo.start.x = dragInfo.start.y = -1;\r
4560     dragInfo.from = dragInfo.start;\r
4561     dragInfo.lastpos = dragInfo.pos;\r
4562     if (appData.highlightDragging) {\r
4563       ClearHighlights();\r
4564     }\r
4565     if(y == -2) {\r
4566       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4567       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4568           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4569       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4570           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4571       }\r
4572     }\r
4573     DrawPosition(TRUE, NULL);\r
4574 \r
4575     switch (gameMode) {\r
4576     case EditPosition:\r
4577     case IcsExamining:\r
4578       if (x < 0 || y < 0) break;\r
4579       fromX = x;\r
4580       fromY = y;\r
4581       if (message == WM_MBUTTONDOWN) {\r
4582         buttonCount = 3;  /* even if system didn't think so */\r
4583         if (wParam & MK_SHIFT) \r
4584           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4585         else\r
4586           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4587       } else { /* message == WM_RBUTTONDOWN */\r
4588 #if 0\r
4589         if (buttonCount == 3) {\r
4590           if (wParam & MK_SHIFT) \r
4591             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4592           else\r
4593             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4594         } else {\r
4595           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4596         }\r
4597 #else\r
4598         /* Just have one menu, on the right button.  Windows users don't\r
4599            think to try the middle one, and sometimes other software steals\r
4600            it, or it doesn't really exist. */\r
4601         if(gameInfo.variant != VariantShogi)\r
4602             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4603         else\r
4604             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4605 #endif\r
4606       }\r
4607       break;\r
4608     case IcsPlayingWhite:\r
4609     case IcsPlayingBlack:\r
4610     case EditGame:\r
4611     case MachinePlaysWhite:\r
4612     case MachinePlaysBlack:\r
4613       if (appData.testLegality &&\r
4614           gameInfo.variant != VariantBughouse &&\r
4615           gameInfo.variant != VariantCrazyhouse) break;\r
4616       if (x < 0 || y < 0) break;\r
4617       fromX = x;\r
4618       fromY = y;\r
4619       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4620       SetupDropMenu(hmenu);\r
4621       MenuPopup(hwnd, pt, hmenu, -1);\r
4622       break;\r
4623     default:\r
4624       break;\r
4625     }\r
4626     break;\r
4627   }\r
4628 \r
4629   recursive--;\r
4630 }\r
4631 \r
4632 /* Preprocess messages for buttons in main window */\r
4633 LRESULT CALLBACK\r
4634 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4635 {\r
4636   int id = GetWindowLong(hwnd, GWL_ID);\r
4637   int i, dir;\r
4638 \r
4639   for (i=0; i<N_BUTTONS; i++) {\r
4640     if (buttonDesc[i].id == id) break;\r
4641   }\r
4642   if (i == N_BUTTONS) return 0;\r
4643   switch (message) {\r
4644   case WM_KEYDOWN:\r
4645     switch (wParam) {\r
4646     case VK_LEFT:\r
4647     case VK_RIGHT:\r
4648       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4649       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4650       return TRUE;\r
4651     }\r
4652     break;\r
4653   case WM_CHAR:\r
4654     switch (wParam) {\r
4655     case '\r':\r
4656       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4657       return TRUE;\r
4658     case '\t':\r
4659       if (appData.icsActive) {\r
4660         if (GetKeyState(VK_SHIFT) < 0) {\r
4661           /* shifted */\r
4662           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4663           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4664           SetFocus(h);\r
4665         } else {\r
4666           /* unshifted */\r
4667           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4668           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4669           SetFocus(h);\r
4670         }\r
4671         return TRUE;\r
4672       }\r
4673       break;\r
4674     default:\r
4675       if (appData.icsActive) {\r
4676         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4677         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4678         SetFocus(h);\r
4679         SendMessage(h, WM_CHAR, wParam, lParam);\r
4680         return TRUE;\r
4681       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4682         PopUpMoveDialog((char)wParam);\r
4683       }\r
4684       break;\r
4685     }\r
4686     break;\r
4687   }\r
4688   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4689 }\r
4690 \r
4691 /* Process messages for Promotion dialog box */\r
4692 LRESULT CALLBACK\r
4693 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4694 {\r
4695   char promoChar;\r
4696 \r
4697   switch (message) {\r
4698   case WM_INITDIALOG: /* message: initialize dialog box */\r
4699     /* Center the dialog over the application window */\r
4700     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4701     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4702       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4703        gameInfo.variant == VariantGiveaway) ?\r
4704                SW_SHOW : SW_HIDE);\r
4705     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4706     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4707        (PieceToChar(WhiteCardinal) != '.' ||\r
4708         PieceToChar(BlackCardinal) != '.'   ) ?\r
4709                SW_SHOW : SW_HIDE);\r
4710     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4711        (PieceToChar(WhiteMarshall) != '.' ||\r
4712         PieceToChar(BlackMarshall) != '.'   ) ?\r
4713                SW_SHOW : SW_HIDE);\r
4714     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4715     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4716        gameInfo.variant != VariantShogi ?\r
4717                SW_SHOW : SW_HIDE);\r
4718     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4719        gameInfo.variant != VariantShogi ?\r
4720                SW_SHOW : SW_HIDE);\r
4721     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4722        gameInfo.variant == VariantShogi ?\r
4723                SW_SHOW : SW_HIDE);\r
4724     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4725        gameInfo.variant == VariantShogi ?\r
4726                SW_SHOW : SW_HIDE);\r
4727     return TRUE;\r
4728 \r
4729   case WM_COMMAND: /* message: received a command */\r
4730     switch (LOWORD(wParam)) {\r
4731     case IDCANCEL:\r
4732       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4733       ClearHighlights();\r
4734       DrawPosition(FALSE, NULL);\r
4735       return TRUE;\r
4736     case PB_King:\r
4737       promoChar = PieceToChar(BlackKing);\r
4738       break;\r
4739     case PB_Queen:\r
4740       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4741       break;\r
4742     case PB_Rook:\r
4743       promoChar = PieceToChar(BlackRook);\r
4744       break;\r
4745     case PB_Bishop:\r
4746       promoChar = PieceToChar(BlackBishop);\r
4747       break;\r
4748     case PB_Chancellor:\r
4749       promoChar = PieceToChar(BlackMarshall);\r
4750       break;\r
4751     case PB_Archbishop:\r
4752       promoChar = PieceToChar(BlackCardinal);\r
4753       break;\r
4754     case PB_Knight:\r
4755       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4756       break;\r
4757     default:\r
4758       return FALSE;\r
4759     }\r
4760     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4761     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
4762        only show the popup when we are already sure the move is valid or\r
4763        legal. We pass a faulty move type, but the kludge is that FinishMove\r
4764        will figure out it is a promotion from the promoChar. */\r
4765     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
4766     if (!appData.highlightLastMove) {\r
4767       ClearHighlights();\r
4768       DrawPosition(FALSE, NULL);\r
4769     }\r
4770     return TRUE;\r
4771   }\r
4772   return FALSE;\r
4773 }\r
4774 \r
4775 /* Pop up promotion dialog */\r
4776 VOID\r
4777 PromotionPopup(HWND hwnd)\r
4778 {\r
4779   FARPROC lpProc;\r
4780 \r
4781   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
4782   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
4783     hwnd, (DLGPROC)lpProc);\r
4784   FreeProcInstance(lpProc);\r
4785 }\r
4786 \r
4787 /* Toggle ShowThinking */\r
4788 VOID\r
4789 ToggleShowThinking()\r
4790 {\r
4791   ShowThinkingEvent(!appData.showThinking);\r
4792 }\r
4793 \r
4794 VOID\r
4795 LoadGameDialog(HWND hwnd, char* title)\r
4796 {\r
4797   UINT number = 0;\r
4798   FILE *f;\r
4799   char fileTitle[MSG_SIZ];\r
4800   f = OpenFileDialog(hwnd, FALSE, "",\r
4801                      appData.oldSaveStyle ? "gam" : "pgn",\r
4802                      GAME_FILT,\r
4803                      title, &number, fileTitle, NULL);\r
4804   if (f != NULL) {\r
4805     cmailMsgLoaded = FALSE;\r
4806     if (number == 0) {\r
4807       int error = GameListBuild(f);\r
4808       if (error) {\r
4809         DisplayError("Cannot build game list", error);\r
4810       } else if (!ListEmpty(&gameList) &&\r
4811                  ((ListGame *) gameList.tailPred)->number > 1) {\r
4812         GameListPopUp(f, fileTitle);\r
4813         return;\r
4814       }\r
4815       GameListDestroy();\r
4816       number = 1;\r
4817     }\r
4818     LoadGame(f, number, fileTitle, FALSE);\r
4819   }\r
4820 }\r
4821 \r
4822 VOID\r
4823 ChangedConsoleFont()\r
4824 {\r
4825   CHARFORMAT cfmt;\r
4826   CHARRANGE tmpsel, sel;\r
4827   MyFont *f = font[boardSize][CONSOLE_FONT];\r
4828   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4829   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4830   PARAFORMAT paraf;\r
4831 \r
4832   cfmt.cbSize = sizeof(CHARFORMAT);\r
4833   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
4834   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
4835   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
4836    * size.  This was undocumented in the version of MSVC++ that I had\r
4837    * when I wrote the code, but is apparently documented now.\r
4838    */\r
4839   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
4840   cfmt.bCharSet = f->lf.lfCharSet;\r
4841   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
4842   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4843   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
4844   /* Why are the following seemingly needed too? */\r
4845   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4846   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
4847   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
4848   tmpsel.cpMin = 0;\r
4849   tmpsel.cpMax = -1; /*999999?*/\r
4850   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
4851   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
4852   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
4853    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
4854    */\r
4855   paraf.cbSize = sizeof(paraf);\r
4856   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
4857   paraf.dxStartIndent = 0;\r
4858   paraf.dxOffset = WRAP_INDENT;\r
4859   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
4860   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
4861 }\r
4862 \r
4863 /*---------------------------------------------------------------------------*\\r
4864  *\r
4865  * Window Proc for main window\r
4866  *\r
4867 \*---------------------------------------------------------------------------*/\r
4868 \r
4869 /* Process messages for main window, etc. */\r
4870 LRESULT CALLBACK\r
4871 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4872 {\r
4873   FARPROC lpProc;\r
4874   int wmId, wmEvent;\r
4875   char *defName;\r
4876   FILE *f;\r
4877   UINT number;\r
4878   char fileTitle[MSG_SIZ];\r
4879   static SnapData sd;\r
4880 \r
4881   switch (message) {\r
4882 \r
4883   case WM_PAINT: /* message: repaint portion of window */\r
4884     PaintProc(hwnd);\r
4885     break;\r
4886 \r
4887   case WM_ERASEBKGND:\r
4888     if (IsIconic(hwnd)) {\r
4889       /* Cheat; change the message */\r
4890       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
4891     } else {\r
4892       return (DefWindowProc(hwnd, message, wParam, lParam));\r
4893     }\r
4894     break;\r
4895 \r
4896   case WM_LBUTTONDOWN:\r
4897   case WM_MBUTTONDOWN:\r
4898   case WM_RBUTTONDOWN:\r
4899   case WM_LBUTTONUP:\r
4900   case WM_MBUTTONUP:\r
4901   case WM_RBUTTONUP:\r
4902   case WM_MOUSEMOVE:\r
4903     MouseEvent(hwnd, message, wParam, lParam);\r
4904     break;\r
4905 \r
4906   case WM_CHAR:\r
4907     \r
4908     if (appData.icsActive) {\r
4909       if (wParam == '\t') {\r
4910         if (GetKeyState(VK_SHIFT) < 0) {\r
4911           /* shifted */\r
4912           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4913           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4914           SetFocus(h);\r
4915         } else {\r
4916           /* unshifted */\r
4917           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
4918           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4919           SetFocus(h);\r
4920         }\r
4921       } else {\r
4922         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4923         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4924         SetFocus(h);\r
4925         SendMessage(h, message, wParam, lParam);\r
4926       }\r
4927     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
4928       PopUpMoveDialog((char)wParam);\r
4929     }\r
4930     break;\r
4931 \r
4932   case WM_PALETTECHANGED:\r
4933     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
4934       int nnew;\r
4935       HDC hdc = GetDC(hwndMain);\r
4936       SelectPalette(hdc, hPal, TRUE);\r
4937       nnew = RealizePalette(hdc);\r
4938       if (nnew > 0) {\r
4939         paletteChanged = TRUE;\r
4940 #if 0\r
4941         UpdateColors(hdc);\r
4942 #else\r
4943         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
4944 #endif\r
4945       }\r
4946       ReleaseDC(hwnd, hdc);\r
4947     }\r
4948     break;\r
4949 \r
4950   case WM_QUERYNEWPALETTE:\r
4951     if (!appData.monoMode /*&& paletteChanged*/) {\r
4952       int nnew;\r
4953       HDC hdc = GetDC(hwndMain);\r
4954       paletteChanged = FALSE;\r
4955       SelectPalette(hdc, hPal, FALSE);\r
4956       nnew = RealizePalette(hdc);\r
4957       if (nnew > 0) {\r
4958         InvalidateRect(hwnd, &boardRect, FALSE);\r
4959       }\r
4960       ReleaseDC(hwnd, hdc);\r
4961       return TRUE;\r
4962     }\r
4963     return FALSE;\r
4964 \r
4965   case WM_COMMAND: /* message: command from application menu */\r
4966     wmId    = LOWORD(wParam);\r
4967     wmEvent = HIWORD(wParam);\r
4968 \r
4969     switch (wmId) {\r
4970     case IDM_NewGame:\r
4971       ResetGameEvent();\r
4972       AnalysisPopDown();\r
4973       break;\r
4974 \r
4975     case IDM_NewGameFRC:\r
4976       if( NewGameFRC() == 0 ) {\r
4977         ResetGameEvent();\r
4978         AnalysisPopDown();\r
4979       }\r
4980       break;\r
4981 \r
4982     case IDM_NewVariant:\r
4983       NewVariantPopup(hwnd);\r
4984       break;\r
4985 \r
4986     case IDM_LoadGame:\r
4987       LoadGameDialog(hwnd, "Load Game from File");\r
4988       break;\r
4989 \r
4990     case IDM_LoadNextGame:\r
4991       ReloadGame(1);\r
4992       break;\r
4993 \r
4994     case IDM_LoadPrevGame:\r
4995       ReloadGame(-1);\r
4996       break;\r
4997 \r
4998     case IDM_ReloadGame:\r
4999       ReloadGame(0);\r
5000       break;\r
5001 \r
5002     case IDM_LoadPosition:\r
5003       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5004         Reset(FALSE, TRUE);\r
5005       }\r
5006       number = 1;\r
5007       f = OpenFileDialog(hwnd, FALSE, "",\r
5008                          appData.oldSaveStyle ? "pos" : "fen",\r
5009                          POSITION_FILT,\r
5010                          "Load Position from File", &number, fileTitle, NULL);\r
5011       if (f != NULL) {\r
5012         LoadPosition(f, number, fileTitle);\r
5013       }\r
5014       break;\r
5015 \r
5016     case IDM_LoadNextPosition:\r
5017       ReloadPosition(1);\r
5018       break;\r
5019 \r
5020     case IDM_LoadPrevPosition:\r
5021       ReloadPosition(-1);\r
5022       break;\r
5023 \r
5024     case IDM_ReloadPosition:\r
5025       ReloadPosition(0);\r
5026       break;\r
5027 \r
5028     case IDM_SaveGame:\r
5029       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5030       f = OpenFileDialog(hwnd, TRUE, defName,\r
5031                          appData.oldSaveStyle ? "gam" : "pgn",\r
5032                          GAME_FILT,\r
5033                          "Save Game to File", NULL, fileTitle, NULL);\r
5034       if (f != NULL) {\r
5035         SaveGame(f, 0, "");\r
5036       }\r
5037       break;\r
5038 \r
5039     case IDM_SavePosition:\r
5040       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5041       f = OpenFileDialog(hwnd, TRUE, defName,\r
5042                          appData.oldSaveStyle ? "pos" : "fen",\r
5043                          POSITION_FILT,\r
5044                          "Save Position to File", NULL, fileTitle, NULL);\r
5045       if (f != NULL) {\r
5046         SavePosition(f, 0, "");\r
5047       }\r
5048       break;\r
5049 \r
5050     case IDM_CopyGame:\r
5051       CopyGameToClipboard();\r
5052       break;\r
5053 \r
5054     case IDM_PasteGame:\r
5055       PasteGameFromClipboard();\r
5056       break;\r
5057 \r
5058     case IDM_CopyGameListToClipboard:\r
5059       CopyGameListToClipboard();\r
5060       break;\r
5061 \r
5062     /* [AS] Autodetect FEN or PGN data */\r
5063     case IDM_PasteAny:\r
5064       PasteGameOrFENFromClipboard();\r
5065       break;\r
5066 \r
5067     /* [AS] Move history */\r
5068     case IDM_ShowMoveHistory:\r
5069         if( MoveHistoryIsUp() ) {\r
5070             MoveHistoryPopDown();\r
5071         }\r
5072         else {\r
5073             MoveHistoryPopUp();\r
5074         }\r
5075         break;\r
5076 \r
5077     /* [AS] Eval graph */\r
5078     case IDM_ShowEvalGraph:\r
5079         if( EvalGraphIsUp() ) {\r
5080             EvalGraphPopDown();\r
5081         }\r
5082         else {\r
5083             EvalGraphPopUp();\r
5084         }\r
5085         break;\r
5086 \r
5087     /* [AS] Engine output */\r
5088     case IDM_ShowEngineOutput:\r
5089         if( EngineOutputIsUp() ) {\r
5090             EngineOutputPopDown();\r
5091         }\r
5092         else {\r
5093             EngineOutputPopUp();\r
5094         }\r
5095         break;\r
5096 \r
5097     /* [AS] User adjudication */\r
5098     case IDM_UserAdjudication_White:\r
5099         UserAdjudicationEvent( +1 );\r
5100         break;\r
5101 \r
5102     case IDM_UserAdjudication_Black:\r
5103         UserAdjudicationEvent( -1 );\r
5104         break;\r
5105 \r
5106     case IDM_UserAdjudication_Draw:\r
5107         UserAdjudicationEvent( 0 );\r
5108         break;\r
5109 \r
5110     /* [AS] Game list options dialog */\r
5111     case IDM_GameListOptions:\r
5112       GameListOptions();\r
5113       break;\r
5114 \r
5115     case IDM_CopyPosition:\r
5116       CopyFENToClipboard();\r
5117       break;\r
5118 \r
5119     case IDM_PastePosition:\r
5120       PasteFENFromClipboard();\r
5121       break;\r
5122 \r
5123     case IDM_MailMove:\r
5124       MailMoveEvent();\r
5125       break;\r
5126 \r
5127     case IDM_ReloadCMailMsg:\r
5128       Reset(TRUE, TRUE);\r
5129       ReloadCmailMsgEvent(FALSE);\r
5130       break;\r
5131 \r
5132     case IDM_Minimize:\r
5133       ShowWindow(hwnd, SW_MINIMIZE);\r
5134       break;\r
5135 \r
5136     case IDM_Exit:\r
5137       ExitEvent(0);\r
5138       break;\r
5139 \r
5140     case IDM_MachineWhite:\r
5141       MachineWhiteEvent();\r
5142       /*\r
5143        * refresh the tags dialog only if it's visible\r
5144        */\r
5145       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5146           char *tags;\r
5147           tags = PGNTags(&gameInfo);\r
5148           TagsPopUp(tags, CmailMsg());\r
5149           free(tags);\r
5150       }\r
5151       break;\r
5152 \r
5153     case IDM_MachineBlack:\r
5154       MachineBlackEvent();\r
5155       /*\r
5156        * refresh the tags dialog only if it's visible\r
5157        */\r
5158       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5159           char *tags;\r
5160           tags = PGNTags(&gameInfo);\r
5161           TagsPopUp(tags, CmailMsg());\r
5162           free(tags);\r
5163       }\r
5164       break;\r
5165 \r
5166     case IDM_TwoMachines:\r
5167       TwoMachinesEvent();\r
5168       /*\r
5169        * refresh the tags dialog only if it's visible\r
5170        */\r
5171       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5172           char *tags;\r
5173           tags = PGNTags(&gameInfo);\r
5174           TagsPopUp(tags, CmailMsg());\r
5175           free(tags);\r
5176       }\r
5177       break;\r
5178 \r
5179     case IDM_AnalysisMode:\r
5180       if (!first.analysisSupport) {\r
5181         char buf[MSG_SIZ];\r
5182         sprintf(buf, "%s does not support analysis", first.tidy);\r
5183         DisplayError(buf, 0);\r
5184       } else {\r
5185         if (!appData.showThinking) ToggleShowThinking();\r
5186         AnalyzeModeEvent();\r
5187       }\r
5188       break;\r
5189 \r
5190     case IDM_AnalyzeFile:\r
5191       if (!first.analysisSupport) {\r
5192         char buf[MSG_SIZ];\r
5193         sprintf(buf, "%s does not support analysis", first.tidy);\r
5194         DisplayError(buf, 0);\r
5195       } else {\r
5196         if (!appData.showThinking) ToggleShowThinking();\r
5197         AnalyzeFileEvent();\r
5198         LoadGameDialog(hwnd, "Analyze Game from File");\r
5199         AnalysisPeriodicEvent(1);\r
5200       }\r
5201       break;\r
5202 \r
5203     case IDM_IcsClient:\r
5204       IcsClientEvent();\r
5205       break;\r
5206 \r
5207     case IDM_EditGame:\r
5208       EditGameEvent();\r
5209       break;\r
5210 \r
5211     case IDM_EditPosition:\r
5212       EditPositionEvent();\r
5213       break;\r
5214 \r
5215     case IDM_Training:\r
5216       TrainingEvent();\r
5217       break;\r
5218 \r
5219     case IDM_ShowGameList:\r
5220       ShowGameListProc();\r
5221       break;\r
5222 \r
5223     case IDM_EditTags:\r
5224       EditTagsProc();\r
5225       break;\r
5226 \r
5227     case IDM_EditComment:\r
5228       if (commentDialogUp && editComment) {\r
5229         CommentPopDown();\r
5230       } else {\r
5231         EditCommentEvent();\r
5232       }\r
5233       break;\r
5234 \r
5235     case IDM_Pause:\r
5236       PauseEvent();\r
5237       break;\r
5238 \r
5239     case IDM_Accept:\r
5240       AcceptEvent();\r
5241       break;\r
5242 \r
5243     case IDM_Decline:\r
5244       DeclineEvent();\r
5245       break;\r
5246 \r
5247     case IDM_Rematch:\r
5248       RematchEvent();\r
5249       break;\r
5250 \r
5251     case IDM_CallFlag:\r
5252       CallFlagEvent();\r
5253       break;\r
5254 \r
5255     case IDM_Draw:\r
5256       DrawEvent();\r
5257       break;\r
5258 \r
5259     case IDM_Adjourn:\r
5260       AdjournEvent();\r
5261       break;\r
5262 \r
5263     case IDM_Abort:\r
5264       AbortEvent();\r
5265       break;\r
5266 \r
5267     case IDM_Resign:\r
5268       ResignEvent();\r
5269       break;\r
5270 \r
5271     case IDM_StopObserving:\r
5272       StopObservingEvent();\r
5273       break;\r
5274 \r
5275     case IDM_StopExamining:\r
5276       StopExaminingEvent();\r
5277       break;\r
5278 \r
5279     case IDM_TypeInMove:\r
5280       PopUpMoveDialog('\000');\r
5281       break;\r
5282 \r
5283     case IDM_Backward:\r
5284       BackwardEvent();\r
5285       SetFocus(hwndMain);\r
5286       break;\r
5287 \r
5288     case IDM_Forward:\r
5289       ForwardEvent();\r
5290       SetFocus(hwndMain);\r
5291       break;\r
5292 \r
5293     case IDM_ToStart:\r
5294       ToStartEvent();\r
5295       SetFocus(hwndMain);\r
5296       break;\r
5297 \r
5298     case IDM_ToEnd:\r
5299       ToEndEvent();\r
5300       SetFocus(hwndMain);\r
5301       break;\r
5302 \r
5303     case IDM_Revert:\r
5304       RevertEvent();\r
5305       break;\r
5306 \r
5307     case IDM_TruncateGame:\r
5308       TruncateGameEvent();\r
5309       break;\r
5310 \r
5311     case IDM_MoveNow:\r
5312       MoveNowEvent();\r
5313       break;\r
5314 \r
5315     case IDM_RetractMove:\r
5316       RetractMoveEvent();\r
5317       break;\r
5318 \r
5319     case IDM_FlipView:\r
5320       flipView = !flipView;\r
5321       DrawPosition(FALSE, NULL);\r
5322       break;\r
5323 \r
5324     case IDM_FlipClock:\r
5325       flipClock = !flipClock;\r
5326       DisplayBothClocks();\r
5327       break;\r
5328 \r
5329     case IDM_GeneralOptions:\r
5330       GeneralOptionsPopup(hwnd);\r
5331       DrawPosition(TRUE, NULL);\r
5332       break;\r
5333 \r
5334     case IDM_BoardOptions:\r
5335       BoardOptionsPopup(hwnd);\r
5336       break;\r
5337 \r
5338     case IDM_EnginePlayOptions:\r
5339       EnginePlayOptionsPopup(hwnd);\r
5340       break;\r
5341 \r
5342     case IDM_OptionsUCI:\r
5343       UciOptionsPopup(hwnd);\r
5344       break;\r
5345 \r
5346     case IDM_IcsOptions:\r
5347       IcsOptionsPopup(hwnd);\r
5348       break;\r
5349 \r
5350     case IDM_Fonts:\r
5351       FontsOptionsPopup(hwnd);\r
5352       break;\r
5353 \r
5354     case IDM_Sounds:\r
5355       SoundOptionsPopup(hwnd);\r
5356       break;\r
5357 \r
5358     case IDM_CommPort:\r
5359       CommPortOptionsPopup(hwnd);\r
5360       break;\r
5361 \r
5362     case IDM_LoadOptions:\r
5363       LoadOptionsPopup(hwnd);\r
5364       break;\r
5365 \r
5366     case IDM_SaveOptions:\r
5367       SaveOptionsPopup(hwnd);\r
5368       break;\r
5369 \r
5370     case IDM_TimeControl:\r
5371       TimeControlOptionsPopup(hwnd);\r
5372       break;\r
5373 \r
5374     case IDM_SaveSettings:\r
5375       SaveSettings(settingsFileName);\r
5376       break;\r
5377 \r
5378     case IDM_SaveSettingsOnExit:\r
5379       saveSettingsOnExit = !saveSettingsOnExit;\r
5380       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5381                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5382                                          MF_CHECKED : MF_UNCHECKED));\r
5383       break;\r
5384 \r
5385     case IDM_Hint:\r
5386       HintEvent();\r
5387       break;\r
5388 \r
5389     case IDM_Book:\r
5390       BookEvent();\r
5391       break;\r
5392 \r
5393     case IDM_AboutGame:\r
5394       AboutGameEvent();\r
5395       break;\r
5396 \r
5397     case IDM_Debug:\r
5398       appData.debugMode = !appData.debugMode;\r
5399       if (appData.debugMode) {\r
5400         char dir[MSG_SIZ];\r
5401         GetCurrentDirectory(MSG_SIZ, dir);\r
5402         SetCurrentDirectory(installDir);\r
5403         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5404         SetCurrentDirectory(dir);\r
5405         setbuf(debugFP, NULL);\r
5406       } else {\r
5407         fclose(debugFP);\r
5408         debugFP = NULL;\r
5409       }\r
5410       break;\r
5411 \r
5412     case IDM_HELPCONTENTS:\r
5413       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5414         MessageBox (GetFocus(),\r
5415                     "Unable to activate help",\r
5416                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5417       }\r
5418       break;\r
5419 \r
5420     case IDM_HELPSEARCH:\r
5421       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5422         MessageBox (GetFocus(),\r
5423                     "Unable to activate help",\r
5424                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5425       }\r
5426       break;\r
5427 \r
5428     case IDM_HELPHELP:\r
5429       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5430         MessageBox (GetFocus(),\r
5431                     "Unable to activate help",\r
5432                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5433       }\r
5434       break;\r
5435 \r
5436     case IDM_ABOUT:\r
5437       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5438       DialogBox(hInst, \r
5439         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5440         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5441       FreeProcInstance(lpProc);\r
5442       break;\r
5443 \r
5444     case IDM_DirectCommand1:\r
5445       AskQuestionEvent("Direct Command",\r
5446                        "Send to chess program:", "", "1");\r
5447       break;\r
5448     case IDM_DirectCommand2:\r
5449       AskQuestionEvent("Direct Command",\r
5450                        "Send to second chess program:", "", "2");\r
5451       break;\r
5452 \r
5453     case EP_WhitePawn:\r
5454       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5455       fromX = fromY = -1;\r
5456       break;\r
5457 \r
5458     case EP_WhiteKnight:\r
5459       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5460       fromX = fromY = -1;\r
5461       break;\r
5462 \r
5463     case EP_WhiteBishop:\r
5464       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5465       fromX = fromY = -1;\r
5466       break;\r
5467 \r
5468     case EP_WhiteRook:\r
5469       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5470       fromX = fromY = -1;\r
5471       break;\r
5472 \r
5473     case EP_WhiteQueen:\r
5474       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5475       fromX = fromY = -1;\r
5476       break;\r
5477 \r
5478     case EP_WhiteFerz:\r
5479       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5480       fromX = fromY = -1;\r
5481       break;\r
5482 \r
5483     case EP_WhiteWazir:\r
5484       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5485       fromX = fromY = -1;\r
5486       break;\r
5487 \r
5488     case EP_WhiteAlfil:\r
5489       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5490       fromX = fromY = -1;\r
5491       break;\r
5492 \r
5493     case EP_WhiteCannon:\r
5494       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5495       fromX = fromY = -1;\r
5496       break;\r
5497 \r
5498     case EP_WhiteCardinal:\r
5499       EditPositionMenuEvent(WhiteCardinal, fromX, fromY);\r
5500       fromX = fromY = -1;\r
5501       break;\r
5502 \r
5503     case EP_WhiteMarshall:\r
5504       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5505       fromX = fromY = -1;\r
5506       break;\r
5507 \r
5508     case EP_WhiteKing:\r
5509       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5510       fromX = fromY = -1;\r
5511       break;\r
5512 \r
5513     case EP_BlackPawn:\r
5514       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5515       fromX = fromY = -1;\r
5516       break;\r
5517 \r
5518     case EP_BlackKnight:\r
5519       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5520       fromX = fromY = -1;\r
5521       break;\r
5522 \r
5523     case EP_BlackBishop:\r
5524       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5525       fromX = fromY = -1;\r
5526       break;\r
5527 \r
5528     case EP_BlackRook:\r
5529       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5530       fromX = fromY = -1;\r
5531       break;\r
5532 \r
5533     case EP_BlackQueen:\r
5534       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5535       fromX = fromY = -1;\r
5536       break;\r
5537 \r
5538     case EP_BlackFerz:\r
5539       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5540       fromX = fromY = -1;\r
5541       break;\r
5542 \r
5543     case EP_BlackWazir:\r
5544       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5545       fromX = fromY = -1;\r
5546       break;\r
5547 \r
5548     case EP_BlackAlfil:\r
5549       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5550       fromX = fromY = -1;\r
5551       break;\r
5552 \r
5553     case EP_BlackCannon:\r
5554       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5555       fromX = fromY = -1;\r
5556       break;\r
5557 \r
5558     case EP_BlackCardinal:\r
5559       EditPositionMenuEvent(BlackCardinal, fromX, fromY);\r
5560       fromX = fromY = -1;\r
5561       break;\r
5562 \r
5563     case EP_BlackMarshall:\r
5564       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5565       fromX = fromY = -1;\r
5566       break;\r
5567 \r
5568     case EP_BlackKing:\r
5569       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5570       fromX = fromY = -1;\r
5571       break;\r
5572 \r
5573     case EP_EmptySquare:\r
5574       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5575       fromX = fromY = -1;\r
5576       break;\r
5577 \r
5578     case EP_ClearBoard:\r
5579       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5580       fromX = fromY = -1;\r
5581       break;\r
5582 \r
5583     case EP_White:\r
5584       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5585       fromX = fromY = -1;\r
5586       break;\r
5587 \r
5588     case EP_Black:\r
5589       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5590       fromX = fromY = -1;\r
5591       break;\r
5592 \r
5593     case EP_Promote:\r
5594       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5595       fromX = fromY = -1;\r
5596       break;\r
5597 \r
5598     case EP_Demote:\r
5599       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5600       fromX = fromY = -1;\r
5601       break;\r
5602 \r
5603     case DP_Pawn:\r
5604       DropMenuEvent(WhitePawn, fromX, fromY);\r
5605       fromX = fromY = -1;\r
5606       break;\r
5607 \r
5608     case DP_Knight:\r
5609       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5610       fromX = fromY = -1;\r
5611       break;\r
5612 \r
5613     case DP_Bishop:\r
5614       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5615       fromX = fromY = -1;\r
5616       break;\r
5617 \r
5618     case DP_Rook:\r
5619       DropMenuEvent(WhiteRook, fromX, fromY);\r
5620       fromX = fromY = -1;\r
5621       break;\r
5622 \r
5623     case DP_Queen:\r
5624       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5625       fromX = fromY = -1;\r
5626       break;\r
5627 \r
5628     default:\r
5629       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5630     }\r
5631     break;\r
5632 \r
5633   case WM_TIMER:\r
5634     switch (wParam) {\r
5635     case CLOCK_TIMER_ID:\r
5636       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
5637       clockTimerEvent = 0;\r
5638       DecrementClocks(); /* call into back end */\r
5639       break;\r
5640     case LOAD_GAME_TIMER_ID:\r
5641       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
5642       loadGameTimerEvent = 0;\r
5643       AutoPlayGameLoop(); /* call into back end */\r
5644       break;\r
5645     case ANALYSIS_TIMER_ID:\r
5646       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
5647           appData.periodicUpdates) {\r
5648         AnalysisPeriodicEvent(0);\r
5649       } else {\r
5650         KillTimer(hwnd, analysisTimerEvent);\r
5651         analysisTimerEvent = 0;\r
5652       }\r
5653       break;\r
5654     case DELAYED_TIMER_ID:\r
5655       KillTimer(hwnd, delayedTimerEvent);\r
5656       delayedTimerEvent = 0;\r
5657       delayedTimerCallback();\r
5658       break;\r
5659     }\r
5660     break;\r
5661 \r
5662   case WM_USER_Input:\r
5663     InputEvent(hwnd, message, wParam, lParam);\r
5664     break;\r
5665 \r
5666   /* [AS] Also move "attached" child windows */\r
5667   case WM_WINDOWPOSCHANGING:\r
5668     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
5669         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
5670 \r
5671         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
5672             /* Window is moving */\r
5673             RECT rcMain;\r
5674 \r
5675             GetWindowRect( hwnd, &rcMain );\r
5676             \r
5677             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
5678             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
5679             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
5680         }\r
5681     }\r
5682     break;\r
5683 \r
5684   /* [AS] Snapping */\r
5685   case WM_ENTERSIZEMOVE:\r
5686     if (hwnd == hwndMain) {\r
5687       doingSizing = TRUE;\r
5688       lastSizing = 0;\r
5689     }\r
5690     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
5691     break;\r
5692 \r
5693   case WM_SIZING:\r
5694     if (hwnd == hwndMain) {\r
5695       lastSizing = wParam;\r
5696     }\r
5697     break;\r
5698 \r
5699   case WM_MOVING:\r
5700       return OnMoving( &sd, hwnd, wParam, lParam );\r
5701 \r
5702   case WM_EXITSIZEMOVE:\r
5703     if (hwnd == hwndMain) {\r
5704       RECT client;\r
5705       doingSizing = FALSE;\r
5706       InvalidateRect(hwnd, &boardRect, FALSE);\r
5707       GetClientRect(hwnd, &client);\r
5708       ResizeBoard(client.right, client.bottom, lastSizing);\r
5709       lastSizing = 0;\r
5710     }\r
5711     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
5712     break;\r
5713 \r
5714   case WM_DESTROY: /* message: window being destroyed */\r
5715     PostQuitMessage(0);\r
5716     break;\r
5717 \r
5718   case WM_CLOSE:\r
5719     if (hwnd == hwndMain) {\r
5720       ExitEvent(0);\r
5721     }\r
5722     break;\r
5723 \r
5724   default:      /* Passes it on if unprocessed */\r
5725     return (DefWindowProc(hwnd, message, wParam, lParam));\r
5726   }\r
5727   return 0;\r
5728 }\r
5729 \r
5730 /*---------------------------------------------------------------------------*\\r
5731  *\r
5732  * Misc utility routines\r
5733  *\r
5734 \*---------------------------------------------------------------------------*/\r
5735 \r
5736 /*\r
5737  * Decent random number generator, at least not as bad as Windows\r
5738  * standard rand, which returns a value in the range 0 to 0x7fff.\r
5739  */\r
5740 unsigned int randstate;\r
5741 \r
5742 int\r
5743 myrandom(void)\r
5744 {\r
5745   randstate = randstate * 1664525 + 1013904223;\r
5746   return (int) randstate & 0x7fffffff;\r
5747 }\r
5748 \r
5749 void\r
5750 mysrandom(unsigned int seed)\r
5751 {\r
5752   randstate = seed;\r
5753 }\r
5754 \r
5755 \r
5756 /* \r
5757  * returns TRUE if user selects a different color, FALSE otherwise \r
5758  */\r
5759 \r
5760 BOOL\r
5761 ChangeColor(HWND hwnd, COLORREF *which)\r
5762 {\r
5763   static BOOL firstTime = TRUE;\r
5764   static DWORD customColors[16];\r
5765   CHOOSECOLOR cc;\r
5766   COLORREF newcolor;\r
5767   int i;\r
5768   ColorClass ccl;\r
5769 \r
5770   if (firstTime) {\r
5771     /* Make initial colors in use available as custom colors */\r
5772     /* Should we put the compiled-in defaults here instead? */\r
5773     i = 0;\r
5774     customColors[i++] = lightSquareColor & 0xffffff;\r
5775     customColors[i++] = darkSquareColor & 0xffffff;\r
5776     customColors[i++] = whitePieceColor & 0xffffff;\r
5777     customColors[i++] = blackPieceColor & 0xffffff;\r
5778     customColors[i++] = highlightSquareColor & 0xffffff;\r
5779     customColors[i++] = premoveHighlightColor & 0xffffff;\r
5780 \r
5781     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
5782       customColors[i++] = textAttribs[ccl].color;\r
5783     }\r
5784     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
5785     firstTime = FALSE;\r
5786   }\r
5787 \r
5788   cc.lStructSize = sizeof(cc);\r
5789   cc.hwndOwner = hwnd;\r
5790   cc.hInstance = NULL;\r
5791   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
5792   cc.lpCustColors = (LPDWORD) customColors;\r
5793   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
5794 \r
5795   if (!ChooseColor(&cc)) return FALSE;\r
5796 \r
5797   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
5798   if (newcolor == *which) return FALSE;\r
5799   *which = newcolor;\r
5800   return TRUE;\r
5801 \r
5802   /*\r
5803   InitDrawingColors();\r
5804   InvalidateRect(hwnd, &boardRect, FALSE);\r
5805   */\r
5806 }\r
5807 \r
5808 BOOLEAN\r
5809 MyLoadSound(MySound *ms)\r
5810 {\r
5811   BOOL ok = FALSE;\r
5812   struct stat st;\r
5813   FILE *f;\r
5814 \r
5815   if (ms->data) free(ms->data);\r
5816   ms->data = NULL;\r
5817 \r
5818   switch (ms->name[0]) {\r
5819   case NULLCHAR:\r
5820     /* Silence */\r
5821     ok = TRUE;\r
5822     break;\r
5823   case '$':\r
5824     /* System sound from Control Panel.  Don't preload here. */\r
5825     ok = TRUE;\r
5826     break;\r
5827   case '!':\r
5828     if (ms->name[1] == NULLCHAR) {\r
5829       /* "!" alone = silence */\r
5830       ok = TRUE;\r
5831     } else {\r
5832       /* Builtin wave resource.  Error if not found. */\r
5833       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
5834       if (h == NULL) break;\r
5835       ms->data = (void *)LoadResource(hInst, h);\r
5836       if (h == NULL) break;\r
5837       ok = TRUE;\r
5838     }\r
5839     break;\r
5840   default:\r
5841     /* .wav file.  Error if not found. */\r
5842     f = fopen(ms->name, "rb");\r
5843     if (f == NULL) break;\r
5844     if (fstat(fileno(f), &st) < 0) break;\r
5845     ms->data = malloc(st.st_size);\r
5846     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
5847     fclose(f);\r
5848     ok = TRUE;\r
5849     break;\r
5850   }\r
5851   if (!ok) {\r
5852     char buf[MSG_SIZ];\r
5853     sprintf(buf, "Error loading sound %s", ms->name);\r
5854     DisplayError(buf, GetLastError());\r
5855   }\r
5856   return ok;\r
5857 }\r
5858 \r
5859 BOOLEAN\r
5860 MyPlaySound(MySound *ms)\r
5861 {\r
5862   BOOLEAN ok = FALSE;\r
5863   switch (ms->name[0]) {\r
5864   case NULLCHAR:\r
5865     /* Silence */\r
5866     ok = TRUE;\r
5867     break;\r
5868   case '$':\r
5869     /* System sound from Control Panel (deprecated feature).\r
5870        "$" alone or an unset sound name gets default beep (still in use). */\r
5871     if (ms->name[1]) {\r
5872       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
5873     }\r
5874     if (!ok) ok = MessageBeep(MB_OK);\r
5875     break; \r
5876   case '!':\r
5877     /* Builtin wave resource, or "!" alone for silence */\r
5878     if (ms->name[1]) {\r
5879       if (ms->data == NULL) return FALSE;\r
5880       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5881     } else {\r
5882       ok = TRUE;\r
5883     }\r
5884     break;\r
5885   default:\r
5886     /* .wav file.  Error if not found. */\r
5887     if (ms->data == NULL) return FALSE;\r
5888     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
5889     break;\r
5890   }\r
5891   /* Don't print an error: this can happen innocently if the sound driver\r
5892      is busy; for instance, if another instance of WinBoard is playing\r
5893      a sound at about the same time. */\r
5894 #if 0\r
5895   if (!ok) {\r
5896     char buf[MSG_SIZ];\r
5897     sprintf(buf, "Error playing sound %s", ms->name);\r
5898     DisplayError(buf, GetLastError());\r
5899   }\r
5900 #endif\r
5901   return ok;\r
5902 }\r
5903 \r
5904 \r
5905 LRESULT CALLBACK\r
5906 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5907 {\r
5908   BOOL ok;\r
5909   OPENFILENAME *ofn;\r
5910   static UINT *number; /* gross that this is static */\r
5911 \r
5912   switch (message) {\r
5913   case WM_INITDIALOG: /* message: initialize dialog box */\r
5914     /* Center the dialog over the application window */\r
5915     ofn = (OPENFILENAME *) lParam;\r
5916     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
5917       number = (UINT *) ofn->lCustData;\r
5918       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
5919     } else {\r
5920       number = NULL;\r
5921     }\r
5922     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
5923     return FALSE;  /* Allow for further processing */\r
5924 \r
5925   case WM_COMMAND:\r
5926     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
5927       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
5928     }\r
5929     return FALSE;  /* Allow for further processing */\r
5930   }\r
5931   return FALSE;\r
5932 }\r
5933 \r
5934 UINT APIENTRY\r
5935 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
5936 {\r
5937   static UINT *number;\r
5938   OPENFILENAME *ofname;\r
5939   OFNOTIFY *ofnot;\r
5940   switch (uiMsg) {\r
5941   case WM_INITDIALOG:\r
5942     ofname = (OPENFILENAME *)lParam;\r
5943     number = (UINT *)(ofname->lCustData);\r
5944     break;\r
5945   case WM_NOTIFY:\r
5946     ofnot = (OFNOTIFY *)lParam;\r
5947     if (ofnot->hdr.code == CDN_FILEOK) {\r
5948       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
5949     }\r
5950     break;\r
5951   }\r
5952   return 0;\r
5953 }\r
5954 \r
5955 \r
5956 FILE *\r
5957 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
5958                char *nameFilt, char *dlgTitle, UINT *number,\r
5959                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
5960 {\r
5961   OPENFILENAME openFileName;\r
5962   char buf1[MSG_SIZ];\r
5963   FILE *f;\r
5964 \r
5965   if (fileName == NULL) fileName = buf1;\r
5966   if (defName == NULL) {\r
5967     strcpy(fileName, "*.");\r
5968     strcat(fileName, defExt);\r
5969   } else {\r
5970     strcpy(fileName, defName);\r
5971   }\r
5972   if (fileTitle) strcpy(fileTitle, "");\r
5973   if (number) *number = 0;\r
5974 \r
5975   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
5976   openFileName.hwndOwner         = hwnd;\r
5977   openFileName.hInstance         = (HANDLE) hInst;\r
5978   openFileName.lpstrFilter       = nameFilt;\r
5979   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
5980   openFileName.nMaxCustFilter    = 0L;\r
5981   openFileName.nFilterIndex      = 1L;\r
5982   openFileName.lpstrFile         = fileName;\r
5983   openFileName.nMaxFile          = MSG_SIZ;\r
5984   openFileName.lpstrFileTitle    = fileTitle;\r
5985   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
5986   openFileName.lpstrInitialDir   = NULL;\r
5987   openFileName.lpstrTitle        = dlgTitle;\r
5988   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
5989     | (write ? 0 : OFN_FILEMUSTEXIST) \r
5990     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
5991     | (oldDialog ? 0 : OFN_EXPLORER);\r
5992   openFileName.nFileOffset       = 0;\r
5993   openFileName.nFileExtension    = 0;\r
5994   openFileName.lpstrDefExt       = defExt;\r
5995   openFileName.lCustData         = (LONG) number;\r
5996   openFileName.lpfnHook          = oldDialog ?\r
5997     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
5998   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
5999 \r
6000   if (write ? GetSaveFileName(&openFileName) : \r
6001               GetOpenFileName(&openFileName)) {\r
6002     /* open the file */\r
6003     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");\r
6004     if (f == NULL) {\r
6005       MessageBox(hwnd, "File open failed", NULL,\r
6006                  MB_OK|MB_ICONEXCLAMATION);\r
6007       return NULL;\r
6008     }\r
6009   } else {\r
6010     int err = CommDlgExtendedError();\r
6011     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6012     return FALSE;\r
6013   }\r
6014   return f;\r
6015 }\r
6016 \r
6017 \r
6018 \r
6019 VOID APIENTRY\r
6020 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6021 {\r
6022   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6023 \r
6024   /*\r
6025    * Get the first pop-up menu in the menu template. This is the\r
6026    * menu that TrackPopupMenu displays.\r
6027    */\r
6028   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6029 \r
6030   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6031 \r
6032   /*\r
6033    * TrackPopup uses screen coordinates, so convert the\r
6034    * coordinates of the mouse click to screen coordinates.\r
6035    */\r
6036   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6037 \r
6038   /* Draw and track the floating pop-up menu. */\r
6039   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6040                  pt.x, pt.y, 0, hwnd, NULL);\r
6041 \r
6042   /* Destroy the menu.*/\r
6043   DestroyMenu(hmenu);\r
6044 }\r
6045    \r
6046 typedef struct {\r
6047   HWND hDlg, hText;\r
6048   int sizeX, sizeY, newSizeX, newSizeY;\r
6049   HDWP hdwp;\r
6050 } ResizeEditPlusButtonsClosure;\r
6051 \r
6052 BOOL CALLBACK\r
6053 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6054 {\r
6055   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6056   RECT rect;\r
6057   POINT pt;\r
6058 \r
6059   if (hChild == cl->hText) return TRUE;\r
6060   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6061   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6062   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6063   ScreenToClient(cl->hDlg, &pt);\r
6064   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6065     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6066   return TRUE;\r
6067 }\r
6068 \r
6069 /* Resize a dialog that has a (rich) edit field filling most of\r
6070    the top, with a row of buttons below */\r
6071 VOID\r
6072 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6073 {\r
6074   RECT rectText;\r
6075   int newTextHeight, newTextWidth;\r
6076   ResizeEditPlusButtonsClosure cl;\r
6077   \r
6078   /*if (IsIconic(hDlg)) return;*/\r
6079   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6080   \r
6081   cl.hdwp = BeginDeferWindowPos(8);\r
6082 \r
6083   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6084   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6085   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6086   if (newTextHeight < 0) {\r
6087     newSizeY += -newTextHeight;\r
6088     newTextHeight = 0;\r
6089   }\r
6090   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6091     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6092 \r
6093   cl.hDlg = hDlg;\r
6094   cl.hText = hText;\r
6095   cl.sizeX = sizeX;\r
6096   cl.sizeY = sizeY;\r
6097   cl.newSizeX = newSizeX;\r
6098   cl.newSizeY = newSizeY;\r
6099   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6100 \r
6101   EndDeferWindowPos(cl.hdwp);\r
6102 }\r
6103 \r
6104 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6105 {\r
6106     RECT    rChild, rParent;\r
6107     int     wChild, hChild, wParent, hParent;\r
6108     int     wScreen, hScreen, xNew, yNew;\r
6109     HDC     hdc;\r
6110 \r
6111     /* Get the Height and Width of the child window */\r
6112     GetWindowRect (hwndChild, &rChild);\r
6113     wChild = rChild.right - rChild.left;\r
6114     hChild = rChild.bottom - rChild.top;\r
6115 \r
6116     /* Get the Height and Width of the parent window */\r
6117     GetWindowRect (hwndParent, &rParent);\r
6118     wParent = rParent.right - rParent.left;\r
6119     hParent = rParent.bottom - rParent.top;\r
6120 \r
6121     /* Get the display limits */\r
6122     hdc = GetDC (hwndChild);\r
6123     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6124     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6125     ReleaseDC(hwndChild, hdc);\r
6126 \r
6127     /* Calculate new X position, then adjust for screen */\r
6128     xNew = rParent.left + ((wParent - wChild) /2);\r
6129     if (xNew < 0) {\r
6130         xNew = 0;\r
6131     } else if ((xNew+wChild) > wScreen) {\r
6132         xNew = wScreen - wChild;\r
6133     }\r
6134 \r
6135     /* Calculate new Y position, then adjust for screen */\r
6136     if( mode == 0 ) {\r
6137         yNew = rParent.top  + ((hParent - hChild) /2);\r
6138     }\r
6139     else {\r
6140         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6141     }\r
6142 \r
6143     if (yNew < 0) {\r
6144         yNew = 0;\r
6145     } else if ((yNew+hChild) > hScreen) {\r
6146         yNew = hScreen - hChild;\r
6147     }\r
6148 \r
6149     /* Set it, and return */\r
6150     return SetWindowPos (hwndChild, NULL,\r
6151                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6152 }\r
6153 \r
6154 /* Center one window over another */\r
6155 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6156 {\r
6157     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6158 }\r
6159 \r
6160 /*---------------------------------------------------------------------------*\\r
6161  *\r
6162  * Startup Dialog functions\r
6163  *\r
6164 \*---------------------------------------------------------------------------*/\r
6165 void\r
6166 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6167 {\r
6168   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6169 \r
6170   while (*cd != NULL) {\r
6171     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6172     cd++;\r
6173   }\r
6174 }\r
6175 \r
6176 void\r
6177 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6178 {\r
6179   char buf1[ARG_MAX];\r
6180   int len;\r
6181 \r
6182   if (str[0] == '@') {\r
6183     FILE* f = fopen(str + 1, "r");\r
6184     if (f == NULL) {\r
6185       DisplayFatalError(str + 1, errno, 2);\r
6186       return;\r
6187     }\r
6188     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6189     fclose(f);\r
6190     buf1[len] = NULLCHAR;\r
6191     str = buf1;\r
6192   }\r
6193 \r
6194   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6195 \r
6196   for (;;) {\r
6197     char buf[MSG_SIZ];\r
6198     char *end = strchr(str, '\n');\r
6199     if (end == NULL) return;\r
6200     memcpy(buf, str, end - str);\r
6201     buf[end - str] = NULLCHAR;\r
6202     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6203     str = end + 1;\r
6204   }\r
6205 }\r
6206 \r
6207 void\r
6208 SetStartupDialogEnables(HWND hDlg)\r
6209 {\r
6210   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6211     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6212     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6213   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6214     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6215   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6216     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6217   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6218     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6219   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6220     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6221     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6222     IsDlgButtonChecked(hDlg, OPT_View));\r
6223 }\r
6224 \r
6225 char *\r
6226 QuoteForFilename(char *filename)\r
6227 {\r
6228   int dquote, space;\r
6229   dquote = strchr(filename, '"') != NULL;\r
6230   space = strchr(filename, ' ') != NULL;\r
6231   if (dquote || space) {\r
6232     if (dquote) {\r
6233       return "'";\r
6234     } else {\r
6235       return "\"";\r
6236     }\r
6237   } else {\r
6238     return "";\r
6239   }\r
6240 }\r
6241 \r
6242 VOID\r
6243 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6244 {\r
6245   char buf[MSG_SIZ];\r
6246   char *q;\r
6247 \r
6248   InitComboStringsFromOption(hwndCombo, nthnames);\r
6249   q = QuoteForFilename(nthcp);\r
6250   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6251   if (*nthdir != NULLCHAR) {\r
6252     q = QuoteForFilename(nthdir);\r
6253     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6254   }\r
6255   if (*nthcp == NULLCHAR) {\r
6256     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6257   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6258     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6259     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6260   }\r
6261 }\r
6262 \r
6263 LRESULT CALLBACK\r
6264 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6265 {\r
6266   char buf[MSG_SIZ];\r
6267   HANDLE hwndCombo;\r
6268   char *p;\r
6269 \r
6270   switch (message) {\r
6271   case WM_INITDIALOG:\r
6272     /* Center the dialog */\r
6273     CenterWindow (hDlg, GetDesktopWindow());\r
6274     /* Initialize the dialog items */\r
6275     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6276                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6277                   firstChessProgramNames);\r
6278     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6279                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6280                   secondChessProgramNames);\r
6281     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6282     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6283     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6284     if (*appData.icsHelper != NULLCHAR) {\r
6285       char *q = QuoteForFilename(appData.icsHelper);\r
6286       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6287     }\r
6288     if (*appData.icsHost == NULLCHAR) {\r
6289       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6290       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6291     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6292       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6293       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6294     }\r
6295 \r
6296     if (appData.icsActive) {\r
6297       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6298     }\r
6299     else if (appData.noChessProgram) {\r
6300       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6301     }\r
6302     else {\r
6303       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6304     }\r
6305 \r
6306     SetStartupDialogEnables(hDlg);\r
6307     return TRUE;\r
6308 \r
6309   case WM_COMMAND:\r
6310     switch (LOWORD(wParam)) {\r
6311     case IDOK:\r
6312       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6313         strcpy(buf, "/fcp=");\r
6314         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6315         p = buf;\r
6316         ParseArgs(StringGet, &p);\r
6317         strcpy(buf, "/scp=");\r
6318         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6319         p = buf;\r
6320         ParseArgs(StringGet, &p);\r
6321         appData.noChessProgram = FALSE;\r
6322         appData.icsActive = FALSE;\r
6323       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6324         strcpy(buf, "/ics /icshost=");\r
6325         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6326         p = buf;\r
6327         ParseArgs(StringGet, &p);\r
6328         if (appData.zippyPlay) {\r
6329           strcpy(buf, "/fcp=");\r
6330           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6331           p = buf;\r
6332           ParseArgs(StringGet, &p);\r
6333         }\r
6334       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6335         appData.noChessProgram = TRUE;\r
6336         appData.icsActive = FALSE;\r
6337       } else {\r
6338         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6339                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6340         return TRUE;\r
6341       }\r
6342       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6343         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6344         p = buf;\r
6345         ParseArgs(StringGet, &p);\r
6346       }\r
6347       EndDialog(hDlg, TRUE);\r
6348       return TRUE;\r
6349 \r
6350     case IDCANCEL:\r
6351       ExitEvent(0);\r
6352       return TRUE;\r
6353 \r
6354     case IDM_HELPCONTENTS:\r
6355       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6356         MessageBox (GetFocus(),\r
6357                     "Unable to activate help",\r
6358                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6359       }\r
6360       break;\r
6361 \r
6362     default:\r
6363       SetStartupDialogEnables(hDlg);\r
6364       break;\r
6365     }\r
6366     break;\r
6367   }\r
6368   return FALSE;\r
6369 }\r
6370 \r
6371 /*---------------------------------------------------------------------------*\\r
6372  *\r
6373  * About box dialog functions\r
6374  *\r
6375 \*---------------------------------------------------------------------------*/\r
6376 \r
6377 /* Process messages for "About" dialog box */\r
6378 LRESULT CALLBACK\r
6379 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6380 {\r
6381   switch (message) {\r
6382   case WM_INITDIALOG: /* message: initialize dialog box */\r
6383     /* Center the dialog over the application window */\r
6384     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6385     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6386     return (TRUE);\r
6387 \r
6388   case WM_COMMAND: /* message: received a command */\r
6389     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6390         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6391       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6392       return (TRUE);\r
6393     }\r
6394     break;\r
6395   }\r
6396   return (FALSE);\r
6397 }\r
6398 \r
6399 /*---------------------------------------------------------------------------*\\r
6400  *\r
6401  * Comment Dialog functions\r
6402  *\r
6403 \*---------------------------------------------------------------------------*/\r
6404 \r
6405 LRESULT CALLBACK\r
6406 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6407 {\r
6408   static HANDLE hwndText = NULL;\r
6409   int len, newSizeX, newSizeY, flags;\r
6410   static int sizeX, sizeY;\r
6411   char *str;\r
6412   RECT rect;\r
6413   MINMAXINFO *mmi;\r
6414 \r
6415   switch (message) {\r
6416   case WM_INITDIALOG: /* message: initialize dialog box */\r
6417     /* Initialize the dialog items */\r
6418     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6419     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6420     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6421     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6422     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6423     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6424     SetWindowText(hDlg, commentTitle);\r
6425     if (editComment) {\r
6426       SetFocus(hwndText);\r
6427     } else {\r
6428       SetFocus(GetDlgItem(hDlg, IDOK));\r
6429     }\r
6430     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6431                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6432                 MAKELPARAM(FALSE, 0));\r
6433     /* Size and position the dialog */\r
6434     if (!commentDialog) {\r
6435       commentDialog = hDlg;\r
6436       flags = SWP_NOZORDER;\r
6437       GetClientRect(hDlg, &rect);\r
6438       sizeX = rect.right;\r
6439       sizeY = rect.bottom;\r
6440       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6441           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6442         WINDOWPLACEMENT wp;\r
6443         EnsureOnScreen(&commentX, &commentY);\r
6444         wp.length = sizeof(WINDOWPLACEMENT);\r
6445         wp.flags = 0;\r
6446         wp.showCmd = SW_SHOW;\r
6447         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6448         wp.rcNormalPosition.left = commentX;\r
6449         wp.rcNormalPosition.right = commentX + commentW;\r
6450         wp.rcNormalPosition.top = commentY;\r
6451         wp.rcNormalPosition.bottom = commentY + commentH;\r
6452         SetWindowPlacement(hDlg, &wp);\r
6453 \r
6454         GetClientRect(hDlg, &rect);\r
6455         newSizeX = rect.right;\r
6456         newSizeY = rect.bottom;\r
6457         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6458                               newSizeX, newSizeY);\r
6459         sizeX = newSizeX;\r
6460         sizeY = newSizeY;\r
6461       }\r
6462     }\r
6463     return FALSE;\r
6464 \r
6465   case WM_COMMAND: /* message: received a command */\r
6466     switch (LOWORD(wParam)) {\r
6467     case IDOK:\r
6468       if (editComment) {\r
6469         char *p, *q;\r
6470         /* Read changed options from the dialog box */\r
6471         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6472         len = GetWindowTextLength(hwndText);\r
6473         str = (char *) malloc(len + 1);\r
6474         GetWindowText(hwndText, str, len + 1);\r
6475         p = q = str;\r
6476         while (*q) {\r
6477           if (*q == '\r')\r
6478             q++;\r
6479           else\r
6480             *p++ = *q++;\r
6481         }\r
6482         *p = NULLCHAR;\r
6483         ReplaceComment(commentIndex, str);\r
6484         free(str);\r
6485       }\r
6486       CommentPopDown();\r
6487       return TRUE;\r
6488 \r
6489     case IDCANCEL:\r
6490     case OPT_CancelComment:\r
6491       CommentPopDown();\r
6492       return TRUE;\r
6493 \r
6494     case OPT_ClearComment:\r
6495       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6496       break;\r
6497 \r
6498     case OPT_EditComment:\r
6499       EditCommentEvent();\r
6500       return TRUE;\r
6501 \r
6502     default:\r
6503       break;\r
6504     }\r
6505     break;\r
6506 \r
6507   case WM_SIZE:\r
6508     newSizeX = LOWORD(lParam);\r
6509     newSizeY = HIWORD(lParam);\r
6510     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6511     sizeX = newSizeX;\r
6512     sizeY = newSizeY;\r
6513     break;\r
6514 \r
6515   case WM_GETMINMAXINFO:\r
6516     /* Prevent resizing window too small */\r
6517     mmi = (MINMAXINFO *) lParam;\r
6518     mmi->ptMinTrackSize.x = 100;\r
6519     mmi->ptMinTrackSize.y = 100;\r
6520     break;\r
6521   }\r
6522   return FALSE;\r
6523 }\r
6524 \r
6525 VOID\r
6526 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6527 {\r
6528   FARPROC lpProc;\r
6529   char *p, *q;\r
6530 \r
6531   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6532 \r
6533   if (str == NULL) str = "";\r
6534   p = (char *) malloc(2 * strlen(str) + 2);\r
6535   q = p;\r
6536   while (*str) {\r
6537     if (*str == '\n') *q++ = '\r';\r
6538     *q++ = *str++;\r
6539   }\r
6540   *q = NULLCHAR;\r
6541   if (commentText != NULL) free(commentText);\r
6542 \r
6543   commentIndex = index;\r
6544   commentTitle = title;\r
6545   commentText = p;\r
6546   editComment = edit;\r
6547 \r
6548   if (commentDialog) {\r
6549     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6550     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6551   } else {\r
6552     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6553     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6554                  hwndMain, (DLGPROC)lpProc);\r
6555     FreeProcInstance(lpProc);\r
6556   }\r
6557   commentDialogUp = TRUE;\r
6558 }\r
6559 \r
6560 \r
6561 /*---------------------------------------------------------------------------*\\r
6562  *\r
6563  * Type-in move dialog functions\r
6564  * \r
6565 \*---------------------------------------------------------------------------*/\r
6566 \r
6567 LRESULT CALLBACK\r
6568 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6569 {\r
6570   char move[MSG_SIZ];\r
6571   HWND hInput;\r
6572   ChessMove moveType;\r
6573   int fromX, fromY, toX, toY;\r
6574   char promoChar;\r
6575 \r
6576   switch (message) {\r
6577   case WM_INITDIALOG:\r
6578     move[0] = (char) lParam;\r
6579     move[1] = NULLCHAR;\r
6580     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6581     hInput = GetDlgItem(hDlg, OPT_Move);\r
6582     SetWindowText(hInput, move);\r
6583     SetFocus(hInput);\r
6584     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6585     return FALSE;\r
6586 \r
6587   case WM_COMMAND:\r
6588     switch (LOWORD(wParam)) {\r
6589     case IDOK:\r
6590       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6591         gameMode != Training) {\r
6592         DisplayMoveError("Displayed move is not current");\r
6593       } else {\r
6594         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6595         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6596           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6597           if (gameMode != Training)\r
6598               forwardMostMove = currentMove;\r
6599           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6600         } else {\r
6601           DisplayMoveError("Could not parse move");\r
6602         }\r
6603       }\r
6604       EndDialog(hDlg, TRUE);\r
6605       return TRUE;\r
6606     case IDCANCEL:\r
6607       EndDialog(hDlg, FALSE);\r
6608       return TRUE;\r
6609     default:\r
6610       break;\r
6611     }\r
6612     break;\r
6613   }\r
6614   return FALSE;\r
6615 }\r
6616 \r
6617 VOID\r
6618 PopUpMoveDialog(char firstchar)\r
6619 {\r
6620     FARPROC lpProc;\r
6621     \r
6622     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
6623         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
6624         gameMode == AnalyzeMode || gameMode == EditGame || \r
6625         gameMode == EditPosition || gameMode == IcsExamining ||\r
6626         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
6627         gameMode == Training) {\r
6628       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
6629       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
6630         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
6631       FreeProcInstance(lpProc);\r
6632     }\r
6633 }\r
6634 \r
6635 /*---------------------------------------------------------------------------*\\r
6636  *\r
6637  *  Error dialogs\r
6638  * \r
6639 \*---------------------------------------------------------------------------*/\r
6640 \r
6641 /* Nonmodal error box */\r
6642 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
6643                              WPARAM wParam, LPARAM lParam);\r
6644 \r
6645 VOID\r
6646 ErrorPopUp(char *title, char *content)\r
6647 {\r
6648   FARPROC lpProc;\r
6649   char *p, *q;\r
6650   BOOLEAN modal = hwndMain == NULL;\r
6651 \r
6652   p = content;\r
6653   q = errorMessage;\r
6654   while (*p) {\r
6655     if (*p == '\n') {\r
6656       if (modal) {\r
6657         *q++ = ' ';\r
6658         p++;\r
6659       } else {\r
6660         *q++ = '\r';\r
6661         *q++ = *p++;\r
6662       }\r
6663     } else {\r
6664       *q++ = *p++;\r
6665     }\r
6666   }\r
6667   *q = NULLCHAR;\r
6668   strncpy(errorTitle, title, sizeof(errorTitle));\r
6669   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6670   \r
6671   if (modal) {\r
6672     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
6673   } else {\r
6674     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6675     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6676                  hwndMain, (DLGPROC)lpProc);\r
6677     FreeProcInstance(lpProc);\r
6678   }\r
6679 }\r
6680 \r
6681 VOID\r
6682 ErrorPopDown()\r
6683 {\r
6684   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
6685   if (errorDialog == NULL) return;\r
6686   DestroyWindow(errorDialog);\r
6687   errorDialog = NULL;\r
6688 }\r
6689 \r
6690 LRESULT CALLBACK\r
6691 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6692 {\r
6693   HANDLE hwndText;\r
6694   RECT rChild;\r
6695 \r
6696   switch (message) {\r
6697   case WM_INITDIALOG:\r
6698     GetWindowRect(hDlg, &rChild);\r
6699 \r
6700     /*\r
6701     SetWindowPos(hDlg, NULL, rChild.left,\r
6702       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
6703       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6704     */\r
6705 \r
6706     /* \r
6707         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6708         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6709         and it doesn't work when you resize the dialog.\r
6710         For now, just give it a default position.\r
6711     */\r
6712     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
6713 \r
6714     errorDialog = hDlg;\r
6715     SetWindowText(hDlg, errorTitle);\r
6716     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6717     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6718     return FALSE;\r
6719 \r
6720   case WM_COMMAND:\r
6721     switch (LOWORD(wParam)) {\r
6722     case IDOK:\r
6723     case IDCANCEL:\r
6724       if (errorDialog == hDlg) errorDialog = NULL;\r
6725       DestroyWindow(hDlg);\r
6726       return TRUE;\r
6727 \r
6728     default:\r
6729       break;\r
6730     }\r
6731     break;\r
6732   }\r
6733   return FALSE;\r
6734 }\r
6735 \r
6736 #ifdef GOTHIC\r
6737 HWND gothicDialog = NULL;\r
6738 \r
6739 LRESULT CALLBACK\r
6740 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6741 {\r
6742   HANDLE hwndText;\r
6743   RECT rChild;\r
6744   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
6745 \r
6746   switch (message) {\r
6747   case WM_INITDIALOG:\r
6748     GetWindowRect(hDlg, &rChild);\r
6749 \r
6750     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
6751                                                              SWP_NOZORDER);\r
6752 \r
6753     /* \r
6754         [AS] It seems that the above code wants to move the dialog up in the "caption\r
6755         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
6756         and it doesn't work when you resize the dialog.\r
6757         For now, just give it a default position.\r
6758     */\r
6759     gothicDialog = hDlg;\r
6760     SetWindowText(hDlg, errorTitle);\r
6761     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
6762     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
6763     return FALSE;\r
6764 \r
6765   case WM_COMMAND:\r
6766     switch (LOWORD(wParam)) {\r
6767     case IDOK:\r
6768     case IDCANCEL:\r
6769       if (errorDialog == hDlg) errorDialog = NULL;\r
6770       DestroyWindow(hDlg);\r
6771       return TRUE;\r
6772 \r
6773     default:\r
6774       break;\r
6775     }\r
6776     break;\r
6777   }\r
6778   return FALSE;\r
6779 }\r
6780 \r
6781 VOID\r
6782 GothicPopUp(char *title, char up)\r
6783 {\r
6784   FARPROC lpProc;\r
6785   char *p, *q;\r
6786   BOOLEAN modal = hwndMain == NULL;\r
6787 \r
6788   strncpy(errorTitle, title, sizeof(errorTitle));\r
6789   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
6790 \r
6791   if(up && gothicDialog == NULL) {\r
6792     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
6793     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6794                  hwndMain, (DLGPROC)lpProc);\r
6795     FreeProcInstance(lpProc);\r
6796   } else if(!up && gothicDialog != NULL) {\r
6797     DestroyWindow(gothicDialog);\r
6798     gothicDialog = NULL;\r
6799   }\r
6800 }\r
6801 #endif\r
6802 \r
6803 /*---------------------------------------------------------------------------*\\r
6804  *\r
6805  *  Ics Interaction console functions\r
6806  *\r
6807 \*---------------------------------------------------------------------------*/\r
6808 \r
6809 #define HISTORY_SIZE 64\r
6810 static char *history[HISTORY_SIZE];\r
6811 int histIn = 0, histP = 0;\r
6812 \r
6813 VOID\r
6814 SaveInHistory(char *cmd)\r
6815 {\r
6816   if (history[histIn] != NULL) {\r
6817     free(history[histIn]);\r
6818     history[histIn] = NULL;\r
6819   }\r
6820   if (*cmd == NULLCHAR) return;\r
6821   history[histIn] = StrSave(cmd);\r
6822   histIn = (histIn + 1) % HISTORY_SIZE;\r
6823   if (history[histIn] != NULL) {\r
6824     free(history[histIn]);\r
6825     history[histIn] = NULL;\r
6826   }\r
6827   histP = histIn;\r
6828 }\r
6829 \r
6830 char *\r
6831 PrevInHistory(char *cmd)\r
6832 {\r
6833   int newhp;\r
6834   if (histP == histIn) {\r
6835     if (history[histIn] != NULL) free(history[histIn]);\r
6836     history[histIn] = StrSave(cmd);\r
6837   }\r
6838   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
6839   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
6840   histP = newhp;\r
6841   return history[histP];\r
6842 }\r
6843 \r
6844 char *\r
6845 NextInHistory()\r
6846 {\r
6847   if (histP == histIn) return NULL;\r
6848   histP = (histP + 1) % HISTORY_SIZE;\r
6849   return history[histP];\r
6850 }\r
6851 \r
6852 typedef struct {\r
6853   char *item;\r
6854   char *command;\r
6855   BOOLEAN getname;\r
6856   BOOLEAN immediate;\r
6857 } IcsTextMenuEntry;\r
6858 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
6859 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
6860 \r
6861 void\r
6862 ParseIcsTextMenu(char *icsTextMenuString)\r
6863 {\r
6864   int flags = 0;\r
6865   IcsTextMenuEntry *e = icsTextMenuEntry;\r
6866   char *p = icsTextMenuString;\r
6867   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6868     free(e->item);\r
6869     e->item = NULL;\r
6870     if (e->command != NULL) {\r
6871       free(e->command);\r
6872       e->command = NULL;\r
6873     }\r
6874     e++;\r
6875   }\r
6876   e = icsTextMenuEntry;\r
6877   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
6878     if (*p == ';' || *p == '\n') {\r
6879       e->item = strdup("-");\r
6880       e->command = NULL;\r
6881       p++;\r
6882     } else if (*p == '-') {\r
6883       e->item = strdup("-");\r
6884       e->command = NULL;\r
6885       p++;\r
6886       if (*p) p++;\r
6887     } else {\r
6888       char *q, *r, *s, *t;\r
6889       char c;\r
6890       q = strchr(p, ',');\r
6891       if (q == NULL) break;\r
6892       *q = NULLCHAR;\r
6893       r = strchr(q + 1, ',');\r
6894       if (r == NULL) break;\r
6895       *r = NULLCHAR;\r
6896       s = strchr(r + 1, ',');\r
6897       if (s == NULL) break;\r
6898       *s = NULLCHAR;\r
6899       c = ';';\r
6900       t = strchr(s + 1, c);\r
6901       if (t == NULL) {\r
6902         c = '\n';\r
6903         t = strchr(s + 1, c);\r
6904       }\r
6905       if (t != NULL) *t = NULLCHAR;\r
6906       e->item = strdup(p);\r
6907       e->command = strdup(q + 1);\r
6908       e->getname = *(r + 1) != '0';\r
6909       e->immediate = *(s + 1) != '0';\r
6910       *q = ',';\r
6911       *r = ',';\r
6912       *s = ',';\r
6913       if (t == NULL) break;\r
6914       *t = c;\r
6915       p = t + 1;\r
6916     }\r
6917     e++;\r
6918   } \r
6919 }\r
6920 \r
6921 HMENU\r
6922 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
6923 {\r
6924   HMENU hmenu, h;\r
6925   int i = 0;\r
6926   hmenu = LoadMenu(hInst, "TextMenu");\r
6927   h = GetSubMenu(hmenu, 0);\r
6928   while (e->item) {\r
6929     if (strcmp(e->item, "-") == 0) {\r
6930       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
6931     } else {\r
6932       if (e->item[0] == '|') {\r
6933         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
6934                    IDM_CommandX + i, &e->item[1]);\r
6935       } else {\r
6936         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
6937       }\r
6938     }\r
6939     e++;\r
6940     i++;\r
6941   } \r
6942   return hmenu;\r
6943 }\r
6944 \r
6945 WNDPROC consoleTextWindowProc;\r
6946 \r
6947 void\r
6948 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
6949 {\r
6950   char buf[MSG_SIZ], name[MSG_SIZ];\r
6951   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6952   CHARRANGE sel;\r
6953 \r
6954   if (!getname) {\r
6955     SetWindowText(hInput, command);\r
6956     if (immediate) {\r
6957       SendMessage(hInput, WM_CHAR, '\r', 0);\r
6958     } else {\r
6959       sel.cpMin = 999999;\r
6960       sel.cpMax = 999999;\r
6961       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
6962       SetFocus(hInput);\r
6963     }\r
6964     return;\r
6965   }    \r
6966   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
6967   if (sel.cpMin == sel.cpMax) {\r
6968     /* Expand to surrounding word */\r
6969     TEXTRANGE tr;\r
6970     do {\r
6971       tr.chrg.cpMax = sel.cpMin;\r
6972       tr.chrg.cpMin = --sel.cpMin;\r
6973       if (sel.cpMin < 0) break;\r
6974       tr.lpstrText = name;\r
6975       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6976     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6977     sel.cpMin++;\r
6978 \r
6979     do {\r
6980       tr.chrg.cpMin = sel.cpMax;\r
6981       tr.chrg.cpMax = ++sel.cpMax;\r
6982       tr.lpstrText = name;\r
6983       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
6984     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
6985     sel.cpMax--;\r
6986 \r
6987     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6988       MessageBeep(MB_ICONEXCLAMATION);\r
6989       return;\r
6990     }\r
6991     tr.chrg = sel;\r
6992     tr.lpstrText = name;\r
6993     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
6994   } else {\r
6995     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
6996       MessageBeep(MB_ICONEXCLAMATION);\r
6997       return;\r
6998     }\r
6999     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7000   }\r
7001   if (immediate) {\r
7002     sprintf(buf, "%s %s", command, name);\r
7003     SetWindowText(hInput, buf);\r
7004     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7005   } else {\r
7006     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7007     SetWindowText(hInput, buf);\r
7008     sel.cpMin = 999999;\r
7009     sel.cpMax = 999999;\r
7010     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7011     SetFocus(hInput);\r
7012   }\r
7013 }\r
7014 \r
7015 LRESULT CALLBACK \r
7016 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7017 {\r
7018   HWND hInput;\r
7019   CHARRANGE sel;\r
7020 \r
7021   switch (message) {\r
7022   case WM_KEYDOWN:\r
7023     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7024     switch (wParam) {\r
7025     case VK_PRIOR:\r
7026       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7027       return 0;\r
7028     case VK_NEXT:\r
7029       sel.cpMin = 999999;\r
7030       sel.cpMax = 999999;\r
7031       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7032       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7033       return 0;\r
7034     }\r
7035     break;\r
7036   case WM_CHAR:\r
7037     if (wParam == '\t') {\r
7038       if (GetKeyState(VK_SHIFT) < 0) {\r
7039         /* shifted */\r
7040         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7041         if (buttonDesc[0].hwnd) {\r
7042           SetFocus(buttonDesc[0].hwnd);\r
7043         } else {\r
7044           SetFocus(hwndMain);\r
7045         }\r
7046       } else {\r
7047         /* unshifted */\r
7048         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7049       }\r
7050     } else {\r
7051       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7052       SetFocus(hInput);\r
7053       SendMessage(hInput, message, wParam, lParam);\r
7054     }\r
7055     return 0;\r
7056   case WM_PASTE:\r
7057     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7058     SetFocus(hInput);\r
7059     return SendMessage(hInput, message, wParam, lParam);\r
7060   case WM_MBUTTONDOWN:\r
7061     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7062   case WM_RBUTTONDOWN:\r
7063     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7064       /* Move selection here if it was empty */\r
7065       POINT pt;\r
7066       pt.x = LOWORD(lParam);\r
7067       pt.y = HIWORD(lParam);\r
7068       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7069       if (sel.cpMin == sel.cpMax) {\r
7070         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7071         sel.cpMax = sel.cpMin;\r
7072         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7073       }\r
7074       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7075     }\r
7076     return 0;\r
7077   case WM_RBUTTONUP:\r
7078     if (GetKeyState(VK_SHIFT) & ~1) {\r
7079       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7080         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7081     } else {\r
7082       POINT pt;\r
7083       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7084       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7085       if (sel.cpMin == sel.cpMax) {\r
7086         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7087         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7088       }\r
7089       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7090         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7091       }\r
7092       pt.x = LOWORD(lParam);\r
7093       pt.y = HIWORD(lParam);\r
7094       MenuPopup(hwnd, pt, hmenu, -1);\r
7095     }\r
7096     return 0;\r
7097   case WM_COMMAND:\r
7098     switch (LOWORD(wParam)) {\r
7099     case IDM_QuickPaste:\r
7100       {\r
7101         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7102         if (sel.cpMin == sel.cpMax) {\r
7103           MessageBeep(MB_ICONEXCLAMATION);\r
7104           return 0;\r
7105         }\r
7106         SendMessage(hwnd, WM_COPY, 0, 0);\r
7107         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7108         SendMessage(hInput, WM_PASTE, 0, 0);\r
7109         SetFocus(hInput);\r
7110         return 0;\r
7111       }\r
7112     case IDM_Cut:\r
7113       SendMessage(hwnd, WM_CUT, 0, 0);\r
7114       return 0;\r
7115     case IDM_Paste:\r
7116       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7117       return 0;\r
7118     case IDM_Copy:\r
7119       SendMessage(hwnd, WM_COPY, 0, 0);\r
7120       return 0;\r
7121     default:\r
7122       {\r
7123         int i = LOWORD(wParam) - IDM_CommandX;\r
7124         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7125             icsTextMenuEntry[i].command != NULL) {\r
7126           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7127                    icsTextMenuEntry[i].getname,\r
7128                    icsTextMenuEntry[i].immediate);\r
7129           return 0;\r
7130         }\r
7131       }\r
7132       break;\r
7133     }\r
7134     break;\r
7135   }\r
7136   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7137 }\r
7138 \r
7139 WNDPROC consoleInputWindowProc;\r
7140 \r
7141 LRESULT CALLBACK\r
7142 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7143 {\r
7144   char buf[MSG_SIZ];\r
7145   char *p;\r
7146   static BOOL sendNextChar = FALSE;\r
7147   static BOOL quoteNextChar = FALSE;\r
7148   InputSource *is = consoleInputSource;\r
7149   CHARFORMAT cf;\r
7150   CHARRANGE sel;\r
7151 \r
7152   switch (message) {\r
7153   case WM_CHAR:\r
7154     if (!appData.localLineEditing || sendNextChar) {\r
7155       is->buf[0] = (CHAR) wParam;\r
7156       is->count = 1;\r
7157       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7158       sendNextChar = FALSE;\r
7159       return 0;\r
7160     }\r
7161     if (quoteNextChar) {\r
7162       buf[0] = (char) wParam;\r
7163       buf[1] = NULLCHAR;\r
7164       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7165       quoteNextChar = FALSE;\r
7166       return 0;\r
7167     }\r
7168     switch (wParam) {\r
7169     case '\r':   /* Enter key */\r
7170       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7171       if (consoleEcho) SaveInHistory(is->buf);\r
7172       is->buf[is->count++] = '\n';\r
7173       is->buf[is->count] = NULLCHAR;\r
7174       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7175       if (consoleEcho) {\r
7176         ConsoleOutput(is->buf, is->count, TRUE);\r
7177       } else if (appData.localLineEditing) {\r
7178         ConsoleOutput("\n", 1, TRUE);\r
7179       }\r
7180       /* fall thru */\r
7181     case '\033': /* Escape key */\r
7182       SetWindowText(hwnd, "");\r
7183       cf.cbSize = sizeof(CHARFORMAT);\r
7184       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7185       if (consoleEcho) {\r
7186         cf.crTextColor = textAttribs[ColorNormal].color;\r
7187       } else {\r
7188         cf.crTextColor = COLOR_ECHOOFF;\r
7189       }\r
7190       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7191       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7192       return 0;\r
7193     case '\t':   /* Tab key */\r
7194       if (GetKeyState(VK_SHIFT) < 0) {\r
7195         /* shifted */\r
7196         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7197       } else {\r
7198         /* unshifted */\r
7199         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7200         if (buttonDesc[0].hwnd) {\r
7201           SetFocus(buttonDesc[0].hwnd);\r
7202         } else {\r
7203           SetFocus(hwndMain);\r
7204         }\r
7205       }\r
7206       return 0;\r
7207     case '\023': /* Ctrl+S */\r
7208       sendNextChar = TRUE;\r
7209       return 0;\r
7210     case '\021': /* Ctrl+Q */\r
7211       quoteNextChar = TRUE;\r
7212       return 0;\r
7213     default:\r
7214       break;\r
7215     }\r
7216     break;\r
7217   case WM_KEYDOWN:\r
7218     switch (wParam) {\r
7219     case VK_UP:\r
7220       GetWindowText(hwnd, buf, MSG_SIZ);\r
7221       p = PrevInHistory(buf);\r
7222       if (p != NULL) {\r
7223         SetWindowText(hwnd, p);\r
7224         sel.cpMin = 999999;\r
7225         sel.cpMax = 999999;\r
7226         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7227         return 0;\r
7228       }\r
7229       break;\r
7230     case VK_DOWN:\r
7231       p = NextInHistory();\r
7232       if (p != NULL) {\r
7233         SetWindowText(hwnd, p);\r
7234         sel.cpMin = 999999;\r
7235         sel.cpMax = 999999;\r
7236         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7237         return 0;\r
7238       }\r
7239       break;\r
7240     case VK_HOME:\r
7241     case VK_END:\r
7242       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7243       /* fall thru */\r
7244     case VK_PRIOR:\r
7245     case VK_NEXT:\r
7246       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7247       return 0;\r
7248     }\r
7249     break;\r
7250   case WM_MBUTTONDOWN:\r
7251     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7252       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7253     break;\r
7254   case WM_RBUTTONUP:\r
7255     if (GetKeyState(VK_SHIFT) & ~1) {\r
7256       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7257         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7258     } else {\r
7259       POINT pt;\r
7260       HMENU hmenu;\r
7261       hmenu = LoadMenu(hInst, "InputMenu");\r
7262       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7263       if (sel.cpMin == sel.cpMax) {\r
7264         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7265         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7266       }\r
7267       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7268         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7269       }\r
7270       pt.x = LOWORD(lParam);\r
7271       pt.y = HIWORD(lParam);\r
7272       MenuPopup(hwnd, pt, hmenu, -1);\r
7273     }\r
7274     return 0;\r
7275   case WM_COMMAND:\r
7276     switch (LOWORD(wParam)) { \r
7277     case IDM_Undo:\r
7278       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7279       return 0;\r
7280     case IDM_SelectAll:\r
7281       sel.cpMin = 0;\r
7282       sel.cpMax = -1; /*999999?*/\r
7283       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7284       return 0;\r
7285     case IDM_Cut:\r
7286       SendMessage(hwnd, WM_CUT, 0, 0);\r
7287       return 0;\r
7288     case IDM_Paste:\r
7289       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7290       return 0;\r
7291     case IDM_Copy:\r
7292       SendMessage(hwnd, WM_COPY, 0, 0);\r
7293       return 0;\r
7294     }\r
7295     break;\r
7296   }\r
7297   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7298 }\r
7299 \r
7300 #define CO_MAX  100000\r
7301 #define CO_TRIM   1000\r
7302 \r
7303 LRESULT CALLBACK\r
7304 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7305 {\r
7306   static SnapData sd;\r
7307   static HWND hText, hInput, hFocus;\r
7308   InputSource *is = consoleInputSource;\r
7309   RECT rect;\r
7310   static int sizeX, sizeY;\r
7311   int newSizeX, newSizeY;\r
7312   MINMAXINFO *mmi;\r
7313 \r
7314   switch (message) {\r
7315   case WM_INITDIALOG: /* message: initialize dialog box */\r
7316     hwndConsole = hDlg;\r
7317     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7318     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7319     SetFocus(hInput);\r
7320     consoleTextWindowProc = (WNDPROC)\r
7321       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7322     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7323     consoleInputWindowProc = (WNDPROC)\r
7324       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7325     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7326     Colorize(ColorNormal, TRUE);\r
7327     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7328     ChangedConsoleFont();\r
7329     GetClientRect(hDlg, &rect);\r
7330     sizeX = rect.right;\r
7331     sizeY = rect.bottom;\r
7332     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7333         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7334       WINDOWPLACEMENT wp;\r
7335       EnsureOnScreen(&consoleX, &consoleY);\r
7336       wp.length = sizeof(WINDOWPLACEMENT);\r
7337       wp.flags = 0;\r
7338       wp.showCmd = SW_SHOW;\r
7339       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7340       wp.rcNormalPosition.left = consoleX;\r
7341       wp.rcNormalPosition.right = consoleX + consoleW;\r
7342       wp.rcNormalPosition.top = consoleY;\r
7343       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7344       SetWindowPlacement(hDlg, &wp);\r
7345     }\r
7346     return FALSE;\r
7347 \r
7348   case WM_SETFOCUS:\r
7349     SetFocus(hInput);\r
7350     return 0;\r
7351 \r
7352   case WM_CLOSE:\r
7353     ExitEvent(0);\r
7354     /* not reached */\r
7355     break;\r
7356 \r
7357   case WM_SIZE:\r
7358     if (IsIconic(hDlg)) break;\r
7359     newSizeX = LOWORD(lParam);\r
7360     newSizeY = HIWORD(lParam);\r
7361     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7362       RECT rectText, rectInput;\r
7363       POINT pt;\r
7364       int newTextHeight, newTextWidth;\r
7365       GetWindowRect(hText, &rectText);\r
7366       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7367       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7368       if (newTextHeight < 0) {\r
7369         newSizeY += -newTextHeight;\r
7370         newTextHeight = 0;\r
7371       }\r
7372       SetWindowPos(hText, NULL, 0, 0,\r
7373         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7374       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7375       pt.x = rectInput.left;\r
7376       pt.y = rectInput.top + newSizeY - sizeY;\r
7377       ScreenToClient(hDlg, &pt);\r
7378       SetWindowPos(hInput, NULL, \r
7379         pt.x, pt.y, /* needs client coords */   \r
7380         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7381         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7382     }\r
7383     sizeX = newSizeX;\r
7384     sizeY = newSizeY;\r
7385     break;\r
7386 \r
7387   case WM_GETMINMAXINFO:\r
7388     /* Prevent resizing window too small */\r
7389     mmi = (MINMAXINFO *) lParam;\r
7390     mmi->ptMinTrackSize.x = 100;\r
7391     mmi->ptMinTrackSize.y = 100;\r
7392     break;\r
7393 \r
7394   /* [AS] Snapping */\r
7395   case WM_ENTERSIZEMOVE:\r
7396     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7397 \r
7398   case WM_SIZING:\r
7399     return OnSizing( &sd, hDlg, wParam, lParam );\r
7400 \r
7401   case WM_MOVING:\r
7402     return OnMoving( &sd, hDlg, wParam, lParam );\r
7403 \r
7404   case WM_EXITSIZEMOVE:\r
7405     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7406   }\r
7407 \r
7408   return DefWindowProc(hDlg, message, wParam, lParam);\r
7409 }\r
7410 \r
7411 \r
7412 VOID\r
7413 ConsoleCreate()\r
7414 {\r
7415   HWND hCons;\r
7416   if (hwndConsole) return;\r
7417   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7418   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7419 }\r
7420 \r
7421 \r
7422 VOID\r
7423 ConsoleOutput(char* data, int length, int forceVisible)\r
7424 {\r
7425   HWND hText;\r
7426   int trim, exlen;\r
7427   char *p, *q;\r
7428   char buf[CO_MAX+1];\r
7429   POINT pEnd;\r
7430   RECT rect;\r
7431   static int delayLF = 0;\r
7432   CHARRANGE savesel, sel;\r
7433 \r
7434   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7435   p = data;\r
7436   q = buf;\r
7437   if (delayLF) {\r
7438     *q++ = '\r';\r
7439     *q++ = '\n';\r
7440     delayLF = 0;\r
7441   }\r
7442   while (length--) {\r
7443     if (*p == '\n') {\r
7444       if (*++p) {\r
7445         *q++ = '\r';\r
7446         *q++ = '\n';\r
7447       } else {\r
7448         delayLF = 1;\r
7449       }\r
7450     } else if (*p == '\007') {\r
7451        MyPlaySound(&sounds[(int)SoundBell]);\r
7452        p++;\r
7453     } else {\r
7454       *q++ = *p++;\r
7455     }\r
7456   }\r
7457   *q = NULLCHAR;\r
7458   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7459   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7460   /* Save current selection */\r
7461   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7462   exlen = GetWindowTextLength(hText);\r
7463   /* Find out whether current end of text is visible */\r
7464   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7465   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7466   /* Trim existing text if it's too long */\r
7467   if (exlen + (q - buf) > CO_MAX) {\r
7468     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7469     sel.cpMin = 0;\r
7470     sel.cpMax = trim;\r
7471     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7472     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7473     exlen -= trim;\r
7474     savesel.cpMin -= trim;\r
7475     savesel.cpMax -= trim;\r
7476     if (exlen < 0) exlen = 0;\r
7477     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7478     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7479   }\r
7480   /* Append the new text */\r
7481   sel.cpMin = exlen;\r
7482   sel.cpMax = exlen;\r
7483   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7484   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7485   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7486   if (forceVisible || exlen == 0 ||\r
7487       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7488        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7489     /* Scroll to make new end of text visible if old end of text\r
7490        was visible or new text is an echo of user typein */\r
7491     sel.cpMin = 9999999;\r
7492     sel.cpMax = 9999999;\r
7493     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7494     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7495     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7496     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7497   }\r
7498   if (savesel.cpMax == exlen || forceVisible) {\r
7499     /* Move insert point to new end of text if it was at the old\r
7500        end of text or if the new text is an echo of user typein */\r
7501     sel.cpMin = 9999999;\r
7502     sel.cpMax = 9999999;\r
7503     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7504   } else {\r
7505     /* Restore previous selection */\r
7506     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7507   }\r
7508   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7509 }\r
7510 \r
7511 /*---------*/\r
7512 \r
7513 \r
7514 void\r
7515 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7516 {\r
7517   char buf[100];\r
7518   char *str;\r
7519   COLORREF oldFg, oldBg;\r
7520   HFONT oldFont;\r
7521   RECT rect;\r
7522 \r
7523   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
7524 \r
7525   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7526   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7527   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7528 \r
7529   rect.left = x;\r
7530   rect.right = x + squareSize;\r
7531   rect.top  = y;\r
7532   rect.bottom = y + squareSize;\r
7533   str = buf;\r
7534 \r
7535   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
7536                     + (rightAlign ? (squareSize*2)/3 : 0),\r
7537              y, ETO_CLIPPED|ETO_OPAQUE,\r
7538              &rect, str, strlen(str), NULL);\r
7539 \r
7540   (void) SetTextColor(hdc, oldFg);\r
7541   (void) SetBkColor(hdc, oldBg);\r
7542   (void) SelectObject(hdc, oldFont);\r
7543 }\r
7544 \r
7545 void\r
7546 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
7547               RECT *rect, char *color, char *flagFell)\r
7548 {\r
7549   char buf[100];\r
7550   char *str;\r
7551   COLORREF oldFg, oldBg;\r
7552   HFONT oldFont;\r
7553 \r
7554   if (appData.clockMode) {\r
7555     if (tinyLayout)\r
7556       sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell);\r
7557     else\r
7558       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
7559     str = buf;\r
7560   } else {\r
7561     str = color;\r
7562   }\r
7563 \r
7564   if (highlight) {\r
7565     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
7566     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
7567   } else {\r
7568     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
7569     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
7570   }\r
7571   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
7572 \r
7573   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
7574              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
7575              rect, str, strlen(str), NULL);\r
7576 \r
7577   (void) SetTextColor(hdc, oldFg);\r
7578   (void) SetBkColor(hdc, oldBg);\r
7579   (void) SelectObject(hdc, oldFont);\r
7580 }\r
7581 \r
7582 \r
7583 int\r
7584 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7585            OVERLAPPED *ovl)\r
7586 {\r
7587   int ok, err;\r
7588 \r
7589   /* [AS]  */\r
7590   if( count <= 0 ) {\r
7591     if (appData.debugMode) {\r
7592       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
7593     }\r
7594 \r
7595     return ERROR_INVALID_USER_BUFFER;\r
7596   }\r
7597 \r
7598   ResetEvent(ovl->hEvent);\r
7599   ovl->Offset = ovl->OffsetHigh = 0;\r
7600   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
7601   if (ok) {\r
7602     err = NO_ERROR;\r
7603   } else {\r
7604     err = GetLastError();\r
7605     if (err == ERROR_IO_PENDING) {\r
7606       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7607       if (ok)\r
7608         err = NO_ERROR;\r
7609       else\r
7610         err = GetLastError();\r
7611     }\r
7612   }\r
7613   return err;\r
7614 }\r
7615 \r
7616 int\r
7617 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
7618             OVERLAPPED *ovl)\r
7619 {\r
7620   int ok, err;\r
7621 \r
7622   ResetEvent(ovl->hEvent);\r
7623   ovl->Offset = ovl->OffsetHigh = 0;\r
7624   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
7625   if (ok) {\r
7626     err = NO_ERROR;\r
7627   } else {\r
7628     err = GetLastError();\r
7629     if (err == ERROR_IO_PENDING) {\r
7630       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
7631       if (ok)\r
7632         err = NO_ERROR;\r
7633       else\r
7634         err = GetLastError();\r
7635     }\r
7636   }\r
7637   return err;\r
7638 }\r
7639 \r
7640 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
7641 void CheckForInputBufferFull( InputSource * is )\r
7642 {\r
7643     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
7644         /* Look for end of line */\r
7645         char * p = is->buf;\r
7646         \r
7647         while( p < is->next && *p != '\n' ) {\r
7648             p++;\r
7649         }\r
7650 \r
7651         if( p >= is->next ) {\r
7652             if (appData.debugMode) {\r
7653                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
7654             }\r
7655 \r
7656             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
7657             is->count = (DWORD) -1;\r
7658             is->next = is->buf;\r
7659         }\r
7660     }\r
7661 }\r
7662 \r
7663 DWORD\r
7664 InputThread(LPVOID arg)\r
7665 {\r
7666   InputSource *is;\r
7667   OVERLAPPED ovl;\r
7668 \r
7669   is = (InputSource *) arg;\r
7670   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7671   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7672   while (is->hThread != NULL) {\r
7673     is->error = DoReadFile(is->hFile, is->next,\r
7674                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7675                            &is->count, &ovl);\r
7676     if (is->error == NO_ERROR) {\r
7677       is->next += is->count;\r
7678     } else {\r
7679       if (is->error == ERROR_BROKEN_PIPE) {\r
7680         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7681         is->count = 0;\r
7682       } else {\r
7683         is->count = (DWORD) -1;\r
7684         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
7685         break; \r
7686       }\r
7687     }\r
7688 \r
7689     CheckForInputBufferFull( is );\r
7690 \r
7691     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7692 \r
7693     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7694 \r
7695     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7696   }\r
7697 \r
7698   CloseHandle(ovl.hEvent);\r
7699   CloseHandle(is->hFile);\r
7700 \r
7701   if (appData.debugMode) {\r
7702     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
7703   }\r
7704 \r
7705   return 0;\r
7706 }\r
7707 \r
7708 \r
7709 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
7710 DWORD\r
7711 NonOvlInputThread(LPVOID arg)\r
7712 {\r
7713   InputSource *is;\r
7714   char *p, *q;\r
7715   int i;\r
7716   char prev;\r
7717 \r
7718   is = (InputSource *) arg;\r
7719   while (is->hThread != NULL) {\r
7720     is->error = ReadFile(is->hFile, is->next,\r
7721                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
7722                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
7723     if (is->error == NO_ERROR) {\r
7724       /* Change CRLF to LF */\r
7725       if (is->next > is->buf) {\r
7726         p = is->next - 1;\r
7727         i = is->count + 1;\r
7728       } else {\r
7729         p = is->next;\r
7730         i = is->count;\r
7731       }\r
7732       q = p;\r
7733       prev = NULLCHAR;\r
7734       while (i > 0) {\r
7735         if (prev == '\r' && *p == '\n') {\r
7736           *(q-1) = '\n';\r
7737           is->count--;\r
7738         } else { \r
7739           *q++ = *p;\r
7740         }\r
7741         prev = *p++;\r
7742         i--;\r
7743       }\r
7744       *q = NULLCHAR;\r
7745       is->next = q;\r
7746     } else {\r
7747       if (is->error == ERROR_BROKEN_PIPE) {\r
7748         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
7749         is->count = 0; \r
7750       } else {\r
7751         is->count = (DWORD) -1;\r
7752       }\r
7753     }\r
7754 \r
7755     CheckForInputBufferFull( is );\r
7756 \r
7757     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7758 \r
7759     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7760 \r
7761     if (is->count < 0) break;  /* Quit on error */\r
7762   }\r
7763   CloseHandle(is->hFile);\r
7764   return 0;\r
7765 }\r
7766 \r
7767 DWORD\r
7768 SocketInputThread(LPVOID arg)\r
7769 {\r
7770   InputSource *is;\r
7771 \r
7772   is = (InputSource *) arg;\r
7773   while (is->hThread != NULL) {\r
7774     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
7775     if ((int)is->count == SOCKET_ERROR) {\r
7776       is->count = (DWORD) -1;\r
7777       is->error = WSAGetLastError();\r
7778     } else {\r
7779       is->error = NO_ERROR;\r
7780       is->next += is->count;\r
7781       if (is->count == 0 && is->second == is) {\r
7782         /* End of file on stderr; quit with no message */\r
7783         break;\r
7784       }\r
7785     }\r
7786     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7787 \r
7788     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
7789 \r
7790     if (is->count <= 0) break;  /* Quit on EOF or error */\r
7791   }\r
7792   return 0;\r
7793 }\r
7794 \r
7795 VOID\r
7796 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7797 {\r
7798   InputSource *is;\r
7799 \r
7800   is = (InputSource *) lParam;\r
7801   if (is->lineByLine) {\r
7802     /* Feed in lines one by one */\r
7803     char *p = is->buf;\r
7804     char *q = p;\r
7805     while (q < is->next) {\r
7806       if (*q++ == '\n') {\r
7807         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
7808         p = q;\r
7809       }\r
7810     }\r
7811     \r
7812     /* Move any partial line to the start of the buffer */\r
7813     q = is->buf;\r
7814     while (p < is->next) {\r
7815       *q++ = *p++;\r
7816     }\r
7817     is->next = q;\r
7818 \r
7819     if (is->error != NO_ERROR || is->count == 0) {\r
7820       /* Notify backend of the error.  Note: If there was a partial\r
7821          line at the end, it is not flushed through. */\r
7822       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
7823     }\r
7824   } else {\r
7825     /* Feed in the whole chunk of input at once */\r
7826     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
7827     is->next = is->buf;\r
7828   }\r
7829 }\r
7830 \r
7831 /*---------------------------------------------------------------------------*\\r
7832  *\r
7833  *  Menu enables. Used when setting various modes.\r
7834  *\r
7835 \*---------------------------------------------------------------------------*/\r
7836 \r
7837 typedef struct {\r
7838   int item;\r
7839   int flags;\r
7840 } Enables;\r
7841 \r
7842 VOID\r
7843 SetMenuEnables(HMENU hmenu, Enables *enab)\r
7844 {\r
7845   while (enab->item > 0) {\r
7846     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
7847     enab++;\r
7848   }\r
7849 }\r
7850 \r
7851 Enables gnuEnables[] = {\r
7852   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7853   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7854   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7855   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
7856   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
7857   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
7858   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7859   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
7860   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
7861   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7862   { -1, -1 }\r
7863 };\r
7864 \r
7865 Enables icsEnables[] = {\r
7866   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7867   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7868   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7869   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7870   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7871   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7872   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7873   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7874   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7875   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7876   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7877   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
7878   { -1, -1 }\r
7879 };\r
7880 \r
7881 #ifdef ZIPPY\r
7882 Enables zippyEnables[] = {\r
7883   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7884   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
7885   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
7886   { -1, -1 }\r
7887 };\r
7888 #endif\r
7889 \r
7890 Enables ncpEnables[] = {\r
7891   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
7892   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
7893   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7894   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7895   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7896   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
7897   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
7898   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
7899   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
7900   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
7901   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7902   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7903   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
7904   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
7905   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
7906   { -1, -1 }\r
7907 };\r
7908 \r
7909 Enables trainingOnEnables[] = {\r
7910   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
7911   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
7912   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
7913   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
7914   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
7915   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
7916   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
7917   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
7918   { -1, -1 }\r
7919 };\r
7920 \r
7921 Enables trainingOffEnables[] = {\r
7922   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
7923   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
7924   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
7925   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
7926   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
7927   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
7928   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
7929   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
7930   { -1, -1 }\r
7931 };\r
7932 \r
7933 /* These modify either ncpEnables or gnuEnables */\r
7934 Enables cmailEnables[] = {\r
7935   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
7936   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
7937   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
7938   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
7939   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
7940   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
7941   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
7942   { -1, -1 }\r
7943 };\r
7944 \r
7945 Enables machineThinkingEnables[] = {\r
7946   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
7947   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
7948   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
7949   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
7950   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
7951   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7952   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
7953   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
7954   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
7955   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
7956   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
7957   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
7958   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
7959   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
7960   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
7961   { -1, -1 }\r
7962 };\r
7963 \r
7964 Enables userThinkingEnables[] = {\r
7965   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
7966   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
7967   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
7968   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
7969   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
7970   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7971   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
7972   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
7973   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
7974   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
7975   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
7976   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
7977   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
7978   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
7979   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
7980   { -1, -1 }\r
7981 };\r
7982 \r
7983 /*---------------------------------------------------------------------------*\\r
7984  *\r
7985  *  Front-end interface functions exported by XBoard.\r
7986  *  Functions appear in same order as prototypes in frontend.h.\r
7987  * \r
7988 \*---------------------------------------------------------------------------*/\r
7989 VOID\r
7990 ModeHighlight()\r
7991 {\r
7992   static UINT prevChecked = 0;\r
7993   static int prevPausing = 0;\r
7994   UINT nowChecked;\r
7995 \r
7996   if (pausing != prevPausing) {\r
7997     prevPausing = pausing;\r
7998     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
7999                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8000     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8001   }\r
8002 \r
8003   switch (gameMode) {\r
8004   case BeginningOfGame:\r
8005     if (appData.icsActive)\r
8006       nowChecked = IDM_IcsClient;\r
8007     else if (appData.noChessProgram)\r
8008       nowChecked = IDM_EditGame;\r
8009     else\r
8010       nowChecked = IDM_MachineBlack;\r
8011     break;\r
8012   case MachinePlaysBlack:\r
8013     nowChecked = IDM_MachineBlack;\r
8014     break;\r
8015   case MachinePlaysWhite:\r
8016     nowChecked = IDM_MachineWhite;\r
8017     break;\r
8018   case TwoMachinesPlay:\r
8019     nowChecked = IDM_TwoMachines;\r
8020     break;\r
8021   case AnalyzeMode:\r
8022     nowChecked = IDM_AnalysisMode;\r
8023     break;\r
8024   case AnalyzeFile:\r
8025     nowChecked = IDM_AnalyzeFile;\r
8026     break;\r
8027   case EditGame:\r
8028     nowChecked = IDM_EditGame;\r
8029     break;\r
8030   case PlayFromGameFile:\r
8031     nowChecked = IDM_LoadGame;\r
8032     break;\r
8033   case EditPosition:\r
8034     nowChecked = IDM_EditPosition;\r
8035     break;\r
8036   case Training:\r
8037     nowChecked = IDM_Training;\r
8038     break;\r
8039   case IcsPlayingWhite:\r
8040   case IcsPlayingBlack:\r
8041   case IcsObserving:\r
8042   case IcsIdle:\r
8043     nowChecked = IDM_IcsClient;\r
8044     break;\r
8045   default:\r
8046   case EndOfGame:\r
8047     nowChecked = 0;\r
8048     break;\r
8049   }\r
8050   if (prevChecked != 0)\r
8051     (void) CheckMenuItem(GetMenu(hwndMain),\r
8052                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8053   if (nowChecked != 0)\r
8054     (void) CheckMenuItem(GetMenu(hwndMain),\r
8055                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8056 \r
8057   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8058     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8059                           MF_BYCOMMAND|MF_ENABLED);\r
8060   } else {\r
8061     (void) EnableMenuItem(GetMenu(hwndMain), \r
8062                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8063   }\r
8064 \r
8065   prevChecked = nowChecked;\r
8066 }\r
8067 \r
8068 VOID\r
8069 SetICSMode()\r
8070 {\r
8071   HMENU hmenu = GetMenu(hwndMain);\r
8072   SetMenuEnables(hmenu, icsEnables);\r
8073   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8074     MF_BYPOSITION|MF_ENABLED);\r
8075 #ifdef ZIPPY\r
8076   if (appData.zippyPlay) {\r
8077     SetMenuEnables(hmenu, zippyEnables);\r
8078   }\r
8079 #endif\r
8080 }\r
8081 \r
8082 VOID\r
8083 SetGNUMode()\r
8084 {\r
8085   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8086 }\r
8087 \r
8088 VOID\r
8089 SetNCPMode()\r
8090 {\r
8091   HMENU hmenu = GetMenu(hwndMain);\r
8092   SetMenuEnables(hmenu, ncpEnables);\r
8093   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8094     MF_BYPOSITION|MF_GRAYED);\r
8095     DrawMenuBar(hwndMain);\r
8096 }\r
8097 \r
8098 VOID\r
8099 SetCmailMode()\r
8100 {\r
8101   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8102 }\r
8103 \r
8104 VOID \r
8105 SetTrainingModeOn()\r
8106 {\r
8107   int i;\r
8108   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8109   for (i = 0; i < N_BUTTONS; i++) {\r
8110     if (buttonDesc[i].hwnd != NULL)\r
8111       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8112   }\r
8113   CommentPopDown();\r
8114 }\r
8115 \r
8116 VOID SetTrainingModeOff()\r
8117 {\r
8118   int i;\r
8119   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8120   for (i = 0; i < N_BUTTONS; i++) {\r
8121     if (buttonDesc[i].hwnd != NULL)\r
8122       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8123   }\r
8124 }\r
8125 \r
8126 \r
8127 VOID\r
8128 SetUserThinkingEnables()\r
8129 {\r
8130   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8131 }\r
8132 \r
8133 VOID\r
8134 SetMachineThinkingEnables()\r
8135 {\r
8136   HMENU hMenu = GetMenu(hwndMain);\r
8137   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8138 \r
8139   SetMenuEnables(hMenu, machineThinkingEnables);\r
8140 \r
8141   if (gameMode == MachinePlaysBlack) {\r
8142     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8143   } else if (gameMode == MachinePlaysWhite) {\r
8144     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8145   } else if (gameMode == TwoMachinesPlay) {\r
8146     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8147   }\r
8148 }\r
8149 \r
8150 \r
8151 VOID\r
8152 DisplayTitle(char *str)\r
8153 {\r
8154   char title[MSG_SIZ], *host;\r
8155   if (str[0] != NULLCHAR) {\r
8156     strcpy(title, str);\r
8157   } else if (appData.icsActive) {\r
8158     if (appData.icsCommPort[0] != NULLCHAR)\r
8159       host = "ICS";\r
8160     else \r
8161       host = appData.icsHost;\r
8162     sprintf(title, "%s: %s", szTitle, host);\r
8163   } else if (appData.noChessProgram) {\r
8164     strcpy(title, szTitle);\r
8165   } else {\r
8166     strcpy(title, szTitle);\r
8167     strcat(title, ": ");\r
8168     strcat(title, first.tidy);\r
8169   }\r
8170   SetWindowText(hwndMain, title);\r
8171 }\r
8172 \r
8173 \r
8174 VOID\r
8175 DisplayMessage(char *str1, char *str2)\r
8176 {\r
8177   HDC hdc;\r
8178   HFONT oldFont;\r
8179   int remain = MESSAGE_TEXT_MAX - 1;\r
8180   int len;\r
8181 \r
8182   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8183   messageText[0] = NULLCHAR;\r
8184   if (*str1) {\r
8185     len = strlen(str1);\r
8186     if (len > remain) len = remain;\r
8187     strncpy(messageText, str1, len);\r
8188     messageText[len] = NULLCHAR;\r
8189     remain -= len;\r
8190   }\r
8191   if (*str2 && remain >= 2) {\r
8192     if (*str1) {\r
8193       strcat(messageText, "  ");\r
8194       remain -= 2;\r
8195     }\r
8196     len = strlen(str2);\r
8197     if (len > remain) len = remain;\r
8198     strncat(messageText, str2, len);\r
8199   }\r
8200   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8201 \r
8202   if (IsIconic(hwndMain)) return;\r
8203   hdc = GetDC(hwndMain);\r
8204   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8205   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8206              &messageRect, messageText, strlen(messageText), NULL);\r
8207   (void) SelectObject(hdc, oldFont);\r
8208   (void) ReleaseDC(hwndMain, hdc);\r
8209 }\r
8210 \r
8211 VOID\r
8212 DisplayError(char *str, int error)\r
8213 {\r
8214   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8215   int len;\r
8216 \r
8217   if (error == 0) {\r
8218     strcpy(buf, str);\r
8219   } else {\r
8220     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8221                         NULL, error, LANG_NEUTRAL,\r
8222                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8223     if (len > 0) {\r
8224       sprintf(buf, "%s:\n%s", str, buf2);\r
8225     } else {\r
8226       ErrorMap *em = errmap;\r
8227       while (em->err != 0 && em->err != error) em++;\r
8228       if (em->err != 0) {\r
8229         sprintf(buf, "%s:\n%s", str, em->msg);\r
8230       } else {\r
8231         sprintf(buf, "%s:\nError code %d", str, error);\r
8232       }\r
8233     }\r
8234   }\r
8235   \r
8236   ErrorPopUp("Error", buf);\r
8237 }\r
8238 \r
8239 \r
8240 VOID\r
8241 DisplayMoveError(char *str)\r
8242 {\r
8243   fromX = fromY = -1;\r
8244   ClearHighlights();\r
8245   DrawPosition(FALSE, NULL);\r
8246   if (appData.popupMoveErrors) {\r
8247     ErrorPopUp("Error", str);\r
8248   } else {\r
8249     DisplayMessage(str, "");\r
8250     moveErrorMessageUp = TRUE;\r
8251   }\r
8252 }\r
8253 \r
8254 VOID\r
8255 DisplayFatalError(char *str, int error, int exitStatus)\r
8256 {\r
8257   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8258   int len;\r
8259   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8260 \r
8261   if (error != 0) {\r
8262     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8263                         NULL, error, LANG_NEUTRAL,\r
8264                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8265     if (len > 0) {\r
8266       sprintf(buf, "%s:\n%s", str, buf2);\r
8267     } else {\r
8268       ErrorMap *em = errmap;\r
8269       while (em->err != 0 && em->err != error) em++;\r
8270       if (em->err != 0) {\r
8271         sprintf(buf, "%s:\n%s", str, em->msg);\r
8272       } else {\r
8273         sprintf(buf, "%s:\nError code %d", str, error);\r
8274       }\r
8275     }\r
8276     str = buf;\r
8277   }\r
8278   if (appData.debugMode) {\r
8279     fprintf(debugFP, "%s: %s\n", label, str);\r
8280   }\r
8281   if (appData.popupExitMessage) {\r
8282     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8283                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8284   }\r
8285   ExitEvent(exitStatus);\r
8286 }\r
8287 \r
8288 \r
8289 VOID\r
8290 DisplayInformation(char *str)\r
8291 {\r
8292   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8293 }\r
8294 \r
8295 \r
8296 VOID\r
8297 DisplayNote(char *str)\r
8298 {\r
8299   ErrorPopUp("Note", str);\r
8300 }\r
8301 \r
8302 \r
8303 typedef struct {\r
8304   char *title, *question, *replyPrefix;\r
8305   ProcRef pr;\r
8306 } QuestionParams;\r
8307 \r
8308 LRESULT CALLBACK\r
8309 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8310 {\r
8311   static QuestionParams *qp;\r
8312   char reply[MSG_SIZ];\r
8313   int len, err;\r
8314 \r
8315   switch (message) {\r
8316   case WM_INITDIALOG:\r
8317     qp = (QuestionParams *) lParam;\r
8318     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8319     SetWindowText(hDlg, qp->title);\r
8320     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8321     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8322     return FALSE;\r
8323 \r
8324   case WM_COMMAND:\r
8325     switch (LOWORD(wParam)) {\r
8326     case IDOK:\r
8327       strcpy(reply, qp->replyPrefix);\r
8328       if (*reply) strcat(reply, " ");\r
8329       len = strlen(reply);\r
8330       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8331       strcat(reply, "\n");\r
8332       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8333       EndDialog(hDlg, TRUE);\r
8334       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8335       return TRUE;\r
8336     case IDCANCEL:\r
8337       EndDialog(hDlg, FALSE);\r
8338       return TRUE;\r
8339     default:\r
8340       break;\r
8341     }\r
8342     break;\r
8343   }\r
8344   return FALSE;\r
8345 }\r
8346 \r
8347 VOID\r
8348 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8349 {\r
8350     QuestionParams qp;\r
8351     FARPROC lpProc;\r
8352     \r
8353     qp.title = title;\r
8354     qp.question = question;\r
8355     qp.replyPrefix = replyPrefix;\r
8356     qp.pr = pr;\r
8357     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8358     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8359       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8360     FreeProcInstance(lpProc);\r
8361 }\r
8362 \r
8363 /* [AS] Pick FRC position */\r
8364 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8365 {\r
8366     static int * lpIndexFRC;\r
8367     BOOL index_is_ok;\r
8368     char buf[16];\r
8369 \r
8370     switch( message )\r
8371     {\r
8372     case WM_INITDIALOG:\r
8373         lpIndexFRC = (int *) lParam;\r
8374 \r
8375         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8376 \r
8377         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8378         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8379         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8380         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8381 \r
8382         break;\r
8383 \r
8384     case WM_COMMAND:\r
8385         switch( LOWORD(wParam) ) {\r
8386         case IDOK:\r
8387             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8388             EndDialog( hDlg, 0 );\r
8389             return TRUE;\r
8390         case IDCANCEL:\r
8391             EndDialog( hDlg, 1 );   \r
8392             return TRUE;\r
8393         case IDC_NFG_Edit:\r
8394             if( HIWORD(wParam) == EN_CHANGE ) {\r
8395                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8396 \r
8397                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8398             }\r
8399             return TRUE;\r
8400         case IDC_NFG_Random:\r
8401             sprintf( buf, "%d", myrandom() % 960 );\r
8402             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8403             return TRUE;\r
8404         }\r
8405 \r
8406         break;\r
8407     }\r
8408 \r
8409     return FALSE;\r
8410 }\r
8411 \r
8412 int NewGameFRC()\r
8413 {\r
8414     int result;\r
8415     int index = appData.defaultFrcPosition;\r
8416     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8417 \r
8418     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8419 \r
8420     if( result == 0 ) {\r
8421         appData.defaultFrcPosition = index;\r
8422     }\r
8423 \r
8424     return result;\r
8425 }\r
8426 \r
8427 /* [AS] Game list options */\r
8428 typedef struct {\r
8429     char id;\r
8430     char * name;\r
8431 } GLT_Item;\r
8432 \r
8433 static GLT_Item GLT_ItemInfo[] = {\r
8434     { GLT_EVENT,      "Event" },\r
8435     { GLT_SITE,       "Site" },\r
8436     { GLT_DATE,       "Date" },\r
8437     { GLT_ROUND,      "Round" },\r
8438     { GLT_PLAYERS,    "Players" },\r
8439     { GLT_RESULT,     "Result" },\r
8440     { GLT_WHITE_ELO,  "White Rating" },\r
8441     { GLT_BLACK_ELO,  "Black Rating" },\r
8442     { GLT_TIME_CONTROL,"Time Control" },\r
8443     { GLT_VARIANT,    "Variant" },\r
8444     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8445     { 0, 0 }\r
8446 };\r
8447 \r
8448 const char * GLT_FindItem( char id )\r
8449 {\r
8450     const char * result = 0;\r
8451 \r
8452     GLT_Item * list = GLT_ItemInfo;\r
8453 \r
8454     while( list->id != 0 ) {\r
8455         if( list->id == id ) {\r
8456             result = list->name;\r
8457             break;\r
8458         }\r
8459 \r
8460         list++;\r
8461     }\r
8462 \r
8463     return result;\r
8464 }\r
8465 \r
8466 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8467 {\r
8468     const char * name = GLT_FindItem( id );\r
8469 \r
8470     if( name != 0 ) {\r
8471         if( index >= 0 ) {\r
8472             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8473         }\r
8474         else {\r
8475             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8476         }\r
8477     }\r
8478 }\r
8479 \r
8480 void GLT_TagsToList( HWND hDlg, char * tags )\r
8481 {\r
8482     char * pc = tags;\r
8483 \r
8484     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8485 \r
8486     while( *pc ) {\r
8487         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8488         pc++;\r
8489     }\r
8490 \r
8491     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8492 \r
8493     pc = GLT_ALL_TAGS;\r
8494 \r
8495     while( *pc ) {\r
8496         if( strchr( tags, *pc ) == 0 ) {\r
8497             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8498         }\r
8499         pc++;\r
8500     }\r
8501 \r
8502     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8503 }\r
8504 \r
8505 char GLT_ListItemToTag( HWND hDlg, int index )\r
8506 {\r
8507     char result = '\0';\r
8508     char name[128];\r
8509 \r
8510     GLT_Item * list = GLT_ItemInfo;\r
8511 \r
8512     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8513         while( list->id != 0 ) {\r
8514             if( strcmp( list->name, name ) == 0 ) {\r
8515                 result = list->id;\r
8516                 break;\r
8517             }\r
8518 \r
8519             list++;\r
8520         }\r
8521     }\r
8522 \r
8523     return result;\r
8524 }\r
8525 \r
8526 void GLT_MoveSelection( HWND hDlg, int delta )\r
8527 {\r
8528     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
8529     int idx2 = idx1 + delta;\r
8530     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8531 \r
8532     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
8533         char buf[128];\r
8534 \r
8535         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
8536         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
8537         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
8538         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
8539     }\r
8540 }\r
8541 \r
8542 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8543 {\r
8544     static char glt[64];\r
8545     static char * lpUserGLT;\r
8546 \r
8547     switch( message )\r
8548     {\r
8549     case WM_INITDIALOG:\r
8550         lpUserGLT = (char *) lParam;\r
8551         \r
8552         strcpy( glt, lpUserGLT );\r
8553 \r
8554         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8555 \r
8556         /* Initialize list */\r
8557         GLT_TagsToList( hDlg, glt );\r
8558 \r
8559         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
8560 \r
8561         break;\r
8562 \r
8563     case WM_COMMAND:\r
8564         switch( LOWORD(wParam) ) {\r
8565         case IDOK:\r
8566             {\r
8567                 char * pc = lpUserGLT;\r
8568                 int idx = 0;\r
8569                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
8570                 char id;\r
8571 \r
8572                 do {\r
8573                     id = GLT_ListItemToTag( hDlg, idx );\r
8574 \r
8575                     *pc++ = id;\r
8576                     idx++;\r
8577                 } while( id != '\0' );\r
8578             }\r
8579             EndDialog( hDlg, 0 );\r
8580             return TRUE;\r
8581         case IDCANCEL:\r
8582             EndDialog( hDlg, 1 );\r
8583             return TRUE;\r
8584 \r
8585         case IDC_GLT_Default:\r
8586             strcpy( glt, GLT_DEFAULT_TAGS );\r
8587             GLT_TagsToList( hDlg, glt );\r
8588             return TRUE;\r
8589 \r
8590         case IDC_GLT_Restore:\r
8591             strcpy( glt, lpUserGLT );\r
8592             GLT_TagsToList( hDlg, glt );\r
8593             return TRUE;\r
8594 \r
8595         case IDC_GLT_Up:\r
8596             GLT_MoveSelection( hDlg, -1 );\r
8597             return TRUE;\r
8598 \r
8599         case IDC_GLT_Down:\r
8600             GLT_MoveSelection( hDlg, +1 );\r
8601             return TRUE;\r
8602         }\r
8603 \r
8604         break;\r
8605     }\r
8606 \r
8607     return FALSE;\r
8608 }\r
8609 \r
8610 int GameListOptions()\r
8611 {\r
8612     char glt[64];\r
8613     int result;\r
8614     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
8615 \r
8616     strcpy( glt, appData.gameListTags );\r
8617 \r
8618     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
8619 \r
8620     if( result == 0 ) {\r
8621         /* [AS] Memory leak here! */\r
8622         appData.gameListTags = strdup( glt ); \r
8623     }\r
8624 \r
8625     return result;\r
8626 }\r
8627 \r
8628 \r
8629 VOID\r
8630 DisplayIcsInteractionTitle(char *str)\r
8631 {\r
8632   char consoleTitle[MSG_SIZ];\r
8633 \r
8634   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
8635   SetWindowText(hwndConsole, consoleTitle);\r
8636 }\r
8637 \r
8638 void\r
8639 DrawPosition(int fullRedraw, Board board)\r
8640 {\r
8641   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
8642 }\r
8643 \r
8644 \r
8645 VOID\r
8646 ResetFrontEnd()\r
8647 {\r
8648   fromX = fromY = -1;\r
8649   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
8650     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8651     dragInfo.pos.x = dragInfo.pos.y = -1;\r
8652     dragInfo.lastpos = dragInfo.pos;\r
8653     dragInfo.start.x = dragInfo.start.y = -1;\r
8654     dragInfo.from = dragInfo.start;\r
8655     ReleaseCapture();\r
8656     DrawPosition(TRUE, NULL);\r
8657   }\r
8658 }\r
8659 \r
8660 \r
8661 VOID\r
8662 CommentPopUp(char *title, char *str)\r
8663 {\r
8664   HWND hwnd = GetActiveWindow();\r
8665   EitherCommentPopUp(0, title, str, FALSE);\r
8666   SetActiveWindow(hwnd);\r
8667 }\r
8668 \r
8669 VOID\r
8670 CommentPopDown(void)\r
8671 {\r
8672   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
8673   if (commentDialog) {\r
8674     ShowWindow(commentDialog, SW_HIDE);\r
8675   }\r
8676   commentDialogUp = FALSE;\r
8677 }\r
8678 \r
8679 VOID\r
8680 EditCommentPopUp(int index, char *title, char *str)\r
8681 {\r
8682   EitherCommentPopUp(index, title, str, TRUE);\r
8683 }\r
8684 \r
8685 \r
8686 VOID\r
8687 RingBell()\r
8688 {\r
8689   MyPlaySound(&sounds[(int)SoundMove]);\r
8690 }\r
8691 \r
8692 VOID PlayIcsWinSound()\r
8693 {\r
8694   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
8695 }\r
8696 \r
8697 VOID PlayIcsLossSound()\r
8698 {\r
8699   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
8700 }\r
8701 \r
8702 VOID PlayIcsDrawSound()\r
8703 {\r
8704   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
8705 }\r
8706 \r
8707 VOID PlayIcsUnfinishedSound()\r
8708 {\r
8709   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
8710 }\r
8711 \r
8712 VOID\r
8713 PlayAlarmSound()\r
8714 {\r
8715   MyPlaySound(&sounds[(int)SoundAlarm]);\r
8716 }\r
8717 \r
8718 \r
8719 VOID\r
8720 EchoOn()\r
8721 {\r
8722   HWND hInput;\r
8723   consoleEcho = TRUE;\r
8724   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8725   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
8726   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8727 }\r
8728 \r
8729 \r
8730 VOID\r
8731 EchoOff()\r
8732 {\r
8733   CHARFORMAT cf;\r
8734   HWND hInput;\r
8735   consoleEcho = FALSE;\r
8736   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8737   /* This works OK: set text and background both to the same color */\r
8738   cf = consoleCF;\r
8739   cf.crTextColor = COLOR_ECHOOFF;\r
8740   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8741   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
8742 }\r
8743 \r
8744 /* No Raw()...? */\r
8745 \r
8746 void Colorize(ColorClass cc, int continuation)\r
8747 {\r
8748   currentColorClass = cc;\r
8749   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8750   consoleCF.crTextColor = textAttribs[cc].color;\r
8751   consoleCF.dwEffects = textAttribs[cc].effects;\r
8752   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
8753 }\r
8754 \r
8755 char *\r
8756 UserName()\r
8757 {\r
8758   static char buf[MSG_SIZ];\r
8759   DWORD bufsiz = MSG_SIZ;\r
8760 \r
8761   if (!GetUserName(buf, &bufsiz)) {\r
8762     /*DisplayError("Error getting user name", GetLastError());*/\r
8763     strcpy(buf, "User");\r
8764   }\r
8765   return buf;\r
8766 }\r
8767 \r
8768 char *\r
8769 HostName()\r
8770 {\r
8771   static char buf[MSG_SIZ];\r
8772   DWORD bufsiz = MSG_SIZ;\r
8773 \r
8774   if (!GetComputerName(buf, &bufsiz)) {\r
8775     /*DisplayError("Error getting host name", GetLastError());*/\r
8776     strcpy(buf, "Unknown");\r
8777   }\r
8778   return buf;\r
8779 }\r
8780 \r
8781 \r
8782 int\r
8783 ClockTimerRunning()\r
8784 {\r
8785   return clockTimerEvent != 0;\r
8786 }\r
8787 \r
8788 int\r
8789 StopClockTimer()\r
8790 {\r
8791   if (clockTimerEvent == 0) return FALSE;\r
8792   KillTimer(hwndMain, clockTimerEvent);\r
8793   clockTimerEvent = 0;\r
8794   return TRUE;\r
8795 }\r
8796 \r
8797 void\r
8798 StartClockTimer(long millisec)\r
8799 {\r
8800   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
8801                              (UINT) millisec, NULL);\r
8802 }\r
8803 \r
8804 void\r
8805 DisplayWhiteClock(long timeRemaining, int highlight)\r
8806 {\r
8807   HDC hdc;\r
8808   hdc = GetDC(hwndMain);\r
8809   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8810 \r
8811   if (!IsIconic(hwndMain)) {\r
8812     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &blackRect : &whiteRect, "White", flag);\r
8813   }\r
8814   if (highlight && iconCurrent == iconBlack) {\r
8815     iconCurrent = iconWhite;\r
8816     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8817     if (IsIconic(hwndMain)) {\r
8818       DrawIcon(hdc, 2, 2, iconCurrent);\r
8819     }\r
8820   }\r
8821   (void) ReleaseDC(hwndMain, hdc);\r
8822   if (hwndConsole)\r
8823     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8824 }\r
8825 \r
8826 void\r
8827 DisplayBlackClock(long timeRemaining, int highlight)\r
8828 {\r
8829   HDC hdc;\r
8830   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
8831 \r
8832   hdc = GetDC(hwndMain);\r
8833   if (!IsIconic(hwndMain)) {\r
8834     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &whiteRect : &blackRect, "Black", flag);\r
8835   }\r
8836   if (highlight && iconCurrent == iconWhite) {\r
8837     iconCurrent = iconBlack;\r
8838     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8839     if (IsIconic(hwndMain)) {\r
8840       DrawIcon(hdc, 2, 2, iconCurrent);\r
8841     }\r
8842   }\r
8843   (void) ReleaseDC(hwndMain, hdc);\r
8844   if (hwndConsole)\r
8845     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
8846 }\r
8847 \r
8848 \r
8849 int\r
8850 LoadGameTimerRunning()\r
8851 {\r
8852   return loadGameTimerEvent != 0;\r
8853 }\r
8854 \r
8855 int\r
8856 StopLoadGameTimer()\r
8857 {\r
8858   if (loadGameTimerEvent == 0) return FALSE;\r
8859   KillTimer(hwndMain, loadGameTimerEvent);\r
8860   loadGameTimerEvent = 0;\r
8861   return TRUE;\r
8862 }\r
8863 \r
8864 void\r
8865 StartLoadGameTimer(long millisec)\r
8866 {\r
8867   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
8868                                 (UINT) millisec, NULL);\r
8869 }\r
8870 \r
8871 void\r
8872 AutoSaveGame()\r
8873 {\r
8874   char *defName;\r
8875   FILE *f;\r
8876   char fileTitle[MSG_SIZ];\r
8877 \r
8878   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
8879   f = OpenFileDialog(hwndMain, TRUE, defName,\r
8880                      appData.oldSaveStyle ? "gam" : "pgn",\r
8881                      GAME_FILT, \r
8882                      "Save Game to File", NULL, fileTitle, NULL);\r
8883   if (f != NULL) {\r
8884     SaveGame(f, 0, "");\r
8885     fclose(f);\r
8886   }\r
8887 }\r
8888 \r
8889 \r
8890 void\r
8891 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
8892 {\r
8893   if (delayedTimerEvent != 0) {\r
8894     if (appData.debugMode) {\r
8895       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
8896     }\r
8897     KillTimer(hwndMain, delayedTimerEvent);\r
8898     delayedTimerEvent = 0;\r
8899     delayedTimerCallback();\r
8900   }\r
8901   delayedTimerCallback = cb;\r
8902   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
8903                                 (UINT) millisec, NULL);\r
8904 }\r
8905 \r
8906 DelayedEventCallback\r
8907 GetDelayedEvent()\r
8908 {\r
8909   if (delayedTimerEvent) {\r
8910     return delayedTimerCallback;\r
8911   } else {\r
8912     return NULL;\r
8913   }\r
8914 }\r
8915 \r
8916 void\r
8917 CancelDelayedEvent()\r
8918 {\r
8919   if (delayedTimerEvent) {\r
8920     KillTimer(hwndMain, delayedTimerEvent);\r
8921     delayedTimerEvent = 0;\r
8922   }\r
8923 }\r
8924 \r
8925 /* Start a child process running the given program.\r
8926    The process's standard output can be read from "from", and its\r
8927    standard input can be written to "to".\r
8928    Exit with fatal error if anything goes wrong.\r
8929    Returns an opaque pointer that can be used to destroy the process\r
8930    later.\r
8931 */\r
8932 int\r
8933 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
8934 {\r
8935 #define BUFSIZE 4096\r
8936 \r
8937   HANDLE hChildStdinRd, hChildStdinWr,\r
8938     hChildStdoutRd, hChildStdoutWr;\r
8939   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
8940   SECURITY_ATTRIBUTES saAttr;\r
8941   BOOL fSuccess;\r
8942   PROCESS_INFORMATION piProcInfo;\r
8943   STARTUPINFO siStartInfo;\r
8944   ChildProc *cp;\r
8945   char buf[MSG_SIZ];\r
8946   DWORD err;\r
8947 \r
8948   if (appData.debugMode) {\r
8949     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
8950   }\r
8951 \r
8952   *pr = NoProc;\r
8953 \r
8954   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
8955   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
8956   saAttr.bInheritHandle = TRUE;\r
8957   saAttr.lpSecurityDescriptor = NULL;\r
8958 \r
8959   /*\r
8960    * The steps for redirecting child's STDOUT:\r
8961    *     1. Create anonymous pipe to be STDOUT for child.\r
8962    *     2. Create a noninheritable duplicate of read handle,\r
8963    *         and close the inheritable read handle.\r
8964    */\r
8965 \r
8966   /* Create a pipe for the child's STDOUT. */\r
8967   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
8968     return GetLastError();\r
8969   }\r
8970 \r
8971   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
8972   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
8973                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
8974                              FALSE,     /* not inherited */\r
8975                              DUPLICATE_SAME_ACCESS);\r
8976   if (! fSuccess) {\r
8977     return GetLastError();\r
8978   }\r
8979   CloseHandle(hChildStdoutRd);\r
8980 \r
8981   /*\r
8982    * The steps for redirecting child's STDIN:\r
8983    *     1. Create anonymous pipe to be STDIN for child.\r
8984    *     2. Create a noninheritable duplicate of write handle,\r
8985    *         and close the inheritable write handle.\r
8986    */\r
8987 \r
8988   /* Create a pipe for the child's STDIN. */\r
8989   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
8990     return GetLastError();\r
8991   }\r
8992 \r
8993   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
8994   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
8995                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
8996                              FALSE,     /* not inherited */\r
8997                              DUPLICATE_SAME_ACCESS);\r
8998   if (! fSuccess) {\r
8999     return GetLastError();\r
9000   }\r
9001   CloseHandle(hChildStdinWr);\r
9002 \r
9003   /* Arrange to (1) look in dir for the child .exe file, and\r
9004    * (2) have dir be the child's working directory.  Interpret\r
9005    * dir relative to the directory WinBoard loaded from. */\r
9006   GetCurrentDirectory(MSG_SIZ, buf);\r
9007   SetCurrentDirectory(installDir);\r
9008   SetCurrentDirectory(dir);\r
9009 \r
9010   /* Now create the child process. */\r
9011 \r
9012   siStartInfo.cb = sizeof(STARTUPINFO);\r
9013   siStartInfo.lpReserved = NULL;\r
9014   siStartInfo.lpDesktop = NULL;\r
9015   siStartInfo.lpTitle = NULL;\r
9016   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9017   siStartInfo.cbReserved2 = 0;\r
9018   siStartInfo.lpReserved2 = NULL;\r
9019   siStartInfo.hStdInput = hChildStdinRd;\r
9020   siStartInfo.hStdOutput = hChildStdoutWr;\r
9021   siStartInfo.hStdError = hChildStdoutWr;\r
9022 \r
9023   fSuccess = CreateProcess(NULL,\r
9024                            cmdLine,        /* command line */\r
9025                            NULL,           /* process security attributes */\r
9026                            NULL,           /* primary thread security attrs */\r
9027                            TRUE,           /* handles are inherited */\r
9028                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9029                            NULL,           /* use parent's environment */\r
9030                            NULL,\r
9031                            &siStartInfo, /* STARTUPINFO pointer */\r
9032                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9033 \r
9034   err = GetLastError();\r
9035   SetCurrentDirectory(buf); /* return to prev directory */\r
9036   if (! fSuccess) {\r
9037     return err;\r
9038   }\r
9039 \r
9040   /* Close the handles we don't need in the parent */\r
9041   CloseHandle(piProcInfo.hThread);\r
9042   CloseHandle(hChildStdinRd);\r
9043   CloseHandle(hChildStdoutWr);\r
9044 \r
9045   /* Prepare return value */\r
9046   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9047   cp->kind = CPReal;\r
9048   cp->hProcess = piProcInfo.hProcess;\r
9049   cp->pid = piProcInfo.dwProcessId;\r
9050   cp->hFrom = hChildStdoutRdDup;\r
9051   cp->hTo = hChildStdinWrDup;\r
9052 \r
9053   *pr = (void *) cp;\r
9054 \r
9055   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9056      2000 where engines sometimes don't see the initial command(s)\r
9057      from WinBoard and hang.  I don't understand how that can happen,\r
9058      but the Sleep is harmless, so I've put it in.  Others have also\r
9059      reported what may be the same problem, so hopefully this will fix\r
9060      it for them too.  */\r
9061   Sleep(500);\r
9062 \r
9063   return NO_ERROR;\r
9064 }\r
9065 \r
9066 \r
9067 void\r
9068 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9069 {\r
9070   ChildProc *cp;\r
9071 \r
9072   cp = (ChildProc *) pr;\r
9073   if (cp == NULL) return;\r
9074 \r
9075   switch (cp->kind) {\r
9076   case CPReal:\r
9077     /* TerminateProcess is considered harmful, so... */\r
9078     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9079     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9080     /* The following doesn't work because the chess program\r
9081        doesn't "have the same console" as WinBoard.  Maybe\r
9082        we could arrange for this even though neither WinBoard\r
9083        nor the chess program uses a console for stdio? */\r
9084     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9085 \r
9086     /* [AS] Special termination modes for misbehaving programs... */\r
9087     if( signal == 9 ) {\r
9088         if ( appData.debugMode) {\r
9089             fprintf( debugFP, "Terminating process %u\n", cp->pid );\r
9090         }\r
9091 \r
9092         TerminateProcess( cp->hProcess, 0 );\r
9093     }\r
9094     else if( signal == 10 ) {\r
9095         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9096 \r
9097         if( dw != WAIT_OBJECT_0 ) {\r
9098             if ( appData.debugMode) {\r
9099                 fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );\r
9100             }\r
9101 \r
9102             TerminateProcess( cp->hProcess, 0 );\r
9103         }\r
9104     }\r
9105 \r
9106     CloseHandle(cp->hProcess);\r
9107     break;\r
9108 \r
9109   case CPComm:\r
9110     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9111     break;\r
9112 \r
9113   case CPSock:\r
9114     closesocket(cp->sock);\r
9115     WSACleanup();\r
9116     break;\r
9117 \r
9118   case CPRcmd:\r
9119     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9120     closesocket(cp->sock);\r
9121     closesocket(cp->sock2);\r
9122     WSACleanup();\r
9123     break;\r
9124   }\r
9125   free(cp);\r
9126 }\r
9127 \r
9128 void\r
9129 InterruptChildProcess(ProcRef pr)\r
9130 {\r
9131   ChildProc *cp;\r
9132 \r
9133   cp = (ChildProc *) pr;\r
9134   if (cp == NULL) return;\r
9135   switch (cp->kind) {\r
9136   case CPReal:\r
9137     /* The following doesn't work because the chess program\r
9138        doesn't "have the same console" as WinBoard.  Maybe\r
9139        we could arrange for this even though neither WinBoard\r
9140        nor the chess program uses a console for stdio */\r
9141     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9142     break;\r
9143 \r
9144   case CPComm:\r
9145   case CPSock:\r
9146     /* Can't interrupt */\r
9147     break;\r
9148 \r
9149   case CPRcmd:\r
9150     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9151     break;\r
9152   }\r
9153 }\r
9154 \r
9155 \r
9156 int\r
9157 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9158 {\r
9159   char cmdLine[MSG_SIZ];\r
9160 \r
9161   if (port[0] == NULLCHAR) {\r
9162     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9163   } else {\r
9164     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9165   }\r
9166   return StartChildProcess(cmdLine, "", pr);\r
9167 }\r
9168 \r
9169 \r
9170 /* Code to open TCP sockets */\r
9171 \r
9172 int\r
9173 OpenTCP(char *host, char *port, ProcRef *pr)\r
9174 {\r
9175   ChildProc *cp;\r
9176   int err;\r
9177   SOCKET s;\r
9178   struct sockaddr_in sa, mysa;\r
9179   struct hostent FAR *hp;\r
9180   unsigned short uport;\r
9181   WORD wVersionRequested;\r
9182   WSADATA wsaData;\r
9183 \r
9184   /* Initialize socket DLL */\r
9185   wVersionRequested = MAKEWORD(1, 1);\r
9186   err = WSAStartup(wVersionRequested, &wsaData);\r
9187   if (err != 0) return err;\r
9188 \r
9189   /* Make socket */\r
9190   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9191     err = WSAGetLastError();\r
9192     WSACleanup();\r
9193     return err;\r
9194   }\r
9195 \r
9196   /* Bind local address using (mostly) don't-care values.\r
9197    */\r
9198   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9199   mysa.sin_family = AF_INET;\r
9200   mysa.sin_addr.s_addr = INADDR_ANY;\r
9201   uport = (unsigned short) 0;\r
9202   mysa.sin_port = htons(uport);\r
9203   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9204       == SOCKET_ERROR) {\r
9205     err = WSAGetLastError();\r
9206     WSACleanup();\r
9207     return err;\r
9208   }\r
9209 \r
9210   /* Resolve remote host name */\r
9211   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9212   if (!(hp = gethostbyname(host))) {\r
9213     unsigned int b0, b1, b2, b3;\r
9214 \r
9215     err = WSAGetLastError();\r
9216 \r
9217     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9218       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9219       hp->h_addrtype = AF_INET;\r
9220       hp->h_length = 4;\r
9221       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9222       hp->h_addr_list[0] = (char *) malloc(4);\r
9223       hp->h_addr_list[0][0] = (char) b0;\r
9224       hp->h_addr_list[0][1] = (char) b1;\r
9225       hp->h_addr_list[0][2] = (char) b2;\r
9226       hp->h_addr_list[0][3] = (char) b3;\r
9227     } else {\r
9228       WSACleanup();\r
9229       return err;\r
9230     }\r
9231   }\r
9232   sa.sin_family = hp->h_addrtype;\r
9233   uport = (unsigned short) atoi(port);\r
9234   sa.sin_port = htons(uport);\r
9235   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9236 \r
9237   /* Make connection */\r
9238   if (connect(s, (struct sockaddr *) &sa,\r
9239               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9240     err = WSAGetLastError();\r
9241     WSACleanup();\r
9242     return err;\r
9243   }\r
9244 \r
9245   /* Prepare return value */\r
9246   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9247   cp->kind = CPSock;\r
9248   cp->sock = s;\r
9249   *pr = (ProcRef *) cp;\r
9250 \r
9251   return NO_ERROR;\r
9252 }\r
9253 \r
9254 int\r
9255 OpenCommPort(char *name, ProcRef *pr)\r
9256 {\r
9257   HANDLE h;\r
9258   COMMTIMEOUTS ct;\r
9259   ChildProc *cp;\r
9260   char fullname[MSG_SIZ];\r
9261 \r
9262   if (*name != '\\')\r
9263     sprintf(fullname, "\\\\.\\%s", name);\r
9264   else\r
9265     strcpy(fullname, name);\r
9266 \r
9267   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9268                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9269   if (h == (HANDLE) -1) {\r
9270     return GetLastError();\r
9271   }\r
9272   hCommPort = h;\r
9273 \r
9274   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9275 \r
9276   /* Accumulate characters until a 100ms pause, then parse */\r
9277   ct.ReadIntervalTimeout = 100;\r
9278   ct.ReadTotalTimeoutMultiplier = 0;\r
9279   ct.ReadTotalTimeoutConstant = 0;\r
9280   ct.WriteTotalTimeoutMultiplier = 0;\r
9281   ct.WriteTotalTimeoutConstant = 0;\r
9282   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9283 \r
9284   /* Prepare return value */\r
9285   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9286   cp->kind = CPComm;\r
9287   cp->hFrom = h;\r
9288   cp->hTo = h;\r
9289   *pr = (ProcRef *) cp;\r
9290 \r
9291   return NO_ERROR;\r
9292 }\r
9293 \r
9294 int\r
9295 OpenLoopback(ProcRef *pr)\r
9296 {\r
9297   DisplayFatalError("Not implemented", 0, 1);\r
9298   return NO_ERROR;\r
9299 }\r
9300 \r
9301 \r
9302 int\r
9303 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9304 {\r
9305   ChildProc *cp;\r
9306   int err;\r
9307   SOCKET s, s2, s3;\r
9308   struct sockaddr_in sa, mysa;\r
9309   struct hostent FAR *hp;\r
9310   unsigned short uport;\r
9311   WORD wVersionRequested;\r
9312   WSADATA wsaData;\r
9313   int fromPort;\r
9314   char stderrPortStr[MSG_SIZ];\r
9315 \r
9316   /* Initialize socket DLL */\r
9317   wVersionRequested = MAKEWORD(1, 1);\r
9318   err = WSAStartup(wVersionRequested, &wsaData);\r
9319   if (err != 0) return err;\r
9320 \r
9321   /* Resolve remote host name */\r
9322   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9323   if (!(hp = gethostbyname(host))) {\r
9324     unsigned int b0, b1, b2, b3;\r
9325 \r
9326     err = WSAGetLastError();\r
9327 \r
9328     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9329       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9330       hp->h_addrtype = AF_INET;\r
9331       hp->h_length = 4;\r
9332       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9333       hp->h_addr_list[0] = (char *) malloc(4);\r
9334       hp->h_addr_list[0][0] = (char) b0;\r
9335       hp->h_addr_list[0][1] = (char) b1;\r
9336       hp->h_addr_list[0][2] = (char) b2;\r
9337       hp->h_addr_list[0][3] = (char) b3;\r
9338     } else {\r
9339       WSACleanup();\r
9340       return err;\r
9341     }\r
9342   }\r
9343   sa.sin_family = hp->h_addrtype;\r
9344   uport = (unsigned short) 514;\r
9345   sa.sin_port = htons(uport);\r
9346   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9347 \r
9348   /* Bind local socket to unused "privileged" port address\r
9349    */\r
9350   s = INVALID_SOCKET;\r
9351   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9352   mysa.sin_family = AF_INET;\r
9353   mysa.sin_addr.s_addr = INADDR_ANY;\r
9354   for (fromPort = 1023;; fromPort--) {\r
9355     if (fromPort < 0) {\r
9356       WSACleanup();\r
9357       return WSAEADDRINUSE;\r
9358     }\r
9359     if (s == INVALID_SOCKET) {\r
9360       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9361         err = WSAGetLastError();\r
9362         WSACleanup();\r
9363         return err;\r
9364       }\r
9365     }\r
9366     uport = (unsigned short) fromPort;\r
9367     mysa.sin_port = htons(uport);\r
9368     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9369         == SOCKET_ERROR) {\r
9370       err = WSAGetLastError();\r
9371       if (err == WSAEADDRINUSE) continue;\r
9372       WSACleanup();\r
9373       return err;\r
9374     }\r
9375     if (connect(s, (struct sockaddr *) &sa,\r
9376       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9377       err = WSAGetLastError();\r
9378       if (err == WSAEADDRINUSE) {\r
9379         closesocket(s);\r
9380         s = -1;\r
9381         continue;\r
9382       }\r
9383       WSACleanup();\r
9384       return err;\r
9385     }\r
9386     break;\r
9387   }\r
9388 \r
9389   /* Bind stderr local socket to unused "privileged" port address\r
9390    */\r
9391   s2 = INVALID_SOCKET;\r
9392   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9393   mysa.sin_family = AF_INET;\r
9394   mysa.sin_addr.s_addr = INADDR_ANY;\r
9395   for (fromPort = 1023;; fromPort--) {\r
9396     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9397     if (fromPort < 0) {\r
9398       (void) closesocket(s);\r
9399       WSACleanup();\r
9400       return WSAEADDRINUSE;\r
9401     }\r
9402     if (s2 == INVALID_SOCKET) {\r
9403       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9404         err = WSAGetLastError();\r
9405         closesocket(s);\r
9406         WSACleanup();\r
9407         return err;\r
9408       }\r
9409     }\r
9410     uport = (unsigned short) fromPort;\r
9411     mysa.sin_port = htons(uport);\r
9412     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9413         == SOCKET_ERROR) {\r
9414       err = WSAGetLastError();\r
9415       if (err == WSAEADDRINUSE) continue;\r
9416       (void) closesocket(s);\r
9417       WSACleanup();\r
9418       return err;\r
9419     }\r
9420     if (listen(s2, 1) == SOCKET_ERROR) {\r
9421       err = WSAGetLastError();\r
9422       if (err == WSAEADDRINUSE) {\r
9423         closesocket(s2);\r
9424         s2 = INVALID_SOCKET;\r
9425         continue;\r
9426       }\r
9427       (void) closesocket(s);\r
9428       (void) closesocket(s2);\r
9429       WSACleanup();\r
9430       return err;\r
9431     }\r
9432     break;\r
9433   }\r
9434   prevStderrPort = fromPort; // remember port used\r
9435   sprintf(stderrPortStr, "%d", fromPort);\r
9436 \r
9437   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9438     err = WSAGetLastError();\r
9439     (void) closesocket(s);\r
9440     (void) closesocket(s2);\r
9441     WSACleanup();\r
9442     return err;\r
9443   }\r
9444 \r
9445   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9446     err = WSAGetLastError();\r
9447     (void) closesocket(s);\r
9448     (void) closesocket(s2);\r
9449     WSACleanup();\r
9450     return err;\r
9451   }\r
9452   if (*user == NULLCHAR) user = UserName();\r
9453   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9454     err = WSAGetLastError();\r
9455     (void) closesocket(s);\r
9456     (void) closesocket(s2);\r
9457     WSACleanup();\r
9458     return err;\r
9459   }\r
9460   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9461     err = WSAGetLastError();\r
9462     (void) closesocket(s);\r
9463     (void) closesocket(s2);\r
9464     WSACleanup();\r
9465     return err;\r
9466   }\r
9467 \r
9468   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9469     err = WSAGetLastError();\r
9470     (void) closesocket(s);\r
9471     (void) closesocket(s2);\r
9472     WSACleanup();\r
9473     return err;\r
9474   }\r
9475   (void) closesocket(s2);  /* Stop listening */\r
9476 \r
9477   /* Prepare return value */\r
9478   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9479   cp->kind = CPRcmd;\r
9480   cp->sock = s;\r
9481   cp->sock2 = s3;\r
9482   *pr = (ProcRef *) cp;\r
9483 \r
9484   return NO_ERROR;\r
9485 }\r
9486 \r
9487 \r
9488 InputSourceRef\r
9489 AddInputSource(ProcRef pr, int lineByLine,\r
9490                InputCallback func, VOIDSTAR closure)\r
9491 {\r
9492   InputSource *is, *is2 = NULL;\r
9493   ChildProc *cp = (ChildProc *) pr;\r
9494 \r
9495   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9496   is->lineByLine = lineByLine;\r
9497   is->func = func;\r
9498   is->closure = closure;\r
9499   is->second = NULL;\r
9500   is->next = is->buf;\r
9501   if (pr == NoProc) {\r
9502     is->kind = CPReal;\r
9503     consoleInputSource = is;\r
9504   } else {\r
9505     is->kind = cp->kind;\r
9506     /* \r
9507         [AS] Try to avoid a race condition if the thread is given control too early:\r
9508         we create all threads suspended so that the is->hThread variable can be\r
9509         safely assigned, then let the threads start with ResumeThread.\r
9510     */\r
9511     switch (cp->kind) {\r
9512     case CPReal:\r
9513       is->hFile = cp->hFrom;\r
9514       cp->hFrom = NULL; /* now owned by InputThread */\r
9515       is->hThread =\r
9516         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
9517                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9518       break;\r
9519 \r
9520     case CPComm:\r
9521       is->hFile = cp->hFrom;\r
9522       cp->hFrom = NULL; /* now owned by InputThread */\r
9523       is->hThread =\r
9524         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
9525                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9526       break;\r
9527 \r
9528     case CPSock:\r
9529       is->sock = cp->sock;\r
9530       is->hThread =\r
9531         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9532                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9533       break;\r
9534 \r
9535     case CPRcmd:\r
9536       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
9537       *is2 = *is;\r
9538       is->sock = cp->sock;\r
9539       is->second = is2;\r
9540       is2->sock = cp->sock2;\r
9541       is2->second = is2;\r
9542       is->hThread =\r
9543         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9544                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
9545       is2->hThread =\r
9546         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
9547                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
9548       break;\r
9549     }\r
9550 \r
9551     if( is->hThread != NULL ) {\r
9552         ResumeThread( is->hThread );\r
9553     }\r
9554 \r
9555     if( is2 != NULL && is2->hThread != NULL ) {\r
9556         ResumeThread( is2->hThread );\r
9557     }\r
9558   }\r
9559 \r
9560   return (InputSourceRef) is;\r
9561 }\r
9562 \r
9563 void\r
9564 RemoveInputSource(InputSourceRef isr)\r
9565 {\r
9566   InputSource *is;\r
9567 \r
9568   is = (InputSource *) isr;\r
9569   is->hThread = NULL;  /* tell thread to stop */\r
9570   CloseHandle(is->hThread);\r
9571   if (is->second != NULL) {\r
9572     is->second->hThread = NULL;\r
9573     CloseHandle(is->second->hThread);\r
9574   }\r
9575 }\r
9576 \r
9577 \r
9578 int\r
9579 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
9580 {\r
9581   DWORD dOutCount;\r
9582   int outCount = SOCKET_ERROR;\r
9583   ChildProc *cp = (ChildProc *) pr;\r
9584   static OVERLAPPED ovl;\r
9585 \r
9586   if (pr == NoProc) {\r
9587     ConsoleOutput(message, count, FALSE);\r
9588     return count;\r
9589   } \r
9590 \r
9591   if (ovl.hEvent == NULL) {\r
9592     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
9593   }\r
9594   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
9595 \r
9596   switch (cp->kind) {\r
9597   case CPSock:\r
9598   case CPRcmd:\r
9599     outCount = send(cp->sock, message, count, 0);\r
9600     if (outCount == SOCKET_ERROR) {\r
9601       *outError = WSAGetLastError();\r
9602     } else {\r
9603       *outError = NO_ERROR;\r
9604     }\r
9605     break;\r
9606 \r
9607   case CPReal:\r
9608     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
9609                   &dOutCount, NULL)) {\r
9610       *outError = NO_ERROR;\r
9611       outCount = (int) dOutCount;\r
9612     } else {\r
9613       *outError = GetLastError();\r
9614     }\r
9615     break;\r
9616 \r
9617   case CPComm:\r
9618     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
9619                             &dOutCount, &ovl);\r
9620     if (*outError == NO_ERROR) {\r
9621       outCount = (int) dOutCount;\r
9622     }\r
9623     break;\r
9624   }\r
9625   return outCount;\r
9626 }\r
9627 \r
9628 int\r
9629 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
9630                        long msdelay)\r
9631 {\r
9632   /* Ignore delay, not implemented for WinBoard */\r
9633   return OutputToProcess(pr, message, count, outError);\r
9634 }\r
9635 \r
9636 \r
9637 void\r
9638 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
9639                         char *buf, int count, int error)\r
9640 {\r
9641   DisplayFatalError("Not implemented", 0, 1);\r
9642 }\r
9643 \r
9644 /* see wgamelist.c for Game List functions */\r
9645 /* see wedittags.c for Edit Tags functions */\r
9646 \r
9647 \r
9648 VOID\r
9649 ICSInitScript()\r
9650 {\r
9651   FILE *f;\r
9652   char buf[MSG_SIZ];\r
9653   char *dummy;\r
9654 \r
9655   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
9656     f = fopen(buf, "r");\r
9657     if (f != NULL) {\r
9658       ProcessICSInitScript(f);\r
9659       fclose(f);\r
9660     }\r
9661   }\r
9662 }\r
9663 \r
9664 \r
9665 VOID\r
9666 StartAnalysisClock()\r
9667 {\r
9668   if (analysisTimerEvent) return;\r
9669   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
9670                                         (UINT) 2000, NULL);\r
9671 }\r
9672 \r
9673 LRESULT CALLBACK\r
9674 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9675 {\r
9676   static HANDLE hwndText;\r
9677   RECT rect;\r
9678   static int sizeX, sizeY;\r
9679   int newSizeX, newSizeY, flags;\r
9680   MINMAXINFO *mmi;\r
9681 \r
9682   switch (message) {\r
9683   case WM_INITDIALOG: /* message: initialize dialog box */\r
9684     /* Initialize the dialog items */\r
9685     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
9686     SetWindowText(hDlg, analysisTitle);\r
9687     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
9688     /* Size and position the dialog */\r
9689     if (!analysisDialog) {\r
9690       analysisDialog = hDlg;\r
9691       flags = SWP_NOZORDER;\r
9692       GetClientRect(hDlg, &rect);\r
9693       sizeX = rect.right;\r
9694       sizeY = rect.bottom;\r
9695       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
9696           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
9697         WINDOWPLACEMENT wp;\r
9698         EnsureOnScreen(&analysisX, &analysisY);\r
9699         wp.length = sizeof(WINDOWPLACEMENT);\r
9700         wp.flags = 0;\r
9701         wp.showCmd = SW_SHOW;\r
9702         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
9703         wp.rcNormalPosition.left = analysisX;\r
9704         wp.rcNormalPosition.right = analysisX + analysisW;\r
9705         wp.rcNormalPosition.top = analysisY;\r
9706         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
9707         SetWindowPlacement(hDlg, &wp);\r
9708 \r
9709         GetClientRect(hDlg, &rect);\r
9710         newSizeX = rect.right;\r
9711         newSizeY = rect.bottom;\r
9712         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
9713                               newSizeX, newSizeY);\r
9714         sizeX = newSizeX;\r
9715         sizeY = newSizeY;\r
9716       }\r
9717     }\r
9718     return FALSE;\r
9719 \r
9720   case WM_COMMAND: /* message: received a command */\r
9721     switch (LOWORD(wParam)) {\r
9722     case IDCANCEL:\r
9723       EditGameEvent();\r
9724       return TRUE;\r
9725     default:\r
9726       break;\r
9727     }\r
9728     break;\r
9729 \r
9730   case WM_SIZE:\r
9731     newSizeX = LOWORD(lParam);\r
9732     newSizeY = HIWORD(lParam);\r
9733     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
9734     sizeX = newSizeX;\r
9735     sizeY = newSizeY;\r
9736     break;\r
9737 \r
9738   case WM_GETMINMAXINFO:\r
9739     /* Prevent resizing window too small */\r
9740     mmi = (MINMAXINFO *) lParam;\r
9741     mmi->ptMinTrackSize.x = 100;\r
9742     mmi->ptMinTrackSize.y = 100;\r
9743     break;\r
9744   }\r
9745   return FALSE;\r
9746 }\r
9747 \r
9748 VOID\r
9749 AnalysisPopUp(char* title, char* str)\r
9750 {\r
9751   FARPROC lpProc;\r
9752   char *p, *q;\r
9753 \r
9754   /* [AS] */\r
9755   EngineOutputPopUp();\r
9756   return;\r
9757 \r
9758   if (str == NULL) str = "";\r
9759   p = (char *) malloc(2 * strlen(str) + 2);\r
9760   q = p;\r
9761   while (*str) {\r
9762     if (*str == '\n') *q++ = '\r';\r
9763     *q++ = *str++;\r
9764   }\r
9765   *q = NULLCHAR;\r
9766   if (analysisText != NULL) free(analysisText);\r
9767   analysisText = p;\r
9768 \r
9769   if (analysisDialog) {\r
9770     SetWindowText(analysisDialog, title);\r
9771     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
9772     ShowWindow(analysisDialog, SW_SHOW);\r
9773   } else {\r
9774     analysisTitle = title;\r
9775     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
9776     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
9777                  hwndMain, (DLGPROC)lpProc);\r
9778     FreeProcInstance(lpProc);\r
9779   }\r
9780   analysisDialogUp = TRUE;  \r
9781 }\r
9782 \r
9783 VOID\r
9784 AnalysisPopDown()\r
9785 {\r
9786   if (analysisDialog) {\r
9787     ShowWindow(analysisDialog, SW_HIDE);\r
9788   }\r
9789   analysisDialogUp = FALSE;  \r
9790 }\r
9791 \r
9792 \r
9793 VOID\r
9794 SetHighlights(int fromX, int fromY, int toX, int toY)\r
9795 {\r
9796   highlightInfo.sq[0].x = fromX;\r
9797   highlightInfo.sq[0].y = fromY;\r
9798   highlightInfo.sq[1].x = toX;\r
9799   highlightInfo.sq[1].y = toY;\r
9800 }\r
9801 \r
9802 VOID\r
9803 ClearHighlights()\r
9804 {\r
9805   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
9806     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
9807 }\r
9808 \r
9809 VOID\r
9810 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
9811 {\r
9812   premoveHighlightInfo.sq[0].x = fromX;\r
9813   premoveHighlightInfo.sq[0].y = fromY;\r
9814   premoveHighlightInfo.sq[1].x = toX;\r
9815   premoveHighlightInfo.sq[1].y = toY;\r
9816 }\r
9817 \r
9818 VOID\r
9819 ClearPremoveHighlights()\r
9820 {\r
9821   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
9822     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
9823 }\r
9824 \r
9825 VOID\r
9826 ShutDownFrontEnd()\r
9827 {\r
9828   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
9829   DeleteClipboardTempFiles();\r
9830 }\r
9831 \r
9832 void\r
9833 BoardToTop()\r
9834 {\r
9835     if (IsIconic(hwndMain))\r
9836       ShowWindow(hwndMain, SW_RESTORE);\r
9837 \r
9838     SetActiveWindow(hwndMain);\r
9839 }\r
9840 \r
9841 /*\r
9842  * Prototypes for animation support routines\r
9843  */\r
9844 static void ScreenSquare(int column, int row, POINT * pt);\r
9845 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
9846      POINT frames[], int * nFrames);\r
9847 \r
9848 \r
9849 #define kFactor 4\r
9850 \r
9851 void\r
9852 AnimateMove(board, fromX, fromY, toX, toY)\r
9853      Board board;\r
9854      int fromX;\r
9855      int fromY;\r
9856      int toX;\r
9857      int toY;\r
9858 {\r
9859   ChessSquare piece;\r
9860   POINT start, finish, mid;\r
9861   POINT frames[kFactor * 2 + 1];\r
9862   int nFrames, n;\r
9863 \r
9864   if (!appData.animate) return;\r
9865   if (doingSizing) return;\r
9866   if (fromY < 0 || fromX < 0) return;\r
9867   piece = board[fromY][fromX];\r
9868   if (piece >= EmptySquare) return;\r
9869 \r
9870   ScreenSquare(fromX, fromY, &start);\r
9871   ScreenSquare(toX, toY, &finish);\r
9872 \r
9873   /* All pieces except knights move in straight line */\r
9874   if (piece != WhiteKnight && piece != BlackKnight) {\r
9875     mid.x = start.x + (finish.x - start.x) / 2;\r
9876     mid.y = start.y + (finish.y - start.y) / 2;\r
9877   } else {\r
9878     /* Knight: make diagonal movement then straight */\r
9879     if (abs(toY - fromY) < abs(toX - fromX)) {\r
9880        mid.x = start.x + (finish.x - start.x) / 2;\r
9881        mid.y = finish.y;\r
9882      } else {\r
9883        mid.x = finish.x;\r
9884        mid.y = start.y + (finish.y - start.y) / 2;\r
9885      }\r
9886   }\r
9887   \r
9888   /* Don't use as many frames for very short moves */\r
9889   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
9890     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
9891   else\r
9892     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
9893 \r
9894   animInfo.from.x = fromX;\r
9895   animInfo.from.y = fromY;\r
9896   animInfo.to.x = toX;\r
9897   animInfo.to.y = toY;\r
9898   animInfo.lastpos = start;\r
9899   animInfo.piece = piece;\r
9900   for (n = 0; n < nFrames; n++) {\r
9901     animInfo.pos = frames[n];\r
9902     DrawPosition(FALSE, NULL);\r
9903     animInfo.lastpos = animInfo.pos;\r
9904     Sleep(appData.animSpeed);\r
9905   }\r
9906   animInfo.pos = finish;\r
9907   DrawPosition(FALSE, NULL);\r
9908   animInfo.piece = EmptySquare;\r
9909 }\r
9910 \r
9911 /*      Convert board position to corner of screen rect and color       */\r
9912 \r
9913 static void\r
9914 ScreenSquare(column, row, pt)\r
9915      int column; int row; POINT * pt;\r
9916 {\r
9917   if (flipView) {\r
9918     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
9919     pt->y = lineGap + row * (squareSize + lineGap);\r
9920   } else {\r
9921     pt->x = lineGap + column * (squareSize + lineGap);\r
9922     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
9923   }\r
9924 }\r
9925 \r
9926 /*      Generate a series of frame coords from start->mid->finish.\r
9927         The movement rate doubles until the half way point is\r
9928         reached, then halves back down to the final destination,\r
9929         which gives a nice slow in/out effect. The algorithmn\r
9930         may seem to generate too many intermediates for short\r
9931         moves, but remember that the purpose is to attract the\r
9932         viewers attention to the piece about to be moved and\r
9933         then to where it ends up. Too few frames would be less\r
9934         noticeable.                                             */\r
9935 \r
9936 static void\r
9937 Tween(start, mid, finish, factor, frames, nFrames)\r
9938      POINT * start; POINT * mid;\r
9939      POINT * finish; int factor;\r
9940      POINT frames[]; int * nFrames;\r
9941 {\r
9942   int n, fraction = 1, count = 0;\r
9943 \r
9944   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
9945   for (n = 0; n < factor; n++)\r
9946     fraction *= 2;\r
9947   for (n = 0; n < factor; n++) {\r
9948     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
9949     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
9950     count ++;\r
9951     fraction = fraction / 2;\r
9952   }\r
9953   \r
9954   /* Midpoint */\r
9955   frames[count] = *mid;\r
9956   count ++;\r
9957   \r
9958   /* Slow out, stepping 1/2, then 1/4, ... */\r
9959   fraction = 2;\r
9960   for (n = 0; n < factor; n++) {\r
9961     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
9962     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
9963     count ++;\r
9964     fraction = fraction * 2;\r
9965   }\r
9966   *nFrames = count;\r
9967 }\r
9968 \r
9969 void\r
9970 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
9971 {\r
9972 #if 0\r
9973     char buf[256];\r
9974 \r
9975     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
9976         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
9977 \r
9978     OutputDebugString( buf );\r
9979 #endif\r
9980 \r
9981     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
9982 \r
9983     EvalGraphSet( first, last, current, pvInfoList );\r
9984 }\r
9985 \r
9986 void SetProgramStats( FrontEndProgramStats * stats )\r
9987 {\r
9988 #if 0\r
9989     char buf[1024];\r
9990 \r
9991     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
9992         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
9993 \r
9994     OutputDebugString( buf );\r
9995 #endif\r
9996 \r
9997     EngineOutputUpdate( stats );\r
9998 }\r