Restructured URL code so it fits better with how winboard is set up.
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 extern HANDLE chatHandle[];\r
104 extern int ics_type;\r
105 \r
106 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
107 VOID NewVariantPopup(HWND hwnd);\r
108 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
109                    /*char*/int promoChar));\r
110 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P(());\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 BOOLEAN saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 \r
159 BoardSize boardSize;\r
160 BOOLEAN chessProgram;\r
161 static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 static int squareSize, lineGap, minorSize;\r
164 static int winWidth, winHeight, winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 HWND hwndMain = NULL;        /* root window*/\r
185 HWND hwndConsole = NULL;\r
186 BOOLEAN alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
201 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 /* [AS] Support for background textures */\r
214 #define BACK_TEXTURE_MODE_DISABLED      0\r
215 #define BACK_TEXTURE_MODE_PLAIN         1\r
216 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 char *defaultTextAttribs[] = \r
236 {\r
237   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
238   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
239   COLOR_NONE\r
240 };\r
241 \r
242 typedef struct {\r
243   char *name;\r
244   int squareSize;\r
245   int lineGap;\r
246   int smallLayout;\r
247   int tinyLayout;\r
248   int cliWidth, cliHeight;\r
249 } SizeInfo;\r
250 \r
251 SizeInfo sizeInfo[] = \r
252 {\r
253   { "tiny",     21, 0, 1, 1, 0, 0 },\r
254   { "teeny",    25, 1, 1, 1, 0, 0 },\r
255   { "dinky",    29, 1, 1, 1, 0, 0 },\r
256   { "petite",   33, 1, 1, 1, 0, 0 },\r
257   { "slim",     37, 2, 1, 0, 0, 0 },\r
258   { "small",    40, 2, 1, 0, 0, 0 },\r
259   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
260   { "middling", 49, 2, 0, 0, 0, 0 },\r
261   { "average",  54, 2, 0, 0, 0, 0 },\r
262   { "moderate", 58, 3, 0, 0, 0, 0 },\r
263   { "medium",   64, 3, 0, 0, 0, 0 },\r
264   { "bulky",    72, 3, 0, 0, 0, 0 },\r
265   { "large",    80, 3, 0, 0, 0, 0 },\r
266   { "big",      87, 3, 0, 0, 0, 0 },\r
267   { "huge",     95, 3, 0, 0, 0, 0 },\r
268   { "giant",    108, 3, 0, 0, 0, 0 },\r
269   { "colossal", 116, 4, 0, 0, 0, 0 },\r
270   { "titanic",  129, 4, 0, 0, 0, 0 },\r
271   { NULL, 0, 0, 0, 0, 0, 0 }\r
272 };\r
273 \r
274 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
275 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
276 {\r
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291   { 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
292   { 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
293   { 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
294   { 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
295 };\r
296 \r
297 MyFont *font[NUM_SIZES][NUM_FONTS];\r
298 \r
299 typedef struct {\r
300   char *label;\r
301   int id;\r
302   HWND hwnd;\r
303   WNDPROC wndproc;\r
304 } MyButtonDesc;\r
305 \r
306 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
307 #define N_BUTTONS 5\r
308 \r
309 MyButtonDesc buttonDesc[N_BUTTONS] =\r
310 {\r
311   {"<<", IDM_ToStart, NULL, NULL},\r
312   {"<", IDM_Backward, NULL, NULL},\r
313   {"P", IDM_Pause, NULL, NULL},\r
314   {">", IDM_Forward, NULL, NULL},\r
315   {">>", IDM_ToEnd, NULL, NULL},\r
316 };\r
317 \r
318 int tinyLayout = 0, smallLayout = 0;\r
319 #define MENU_BAR_ITEMS 7\r
320 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
321   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
322   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
323 };\r
324 \r
325 \r
326 MySound sounds[(int)NSoundClasses];\r
327 MyTextAttribs textAttribs[(int)NColorClasses];\r
328 \r
329 MyColorizeAttribs colorizeAttribs[] = {\r
330   { (COLORREF)0, 0, "Shout Text" },\r
331   { (COLORREF)0, 0, "SShout/CShout" },\r
332   { (COLORREF)0, 0, "Channel 1 Text" },\r
333   { (COLORREF)0, 0, "Channel Text" },\r
334   { (COLORREF)0, 0, "Kibitz Text" },\r
335   { (COLORREF)0, 0, "Tell Text" },\r
336   { (COLORREF)0, 0, "Challenge Text" },\r
337   { (COLORREF)0, 0, "Request Text" },\r
338   { (COLORREF)0, 0, "Seek Text" },\r
339   { (COLORREF)0, 0, "Normal Text" },\r
340   { (COLORREF)0, 0, "None" }\r
341 };\r
342 \r
343 \r
344 \r
345 static char *commentTitle;\r
346 static char *commentText;\r
347 static int commentIndex;\r
348 static Boolean editComment = FALSE;\r
349 HWND commentDialog = NULL;\r
350 BOOLEAN commentDialogUp = FALSE;\r
351 static int commentX, commentY, commentH, commentW;\r
352 \r
353 static char *analysisTitle;\r
354 static char *analysisText;\r
355 HWND analysisDialog = NULL;\r
356 BOOLEAN analysisDialogUp = FALSE;\r
357 static int analysisX, analysisY, analysisH, analysisW;\r
358 \r
359 char errorTitle[MSG_SIZ];\r
360 char errorMessage[2*MSG_SIZ];\r
361 HWND errorDialog = NULL;\r
362 BOOLEAN moveErrorMessageUp = FALSE;\r
363 BOOLEAN consoleEcho = TRUE;\r
364 CHARFORMAT consoleCF;\r
365 COLORREF consoleBackgroundColor;\r
366 \r
367 char *programVersion;\r
368 \r
369 #define CPReal 1\r
370 #define CPComm 2\r
371 #define CPSock 3\r
372 #define CPRcmd 4\r
373 typedef int CPKind;\r
374 \r
375 typedef struct {\r
376   CPKind kind;\r
377   HANDLE hProcess;\r
378   DWORD pid;\r
379   HANDLE hTo;\r
380   HANDLE hFrom;\r
381   SOCKET sock;\r
382   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
383 } ChildProc;\r
384 \r
385 #define INPUT_SOURCE_BUF_SIZE 4096\r
386 \r
387 typedef struct _InputSource {\r
388   CPKind kind;\r
389   HANDLE hFile;\r
390   SOCKET sock;\r
391   int lineByLine;\r
392   HANDLE hThread;\r
393   DWORD id;\r
394   char buf[INPUT_SOURCE_BUF_SIZE];\r
395   char *next;\r
396   DWORD count;\r
397   int error;\r
398   InputCallback func;\r
399   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
400   VOIDSTAR closure;\r
401 } InputSource;\r
402 \r
403 InputSource *consoleInputSource;\r
404 \r
405 DCB dcb;\r
406 \r
407 /* forward */\r
408 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
409 VOID ConsoleCreate();\r
410 LRESULT CALLBACK\r
411   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
412 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
413 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
414 VOID ParseCommSettings(char *arg, DCB *dcb);\r
415 LRESULT CALLBACK\r
416   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
417 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
418 void ParseIcsTextMenu(char *icsTextMenuString);\r
419 VOID PopUpMoveDialog(char firstchar);\r
420 VOID PopUpNameDialog(char firstchar);\r
421 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
422 \r
423 /* [AS] */\r
424 int NewGameFRC();\r
425 int GameListOptions();\r
426 \r
427 HWND moveHistoryDialog = NULL;\r
428 BOOLEAN moveHistoryDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpMoveHistory;\r
431 \r
432 HWND evalGraphDialog = NULL;\r
433 BOOLEAN evalGraphDialogUp = FALSE;\r
434 \r
435 WindowPlacement wpEvalGraph;\r
436 \r
437 HWND engineOutputDialog = NULL;\r
438 BOOLEAN engineOutputDialogUp = FALSE;\r
439 \r
440 WindowPlacement wpEngineOutput;\r
441 WindowPlacement wpGameList;\r
442 WindowPlacement wpConsole;\r
443 \r
444 VOID MoveHistoryPopUp();\r
445 VOID MoveHistoryPopDown();\r
446 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
447 BOOL MoveHistoryIsUp();\r
448 \r
449 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
450 VOID EvalGraphPopUp();\r
451 VOID EvalGraphPopDown();\r
452 BOOL EvalGraphIsUp();\r
453 \r
454 VOID EngineOutputPopUp();\r
455 VOID EngineOutputPopDown();\r
456 BOOL EngineOutputIsUp();\r
457 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
458 \r
459 VOID EngineOptionsPopup(); // [HGM] settings\r
460 \r
461 VOID GothicPopUp(char *title, VariantClass variant);\r
462 /*\r
463  * Setting "frozen" should disable all user input other than deleting\r
464  * the window.  We do this while engines are initializing themselves.\r
465  */\r
466 static int frozen = 0;\r
467 static int oldMenuItemState[MENU_BAR_ITEMS];\r
468 void FreezeUI()\r
469 {\r
470   HMENU hmenu;\r
471   int i;\r
472 \r
473   if (frozen) return;\r
474   frozen = 1;\r
475   hmenu = GetMenu(hwndMain);\r
476   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
477     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
478   }\r
479   DrawMenuBar(hwndMain);\r
480 }\r
481 \r
482 /* Undo a FreezeUI */\r
483 void ThawUI()\r
484 {\r
485   HMENU hmenu;\r
486   int i;\r
487 \r
488   if (!frozen) return;\r
489   frozen = 0;\r
490   hmenu = GetMenu(hwndMain);\r
491   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
492     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
493   }\r
494   DrawMenuBar(hwndMain);\r
495 }\r
496 \r
497 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
498 \r
499 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
500 #ifdef JAWS\r
501 #include "jaws.c"\r
502 #else\r
503 #define JAWS_INIT\r
504 #define JAWS_ARGS\r
505 #define JAWS_ALT_INTERCEPT\r
506 #define JAWS_KB_NAVIGATION\r
507 #define JAWS_MENU_ITEMS\r
508 #define JAWS_SILENCE\r
509 #define JAWS_REPLAY\r
510 #define JAWS_ACCEL\r
511 #define JAWS_COPYRIGHT\r
512 #define JAWS_DELETE(X) X\r
513 #define SAYMACHINEMOVE()\r
514 #define SAY(X)\r
515 #endif\r
516 \r
517 /*---------------------------------------------------------------------------*\\r
518  *\r
519  * WinMain\r
520  *\r
521 \*---------------------------------------------------------------------------*/\r
522 \r
523 int APIENTRY\r
524 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
525         LPSTR lpCmdLine, int nCmdShow)\r
526 {\r
527   MSG msg;\r
528   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
529 //  INITCOMMONCONTROLSEX ex;\r
530 \r
531   debugFP = stderr;\r
532 \r
533   LoadLibrary("RICHED32.DLL");\r
534   consoleCF.cbSize = sizeof(CHARFORMAT);\r
535 \r
536   if (!InitApplication(hInstance)) {\r
537     return (FALSE);\r
538   }\r
539   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
540     return (FALSE);\r
541   }\r
542 \r
543   JAWS_INIT\r
544 \r
545 //  InitCommonControlsEx(&ex);\r
546   InitCommonControls();\r
547 \r
548   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
549   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
550   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
551 \r
552   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
553 \r
554   while (GetMessage(&msg, /* message structure */\r
555                     NULL, /* handle of window receiving the message */\r
556                     0,    /* lowest message to examine */\r
557                     0))   /* highest message to examine */\r
558     {\r
559 \r
560       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
561         // [HGM] navigate: switch between all windows with tab\r
562         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
563         int i, currentElement = 0;\r
564 \r
565         // first determine what element of the chain we come from (if any)\r
566         if(appData.icsActive) {\r
567             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
568             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
569         }\r
570         if(engineOutputDialog && EngineOutputIsUp()) {\r
571             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
572             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
573         }\r
574         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
575             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
576         }\r
577         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
578         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
579         if(msg.hwnd == e1)                 currentElement = 2; else\r
580         if(msg.hwnd == e2)                 currentElement = 3; else\r
581         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
582         if(msg.hwnd == mh)                currentElement = 4; else\r
583         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
584         if(msg.hwnd == hText)  currentElement = 5; else\r
585         if(msg.hwnd == hInput) currentElement = 6; else\r
586         for (i = 0; i < N_BUTTONS; i++) {\r
587             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
588         }\r
589 \r
590         // determine where to go to\r
591         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
592           do {\r
593             currentElement = (currentElement + direction) % 7;\r
594             switch(currentElement) {\r
595                 case 0:\r
596                   h = hwndMain; break; // passing this case always makes the loop exit\r
597                 case 1:\r
598                   h = buttonDesc[0].hwnd; break; // could be NULL\r
599                 case 2:\r
600                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
601                   h = e1; break;\r
602                 case 3:\r
603                   if(!EngineOutputIsUp()) continue;\r
604                   h = e2; break;\r
605                 case 4:\r
606                   if(!MoveHistoryIsUp()) continue;\r
607                   h = mh; break;\r
608 //              case 6: // input to eval graph does not seem to get here!\r
609 //                if(!EvalGraphIsUp()) continue;\r
610 //                h = evalGraphDialog; break;\r
611                 case 5:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("display");\r
614                   h = hText; break;\r
615                 case 6:\r
616                   if(!appData.icsActive) continue;\r
617                   SAY("input");\r
618                   h = hInput; break;\r
619             }\r
620           } while(h == 0);\r
621 \r
622           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
623           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
624           SetFocus(h);\r
625 \r
626           continue; // this message now has been processed\r
627         }\r
628       }\r
629 \r
630       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
631           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
632           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
633           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
634           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
635           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
636           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
637           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
638           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
639           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
640         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
641         for(i=0; i<MAX_CHAT; i++) \r
642             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
643                 done = 1; break;\r
644         }\r
645         if(done) continue; // [HGM] chat: end patch\r
646         TranslateMessage(&msg); /* Translates virtual key codes */\r
647         DispatchMessage(&msg);  /* Dispatches message to window */\r
648       }\r
649     }\r
650 \r
651 \r
652   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
653 }\r
654 \r
655 /*---------------------------------------------------------------------------*\\r
656  *\r
657  * Initialization functions\r
658  *\r
659 \*---------------------------------------------------------------------------*/\r
660 \r
661 void\r
662 SetUserLogo()\r
663 {   // update user logo if necessary\r
664     static char oldUserName[MSG_SIZ], *curName;\r
665 \r
666     if(appData.autoLogo) {\r
667           curName = UserName();\r
668           if(strcmp(curName, oldUserName)) {\r
669                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
670                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
671                 strcpy(oldUserName, curName);\r
672           }\r
673     }\r
674 }\r
675 \r
676 BOOL\r
677 InitApplication(HINSTANCE hInstance)\r
678 {\r
679   WNDCLASS wc;\r
680 \r
681   /* Fill in window class structure with parameters that describe the */\r
682   /* main window. */\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
685   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
686   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
687   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
688   wc.hInstance     = hInstance;         /* Owner of this class */\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
691   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
692   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
693   wc.lpszClassName = szAppName;                 /* Name to register as */\r
694 \r
695   /* Register the window class and return success/failure code. */\r
696   if (!RegisterClass(&wc)) return FALSE;\r
697 \r
698   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
699   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
700   wc.cbClsExtra    = 0;\r
701   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
702   wc.hInstance     = hInstance;\r
703   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
704   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
705   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
706   wc.lpszMenuName  = NULL;\r
707   wc.lpszClassName = szConsoleName;\r
708 \r
709   if (!RegisterClass(&wc)) return FALSE;\r
710   return TRUE;\r
711 }\r
712 \r
713 \r
714 /* Set by InitInstance, used by EnsureOnScreen */\r
715 int screenHeight, screenWidth;\r
716 \r
717 void\r
718 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
719 {\r
720 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
721   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
722   if (*x > screenWidth - 32) *x = 0;\r
723   if (*y > screenHeight - 32) *y = 0;\r
724   if (*x < minX) *x = minX;\r
725   if (*y < minY) *y = minY;\r
726 }\r
727 \r
728 BOOL\r
729 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
730 {\r
731   HWND hwnd; /* Main window handle. */\r
732   int ibs;\r
733   WINDOWPLACEMENT wp;\r
734   char *filepart;\r
735 \r
736   hInst = hInstance;    /* Store instance handle in our global variable */\r
737 \r
738   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
739     *filepart = NULLCHAR;\r
740   } else {\r
741     GetCurrentDirectory(MSG_SIZ, installDir);\r
742   }\r
743   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
744   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
745   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
746   if (appData.debugMode) {\r
747     debugFP = fopen(appData.nameOfDebugFile, "w");\r
748     setbuf(debugFP, NULL);\r
749   }\r
750 \r
751   InitBackEnd1();\r
752 \r
753 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
754 //  InitEngineUCI( installDir, &second );\r
755 \r
756   /* Create a main window for this application instance. */\r
757   hwnd = CreateWindow(szAppName, szTitle,\r
758                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
759                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
760                       NULL, NULL, hInstance, NULL);\r
761   hwndMain = hwnd;\r
762 \r
763   /* If window could not be created, return "failure" */\r
764   if (!hwnd) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
769   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
770       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (first.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
777         char buf[MSG_SIZ];\r
778         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
779         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
780       }\r
781   }\r
782 \r
783   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
784       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
785 \r
786       if (second.programLogo == NULL && appData.debugMode) {\r
787           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
788       }\r
789   } else if(appData.autoLogo) {\r
790       char buf[MSG_SIZ];\r
791       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
792         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
794       } else\r
795       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
796         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
797         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
798       }\r
799   }\r
800 \r
801   SetUserLogo();\r
802 \r
803   iconWhite = LoadIcon(hInstance, "icon_white");\r
804   iconBlack = LoadIcon(hInstance, "icon_black");\r
805   iconCurrent = iconWhite;\r
806   InitDrawingColors();\r
807   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
808   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
809   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
810     /* Compute window size for each board size, and use the largest\r
811        size that fits on this screen as the default. */\r
812     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
813     if (boardSize == (BoardSize)-1 &&\r
814         winH <= screenHeight\r
815            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
816         && winW <= screenWidth) {\r
817       boardSize = (BoardSize)ibs;\r
818     }\r
819   }\r
820 \r
821   InitDrawingSizes(boardSize, 0);\r
822   InitMenuChecks();\r
823   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
824 \r
825   /* [AS] Load textures if specified */\r
826   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
827   \r
828   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
829       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
830       liteBackTextureMode = appData.liteBackTextureMode;\r
831 \r
832       if (liteBackTexture == NULL && appData.debugMode) {\r
833           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
834       }\r
835   }\r
836   \r
837   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
838       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
839       darkBackTextureMode = appData.darkBackTextureMode;\r
840 \r
841       if (darkBackTexture == NULL && appData.debugMode) {\r
842           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
843       }\r
844   }\r
845 \r
846   mysrandom( (unsigned) time(NULL) );\r
847 \r
848   /* [AS] Restore layout */\r
849   if( wpMoveHistory.visible ) {\r
850       MoveHistoryPopUp();\r
851   }\r
852 \r
853   if( wpEvalGraph.visible ) {\r
854       EvalGraphPopUp();\r
855   }\r
856 \r
857   if( wpEngineOutput.visible ) {\r
858       EngineOutputPopUp();\r
859   }\r
860 \r
861   InitBackEnd2();\r
862 \r
863   /* Make the window visible; update its client area; and return "success" */\r
864   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
865   wp.length = sizeof(WINDOWPLACEMENT);\r
866   wp.flags = 0;\r
867   wp.showCmd = nCmdShow;\r
868   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
869   wp.rcNormalPosition.left = boardX;\r
870   wp.rcNormalPosition.right = boardX + winWidth;\r
871   wp.rcNormalPosition.top = boardY;\r
872   wp.rcNormalPosition.bottom = boardY + winHeight;\r
873   SetWindowPlacement(hwndMain, &wp);\r
874 \r
875   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
876                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
877 \r
878   if (hwndConsole) {\r
879 #if AOT_CONSOLE\r
880     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
881                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
882 #endif\r
883     ShowWindow(hwndConsole, nCmdShow);\r
884   }\r
885   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
886   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
887 \r
888   return TRUE;\r
889 \r
890 }\r
891 \r
892 \r
893 typedef enum {\r
894   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
895   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
896   ArgSettingsFilename,\r
897   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
898 } ArgType;\r
899 \r
900 typedef struct {\r
901   char *argName;\r
902   ArgType argType;\r
903   /***\r
904   union {\r
905     String *pString;       // ArgString\r
906     int *pInt;             // ArgInt\r
907     float *pFloat;         // ArgFloat\r
908     Boolean *pBoolean;     // ArgBoolean\r
909     COLORREF *pColor;      // ArgColor\r
910     ColorClass cc;         // ArgAttribs\r
911     String *pFilename;     // ArgFilename\r
912     BoardSize *pBoardSize; // ArgBoardSize\r
913     int whichFont;         // ArgFont\r
914     DCB *pDCB;             // ArgCommSettings\r
915     String *pFilename;     // ArgSettingsFilename\r
916   } argLoc;\r
917   ***/\r
918   LPVOID argLoc;\r
919   BOOL save;\r
920 } ArgDescriptor;\r
921 \r
922 int junk;\r
923 ArgDescriptor argDescriptors[] = {\r
924   /* positional arguments */\r
925   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
926   { "", ArgNone, NULL },\r
927   /* keyword arguments */\r
928   JAWS_ARGS\r
929   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
930   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
931   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
932   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
933   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
934   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
935   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
936   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
937   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
938   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
939   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
940   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
941   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
942   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
943   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
944   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
945   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
946   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
947     FALSE },\r
948   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
949     FALSE },\r
950   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
951     FALSE },\r
952   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
953   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
954     FALSE },\r
955   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
956   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
957   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
958   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
959   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
960   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
961   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
962   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
963   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
964   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
965   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
966   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
967   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
968   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
969   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
970   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
971   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
972   /*!!bitmapDirectory?*/\r
973   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
974   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
975   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
976   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
977   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
978   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
979   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
980   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
981   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
982   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
983   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
984   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
985   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
986   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
987   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
988   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
989   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
990   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
991   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
992   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
993   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
994   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
995   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
996   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
997   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
998   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
999   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1000   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1001   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
1002   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
1003   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
1004   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1005   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1006   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1007   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1008   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1009   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1010   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1011   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1012   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1013   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1014   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1015   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1016   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1017   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1018   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1019   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1020   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1021   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1022   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1023   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1024   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1025   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1026   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1027   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1028   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1029   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1030   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1031   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1032   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1033   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1034   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1035   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1036   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1037   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1038   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1039   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1040   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1041   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1042   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1043   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1044   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1045   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1046   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1047   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1048   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1049   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1050   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1051   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1052   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1053   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1054   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1055   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1056   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1057   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1058   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1059   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1060   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1061   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1062   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1063   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1064   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1065   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1066   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1067     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1068   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1069   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1070   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1071   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1072   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1073   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1074   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1075   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1076     TRUE }, /* must come after all fonts */\r
1077   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1078   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1079     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1080   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1081   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1082   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1083   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1084   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1085   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1086   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1087   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1088   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1089   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1090   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1091   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1092   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1093   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1094   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1095   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1096   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1097   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1098   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1099   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1100   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1101   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1102   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1103   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1104   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1105   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1106   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1107   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1108   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1109   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1110   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1111   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1112   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1113   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1114   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1115   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1116   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1117   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1118   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1119   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1120   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1121   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1122   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1123   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1124   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1125   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1126   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1127   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1128   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1129   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1130   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1131   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1132   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1133   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1134   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1135   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1136   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1137   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1138   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1139   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1140   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1141   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1142   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1143   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1144   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1145   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1146   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1147   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1148   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1149   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1150   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1151   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1152   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1153   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1154   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1155   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1156   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1157   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1158   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1159   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1160   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1161   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1162   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1163   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1164   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1165   { "highlightLastMove", ArgBoolean,\r
1166     (LPVOID) &appData.highlightLastMove, TRUE },\r
1167   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1168   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1169   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1170   { "highlightDragging", ArgBoolean,\r
1171     (LPVOID) &appData.highlightDragging, TRUE },\r
1172   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1173   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1174   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1175   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1176   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1177   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1178   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1179   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1180   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1181   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1182   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1183   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1184   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1185   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1186   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1187   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1188   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1189   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1190   { "soundShout", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1192   { "soundSShout", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1194   { "soundChannel1", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1196   { "soundChannel", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1198   { "soundKibitz", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1200   { "soundTell", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1202   { "soundChallenge", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1204   { "soundRequest", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1206   { "soundSeek", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1208   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1209   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1210   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1211   { "soundIcsLoss", ArgFilename, \r
1212     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1213   { "soundIcsDraw", ArgFilename, \r
1214     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1215   { "soundIcsUnfinished", ArgFilename, \r
1216     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1217   { "soundIcsAlarm", ArgFilename, \r
1218     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1219   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1220   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1221   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1222   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1223   { "reuseChessPrograms", ArgBoolean,\r
1224     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1225   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1226   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1227   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1228   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1229   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1230   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1231   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1232   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1233   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1234   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1235   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1236   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1237   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1238   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1239   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1240     TRUE },\r
1241   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1242     TRUE },\r
1243   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1244   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1245   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1246   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1247   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1248   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1249   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1250   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1251   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1252   /* [AS] New features */\r
1253   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1254   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1255   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1256   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1257   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1258   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1259   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1260   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1261   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1262   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1263   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1264   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1265   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1266   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1267   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1268   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1269   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1270   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1271   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1272   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1273   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1274   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1275   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1276   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1277   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1278   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1279   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1280   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1281   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1282   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1283   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1284   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1285   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1286   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1287   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1288   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1289   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1290   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1291   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1292   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1293   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1294   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1295   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1296   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1297   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1298   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1299   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1300   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1301   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1302   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1303 \r
1304   /* [HGM] board-size, adjudication and misc. options */\r
1305   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1306   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1307   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1308   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1309   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1310   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1311   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1312   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1313   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1314   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1315   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1316   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1317   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1318   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1319   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1320   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1321   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1322   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1323   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1324   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1325   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1326   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1327   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1328   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1329   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1330   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1331   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1332   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1333   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1334   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1335   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1336   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1337   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1338   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1339 \r
1340 #ifdef ZIPPY\r
1341   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1342   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1343   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1344   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1345   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1346   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1347   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1348   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1349   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1350   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1351   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1352   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1353   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1354     FALSE },\r
1355   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1356   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1357   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1358   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1359   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1360   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1361   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1362     FALSE },\r
1363   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1364   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1365   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1366   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1367   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1368   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1369   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1370   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1371   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1372   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1373   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1374   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1375   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1376   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1377   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1378   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1379   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1380   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1381   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1382 #endif\r
1383   /* [HGM] options for broadcasting and time odds */\r
1384   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1385   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1386   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1387   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1388   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1389   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1390   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1391   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1392   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1393   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1394   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1395 \r
1396   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1397   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1398   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1399   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1400   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1401   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1402   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1403   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1404   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1405   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1406   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1407   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1408   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1409   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1410   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1411   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1412   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1413   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1414   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1415   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1416   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1417   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1418   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1419   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1420   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1421   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1422   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1423   /* [AS] Layout stuff */\r
1424   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1425   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1426   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1427   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1428   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1429 \r
1430   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1431   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1432   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1433   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1434   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1435 \r
1436   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1437   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1438   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1439   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1440   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1441 \r
1442   { NULL, ArgNone, NULL, FALSE }\r
1443 };\r
1444 \r
1445 \r
1446 /* Kludge for indirection files on command line */\r
1447 char* lastIndirectionFilename;\r
1448 ArgDescriptor argDescriptorIndirection =\r
1449 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1450 \r
1451 \r
1452 VOID\r
1453 ExitArgError(char *msg, char *badArg)\r
1454 {\r
1455   char buf[MSG_SIZ];\r
1456 \r
1457   sprintf(buf, "%s %s", msg, badArg);\r
1458   DisplayFatalError(buf, 0, 2);\r
1459   exit(2);\r
1460 }\r
1461 \r
1462 /* Command line font name parser.  NULL name means do nothing.\r
1463    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1464    For backward compatibility, syntax without the colon is also\r
1465    accepted, but font names with digits in them won't work in that case.\r
1466 */\r
1467 VOID\r
1468 ParseFontName(char *name, MyFontParams *mfp)\r
1469 {\r
1470   char *p, *q;\r
1471   if (name == NULL) return;\r
1472   p = name;\r
1473   q = strchr(p, ':');\r
1474   if (q) {\r
1475     if (q - p >= sizeof(mfp->faceName))\r
1476       ExitArgError("Font name too long:", name);\r
1477     memcpy(mfp->faceName, p, q - p);\r
1478     mfp->faceName[q - p] = NULLCHAR;\r
1479     p = q + 1;\r
1480   } else {\r
1481     q = mfp->faceName;\r
1482     while (*p && !isdigit(*p)) {\r
1483       *q++ = *p++;\r
1484       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1485         ExitArgError("Font name too long:", name);\r
1486     }\r
1487     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1488     *q = NULLCHAR;\r
1489   }\r
1490   if (!*p) ExitArgError("Font point size missing:", name);\r
1491   mfp->pointSize = (float) atof(p);\r
1492   mfp->bold = (strchr(p, 'b') != NULL);\r
1493   mfp->italic = (strchr(p, 'i') != NULL);\r
1494   mfp->underline = (strchr(p, 'u') != NULL);\r
1495   mfp->strikeout = (strchr(p, 's') != NULL);\r
1496 }\r
1497 \r
1498 /* Color name parser.\r
1499    X version accepts X color names, but this one\r
1500    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1501 COLORREF\r
1502 ParseColorName(char *name)\r
1503 {\r
1504   int red, green, blue, count;\r
1505   char buf[MSG_SIZ];\r
1506 \r
1507   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1508   if (count != 3) {\r
1509     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1510       &red, &green, &blue);\r
1511   }\r
1512   if (count != 3) {\r
1513     sprintf(buf, "Can't parse color name %s", name);\r
1514     DisplayError(buf, 0);\r
1515     return RGB(0, 0, 0);\r
1516   }\r
1517   return PALETTERGB(red, green, blue);\r
1518 }\r
1519 \r
1520 \r
1521 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1522 {\r
1523   char *e = argValue;\r
1524   int eff = 0;\r
1525 \r
1526   while (*e) {\r
1527     if (*e == 'b')      eff |= CFE_BOLD;\r
1528     else if (*e == 'i') eff |= CFE_ITALIC;\r
1529     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1530     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1531     else if (*e == '#' || isdigit(*e)) break;\r
1532     e++;\r
1533   }\r
1534   *effects = eff;\r
1535   *color   = ParseColorName(e);\r
1536 }\r
1537 \r
1538 \r
1539 BoardSize\r
1540 ParseBoardSize(char *name)\r
1541 {\r
1542   BoardSize bs = SizeTiny;\r
1543   while (sizeInfo[bs].name != NULL) {\r
1544     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1545     bs++;\r
1546   }\r
1547   ExitArgError("Unrecognized board size value", name);\r
1548   return bs; /* not reached */\r
1549 }\r
1550 \r
1551 \r
1552 char\r
1553 StringGet(void *getClosure)\r
1554 {\r
1555   char **p = (char **) getClosure;\r
1556   return *((*p)++);\r
1557 }\r
1558 \r
1559 char\r
1560 FileGet(void *getClosure)\r
1561 {\r
1562   int c;\r
1563   FILE* f = (FILE*) getClosure;\r
1564 \r
1565   c = getc(f);\r
1566   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1567   if (c == EOF)\r
1568     return NULLCHAR;\r
1569   else\r
1570     return (char) c;\r
1571 }\r
1572 \r
1573 /* Parse settings file named "name". If file found, return the\r
1574    full name in fullname and return TRUE; else return FALSE */\r
1575 BOOLEAN\r
1576 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1577 {\r
1578   char *dummy;\r
1579   FILE *f;\r
1580   int ok; char buf[MSG_SIZ];\r
1581 \r
1582   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1583   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1584     sprintf(buf, "%s.ini", name);\r
1585     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1586   }\r
1587   if (ok) {\r
1588     f = fopen(fullname, "r");\r
1589     if (f != NULL) {\r
1590       ParseArgs(FileGet, f);\r
1591       fclose(f);\r
1592       return TRUE;\r
1593     }\r
1594   }\r
1595   return FALSE;\r
1596 }\r
1597 \r
1598 VOID\r
1599 ParseArgs(GetFunc get, void *cl)\r
1600 {\r
1601   char argName[ARG_MAX];\r
1602   char argValue[ARG_MAX];\r
1603   ArgDescriptor *ad;\r
1604   char start;\r
1605   char *q;\r
1606   int i, octval;\r
1607   char ch;\r
1608   int posarg = 0;\r
1609 \r
1610   ch = get(cl);\r
1611   for (;;) {\r
1612     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1613     if (ch == NULLCHAR) break;\r
1614     if (ch == ';') {\r
1615       /* Comment to end of line */\r
1616       ch = get(cl);\r
1617       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1618       continue;\r
1619     } else if (ch == '/' || ch == '-') {\r
1620       /* Switch */\r
1621       q = argName;\r
1622       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1623              ch != '\n' && ch != '\t') {\r
1624         *q++ = ch;\r
1625         ch = get(cl);\r
1626       }\r
1627       *q = NULLCHAR;\r
1628 \r
1629       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1630         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1631 \r
1632       if (ad->argName == NULL)\r
1633         ExitArgError("Unrecognized argument", argName);\r
1634 \r
1635     } else if (ch == '@') {\r
1636       /* Indirection file */\r
1637       ad = &argDescriptorIndirection;\r
1638       ch = get(cl);\r
1639     } else {\r
1640       /* Positional argument */\r
1641       ad = &argDescriptors[posarg++];\r
1642       strcpy(argName, ad->argName);\r
1643     }\r
1644 \r
1645     if (ad->argType == ArgTrue) {\r
1646       *(Boolean *) ad->argLoc = TRUE;\r
1647       continue;\r
1648     }\r
1649     if (ad->argType == ArgFalse) {\r
1650       *(Boolean *) ad->argLoc = FALSE;\r
1651       continue;\r
1652     }\r
1653 \r
1654     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1655     if (ch == NULLCHAR || ch == '\n') {\r
1656       ExitArgError("No value provided for argument", argName);\r
1657     }\r
1658     q = argValue;\r
1659     if (ch == '{') {\r
1660       // Quoting with { }.  No characters have to (or can) be escaped.\r
1661       // Thus the string cannot contain a '}' character.\r
1662       start = ch;\r
1663       ch = get(cl);\r
1664       while (start) {\r
1665         switch (ch) {\r
1666         case NULLCHAR:\r
1667           start = NULLCHAR;\r
1668           break;\r
1669           \r
1670         case '}':\r
1671           ch = get(cl);\r
1672           start = NULLCHAR;\r
1673           break;\r
1674 \r
1675         default:\r
1676           *q++ = ch;\r
1677           ch = get(cl);\r
1678           break;\r
1679         }\r
1680       }   \r
1681     } else if (ch == '\'' || ch == '"') {\r
1682       // Quoting with ' ' or " ", with \ as escape character.\r
1683       // Inconvenient for long strings that may contain Windows filenames.\r
1684       start = ch;\r
1685       ch = get(cl);\r
1686       while (start) {\r
1687         switch (ch) {\r
1688         case NULLCHAR:\r
1689           start = NULLCHAR;\r
1690           break;\r
1691 \r
1692         default:\r
1693         not_special:\r
1694           *q++ = ch;\r
1695           ch = get(cl);\r
1696           break;\r
1697 \r
1698         case '\'':\r
1699         case '\"':\r
1700           if (ch == start) {\r
1701             ch = get(cl);\r
1702             start = NULLCHAR;\r
1703             break;\r
1704           } else {\r
1705             goto not_special;\r
1706           }\r
1707 \r
1708         case '\\':\r
1709           if (ad->argType == ArgFilename\r
1710               || ad->argType == ArgSettingsFilename) {\r
1711               goto not_special;\r
1712           }\r
1713           ch = get(cl);\r
1714           switch (ch) {\r
1715           case NULLCHAR:\r
1716             ExitArgError("Incomplete \\ escape in value for", argName);\r
1717             break;\r
1718           case 'n':\r
1719             *q++ = '\n';\r
1720             ch = get(cl);\r
1721             break;\r
1722           case 'r':\r
1723             *q++ = '\r';\r
1724             ch = get(cl);\r
1725             break;\r
1726           case 't':\r
1727             *q++ = '\t';\r
1728             ch = get(cl);\r
1729             break;\r
1730           case 'b':\r
1731             *q++ = '\b';\r
1732             ch = get(cl);\r
1733             break;\r
1734           case 'f':\r
1735             *q++ = '\f';\r
1736             ch = get(cl);\r
1737             break;\r
1738           default:\r
1739             octval = 0;\r
1740             for (i = 0; i < 3; i++) {\r
1741               if (ch >= '0' && ch <= '7') {\r
1742                 octval = octval*8 + (ch - '0');\r
1743                 ch = get(cl);\r
1744               } else {\r
1745                 break;\r
1746               }\r
1747             }\r
1748             if (i > 0) {\r
1749               *q++ = (char) octval;\r
1750             } else {\r
1751               *q++ = ch;\r
1752               ch = get(cl);\r
1753             }\r
1754             break;\r
1755           }\r
1756           break;\r
1757         }\r
1758       }\r
1759     } else {\r
1760       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1761         *q++ = ch;\r
1762         ch = get(cl);\r
1763       }\r
1764     }\r
1765     *q = NULLCHAR;\r
1766 \r
1767     switch (ad->argType) {\r
1768     case ArgInt:\r
1769       *(int *) ad->argLoc = atoi(argValue);\r
1770       break;\r
1771 \r
1772     case ArgX:\r
1773       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1774       break;\r
1775 \r
1776     case ArgY:\r
1777       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1778       break;\r
1779 \r
1780     case ArgZ:\r
1781       *(int *) ad->argLoc = atoi(argValue);\r
1782       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1783       break;\r
1784 \r
1785     case ArgFloat:\r
1786       *(float *) ad->argLoc = (float) atof(argValue);\r
1787       break;\r
1788 \r
1789     case ArgString:\r
1790     case ArgFilename:\r
1791       *(char **) ad->argLoc = strdup(argValue);\r
1792       break;\r
1793 \r
1794     case ArgSettingsFilename:\r
1795       {\r
1796         char fullname[MSG_SIZ];\r
1797         if (ParseSettingsFile(argValue, fullname)) {\r
1798           if (ad->argLoc != NULL) {\r
1799             *(char **) ad->argLoc = strdup(fullname);\r
1800           }\r
1801         } else {\r
1802           if (ad->argLoc != NULL) {\r
1803           } else {\r
1804             ExitArgError("Failed to open indirection file", argValue);\r
1805           }\r
1806         }\r
1807       }\r
1808       break;\r
1809 \r
1810     case ArgBoolean:\r
1811       switch (argValue[0]) {\r
1812       case 't':\r
1813       case 'T':\r
1814         *(Boolean *) ad->argLoc = TRUE;\r
1815         break;\r
1816       case 'f':\r
1817       case 'F':\r
1818         *(Boolean *) ad->argLoc = FALSE;\r
1819         break;\r
1820       default:\r
1821         ExitArgError("Unrecognized boolean argument value", argValue);\r
1822         break;\r
1823       }\r
1824       break;\r
1825 \r
1826     case ArgColor:\r
1827       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1828       break;\r
1829 \r
1830     case ArgAttribs: {\r
1831       ColorClass cc = (ColorClass)ad->argLoc;\r
1832       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1833       }\r
1834       break;\r
1835       \r
1836     case ArgBoardSize:\r
1837       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1838       break;\r
1839 \r
1840     case ArgFont:\r
1841       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1842       break;\r
1843 \r
1844     case ArgCommSettings:\r
1845       ParseCommSettings(argValue, &dcb);\r
1846       break;\r
1847 \r
1848     case ArgNone:\r
1849       ExitArgError("Unrecognized argument", argValue);\r
1850       break;\r
1851     case ArgTrue:\r
1852     case ArgFalse: ;\r
1853     }\r
1854   }\r
1855 }\r
1856 \r
1857 VOID\r
1858 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1859 {\r
1860   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1861   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1862   DeleteDC(hdc);\r
1863   lf->lfWidth = 0;\r
1864   lf->lfEscapement = 0;\r
1865   lf->lfOrientation = 0;\r
1866   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1867   lf->lfItalic = mfp->italic;\r
1868   lf->lfUnderline = mfp->underline;\r
1869   lf->lfStrikeOut = mfp->strikeout;\r
1870   lf->lfCharSet = DEFAULT_CHARSET;\r
1871   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1872   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1873   lf->lfQuality = DEFAULT_QUALITY;\r
1874   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1875   strcpy(lf->lfFaceName, mfp->faceName);\r
1876 }\r
1877 \r
1878 VOID\r
1879 CreateFontInMF(MyFont *mf)\r
1880 {\r
1881   LFfromMFP(&mf->lf, &mf->mfp);\r
1882   if (mf->hf) DeleteObject(mf->hf);\r
1883   mf->hf = CreateFontIndirect(&mf->lf);\r
1884 }\r
1885 \r
1886 VOID\r
1887 SetDefaultTextAttribs()\r
1888 {\r
1889   ColorClass cc;\r
1890   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1891     ParseAttribs(&textAttribs[cc].color, \r
1892                  &textAttribs[cc].effects, \r
1893                  defaultTextAttribs[cc]);\r
1894   }\r
1895 }\r
1896 \r
1897 VOID\r
1898 SetDefaultSounds()\r
1899 {\r
1900   ColorClass cc;\r
1901   SoundClass sc;\r
1902   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1903     textAttribs[cc].sound.name = strdup("");\r
1904     textAttribs[cc].sound.data = NULL;\r
1905   }\r
1906   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1907     sounds[sc].name = strdup("");\r
1908     sounds[sc].data = NULL;\r
1909   }\r
1910   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1911 }\r
1912 \r
1913 VOID\r
1914 LoadAllSounds()\r
1915 {\r
1916   ColorClass cc;\r
1917   SoundClass sc;\r
1918   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1919     MyLoadSound(&textAttribs[cc].sound);\r
1920   }\r
1921   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1922     MyLoadSound(&sounds[sc]);\r
1923   }\r
1924 }\r
1925 \r
1926 VOID\r
1927 InitAppData(LPSTR lpCmdLine)\r
1928 {\r
1929   int i, j;\r
1930   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1931   char *dummy, *p;\r
1932 \r
1933   programName = szAppName;\r
1934 \r
1935   /* Initialize to defaults */\r
1936   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1937   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1938   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1939   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1940   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1941   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1942   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1943   SetDefaultTextAttribs();\r
1944   SetDefaultSounds();\r
1945   appData.movesPerSession = MOVES_PER_SESSION;\r
1946   appData.initString = INIT_STRING;\r
1947   appData.secondInitString = INIT_STRING;\r
1948   appData.firstComputerString = COMPUTER_STRING;\r
1949   appData.secondComputerString = COMPUTER_STRING;\r
1950   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1951   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1952   appData.firstPlaysBlack = FALSE;\r
1953   appData.noChessProgram = FALSE;\r
1954   chessProgram = FALSE;\r
1955   appData.firstHost = FIRST_HOST;\r
1956   appData.secondHost = SECOND_HOST;\r
1957   appData.firstDirectory = FIRST_DIRECTORY;\r
1958   appData.secondDirectory = SECOND_DIRECTORY;\r
1959   appData.bitmapDirectory = "";\r
1960   appData.remoteShell = REMOTE_SHELL;\r
1961   appData.remoteUser = "";\r
1962   appData.timeDelay = TIME_DELAY;\r
1963   appData.timeControl = TIME_CONTROL;\r
1964   appData.timeIncrement = TIME_INCREMENT;\r
1965   appData.icsActive = FALSE;\r
1966   appData.icsHost = "";\r
1967   appData.icsPort = ICS_PORT;\r
1968   appData.icsCommPort = ICS_COMM_PORT;\r
1969   appData.icsLogon = ICS_LOGON;\r
1970   appData.icsHelper = "";\r
1971   appData.useTelnet = FALSE;\r
1972   appData.telnetProgram = TELNET_PROGRAM;\r
1973   appData.gateway = "";\r
1974   appData.loadGameFile = "";\r
1975   appData.loadGameIndex = 0;\r
1976   appData.saveGameFile = "";\r
1977   appData.autoSaveGames = FALSE;\r
1978   appData.loadPositionFile = "";\r
1979   appData.loadPositionIndex = 1;\r
1980   appData.savePositionFile = "";\r
1981   appData.matchMode = FALSE;\r
1982   appData.matchGames = 0;\r
1983   appData.monoMode = FALSE;\r
1984   appData.debugMode = FALSE;\r
1985   appData.clockMode = TRUE;\r
1986   boardSize = (BoardSize) -1; /* determine by screen size */\r
1987   appData.Iconic = FALSE; /*unused*/\r
1988   appData.searchTime = "";\r
1989   appData.searchDepth = 0;\r
1990   appData.showCoords = FALSE;\r
1991   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1992   appData.autoCallFlag = FALSE;\r
1993   appData.flipView = FALSE;\r
1994   appData.autoFlipView = TRUE;\r
1995   appData.cmailGameName = "";\r
1996   appData.alwaysPromoteToQueen = FALSE;\r
1997   appData.oldSaveStyle = FALSE;\r
1998   appData.quietPlay = FALSE;\r
1999   appData.showThinking = FALSE;\r
2000   appData.ponderNextMove = TRUE;\r
2001   appData.periodicUpdates = TRUE;\r
2002   appData.popupExitMessage = TRUE;\r
2003   appData.popupMoveErrors = FALSE;\r
2004   appData.autoObserve = FALSE;\r
2005   appData.autoComment = FALSE;\r
2006   appData.animate = TRUE;\r
2007   appData.animSpeed = 10;\r
2008   appData.animateDragging = TRUE;\r
2009   appData.highlightLastMove = TRUE;\r
2010   appData.getMoveList = TRUE;\r
2011   appData.testLegality = TRUE;\r
2012   appData.premove = TRUE;\r
2013   appData.premoveWhite = FALSE;\r
2014   appData.premoveWhiteText = "";\r
2015   appData.premoveBlack = FALSE;\r
2016   appData.premoveBlackText = "";\r
2017   appData.icsAlarm = TRUE;\r
2018   appData.icsAlarmTime = 5000;\r
2019   appData.autoRaiseBoard = TRUE;\r
2020   appData.localLineEditing = TRUE;\r
2021   appData.colorize = TRUE;\r
2022   appData.reuseFirst = TRUE;\r
2023   appData.reuseSecond = TRUE;\r
2024   appData.blindfold = FALSE;\r
2025   appData.icsEngineAnalyze = FALSE;\r
2026   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2027   dcb.DCBlength = sizeof(DCB);\r
2028   dcb.BaudRate = 9600;\r
2029   dcb.fBinary = TRUE;\r
2030   dcb.fParity = FALSE;\r
2031   dcb.fOutxCtsFlow = FALSE;\r
2032   dcb.fOutxDsrFlow = FALSE;\r
2033   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2034   dcb.fDsrSensitivity = FALSE;\r
2035   dcb.fTXContinueOnXoff = TRUE;\r
2036   dcb.fOutX = FALSE;\r
2037   dcb.fInX = FALSE;\r
2038   dcb.fNull = FALSE;\r
2039   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2040   dcb.fAbortOnError = FALSE;\r
2041   dcb.ByteSize = 7;\r
2042   dcb.Parity = SPACEPARITY;\r
2043   dcb.StopBits = ONESTOPBIT;\r
2044   settingsFileName = SETTINGS_FILE;\r
2045   saveSettingsOnExit = TRUE;\r
2046   boardX = CW_USEDEFAULT;\r
2047   boardY = CW_USEDEFAULT;\r
2048   analysisX = CW_USEDEFAULT; \r
2049   analysisY = CW_USEDEFAULT; \r
2050   analysisW = CW_USEDEFAULT;\r
2051   analysisH = CW_USEDEFAULT;\r
2052   commentX = CW_USEDEFAULT; \r
2053   commentY = CW_USEDEFAULT; \r
2054   commentW = CW_USEDEFAULT;\r
2055   commentH = CW_USEDEFAULT;\r
2056   editTagsX = CW_USEDEFAULT; \r
2057   editTagsY = CW_USEDEFAULT; \r
2058   editTagsW = CW_USEDEFAULT;\r
2059   editTagsH = CW_USEDEFAULT;\r
2060   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2061   icsNames = ICS_NAMES;\r
2062   firstChessProgramNames = FCP_NAMES;\r
2063   secondChessProgramNames = SCP_NAMES;\r
2064   appData.initialMode = "";\r
2065   appData.variant = "normal";\r
2066   appData.firstProtocolVersion = PROTOVER;\r
2067   appData.secondProtocolVersion = PROTOVER;\r
2068   appData.showButtonBar = TRUE;\r
2069 \r
2070    /* [AS] New properties (see comments in header file) */\r
2071   appData.firstScoreIsAbsolute = FALSE;\r
2072   appData.secondScoreIsAbsolute = FALSE;\r
2073   appData.saveExtendedInfoInPGN = FALSE;\r
2074   appData.hideThinkingFromHuman = FALSE;\r
2075   appData.liteBackTextureFile = "";\r
2076   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2077   appData.darkBackTextureFile = "";\r
2078   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2079   appData.renderPiecesWithFont = "";\r
2080   appData.fontToPieceTable = "";\r
2081   appData.fontBackColorWhite = 0;\r
2082   appData.fontForeColorWhite = 0;\r
2083   appData.fontBackColorBlack = 0;\r
2084   appData.fontForeColorBlack = 0;\r
2085   appData.fontPieceSize = 80;\r
2086   appData.overrideLineGap = 1;\r
2087   appData.adjudicateLossThreshold = 0;\r
2088   appData.delayBeforeQuit = 0;\r
2089   appData.delayAfterQuit = 0;\r
2090   appData.nameOfDebugFile = "winboard.debug";\r
2091   appData.pgnEventHeader = "Computer Chess Game";\r
2092   appData.defaultFrcPosition = -1;\r
2093   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2094   appData.saveOutOfBookInfo = TRUE;\r
2095   appData.showEvalInMoveHistory = TRUE;\r
2096   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2097   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2098   appData.highlightMoveWithArrow = FALSE;\r
2099   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2100   appData.useStickyWindows = TRUE;\r
2101   appData.adjudicateDrawMoves = 0;\r
2102   appData.autoDisplayComment = TRUE;\r
2103   appData.autoDisplayTags = TRUE;\r
2104   appData.firstIsUCI = FALSE;\r
2105   appData.secondIsUCI = FALSE;\r
2106   appData.firstHasOwnBookUCI = TRUE;\r
2107   appData.secondHasOwnBookUCI = TRUE;\r
2108   appData.polyglotDir = "";\r
2109   appData.usePolyglotBook = FALSE;\r
2110   appData.polyglotBook = "";\r
2111   appData.defaultHashSize = 64;\r
2112   appData.defaultCacheSizeEGTB = 4;\r
2113   appData.defaultPathEGTB = "c:\\egtb";\r
2114   appData.firstOptions = "";\r
2115   appData.secondOptions = "";\r
2116 \r
2117   InitWindowPlacement( &wpGameList );\r
2118   InitWindowPlacement( &wpMoveHistory );\r
2119   InitWindowPlacement( &wpEvalGraph );\r
2120   InitWindowPlacement( &wpEngineOutput );\r
2121   InitWindowPlacement( &wpConsole );\r
2122 \r
2123   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2124   appData.NrFiles      = -1;\r
2125   appData.NrRanks      = -1;\r
2126   appData.holdingsSize = -1;\r
2127   appData.testClaims   = FALSE;\r
2128   appData.checkMates   = FALSE;\r
2129   appData.materialDraws= FALSE;\r
2130   appData.trivialDraws = FALSE;\r
2131   appData.ruleMoves    = 51;\r
2132   appData.drawRepeats  = 6;\r
2133   appData.matchPause   = 10000;\r
2134   appData.alphaRank    = FALSE;\r
2135   appData.allWhite     = FALSE;\r
2136   appData.upsideDown   = FALSE;\r
2137   appData.serverPause  = 15;\r
2138   appData.serverMovesName   = NULL;\r
2139   appData.suppressLoadMoves = FALSE;\r
2140   appData.firstTimeOdds  = 1;\r
2141   appData.secondTimeOdds = 1;\r
2142   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2143   appData.secondAccumulateTC = 1;\r
2144   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2145   appData.secondNPS = -1;\r
2146   appData.engineComments = 1;\r
2147   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2148   appData.egtFormats = "";\r
2149 \r
2150 #ifdef ZIPPY\r
2151   appData.zippyTalk = ZIPPY_TALK;\r
2152   appData.zippyPlay = ZIPPY_PLAY;\r
2153   appData.zippyLines = ZIPPY_LINES;\r
2154   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2155   appData.zippyPassword = ZIPPY_PASSWORD;\r
2156   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2157   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2158   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2159   appData.zippyUseI = ZIPPY_USE_I;\r
2160   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2161   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2162   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2163   appData.zippyGameStart = ZIPPY_GAME_START;\r
2164   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2165   appData.zippyAbort = ZIPPY_ABORT;\r
2166   appData.zippyVariants = ZIPPY_VARIANTS;\r
2167   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2168   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2169 #endif\r
2170 \r
2171   /* Point font array elements to structures and\r
2172      parse default font names */\r
2173   for (i=0; i<NUM_FONTS; i++) {\r
2174     for (j=0; j<NUM_SIZES; j++) {\r
2175       font[j][i] = &fontRec[j][i];\r
2176       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2177     }\r
2178   }\r
2179   \r
2180   /* Parse default settings file if any */\r
2181   if (ParseSettingsFile(settingsFileName, buf)) {\r
2182     settingsFileName = strdup(buf);\r
2183   }\r
2184 \r
2185   /* Parse command line */\r
2186   ParseArgs(StringGet, &lpCmdLine);\r
2187 \r
2188   /* [HGM] make sure board size is acceptable */\r
2189   if(appData.NrFiles > BOARD_SIZE ||\r
2190      appData.NrRanks > BOARD_SIZE   )\r
2191       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2192 \r
2193   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2194    * with options from the command line, we now make an even higher priority\r
2195    * overrule by WB options attached to the engine command line. This so that\r
2196    * tournament managers can use WB options (such as /timeOdds) that follow\r
2197    * the engines.\r
2198    */\r
2199   if(appData.firstChessProgram != NULL) {\r
2200       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2201       static char *f = "first";\r
2202       char buf[MSG_SIZ], *q = buf;\r
2203       if(p != NULL) { // engine command line contains WinBoard options\r
2204           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2205           ParseArgs(StringGet, &q);\r
2206           p[-1] = 0; // cut them offengine command line\r
2207       }\r
2208   }\r
2209   // now do same for second chess program\r
2210   if(appData.secondChessProgram != NULL) {\r
2211       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2212       static char *s = "second";\r
2213       char buf[MSG_SIZ], *q = buf;\r
2214       if(p != NULL) { // engine command line contains WinBoard options\r
2215           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2216           ParseArgs(StringGet, &q);\r
2217           p[-1] = 0; // cut them offengine command line\r
2218       }\r
2219   }\r
2220 \r
2221 \r
2222   /* Propagate options that affect others */\r
2223   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2224   if (appData.icsActive || appData.noChessProgram) {\r
2225      chessProgram = FALSE;  /* not local chess program mode */\r
2226   }\r
2227 \r
2228   /* Open startup dialog if needed */\r
2229   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2230       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2231       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2232                         *appData.secondChessProgram == NULLCHAR))) {\r
2233     FARPROC lpProc;\r
2234     \r
2235     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2236     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2237     FreeProcInstance(lpProc);\r
2238   }\r
2239 \r
2240   /* Make sure save files land in the right (?) directory */\r
2241   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2242     appData.saveGameFile = strdup(buf);\r
2243   }\r
2244   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2245     appData.savePositionFile = strdup(buf);\r
2246   }\r
2247 \r
2248   /* Finish initialization for fonts and sounds */\r
2249   for (i=0; i<NUM_FONTS; i++) {\r
2250     for (j=0; j<NUM_SIZES; j++) {\r
2251       CreateFontInMF(font[j][i]);\r
2252     }\r
2253   }\r
2254   /* xboard, and older WinBoards, controlled the move sound with the\r
2255      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2256      always turn the option on (so that the backend will call us),\r
2257      then let the user turn the sound off by setting it to silence if\r
2258      desired.  To accommodate old winboard.ini files saved by old\r
2259      versions of WinBoard, we also turn off the sound if the option\r
2260      was initially set to false. */\r
2261   if (!appData.ringBellAfterMoves) {\r
2262     sounds[(int)SoundMove].name = strdup("");\r
2263     appData.ringBellAfterMoves = TRUE;\r
2264   }\r
2265   GetCurrentDirectory(MSG_SIZ, currDir);\r
2266   SetCurrentDirectory(installDir);\r
2267   LoadAllSounds();\r
2268   SetCurrentDirectory(currDir);\r
2269 \r
2270   p = icsTextMenuString;\r
2271   if (p[0] == '@') {\r
2272     FILE* f = fopen(p + 1, "r");\r
2273     if (f == NULL) {\r
2274       DisplayFatalError(p + 1, errno, 2);\r
2275       return;\r
2276     }\r
2277     i = fread(buf, 1, sizeof(buf)-1, f);\r
2278     fclose(f);\r
2279     buf[i] = NULLCHAR;\r
2280     p = buf;\r
2281   }\r
2282   ParseIcsTextMenu(strdup(p));\r
2283 }\r
2284 \r
2285 \r
2286 VOID\r
2287 InitMenuChecks()\r
2288 {\r
2289   HMENU hmenu = GetMenu(hwndMain);\r
2290 \r
2291   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2292                         MF_BYCOMMAND|((appData.icsActive &&\r
2293                                        *appData.icsCommPort != NULLCHAR) ?\r
2294                                       MF_ENABLED : MF_GRAYED));\r
2295   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2296                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2297                                      MF_CHECKED : MF_UNCHECKED));\r
2298 }\r
2299 \r
2300 \r
2301 VOID\r
2302 SaveSettings(char* name)\r
2303 {\r
2304   FILE *f;\r
2305   ArgDescriptor *ad;\r
2306   WINDOWPLACEMENT wp;\r
2307   char dir[MSG_SIZ];\r
2308 \r
2309   if (!hwndMain) return;\r
2310 \r
2311   GetCurrentDirectory(MSG_SIZ, dir);\r
2312   SetCurrentDirectory(installDir);\r
2313   f = fopen(name, "w");\r
2314   SetCurrentDirectory(dir);\r
2315   if (f == NULL) {\r
2316     DisplayError(name, errno);\r
2317     return;\r
2318   }\r
2319   fprintf(f, ";\n");\r
2320   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2321   fprintf(f, ";\n");\r
2322   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2323   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2324   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2325   fprintf(f, ";\n");\r
2326 \r
2327   wp.length = sizeof(WINDOWPLACEMENT);\r
2328   GetWindowPlacement(hwndMain, &wp);\r
2329   boardX = wp.rcNormalPosition.left;\r
2330   boardY = wp.rcNormalPosition.top;\r
2331 \r
2332   if (hwndConsole) {\r
2333     GetWindowPlacement(hwndConsole, &wp);\r
2334     wpConsole.x = wp.rcNormalPosition.left;\r
2335     wpConsole.y = wp.rcNormalPosition.top;\r
2336     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2337     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2338   }\r
2339 \r
2340   if (analysisDialog) {\r
2341     GetWindowPlacement(analysisDialog, &wp);\r
2342     analysisX = wp.rcNormalPosition.left;\r
2343     analysisY = wp.rcNormalPosition.top;\r
2344     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2345     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2346   }\r
2347 \r
2348   if (commentDialog) {\r
2349     GetWindowPlacement(commentDialog, &wp);\r
2350     commentX = wp.rcNormalPosition.left;\r
2351     commentY = wp.rcNormalPosition.top;\r
2352     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2353     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2354   }\r
2355 \r
2356   if (editTagsDialog) {\r
2357     GetWindowPlacement(editTagsDialog, &wp);\r
2358     editTagsX = wp.rcNormalPosition.left;\r
2359     editTagsY = wp.rcNormalPosition.top;\r
2360     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2361     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2362   }\r
2363 \r
2364   if (gameListDialog) {\r
2365     GetWindowPlacement(gameListDialog, &wp);\r
2366     wpGameList.x = wp.rcNormalPosition.left;\r
2367     wpGameList.y = wp.rcNormalPosition.top;\r
2368     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2369     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2370   }\r
2371 \r
2372   /* [AS] Move history */\r
2373   wpMoveHistory.visible = MoveHistoryIsUp();\r
2374   \r
2375   if( moveHistoryDialog ) {\r
2376     GetWindowPlacement(moveHistoryDialog, &wp);\r
2377     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2378     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2379     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2380     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2381   }\r
2382 \r
2383   /* [AS] Eval graph */\r
2384   wpEvalGraph.visible = EvalGraphIsUp();\r
2385 \r
2386   if( evalGraphDialog ) {\r
2387     GetWindowPlacement(evalGraphDialog, &wp);\r
2388     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2389     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2390     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2391     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2392   }\r
2393 \r
2394   /* [AS] Engine output */\r
2395   wpEngineOutput.visible = EngineOutputIsUp();\r
2396 \r
2397   if( engineOutputDialog ) {\r
2398     GetWindowPlacement(engineOutputDialog, &wp);\r
2399     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2400     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2401     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2402     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2403   }\r
2404 \r
2405   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2406     if (!ad->save) continue;\r
2407     switch (ad->argType) {\r
2408     case ArgString:\r
2409       {\r
2410         char *p = *(char **)ad->argLoc;\r
2411         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2412           /* Quote multiline values or \-containing values\r
2413              with { } if possible */\r
2414           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2415         } else {\r
2416           /* Else quote with " " */\r
2417           fprintf(f, "/%s=\"", ad->argName);\r
2418           while (*p) {\r
2419             if (*p == '\n') fprintf(f, "\n");\r
2420             else if (*p == '\r') fprintf(f, "\\r");\r
2421             else if (*p == '\t') fprintf(f, "\\t");\r
2422             else if (*p == '\b') fprintf(f, "\\b");\r
2423             else if (*p == '\f') fprintf(f, "\\f");\r
2424             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2425             else if (*p == '\"') fprintf(f, "\\\"");\r
2426             else if (*p == '\\') fprintf(f, "\\\\");\r
2427             else putc(*p, f);\r
2428             p++;\r
2429           }\r
2430           fprintf(f, "\"\n");\r
2431         }\r
2432       }\r
2433       break;\r
2434     case ArgInt:\r
2435     case ArgZ:\r
2436       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2437       break;\r
2438     case ArgX:\r
2439       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2440       break;\r
2441     case ArgY:\r
2442       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2443       break;\r
2444     case ArgFloat:\r
2445       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2446       break;\r
2447     case ArgBoolean:\r
2448       fprintf(f, "/%s=%s\n", ad->argName, \r
2449         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2450       break;\r
2451     case ArgTrue:\r
2452       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2453       break;\r
2454     case ArgFalse:\r
2455       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2456       break;\r
2457     case ArgColor:\r
2458       {\r
2459         COLORREF color = *(COLORREF *)ad->argLoc;\r
2460         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2461           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2462       }\r
2463       break;\r
2464     case ArgAttribs:\r
2465       {\r
2466         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2467         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2468           (ta->effects & CFE_BOLD) ? "b" : "",\r
2469           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2470           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2471           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2472           (ta->effects) ? " " : "",\r
2473           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2474       }\r
2475       break;\r
2476     case ArgFilename:\r
2477       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2478         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2479       } else {\r
2480         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2481       }\r
2482       break;\r
2483     case ArgBoardSize:\r
2484       fprintf(f, "/%s=%s\n", ad->argName,\r
2485               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2486       break;\r
2487     case ArgFont:\r
2488       {\r
2489         int bs;\r
2490         for (bs=0; bs<NUM_SIZES; bs++) {\r
2491           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2492           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2493           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2494             ad->argName, mfp->faceName, mfp->pointSize,\r
2495             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2496             mfp->bold ? "b" : "",\r
2497             mfp->italic ? "i" : "",\r
2498             mfp->underline ? "u" : "",\r
2499             mfp->strikeout ? "s" : "");\r
2500         }\r
2501       }\r
2502       break;\r
2503     case ArgCommSettings:\r
2504       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2505     case ArgNone:\r
2506     case ArgSettingsFilename: ;\r
2507     }\r
2508   }\r
2509   fclose(f);\r
2510 }\r
2511 \r
2512 \r
2513 \r
2514 /*---------------------------------------------------------------------------*\\r
2515  *\r
2516  * GDI board drawing routines\r
2517  *\r
2518 \*---------------------------------------------------------------------------*/\r
2519 \r
2520 /* [AS] Draw square using background texture */\r
2521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2522 {\r
2523     XFORM   x;\r
2524 \r
2525     if( mode == 0 ) {\r
2526         return; /* Should never happen! */\r
2527     }\r
2528 \r
2529     SetGraphicsMode( dst, GM_ADVANCED );\r
2530 \r
2531     switch( mode ) {\r
2532     case 1:\r
2533         /* Identity */\r
2534         break;\r
2535     case 2:\r
2536         /* X reflection */\r
2537         x.eM11 = -1.0;\r
2538         x.eM12 = 0;\r
2539         x.eM21 = 0;\r
2540         x.eM22 = 1.0;\r
2541         x.eDx = (FLOAT) dw + dx - 1;\r
2542         x.eDy = 0;\r
2543         dx = 0;\r
2544         SetWorldTransform( dst, &x );\r
2545         break;\r
2546     case 3:\r
2547         /* Y reflection */\r
2548         x.eM11 = 1.0;\r
2549         x.eM12 = 0;\r
2550         x.eM21 = 0;\r
2551         x.eM22 = -1.0;\r
2552         x.eDx = 0;\r
2553         x.eDy = (FLOAT) dh + dy - 1;\r
2554         dy = 0;\r
2555         SetWorldTransform( dst, &x );\r
2556         break;\r
2557     case 4:\r
2558         /* X/Y flip */\r
2559         x.eM11 = 0;\r
2560         x.eM12 = 1.0;\r
2561         x.eM21 = 1.0;\r
2562         x.eM22 = 0;\r
2563         x.eDx = (FLOAT) dx;\r
2564         x.eDy = (FLOAT) dy;\r
2565         dx = 0;\r
2566         dy = 0;\r
2567         SetWorldTransform( dst, &x );\r
2568         break;\r
2569     }\r
2570 \r
2571     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2572 \r
2573     x.eM11 = 1.0;\r
2574     x.eM12 = 0;\r
2575     x.eM21 = 0;\r
2576     x.eM22 = 1.0;\r
2577     x.eDx = 0;\r
2578     x.eDy = 0;\r
2579     SetWorldTransform( dst, &x );\r
2580 \r
2581     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2582 }\r
2583 \r
2584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2585 enum {\r
2586     PM_WP = (int) WhitePawn, \r
2587     PM_WN = (int) WhiteKnight, \r
2588     PM_WB = (int) WhiteBishop, \r
2589     PM_WR = (int) WhiteRook, \r
2590     PM_WQ = (int) WhiteQueen, \r
2591     PM_WF = (int) WhiteFerz, \r
2592     PM_WW = (int) WhiteWazir, \r
2593     PM_WE = (int) WhiteAlfil, \r
2594     PM_WM = (int) WhiteMan, \r
2595     PM_WO = (int) WhiteCannon, \r
2596     PM_WU = (int) WhiteUnicorn, \r
2597     PM_WH = (int) WhiteNightrider, \r
2598     PM_WA = (int) WhiteAngel, \r
2599     PM_WC = (int) WhiteMarshall, \r
2600     PM_WAB = (int) WhiteCardinal, \r
2601     PM_WD = (int) WhiteDragon, \r
2602     PM_WL = (int) WhiteLance, \r
2603     PM_WS = (int) WhiteCobra, \r
2604     PM_WV = (int) WhiteFalcon, \r
2605     PM_WSG = (int) WhiteSilver, \r
2606     PM_WG = (int) WhiteGrasshopper, \r
2607     PM_WK = (int) WhiteKing,\r
2608     PM_BP = (int) BlackPawn, \r
2609     PM_BN = (int) BlackKnight, \r
2610     PM_BB = (int) BlackBishop, \r
2611     PM_BR = (int) BlackRook, \r
2612     PM_BQ = (int) BlackQueen, \r
2613     PM_BF = (int) BlackFerz, \r
2614     PM_BW = (int) BlackWazir, \r
2615     PM_BE = (int) BlackAlfil, \r
2616     PM_BM = (int) BlackMan,\r
2617     PM_BO = (int) BlackCannon, \r
2618     PM_BU = (int) BlackUnicorn, \r
2619     PM_BH = (int) BlackNightrider, \r
2620     PM_BA = (int) BlackAngel, \r
2621     PM_BC = (int) BlackMarshall, \r
2622     PM_BG = (int) BlackGrasshopper, \r
2623     PM_BAB = (int) BlackCardinal,\r
2624     PM_BD = (int) BlackDragon,\r
2625     PM_BL = (int) BlackLance,\r
2626     PM_BS = (int) BlackCobra,\r
2627     PM_BV = (int) BlackFalcon,\r
2628     PM_BSG = (int) BlackSilver,\r
2629     PM_BK = (int) BlackKing\r
2630 };\r
2631 \r
2632 static HFONT hPieceFont = NULL;\r
2633 static HBITMAP hPieceMask[(int) EmptySquare];\r
2634 static HBITMAP hPieceFace[(int) EmptySquare];\r
2635 static int fontBitmapSquareSize = 0;\r
2636 static char pieceToFontChar[(int) EmptySquare] =\r
2637                               { 'p', 'n', 'b', 'r', 'q', \r
2638                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2639                       'k', 'o', 'm', 'v', 't', 'w', \r
2640                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2641                                                               'l' };\r
2642 \r
2643 extern BOOL SetCharTable( char *table, const char * map );\r
2644 /* [HGM] moved to backend.c */\r
2645 \r
2646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2647 {\r
2648     HBRUSH hbrush;\r
2649     BYTE r1 = GetRValue( color );\r
2650     BYTE g1 = GetGValue( color );\r
2651     BYTE b1 = GetBValue( color );\r
2652     BYTE r2 = r1 / 2;\r
2653     BYTE g2 = g1 / 2;\r
2654     BYTE b2 = b1 / 2;\r
2655     RECT rc;\r
2656 \r
2657     /* Create a uniform background first */\r
2658     hbrush = CreateSolidBrush( color );\r
2659     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2660     FillRect( hdc, &rc, hbrush );\r
2661     DeleteObject( hbrush );\r
2662     \r
2663     if( mode == 1 ) {\r
2664         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2665         int steps = squareSize / 2;\r
2666         int i;\r
2667 \r
2668         for( i=0; i<steps; i++ ) {\r
2669             BYTE r = r1 - (r1-r2) * i / steps;\r
2670             BYTE g = g1 - (g1-g2) * i / steps;\r
2671             BYTE b = b1 - (b1-b2) * i / steps;\r
2672 \r
2673             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2674             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2675             FillRect( hdc, &rc, hbrush );\r
2676             DeleteObject(hbrush);\r
2677         }\r
2678     }\r
2679     else if( mode == 2 ) {\r
2680         /* Diagonal gradient, good more or less for every piece */\r
2681         POINT triangle[3];\r
2682         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2683         HBRUSH hbrush_old;\r
2684         int steps = squareSize;\r
2685         int i;\r
2686 \r
2687         triangle[0].x = squareSize - steps;\r
2688         triangle[0].y = squareSize;\r
2689         triangle[1].x = squareSize;\r
2690         triangle[1].y = squareSize;\r
2691         triangle[2].x = squareSize;\r
2692         triangle[2].y = squareSize - steps;\r
2693 \r
2694         for( i=0; i<steps; i++ ) {\r
2695             BYTE r = r1 - (r1-r2) * i / steps;\r
2696             BYTE g = g1 - (g1-g2) * i / steps;\r
2697             BYTE b = b1 - (b1-b2) * i / steps;\r
2698 \r
2699             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2700             hbrush_old = SelectObject( hdc, hbrush );\r
2701             Polygon( hdc, triangle, 3 );\r
2702             SelectObject( hdc, hbrush_old );\r
2703             DeleteObject(hbrush);\r
2704             triangle[0].x++;\r
2705             triangle[2].y++;\r
2706         }\r
2707 \r
2708         SelectObject( hdc, hpen );\r
2709     }\r
2710 }\r
2711 \r
2712 /*\r
2713     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2714     seems to work ok. The main problem here is to find the "inside" of a chess\r
2715     piece: follow the steps as explained below.\r
2716 */\r
2717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2718 {\r
2719     HBITMAP hbm;\r
2720     HBITMAP hbm_old;\r
2721     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2722     RECT rc;\r
2723     SIZE sz;\r
2724     POINT pt;\r
2725     int backColor = whitePieceColor; \r
2726     int foreColor = blackPieceColor;\r
2727     \r
2728     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2729         backColor = appData.fontBackColorWhite;\r
2730         foreColor = appData.fontForeColorWhite;\r
2731     }\r
2732     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2733         backColor = appData.fontBackColorBlack;\r
2734         foreColor = appData.fontForeColorBlack;\r
2735     }\r
2736 \r
2737     /* Mask */\r
2738     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2739 \r
2740     hbm_old = SelectObject( hdc, hbm );\r
2741 \r
2742     rc.left = 0;\r
2743     rc.top = 0;\r
2744     rc.right = squareSize;\r
2745     rc.bottom = squareSize;\r
2746 \r
2747     /* Step 1: background is now black */\r
2748     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2749 \r
2750     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2751 \r
2752     pt.x = (squareSize - sz.cx) / 2;\r
2753     pt.y = (squareSize - sz.cy) / 2;\r
2754 \r
2755     SetBkMode( hdc, TRANSPARENT );\r
2756     SetTextColor( hdc, chroma );\r
2757     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2758     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2759 \r
2760     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2761     /* Step 3: the area outside the piece is filled with white */\r
2762 //    FloodFill( hdc, 0, 0, chroma );\r
2763     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2764     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2765     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2766     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2767     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2768     /* \r
2769         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2770         but if the start point is not inside the piece we're lost!\r
2771         There should be a better way to do this... if we could create a region or path\r
2772         from the fill operation we would be fine for example.\r
2773     */\r
2774 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2775     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2776 \r
2777     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2778         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2779         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2780 \r
2781         SelectObject( dc2, bm2 );\r
2782         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2783         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2784         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2785         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2786         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2787 \r
2788         DeleteDC( dc2 );\r
2789         DeleteObject( bm2 );\r
2790     }\r
2791 \r
2792     SetTextColor( hdc, 0 );\r
2793     /* \r
2794         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2795         draw the piece again in black for safety.\r
2796     */\r
2797     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2798 \r
2799     SelectObject( hdc, hbm_old );\r
2800 \r
2801     if( hPieceMask[index] != NULL ) {\r
2802         DeleteObject( hPieceMask[index] );\r
2803     }\r
2804 \r
2805     hPieceMask[index] = hbm;\r
2806 \r
2807     /* Face */\r
2808     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2809 \r
2810     SelectObject( hdc, hbm );\r
2811 \r
2812     {\r
2813         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2814         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2815         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2816 \r
2817         SelectObject( dc1, hPieceMask[index] );\r
2818         SelectObject( dc2, bm2 );\r
2819         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2820         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2821         \r
2822         /* \r
2823             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2824             the piece background and deletes (makes transparent) the rest.\r
2825             Thanks to that mask, we are free to paint the background with the greates\r
2826             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2827             We use this, to make gradients and give the pieces a "roundish" look.\r
2828         */\r
2829         SetPieceBackground( hdc, backColor, 2 );\r
2830         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2831 \r
2832         DeleteDC( dc2 );\r
2833         DeleteDC( dc1 );\r
2834         DeleteObject( bm2 );\r
2835     }\r
2836 \r
2837     SetTextColor( hdc, foreColor );\r
2838     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2839 \r
2840     SelectObject( hdc, hbm_old );\r
2841 \r
2842     if( hPieceFace[index] != NULL ) {\r
2843         DeleteObject( hPieceFace[index] );\r
2844     }\r
2845 \r
2846     hPieceFace[index] = hbm;\r
2847 }\r
2848 \r
2849 static int TranslatePieceToFontPiece( int piece )\r
2850 {\r
2851     switch( piece ) {\r
2852     case BlackPawn:\r
2853         return PM_BP;\r
2854     case BlackKnight:\r
2855         return PM_BN;\r
2856     case BlackBishop:\r
2857         return PM_BB;\r
2858     case BlackRook:\r
2859         return PM_BR;\r
2860     case BlackQueen:\r
2861         return PM_BQ;\r
2862     case BlackKing:\r
2863         return PM_BK;\r
2864     case WhitePawn:\r
2865         return PM_WP;\r
2866     case WhiteKnight:\r
2867         return PM_WN;\r
2868     case WhiteBishop:\r
2869         return PM_WB;\r
2870     case WhiteRook:\r
2871         return PM_WR;\r
2872     case WhiteQueen:\r
2873         return PM_WQ;\r
2874     case WhiteKing:\r
2875         return PM_WK;\r
2876 \r
2877     case BlackAngel:\r
2878         return PM_BA;\r
2879     case BlackMarshall:\r
2880         return PM_BC;\r
2881     case BlackFerz:\r
2882         return PM_BF;\r
2883     case BlackNightrider:\r
2884         return PM_BH;\r
2885     case BlackAlfil:\r
2886         return PM_BE;\r
2887     case BlackWazir:\r
2888         return PM_BW;\r
2889     case BlackUnicorn:\r
2890         return PM_BU;\r
2891     case BlackCannon:\r
2892         return PM_BO;\r
2893     case BlackGrasshopper:\r
2894         return PM_BG;\r
2895     case BlackMan:\r
2896         return PM_BM;\r
2897     case BlackSilver:\r
2898         return PM_BSG;\r
2899     case BlackLance:\r
2900         return PM_BL;\r
2901     case BlackFalcon:\r
2902         return PM_BV;\r
2903     case BlackCobra:\r
2904         return PM_BS;\r
2905     case BlackCardinal:\r
2906         return PM_BAB;\r
2907     case BlackDragon:\r
2908         return PM_BD;\r
2909 \r
2910     case WhiteAngel:\r
2911         return PM_WA;\r
2912     case WhiteMarshall:\r
2913         return PM_WC;\r
2914     case WhiteFerz:\r
2915         return PM_WF;\r
2916     case WhiteNightrider:\r
2917         return PM_WH;\r
2918     case WhiteAlfil:\r
2919         return PM_WE;\r
2920     case WhiteWazir:\r
2921         return PM_WW;\r
2922     case WhiteUnicorn:\r
2923         return PM_WU;\r
2924     case WhiteCannon:\r
2925         return PM_WO;\r
2926     case WhiteGrasshopper:\r
2927         return PM_WG;\r
2928     case WhiteMan:\r
2929         return PM_WM;\r
2930     case WhiteSilver:\r
2931         return PM_WSG;\r
2932     case WhiteLance:\r
2933         return PM_WL;\r
2934     case WhiteFalcon:\r
2935         return PM_WV;\r
2936     case WhiteCobra:\r
2937         return PM_WS;\r
2938     case WhiteCardinal:\r
2939         return PM_WAB;\r
2940     case WhiteDragon:\r
2941         return PM_WD;\r
2942     }\r
2943 \r
2944     return 0;\r
2945 }\r
2946 \r
2947 void CreatePiecesFromFont()\r
2948 {\r
2949     LOGFONT lf;\r
2950     HDC hdc_window = NULL;\r
2951     HDC hdc = NULL;\r
2952     HFONT hfont_old;\r
2953     int fontHeight;\r
2954     int i;\r
2955 \r
2956     if( fontBitmapSquareSize < 0 ) {\r
2957         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2958         return;\r
2959     }\r
2960 \r
2961     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2962         fontBitmapSquareSize = -1;\r
2963         return;\r
2964     }\r
2965 \r
2966     if( fontBitmapSquareSize != squareSize ) {\r
2967         hdc_window = GetDC( hwndMain );\r
2968         hdc = CreateCompatibleDC( hdc_window );\r
2969 \r
2970         if( hPieceFont != NULL ) {\r
2971             DeleteObject( hPieceFont );\r
2972         }\r
2973         else {\r
2974             for( i=0; i<=(int)BlackKing; i++ ) {\r
2975                 hPieceMask[i] = NULL;\r
2976                 hPieceFace[i] = NULL;\r
2977             }\r
2978         }\r
2979 \r
2980         fontHeight = 75;\r
2981 \r
2982         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2983             fontHeight = appData.fontPieceSize;\r
2984         }\r
2985 \r
2986         fontHeight = (fontHeight * squareSize) / 100;\r
2987 \r
2988         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2989         lf.lfWidth = 0;\r
2990         lf.lfEscapement = 0;\r
2991         lf.lfOrientation = 0;\r
2992         lf.lfWeight = FW_NORMAL;\r
2993         lf.lfItalic = 0;\r
2994         lf.lfUnderline = 0;\r
2995         lf.lfStrikeOut = 0;\r
2996         lf.lfCharSet = DEFAULT_CHARSET;\r
2997         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2998         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2999         lf.lfQuality = PROOF_QUALITY;\r
3000         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3001         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3002         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3003 \r
3004         hPieceFont = CreateFontIndirect( &lf );\r
3005 \r
3006         if( hPieceFont == NULL ) {\r
3007             fontBitmapSquareSize = -2;\r
3008         }\r
3009         else {\r
3010             /* Setup font-to-piece character table */\r
3011             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3012                 /* No (or wrong) global settings, try to detect the font */\r
3013                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3014                     /* Alpha */\r
3015                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3016                 }\r
3017                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3018                     /* DiagramTT* family */\r
3019                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3020                 }\r
3021                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3022                     /* Fairy symbols */\r
3023                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3024                 }\r
3025                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3026                     /* Good Companion (Some characters get warped as literal :-( */\r
3027                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3028                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3029                     SetCharTable(pieceToFontChar, s);\r
3030                 }\r
3031                 else {\r
3032                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3033                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3034                 }\r
3035             }\r
3036 \r
3037             /* Create bitmaps */\r
3038             hfont_old = SelectObject( hdc, hPieceFont );\r
3039             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3040                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3041                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3042 \r
3043             SelectObject( hdc, hfont_old );\r
3044 \r
3045             fontBitmapSquareSize = squareSize;\r
3046         }\r
3047     }\r
3048 \r
3049     if( hdc != NULL ) {\r
3050         DeleteDC( hdc );\r
3051     }\r
3052 \r
3053     if( hdc_window != NULL ) {\r
3054         ReleaseDC( hwndMain, hdc_window );\r
3055     }\r
3056 }\r
3057 \r
3058 HBITMAP\r
3059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3060 {\r
3061   char name[128];\r
3062 \r
3063   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3064   if (gameInfo.event &&\r
3065       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3066       strcmp(name, "k80s") == 0) {\r
3067     strcpy(name, "tim");\r
3068   }\r
3069   return LoadBitmap(hinst, name);\r
3070 }\r
3071 \r
3072 \r
3073 /* Insert a color into the program's logical palette\r
3074    structure.  This code assumes the given color is\r
3075    the result of the RGB or PALETTERGB macro, and it\r
3076    knows how those macros work (which is documented).\r
3077 */\r
3078 VOID\r
3079 InsertInPalette(COLORREF color)\r
3080 {\r
3081   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3082 \r
3083   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3084     DisplayFatalError("Too many colors", 0, 1);\r
3085     pLogPal->palNumEntries--;\r
3086     return;\r
3087   }\r
3088 \r
3089   pe->peFlags = (char) 0;\r
3090   pe->peRed = (char) (0xFF & color);\r
3091   pe->peGreen = (char) (0xFF & (color >> 8));\r
3092   pe->peBlue = (char) (0xFF & (color >> 16));\r
3093   return;\r
3094 }\r
3095 \r
3096 \r
3097 VOID\r
3098 InitDrawingColors()\r
3099 {\r
3100   if (pLogPal == NULL) {\r
3101     /* Allocate enough memory for a logical palette with\r
3102      * PALETTESIZE entries and set the size and version fields\r
3103      * of the logical palette structure.\r
3104      */\r
3105     pLogPal = (NPLOGPALETTE)\r
3106       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3107                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3108     pLogPal->palVersion    = 0x300;\r
3109   }\r
3110   pLogPal->palNumEntries = 0;\r
3111 \r
3112   InsertInPalette(lightSquareColor);\r
3113   InsertInPalette(darkSquareColor);\r
3114   InsertInPalette(whitePieceColor);\r
3115   InsertInPalette(blackPieceColor);\r
3116   InsertInPalette(highlightSquareColor);\r
3117   InsertInPalette(premoveHighlightColor);\r
3118 \r
3119   /*  create a logical color palette according the information\r
3120    *  in the LOGPALETTE structure.\r
3121    */\r
3122   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3123 \r
3124   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3125   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3126   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3127   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3128   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3129   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3130   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3131   /* [AS] Force rendering of the font-based pieces */\r
3132   if( fontBitmapSquareSize > 0 ) {\r
3133     fontBitmapSquareSize = 0;\r
3134   }\r
3135 }\r
3136 \r
3137 \r
3138 int\r
3139 BoardWidth(int boardSize, int n)\r
3140 { /* [HGM] argument n added to allow different width and height */\r
3141   int lineGap = sizeInfo[boardSize].lineGap;\r
3142 \r
3143   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3144       lineGap = appData.overrideLineGap;\r
3145   }\r
3146 \r
3147   return (n + 1) * lineGap +\r
3148           n * sizeInfo[boardSize].squareSize;\r
3149 }\r
3150 \r
3151 /* Respond to board resize by dragging edge */\r
3152 VOID\r
3153 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3154 {\r
3155   BoardSize newSize = NUM_SIZES - 1;\r
3156   static int recurse = 0;\r
3157   if (IsIconic(hwndMain)) return;\r
3158   if (recurse > 0) return;\r
3159   recurse++;\r
3160   while (newSize > 0) {\r
3161         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3162         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3163            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3164     newSize--;\r
3165   } \r
3166   boardSize = newSize;\r
3167   InitDrawingSizes(boardSize, flags);\r
3168   recurse--;\r
3169 }\r
3170 \r
3171 \r
3172 \r
3173 VOID\r
3174 InitDrawingSizes(BoardSize boardSize, int flags)\r
3175 {\r
3176   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3177   ChessSquare piece;\r
3178   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3179   HDC hdc;\r
3180   SIZE clockSize, messageSize;\r
3181   HFONT oldFont;\r
3182   char buf[MSG_SIZ];\r
3183   char *str;\r
3184   HMENU hmenu = GetMenu(hwndMain);\r
3185   RECT crect, wrect, oldRect;\r
3186   int offby;\r
3187   LOGBRUSH logbrush;\r
3188 \r
3189   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3190   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3191 \r
3192   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3193   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3194 \r
3195   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3196   oldRect.top = boardY;\r
3197   oldRect.right = boardX + winWidth;\r
3198   oldRect.bottom = boardY + winHeight;\r
3199 \r
3200   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3201   smallLayout = sizeInfo[boardSize].smallLayout;\r
3202   squareSize = sizeInfo[boardSize].squareSize;\r
3203   lineGap = sizeInfo[boardSize].lineGap;\r
3204   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3205 \r
3206   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3207       lineGap = appData.overrideLineGap;\r
3208   }\r
3209 \r
3210   if (tinyLayout != oldTinyLayout) {\r
3211     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3212     if (tinyLayout) {\r
3213       style &= ~WS_SYSMENU;\r
3214       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3215                  "&Minimize\tCtrl+F4");\r
3216     } else {\r
3217       style |= WS_SYSMENU;\r
3218       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3219     }\r
3220     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3221 \r
3222     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3223       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3224         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3225     }\r
3226     DrawMenuBar(hwndMain);\r
3227   }\r
3228 \r
3229   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3230   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3231 \r
3232   /* Get text area sizes */\r
3233   hdc = GetDC(hwndMain);\r
3234   if (appData.clockMode) {\r
3235     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3236   } else {\r
3237     sprintf(buf, "White");\r
3238   }\r
3239   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3240   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3241   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3242   str = "We only care about the height here";\r
3243   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3244   SelectObject(hdc, oldFont);\r
3245   ReleaseDC(hwndMain, hdc);\r
3246 \r
3247   /* Compute where everything goes */\r
3248   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3249         /* [HGM] logo: if either logo is on, reserve space for it */\r
3250         logoHeight =  2*clockSize.cy;\r
3251         leftLogoRect.left   = OUTER_MARGIN;\r
3252         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3253         leftLogoRect.top    = OUTER_MARGIN;\r
3254         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3255 \r
3256         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3257         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3258         rightLogoRect.top    = OUTER_MARGIN;\r
3259         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3260 \r
3261 \r
3262     whiteRect.left = leftLogoRect.right;\r
3263     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3264     whiteRect.top = OUTER_MARGIN;\r
3265     whiteRect.bottom = whiteRect.top + logoHeight;\r
3266 \r
3267     blackRect.right = rightLogoRect.left;\r
3268     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3269     blackRect.top = whiteRect.top;\r
3270     blackRect.bottom = whiteRect.bottom;\r
3271   } else {\r
3272     whiteRect.left = OUTER_MARGIN;\r
3273     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3274     whiteRect.top = OUTER_MARGIN;\r
3275     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3276 \r
3277     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3278     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3279     blackRect.top = whiteRect.top;\r
3280     blackRect.bottom = whiteRect.bottom;\r
3281   }\r
3282 \r
3283   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3284   if (appData.showButtonBar) {\r
3285     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3286       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3287   } else {\r
3288     messageRect.right = OUTER_MARGIN + boardWidth;\r
3289   }\r
3290   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3291   messageRect.bottom = messageRect.top + messageSize.cy;\r
3292 \r
3293   boardRect.left = OUTER_MARGIN;\r
3294   boardRect.right = boardRect.left + boardWidth;\r
3295   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3296   boardRect.bottom = boardRect.top + boardHeight;\r
3297 \r
3298   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3299   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3300   oldBoardSize = boardSize;\r
3301   oldTinyLayout = tinyLayout;\r
3302   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3303   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3304     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3305   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3306   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3307   winHeight = winH; //       without disturbing window attachments\r
3308   GetWindowRect(hwndMain, &wrect);\r
3309   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3310                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3311 \r
3312   // [HGM] placement: let attached windows follow size change.\r
3313   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3314   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3315   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3316   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3317   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3318 \r
3319   /* compensate if menu bar wrapped */\r
3320   GetClientRect(hwndMain, &crect);\r
3321   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3322   winHeight += offby;\r
3323   switch (flags) {\r
3324   case WMSZ_TOPLEFT:\r
3325     SetWindowPos(hwndMain, NULL, \r
3326                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3327                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3328     break;\r
3329 \r
3330   case WMSZ_TOPRIGHT:\r
3331   case WMSZ_TOP:\r
3332     SetWindowPos(hwndMain, NULL, \r
3333                  wrect.left, wrect.bottom - winHeight, \r
3334                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3335     break;\r
3336 \r
3337   case WMSZ_BOTTOMLEFT:\r
3338   case WMSZ_LEFT:\r
3339     SetWindowPos(hwndMain, NULL, \r
3340                  wrect.right - winWidth, wrect.top, \r
3341                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3342     break;\r
3343 \r
3344   case WMSZ_BOTTOMRIGHT:\r
3345   case WMSZ_BOTTOM:\r
3346   case WMSZ_RIGHT:\r
3347   default:\r
3348     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3349                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3350     break;\r
3351   }\r
3352 \r
3353   hwndPause = NULL;\r
3354   for (i = 0; i < N_BUTTONS; i++) {\r
3355     if (buttonDesc[i].hwnd != NULL) {\r
3356       DestroyWindow(buttonDesc[i].hwnd);\r
3357       buttonDesc[i].hwnd = NULL;\r
3358     }\r
3359     if (appData.showButtonBar) {\r
3360       buttonDesc[i].hwnd =\r
3361         CreateWindow("BUTTON", buttonDesc[i].label,\r
3362                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3363                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3364                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3365                      (HMENU) buttonDesc[i].id,\r
3366                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3367       if (tinyLayout) {\r
3368         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3369                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3370                     MAKELPARAM(FALSE, 0));\r
3371       }\r
3372       if (buttonDesc[i].id == IDM_Pause)\r
3373         hwndPause = buttonDesc[i].hwnd;\r
3374       buttonDesc[i].wndproc = (WNDPROC)\r
3375         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3376     }\r
3377   }\r
3378   if (gridPen != NULL) DeleteObject(gridPen);\r
3379   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3380   if (premovePen != NULL) DeleteObject(premovePen);\r
3381   if (lineGap != 0) {\r
3382     logbrush.lbStyle = BS_SOLID;\r
3383     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3384     gridPen =\r
3385       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3386                    lineGap, &logbrush, 0, NULL);\r
3387     logbrush.lbColor = highlightSquareColor;\r
3388     highlightPen =\r
3389       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3390                    lineGap, &logbrush, 0, NULL);\r
3391 \r
3392     logbrush.lbColor = premoveHighlightColor; \r
3393     premovePen =\r
3394       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3395                    lineGap, &logbrush, 0, NULL);\r
3396 \r
3397     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3398     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3399       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3400       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3401         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3402       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3403         BOARD_WIDTH * (squareSize + lineGap);\r
3404       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3405     }\r
3406     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3407       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3408       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3409         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3410         lineGap / 2 + (i * (squareSize + lineGap));\r
3411       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3412         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3413       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3414     }\r
3415   }\r
3416 \r
3417   /* [HGM] Licensing requirement */\r
3418 #ifdef GOTHIC\r
3419   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3420 #endif\r
3421 #ifdef FALCON\r
3422   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3423 #endif\r
3424   GothicPopUp( "", VariantNormal);\r
3425 \r
3426 \r
3427 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3428 \r
3429   /* Load piece bitmaps for this board size */\r
3430   for (i=0; i<=2; i++) {\r
3431     for (piece = WhitePawn;\r
3432          (int) piece < (int) BlackPawn;\r
3433          piece = (ChessSquare) ((int) piece + 1)) {\r
3434       if (pieceBitmap[i][piece] != NULL)\r
3435         DeleteObject(pieceBitmap[i][piece]);\r
3436     }\r
3437   }\r
3438 \r
3439   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3440   // Orthodox Chess pieces\r
3441   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3442   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3443   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3444   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3445   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3446   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3447   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3448   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3449   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3450   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3451   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3452   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3453   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3454   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3455   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3456   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3457     // in Shogi, Hijack the unused Queen for Lance\r
3458     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3459     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3460     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3461   } else {\r
3462     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3463     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3464     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3465   }\r
3466 \r
3467   if(squareSize <= 72 && squareSize >= 33) { \r
3468     /* A & C are available in most sizes now */\r
3469     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3470       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3471       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3472       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3473       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3474       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3475       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3476       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3477       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3478       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3479       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3480       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3481       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3482     } else { // Smirf-like\r
3483       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3484       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3485       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3486     }\r
3487     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3488       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3489       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3490       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3491     } else { // WinBoard standard\r
3492       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3493       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3494       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3495     }\r
3496   }\r
3497 \r
3498 \r
3499   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3500     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3501     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3502     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3503     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3504     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3505     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3506     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3507     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3508     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3509     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3510     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3511     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3512     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3513     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3514     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3515     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3516     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3517     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3518     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3519     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3520     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3521     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3522     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3523     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3524     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3525     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3526     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3527     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3528     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3529     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3530 \r
3531     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3532       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3533       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3534       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3535       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3536       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3537       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3538       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3539       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3540       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3541       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3542       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3543       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3544     } else {\r
3545       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3546       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3547       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3548       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3549       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3550       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3551       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3552       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3553       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3554       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3555       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3556       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3557     }\r
3558 \r
3559   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3560     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3561     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3562     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3563     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3564     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3565     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3566     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3567     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3568     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3569     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3570     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3571     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3572     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3573     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3574   }\r
3575 \r
3576 \r
3577   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3578   /* special Shogi support in this size */\r
3579   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3580       for (piece = WhitePawn;\r
3581            (int) piece < (int) BlackPawn;\r
3582            piece = (ChessSquare) ((int) piece + 1)) {\r
3583         if (pieceBitmap[i][piece] != NULL)\r
3584           DeleteObject(pieceBitmap[i][piece]);\r
3585       }\r
3586     }\r
3587   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3588   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3589   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3590   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3591   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3592   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3593   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3594   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3595   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3596   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3597   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3598   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3599   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3600   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3601   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3602   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3603   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3604   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3605   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3606   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3607   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3608   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3609   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3610   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3611   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3612   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3613   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3614   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3615   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3616   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3617   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3618   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3619   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3620   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3621   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3622   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3623   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3624   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3625   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3626   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3627   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3628   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3629   minorSize = 0;\r
3630   }\r
3631 }\r
3632 \r
3633 HBITMAP\r
3634 PieceBitmap(ChessSquare p, int kind)\r
3635 {\r
3636   if ((int) p >= (int) BlackPawn)\r
3637     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3638 \r
3639   return pieceBitmap[kind][(int) p];\r
3640 }\r
3641 \r
3642 /***************************************************************/\r
3643 \r
3644 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3645 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3646 /*\r
3647 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3648 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3649 */\r
3650 \r
3651 VOID\r
3652 SquareToPos(int row, int column, int * x, int * y)\r
3653 {\r
3654   if (flipView) {\r
3655     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3656     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3657   } else {\r
3658     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3659     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3660   }\r
3661 }\r
3662 \r
3663 VOID\r
3664 DrawCoordsOnDC(HDC hdc)\r
3665 {\r
3666   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
3667   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
3668   char str[2] = { NULLCHAR, NULLCHAR };\r
3669   int oldMode, oldAlign, x, y, start, i;\r
3670   HFONT oldFont;\r
3671   HBRUSH oldBrush;\r
3672 \r
3673   if (!appData.showCoords)\r
3674     return;\r
3675 \r
3676   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3677 \r
3678   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3679   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3680   oldAlign = GetTextAlign(hdc);\r
3681   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3682 \r
3683   y = boardRect.top + lineGap;\r
3684   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3685 \r
3686   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3687   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3688     str[0] = files[start + i];\r
3689     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3690     y += squareSize + lineGap;\r
3691   }\r
3692 \r
3693   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3694 \r
3695   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3696   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3697     str[0] = ranks[start + i];\r
3698     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3699     x += squareSize + lineGap;\r
3700   }    \r
3701 \r
3702   SelectObject(hdc, oldBrush);\r
3703   SetBkMode(hdc, oldMode);\r
3704   SetTextAlign(hdc, oldAlign);\r
3705   SelectObject(hdc, oldFont);\r
3706 }\r
3707 \r
3708 VOID\r
3709 DrawGridOnDC(HDC hdc)\r
3710 {\r
3711   HPEN oldPen;\r
3712  \r
3713   if (lineGap != 0) {\r
3714     oldPen = SelectObject(hdc, gridPen);\r
3715     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3716     SelectObject(hdc, oldPen);\r
3717   }\r
3718 }\r
3719 \r
3720 #define HIGHLIGHT_PEN 0\r
3721 #define PREMOVE_PEN   1\r
3722 \r
3723 VOID\r
3724 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3725 {\r
3726   int x1, y1;\r
3727   HPEN oldPen, hPen;\r
3728   if (lineGap == 0) return;\r
3729   if (flipView) {\r
3730     x1 = boardRect.left +\r
3731       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3732     y1 = boardRect.top +\r
3733       lineGap/2 + y * (squareSize + lineGap);\r
3734   } else {\r
3735     x1 = boardRect.left +\r
3736       lineGap/2 + x * (squareSize + lineGap);\r
3737     y1 = boardRect.top +\r
3738       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3739   }\r
3740   hPen = pen ? premovePen : highlightPen;\r
3741   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3742   MoveToEx(hdc, x1, y1, NULL);\r
3743   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3744   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3745   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3746   LineTo(hdc, x1, y1);\r
3747   SelectObject(hdc, oldPen);\r
3748 }\r
3749 \r
3750 VOID\r
3751 DrawHighlightsOnDC(HDC hdc)\r
3752 {\r
3753   int i;\r
3754   for (i=0; i<2; i++) {\r
3755     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3756       DrawHighlightOnDC(hdc, TRUE,\r
3757                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3758                         HIGHLIGHT_PEN);\r
3759   }\r
3760   for (i=0; i<2; i++) {\r
3761     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3762         premoveHighlightInfo.sq[i].y >= 0) {\r
3763         DrawHighlightOnDC(hdc, TRUE,\r
3764                           premoveHighlightInfo.sq[i].x, \r
3765                           premoveHighlightInfo.sq[i].y,\r
3766                           PREMOVE_PEN);\r
3767     }\r
3768   }\r
3769 }\r
3770 \r
3771 /* Note: sqcolor is used only in monoMode */\r
3772 /* Note that this code is largely duplicated in woptions.c,\r
3773    function DrawSampleSquare, so that needs to be updated too */\r
3774 VOID\r
3775 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3776 {\r
3777   HBITMAP oldBitmap;\r
3778   HBRUSH oldBrush;\r
3779   int tmpSize;\r
3780 \r
3781   if (appData.blindfold) return;\r
3782 \r
3783   /* [AS] Use font-based pieces if needed */\r
3784   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3785     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3786     CreatePiecesFromFont();\r
3787 \r
3788     if( fontBitmapSquareSize == squareSize ) {\r
3789         int index = TranslatePieceToFontPiece(piece);\r
3790 \r
3791         SelectObject( tmphdc, hPieceMask[ index ] );\r
3792 \r
3793         BitBlt( hdc,\r
3794             x, y,\r
3795             squareSize, squareSize,\r
3796             tmphdc,\r
3797             0, 0,\r
3798             SRCAND );\r
3799 \r
3800         SelectObject( tmphdc, hPieceFace[ index ] );\r
3801 \r
3802         BitBlt( hdc,\r
3803             x, y,\r
3804             squareSize, squareSize,\r
3805             tmphdc,\r
3806             0, 0,\r
3807             SRCPAINT );\r
3808 \r
3809         return;\r
3810     }\r
3811   }\r
3812 \r
3813   if (appData.monoMode) {\r
3814     SelectObject(tmphdc, PieceBitmap(piece, \r
3815       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3816     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3817            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3818   } else {\r
3819     tmpSize = squareSize;\r
3820     if(minorSize &&\r
3821         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3822          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3823       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3824       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3825       x += (squareSize - minorSize)>>1;\r
3826       y += squareSize - minorSize - 2;\r
3827       tmpSize = minorSize;\r
3828     }\r
3829     if (color || appData.allWhite ) {\r
3830       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3831       if( color )\r
3832               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3833       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3834       if(appData.upsideDown && color==flipView)\r
3835         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3836       else\r
3837         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3838       /* Use black for outline of white pieces */\r
3839       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3840       if(appData.upsideDown && color==flipView)\r
3841         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3842       else\r
3843         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3844     } else {\r
3845       /* Use square color for details of black pieces */\r
3846       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3847       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3848       if(appData.upsideDown && !flipView)\r
3849         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3850       else\r
3851         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3852     }\r
3853     SelectObject(hdc, oldBrush);\r
3854     SelectObject(tmphdc, oldBitmap);\r
3855   }\r
3856 }\r
3857 \r
3858 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3859 int GetBackTextureMode( int algo )\r
3860 {\r
3861     int result = BACK_TEXTURE_MODE_DISABLED;\r
3862 \r
3863     switch( algo ) \r
3864     {\r
3865         case BACK_TEXTURE_MODE_PLAIN:\r
3866             result = 1; /* Always use identity map */\r
3867             break;\r
3868         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3869             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3870             break;\r
3871     }\r
3872 \r
3873     return result;\r
3874 }\r
3875 \r
3876 /* \r
3877     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3878     to handle redraws cleanly (as random numbers would always be different).\r
3879 */\r
3880 VOID RebuildTextureSquareInfo()\r
3881 {\r
3882     BITMAP bi;\r
3883     int lite_w = 0;\r
3884     int lite_h = 0;\r
3885     int dark_w = 0;\r
3886     int dark_h = 0;\r
3887     int row;\r
3888     int col;\r
3889 \r
3890     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3891 \r
3892     if( liteBackTexture != NULL ) {\r
3893         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3894             lite_w = bi.bmWidth;\r
3895             lite_h = bi.bmHeight;\r
3896         }\r
3897     }\r
3898 \r
3899     if( darkBackTexture != NULL ) {\r
3900         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3901             dark_w = bi.bmWidth;\r
3902             dark_h = bi.bmHeight;\r
3903         }\r
3904     }\r
3905 \r
3906     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3907         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3908             if( (col + row) & 1 ) {\r
3909                 /* Lite square */\r
3910                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3911                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3912                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3913                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3914                 }\r
3915             }\r
3916             else {\r
3917                 /* Dark square */\r
3918                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3919                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3920                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3921                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3922                 }\r
3923             }\r
3924         }\r
3925     }\r
3926 }\r
3927 \r
3928 /* [AS] Arrow highlighting support */\r
3929 \r
3930 static int A_WIDTH = 5; /* Width of arrow body */\r
3931 \r
3932 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3933 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3934 \r
3935 static double Sqr( double x )\r
3936 {\r
3937     return x*x;\r
3938 }\r
3939 \r
3940 static int Round( double x )\r
3941 {\r
3942     return (int) (x + 0.5);\r
3943 }\r
3944 \r
3945 /* Draw an arrow between two points using current settings */\r
3946 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3947 {\r
3948     POINT arrow[7];\r
3949     double dx, dy, j, k, x, y;\r
3950 \r
3951     if( d_x == s_x ) {\r
3952         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3953 \r
3954         arrow[0].x = s_x + A_WIDTH;\r
3955         arrow[0].y = s_y;\r
3956 \r
3957         arrow[1].x = s_x + A_WIDTH;\r
3958         arrow[1].y = d_y - h;\r
3959 \r
3960         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3961         arrow[2].y = d_y - h;\r
3962 \r
3963         arrow[3].x = d_x;\r
3964         arrow[3].y = d_y;\r
3965 \r
3966         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3967         arrow[4].y = d_y - h;\r
3968 \r
3969         arrow[5].x = s_x - A_WIDTH;\r
3970         arrow[5].y = d_y - h;\r
3971 \r
3972         arrow[6].x = s_x - A_WIDTH;\r
3973         arrow[6].y = s_y;\r
3974     }\r
3975     else if( d_y == s_y ) {\r
3976         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3977 \r
3978         arrow[0].x = s_x;\r
3979         arrow[0].y = s_y + A_WIDTH;\r
3980 \r
3981         arrow[1].x = d_x - w;\r
3982         arrow[1].y = s_y + A_WIDTH;\r
3983 \r
3984         arrow[2].x = d_x - w;\r
3985         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3986 \r
3987         arrow[3].x = d_x;\r
3988         arrow[3].y = d_y;\r
3989 \r
3990         arrow[4].x = d_x - w;\r
3991         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3992 \r
3993         arrow[5].x = d_x - w;\r
3994         arrow[5].y = s_y - A_WIDTH;\r
3995 \r
3996         arrow[6].x = s_x;\r
3997         arrow[6].y = s_y - A_WIDTH;\r
3998     }\r
3999     else {\r
4000         /* [AS] Needed a lot of paper for this! :-) */\r
4001         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4002         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4003   \r
4004         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4005 \r
4006         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4007 \r
4008         x = s_x;\r
4009         y = s_y;\r
4010 \r
4011         arrow[0].x = Round(x - j);\r
4012         arrow[0].y = Round(y + j*dx);\r
4013 \r
4014         arrow[1].x = Round(x + j);\r
4015         arrow[1].y = Round(y - j*dx);\r
4016 \r
4017         if( d_x > s_x ) {\r
4018             x = (double) d_x - k;\r
4019             y = (double) d_y - k*dy;\r
4020         }\r
4021         else {\r
4022             x = (double) d_x + k;\r
4023             y = (double) d_y + k*dy;\r
4024         }\r
4025 \r
4026         arrow[2].x = Round(x + j);\r
4027         arrow[2].y = Round(y - j*dx);\r
4028 \r
4029         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4030         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4031 \r
4032         arrow[4].x = d_x;\r
4033         arrow[4].y = d_y;\r
4034 \r
4035         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4036         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4037 \r
4038         arrow[6].x = Round(x - j);\r
4039         arrow[6].y = Round(y + j*dx);\r
4040     }\r
4041 \r
4042     Polygon( hdc, arrow, 7 );\r
4043 }\r
4044 \r
4045 /* [AS] Draw an arrow between two squares */\r
4046 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4047 {\r
4048     int s_x, s_y, d_x, d_y;\r
4049     HPEN hpen;\r
4050     HPEN holdpen;\r
4051     HBRUSH hbrush;\r
4052     HBRUSH holdbrush;\r
4053     LOGBRUSH stLB;\r
4054 \r
4055     if( s_col == d_col && s_row == d_row ) {\r
4056         return;\r
4057     }\r
4058 \r
4059     /* Get source and destination points */\r
4060     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4061     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4062 \r
4063     if( d_y > s_y ) {\r
4064         d_y += squareSize / 4;\r
4065     }\r
4066     else if( d_y < s_y ) {\r
4067         d_y += 3 * squareSize / 4;\r
4068     }\r
4069     else {\r
4070         d_y += squareSize / 2;\r
4071     }\r
4072 \r
4073     if( d_x > s_x ) {\r
4074         d_x += squareSize / 4;\r
4075     }\r
4076     else if( d_x < s_x ) {\r
4077         d_x += 3 * squareSize / 4;\r
4078     }\r
4079     else {\r
4080         d_x += squareSize / 2;\r
4081     }\r
4082 \r
4083     s_x += squareSize / 2;\r
4084     s_y += squareSize / 2;\r
4085 \r
4086     /* Adjust width */\r
4087     A_WIDTH = squareSize / 14;\r
4088 \r
4089     /* Draw */\r
4090     stLB.lbStyle = BS_SOLID;\r
4091     stLB.lbColor = appData.highlightArrowColor;\r
4092     stLB.lbHatch = 0;\r
4093 \r
4094     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4095     holdpen = SelectObject( hdc, hpen );\r
4096     hbrush = CreateBrushIndirect( &stLB );\r
4097     holdbrush = SelectObject( hdc, hbrush );\r
4098 \r
4099     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4100 \r
4101     SelectObject( hdc, holdpen );\r
4102     SelectObject( hdc, holdbrush );\r
4103     DeleteObject( hpen );\r
4104     DeleteObject( hbrush );\r
4105 }\r
4106 \r
4107 BOOL HasHighlightInfo()\r
4108 {\r
4109     BOOL result = FALSE;\r
4110 \r
4111     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4112         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4113     {\r
4114         result = TRUE;\r
4115     }\r
4116 \r
4117     return result;\r
4118 }\r
4119 \r
4120 BOOL IsDrawArrowEnabled()\r
4121 {\r
4122     BOOL result = FALSE;\r
4123 \r
4124     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4125         result = TRUE;\r
4126     }\r
4127 \r
4128     return result;\r
4129 }\r
4130 \r
4131 VOID DrawArrowHighlight( HDC hdc )\r
4132 {\r
4133     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4134         DrawArrowBetweenSquares( hdc,\r
4135             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4136             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4137     }\r
4138 }\r
4139 \r
4140 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4141 {\r
4142     HRGN result = NULL;\r
4143 \r
4144     if( HasHighlightInfo() ) {\r
4145         int x1, y1, x2, y2;\r
4146         int sx, sy, dx, dy;\r
4147 \r
4148         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4149         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4150 \r
4151         sx = MIN( x1, x2 );\r
4152         sy = MIN( y1, y2 );\r
4153         dx = MAX( x1, x2 ) + squareSize;\r
4154         dy = MAX( y1, y2 ) + squareSize;\r
4155 \r
4156         result = CreateRectRgn( sx, sy, dx, dy );\r
4157     }\r
4158 \r
4159     return result;\r
4160 }\r
4161 \r
4162 /*\r
4163     Warning: this function modifies the behavior of several other functions. \r
4164     \r
4165     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4166     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4167     repaint is scattered all over the place, which is not good for features such as\r
4168     "arrow highlighting" that require a full repaint of the board.\r
4169 \r
4170     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4171     user interaction, when speed is not so important) but especially to avoid errors\r
4172     in the displayed graphics.\r
4173 \r
4174     In such patched places, I always try refer to this function so there is a single\r
4175     place to maintain knowledge.\r
4176     \r
4177     To restore the original behavior, just return FALSE unconditionally.\r
4178 */\r
4179 BOOL IsFullRepaintPreferrable()\r
4180 {\r
4181     BOOL result = FALSE;\r
4182 \r
4183     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4184         /* Arrow may appear on the board */\r
4185         result = TRUE;\r
4186     }\r
4187 \r
4188     return result;\r
4189 }\r
4190 \r
4191 /* \r
4192     This function is called by DrawPosition to know whether a full repaint must\r
4193     be forced or not.\r
4194 \r
4195     Only DrawPosition may directly call this function, which makes use of \r
4196     some state information. Other function should call DrawPosition specifying \r
4197     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4198 */\r
4199 BOOL DrawPositionNeedsFullRepaint()\r
4200 {\r
4201     BOOL result = FALSE;\r
4202 \r
4203     /* \r
4204         Probably a slightly better policy would be to trigger a full repaint\r
4205         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4206         but animation is fast enough that it's difficult to notice.\r
4207     */\r
4208     if( animInfo.piece == EmptySquare ) {\r
4209         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4210             result = TRUE;\r
4211         }\r
4212     }\r
4213 \r
4214     return result;\r
4215 }\r
4216 \r
4217 VOID\r
4218 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4219 {\r
4220   int row, column, x, y, square_color, piece_color;\r
4221   ChessSquare piece;\r
4222   HBRUSH oldBrush;\r
4223   HDC texture_hdc = NULL;\r
4224 \r
4225   /* [AS] Initialize background textures if needed */\r
4226   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4227       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4228       if( backTextureSquareSize != squareSize \r
4229        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4230           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4231           backTextureSquareSize = squareSize;\r
4232           RebuildTextureSquareInfo();\r
4233       }\r
4234 \r
4235       texture_hdc = CreateCompatibleDC( hdc );\r
4236   }\r
4237 \r
4238   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4239     for (column = 0; column < BOARD_WIDTH; column++) {\r
4240   \r
4241       SquareToPos(row, column, &x, &y);\r
4242 \r
4243       piece = board[row][column];\r
4244 \r
4245       square_color = ((column + row) % 2) == 1;\r
4246       if( gameInfo.variant == VariantXiangqi ) {\r
4247           square_color = !InPalace(row, column);\r
4248           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4249           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4250       }\r
4251       piece_color = (int) piece < (int) BlackPawn;\r
4252 \r
4253 \r
4254       /* [HGM] holdings file: light square or black */\r
4255       if(column == BOARD_LEFT-2) {\r
4256             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4257                 square_color = 1;\r
4258             else {\r
4259                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4260                 continue;\r
4261             }\r
4262       } else\r
4263       if(column == BOARD_RGHT + 1 ) {\r
4264             if( row < gameInfo.holdingsSize )\r
4265                 square_color = 1;\r
4266             else {\r
4267                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4268                 continue;\r
4269             }\r
4270       }\r
4271       if(column == BOARD_LEFT-1 ) /* left align */\r
4272             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4273       else if( column == BOARD_RGHT) /* right align */\r
4274             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4275       else\r
4276       if (appData.monoMode) {\r
4277         if (piece == EmptySquare) {\r
4278           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4279                  square_color ? WHITENESS : BLACKNESS);\r
4280         } else {\r
4281           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4282         }\r
4283       } \r
4284       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4285           /* [AS] Draw the square using a texture bitmap */\r
4286           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4287           int r = row, c = column; // [HGM] do not flip board in flipView\r
4288           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4289 \r
4290           DrawTile( x, y, \r
4291               squareSize, squareSize, \r
4292               hdc, \r
4293               texture_hdc,\r
4294               backTextureSquareInfo[r][c].mode,\r
4295               backTextureSquareInfo[r][c].x,\r
4296               backTextureSquareInfo[r][c].y );\r
4297 \r
4298           SelectObject( texture_hdc, hbm );\r
4299 \r
4300           if (piece != EmptySquare) {\r
4301               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4302           }\r
4303       }\r
4304       else {\r
4305         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4306 \r
4307         oldBrush = SelectObject(hdc, brush );\r
4308         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4309         SelectObject(hdc, oldBrush);\r
4310         if (piece != EmptySquare)\r
4311           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4312       }\r
4313     }\r
4314   }\r
4315 \r
4316   if( texture_hdc != NULL ) {\r
4317     DeleteDC( texture_hdc );\r
4318   }\r
4319 }\r
4320 \r
4321 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4322 void fputDW(FILE *f, int x)\r
4323 {\r
4324         fputc(x     & 255, f);\r
4325         fputc(x>>8  & 255, f);\r
4326         fputc(x>>16 & 255, f);\r
4327         fputc(x>>24 & 255, f);\r
4328 }\r
4329 \r
4330 #define MAX_CLIPS 200   /* more than enough */\r
4331 \r
4332 VOID\r
4333 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4334 {\r
4335 //  HBITMAP bufferBitmap;\r
4336   BITMAP bi;\r
4337 //  RECT Rect;\r
4338   HDC tmphdc;\r
4339   HBITMAP hbm;\r
4340   int w = 100, h = 50;\r
4341 \r
4342   if(logo == NULL) return;\r
4343 //  GetClientRect(hwndMain, &Rect);\r
4344 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4345 //                                      Rect.bottom-Rect.top+1);\r
4346   tmphdc = CreateCompatibleDC(hdc);\r
4347   hbm = SelectObject(tmphdc, logo);\r
4348   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4349             w = bi.bmWidth;\r
4350             h = bi.bmHeight;\r
4351   }\r
4352   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4353                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4354   SelectObject(tmphdc, hbm);\r
4355   DeleteDC(tmphdc);\r
4356 }\r
4357 \r
4358 VOID\r
4359 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4360 {\r
4361   static Board lastReq, lastDrawn;\r
4362   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4363   static int lastDrawnFlipView = 0;\r
4364   static int lastReqValid = 0, lastDrawnValid = 0;\r
4365   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4366   HDC tmphdc;\r
4367   HDC hdcmem;\r
4368   HBITMAP bufferBitmap;\r
4369   HBITMAP oldBitmap;\r
4370   RECT Rect;\r
4371   HRGN clips[MAX_CLIPS];\r
4372   ChessSquare dragged_piece = EmptySquare;\r
4373 \r
4374   /* I'm undecided on this - this function figures out whether a full\r
4375    * repaint is necessary on its own, so there's no real reason to have the\r
4376    * caller tell it that.  I think this can safely be set to FALSE - but\r
4377    * if we trust the callers not to request full repaints unnessesarily, then\r
4378    * we could skip some clipping work.  In other words, only request a full\r
4379    * redraw when the majority of pieces have changed positions (ie. flip, \r
4380    * gamestart and similar)  --Hawk\r
4381    */\r
4382   Boolean fullrepaint = repaint;\r
4383 \r
4384   if( DrawPositionNeedsFullRepaint() ) {\r
4385       fullrepaint = TRUE;\r
4386   }\r
4387 \r
4388   if (board == NULL) {\r
4389     if (!lastReqValid) {\r
4390       return;\r
4391     }\r
4392     board = lastReq;\r
4393   } else {\r
4394     CopyBoard(lastReq, board);\r
4395     lastReqValid = 1;\r
4396   }\r
4397 \r
4398   if (doingSizing) {\r
4399     return;\r
4400   }\r
4401 \r
4402   if (IsIconic(hwndMain)) {\r
4403     return;\r
4404   }\r
4405 \r
4406   if (hdc == NULL) {\r
4407     hdc = GetDC(hwndMain);\r
4408     if (!appData.monoMode) {\r
4409       SelectPalette(hdc, hPal, FALSE);\r
4410       RealizePalette(hdc);\r
4411     }\r
4412     releaseDC = TRUE;\r
4413   } else {\r
4414     releaseDC = FALSE;\r
4415   }\r
4416 \r
4417   /* Create some work-DCs */\r
4418   hdcmem = CreateCompatibleDC(hdc);\r
4419   tmphdc = CreateCompatibleDC(hdc);\r
4420 \r
4421   /* If dragging is in progress, we temporarely remove the piece */\r
4422   /* [HGM] or temporarily decrease count if stacked              */\r
4423   /*       !! Moved to before board compare !!                   */\r
4424   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4425     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4426     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4427             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4428         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4429     } else \r
4430     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4431             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4432         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4433     } else \r
4434         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4435   }\r
4436 \r
4437   /* Figure out which squares need updating by comparing the \r
4438    * newest board with the last drawn board and checking if\r
4439    * flipping has changed.\r
4440    */\r
4441   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4442     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4443       for (column = 0; column < BOARD_WIDTH; column++) {\r
4444         if (lastDrawn[row][column] != board[row][column]) {\r
4445           SquareToPos(row, column, &x, &y);\r
4446           clips[num_clips++] =\r
4447             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4448         }\r
4449       }\r
4450     }\r
4451     for (i=0; i<2; i++) {\r
4452       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4453           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4454         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4455             lastDrawnHighlight.sq[i].y >= 0) {\r
4456           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4457                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4458           clips[num_clips++] =\r
4459             CreateRectRgn(x - lineGap, y - lineGap, \r
4460                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4461         }\r
4462         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4463           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4464           clips[num_clips++] =\r
4465             CreateRectRgn(x - lineGap, y - lineGap, \r
4466                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4467         }\r
4468       }\r
4469     }\r
4470     for (i=0; i<2; i++) {\r
4471       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4472           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4473         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4474             lastDrawnPremove.sq[i].y >= 0) {\r
4475           SquareToPos(lastDrawnPremove.sq[i].y,\r
4476                       lastDrawnPremove.sq[i].x, &x, &y);\r
4477           clips[num_clips++] =\r
4478             CreateRectRgn(x - lineGap, y - lineGap, \r
4479                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4480         }\r
4481         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4482             premoveHighlightInfo.sq[i].y >= 0) {\r
4483           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4484                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4485           clips[num_clips++] =\r
4486             CreateRectRgn(x - lineGap, y - lineGap, \r
4487                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4488         }\r
4489       }\r
4490     }\r
4491   } else {\r
4492     fullrepaint = TRUE;\r
4493   }\r
4494 \r
4495   /* Create a buffer bitmap - this is the actual bitmap\r
4496    * being written to.  When all the work is done, we can\r
4497    * copy it to the real DC (the screen).  This avoids\r
4498    * the problems with flickering.\r
4499    */\r
4500   GetClientRect(hwndMain, &Rect);\r
4501   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4502                                         Rect.bottom-Rect.top+1);\r
4503   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4504   if (!appData.monoMode) {\r
4505     SelectPalette(hdcmem, hPal, FALSE);\r
4506   }\r
4507 \r
4508   /* Create clips for dragging */\r
4509   if (!fullrepaint) {\r
4510     if (dragInfo.from.x >= 0) {\r
4511       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4512       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4513     }\r
4514     if (dragInfo.start.x >= 0) {\r
4515       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4516       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4517     }\r
4518     if (dragInfo.pos.x >= 0) {\r
4519       x = dragInfo.pos.x - squareSize / 2;\r
4520       y = dragInfo.pos.y - squareSize / 2;\r
4521       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4522     }\r
4523     if (dragInfo.lastpos.x >= 0) {\r
4524       x = dragInfo.lastpos.x - squareSize / 2;\r
4525       y = dragInfo.lastpos.y - squareSize / 2;\r
4526       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4527     }\r
4528   }\r
4529 \r
4530   /* Are we animating a move?  \r
4531    * If so, \r
4532    *   - remove the piece from the board (temporarely)\r
4533    *   - calculate the clipping region\r
4534    */\r
4535   if (!fullrepaint) {\r
4536     if (animInfo.piece != EmptySquare) {\r
4537       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4538       x = boardRect.left + animInfo.lastpos.x;\r
4539       y = boardRect.top + animInfo.lastpos.y;\r
4540       x2 = boardRect.left + animInfo.pos.x;\r
4541       y2 = boardRect.top + animInfo.pos.y;\r
4542       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4543       /* Slight kludge.  The real problem is that after AnimateMove is\r
4544          done, the position on the screen does not match lastDrawn.\r
4545          This currently causes trouble only on e.p. captures in\r
4546          atomic, where the piece moves to an empty square and then\r
4547          explodes.  The old and new positions both had an empty square\r
4548          at the destination, but animation has drawn a piece there and\r
4549          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4550       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4551     }\r
4552   }\r
4553 \r
4554   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4555   if (num_clips == 0)\r
4556     fullrepaint = TRUE;\r
4557 \r
4558   /* Set clipping on the memory DC */\r
4559   if (!fullrepaint) {\r
4560     SelectClipRgn(hdcmem, clips[0]);\r
4561     for (x = 1; x < num_clips; x++) {\r
4562       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4563         abort();  // this should never ever happen!\r
4564     }\r
4565   }\r
4566 \r
4567   /* Do all the drawing to the memory DC */\r
4568   if(explodeInfo.radius) { // [HGM] atomic\r
4569         HBRUSH oldBrush;\r
4570         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4571         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4572         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4573         x += squareSize/2;\r
4574         y += squareSize/2;\r
4575         if(!fullrepaint) {\r
4576           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4577           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4578         }\r
4579         DrawGridOnDC(hdcmem);\r
4580         DrawHighlightsOnDC(hdcmem);\r
4581         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4582         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4583         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4584         SelectObject(hdcmem, oldBrush);\r
4585   } else {\r
4586     DrawGridOnDC(hdcmem);\r
4587     DrawHighlightsOnDC(hdcmem);\r
4588     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4589   }\r
4590   if(logoHeight) {\r
4591         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4592         if(appData.autoLogo) {\r
4593           \r
4594           switch(gameMode) { // pick logos based on game mode\r
4595             case IcsObserving:\r
4596                 whiteLogo = second.programLogo; // ICS logo\r
4597                 blackLogo = second.programLogo;\r
4598             default:\r
4599                 break;\r
4600             case IcsPlayingWhite:\r
4601                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4602                 blackLogo = second.programLogo; // ICS logo\r
4603                 break;\r
4604             case IcsPlayingBlack:\r
4605                 whiteLogo = second.programLogo; // ICS logo\r
4606                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4607                 break;\r
4608             case TwoMachinesPlay:\r
4609                 if(first.twoMachinesColor[0] == 'b') {\r
4610                     whiteLogo = second.programLogo;\r
4611                     blackLogo = first.programLogo;\r
4612                 }\r
4613                 break;\r
4614             case MachinePlaysWhite:\r
4615                 blackLogo = userLogo;\r
4616                 break;\r
4617             case MachinePlaysBlack:\r
4618                 whiteLogo = userLogo;\r
4619                 blackLogo = first.programLogo;\r
4620           }\r
4621         }\r
4622         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4623         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4624   }\r
4625 \r
4626   if( appData.highlightMoveWithArrow ) {\r
4627     DrawArrowHighlight(hdcmem);\r
4628   }\r
4629 \r
4630   DrawCoordsOnDC(hdcmem);\r
4631 \r
4632   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4633                  /* to make sure lastDrawn contains what is actually drawn */\r
4634 \r
4635   /* Put the dragged piece back into place and draw it (out of place!) */\r
4636     if (dragged_piece != EmptySquare) {\r
4637     /* [HGM] or restack */\r
4638     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4639                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4640     else\r
4641     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4642                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4643     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4644     x = dragInfo.pos.x - squareSize / 2;\r
4645     y = dragInfo.pos.y - squareSize / 2;\r
4646     DrawPieceOnDC(hdcmem, dragged_piece,\r
4647                   ((int) dragged_piece < (int) BlackPawn), \r
4648                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4649   }   \r
4650   \r
4651   /* Put the animated piece back into place and draw it */\r
4652   if (animInfo.piece != EmptySquare) {\r
4653     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4654     x = boardRect.left + animInfo.pos.x;\r
4655     y = boardRect.top + animInfo.pos.y;\r
4656     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4657                   ((int) animInfo.piece < (int) BlackPawn),\r
4658                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4659   }\r
4660 \r
4661   /* Release the bufferBitmap by selecting in the old bitmap \r
4662    * and delete the memory DC\r
4663    */\r
4664   SelectObject(hdcmem, oldBitmap);\r
4665   DeleteDC(hdcmem);\r
4666 \r
4667   /* Set clipping on the target DC */\r
4668   if (!fullrepaint) {\r
4669     SelectClipRgn(hdc, clips[0]);\r
4670     for (x = 1; x < num_clips; x++) {\r
4671       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4672         abort();   // this should never ever happen!\r
4673     } \r
4674   }\r
4675 \r
4676   /* Copy the new bitmap onto the screen in one go.\r
4677    * This way we avoid any flickering\r
4678    */\r
4679   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4680   BitBlt(hdc, boardRect.left, boardRect.top,\r
4681          boardRect.right - boardRect.left,\r
4682          boardRect.bottom - boardRect.top,\r
4683          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4684   if(saveDiagFlag) { \r
4685     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4686     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4687 \r
4688     GetObject(bufferBitmap, sizeof(b), &b);\r
4689     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4690         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4691         bih.biWidth = b.bmWidth;\r
4692         bih.biHeight = b.bmHeight;\r
4693         bih.biPlanes = 1;\r
4694         bih.biBitCount = b.bmBitsPixel;\r
4695         bih.biCompression = 0;\r
4696         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4697         bih.biXPelsPerMeter = 0;\r
4698         bih.biYPelsPerMeter = 0;\r
4699         bih.biClrUsed = 0;\r
4700         bih.biClrImportant = 0;\r
4701 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4702 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4703         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4704 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4705 \r
4706         wb = b.bmWidthBytes;\r
4707         // count colors\r
4708         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4709                 int k = ((int*) pData)[i];\r
4710                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4711                 if(j >= 16) break;\r
4712                 color[j] = k;\r
4713                 if(j >= nrColors) nrColors = j+1;\r
4714         }\r
4715         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4716                 INT p = 0;\r
4717                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4718                     for(w=0; w<(wb>>2); w+=2) {\r
4719                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4720                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4721                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4722                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4723                         pData[p++] = m | j<<4;\r
4724                     }\r
4725                     while(p&3) pData[p++] = 0;\r
4726                 }\r
4727                 fac = 3;\r
4728                 wb = ((wb+31)>>5)<<2;\r
4729         }\r
4730         // write BITMAPFILEHEADER\r
4731         fprintf(diagFile, "BM");\r
4732         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4733         fputDW(diagFile, 0);\r
4734         fputDW(diagFile, 0x36 + (fac?64:0));\r
4735         // write BITMAPINFOHEADER\r
4736         fputDW(diagFile, 40);\r
4737         fputDW(diagFile, b.bmWidth);\r
4738         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4739         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4740         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4741         fputDW(diagFile, 0);\r
4742         fputDW(diagFile, 0);\r
4743         fputDW(diagFile, 0);\r
4744         fputDW(diagFile, 0);\r
4745         fputDW(diagFile, 0);\r
4746         fputDW(diagFile, 0);\r
4747         // write color table\r
4748         if(fac)\r
4749         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4750         // write bitmap data\r
4751         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4752                 fputc(pData[i], diagFile);\r
4753      }\r
4754   }\r
4755 \r
4756   SelectObject(tmphdc, oldBitmap);\r
4757 \r
4758   /* Massive cleanup */\r
4759   for (x = 0; x < num_clips; x++)\r
4760     DeleteObject(clips[x]);\r
4761 \r
4762   DeleteDC(tmphdc);\r
4763   DeleteObject(bufferBitmap);\r
4764 \r
4765   if (releaseDC) \r
4766     ReleaseDC(hwndMain, hdc);\r
4767   \r
4768   if (lastDrawnFlipView != flipView) {\r
4769     if (flipView)\r
4770       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4771     else\r
4772       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4773   }\r
4774 \r
4775 /*  CopyBoard(lastDrawn, board);*/\r
4776   lastDrawnHighlight = highlightInfo;\r
4777   lastDrawnPremove   = premoveHighlightInfo;\r
4778   lastDrawnFlipView = flipView;\r
4779   lastDrawnValid = 1;\r
4780 }\r
4781 \r
4782 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4783 int\r
4784 SaveDiagram(f)\r
4785      FILE *f;\r
4786 {\r
4787     saveDiagFlag = 1; diagFile = f;\r
4788     HDCDrawPosition(NULL, TRUE, NULL);\r
4789 \r
4790     saveDiagFlag = 0;\r
4791 \r
4792 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4793     \r
4794     fclose(f);\r
4795     return TRUE;\r
4796 }\r
4797 \r
4798 \r
4799 /*---------------------------------------------------------------------------*\\r
4800 | CLIENT PAINT PROCEDURE\r
4801 |   This is the main event-handler for the WM_PAINT message.\r
4802 |\r
4803 \*---------------------------------------------------------------------------*/\r
4804 VOID\r
4805 PaintProc(HWND hwnd)\r
4806 {\r
4807   HDC         hdc;\r
4808   PAINTSTRUCT ps;\r
4809   HFONT       oldFont;\r
4810 \r
4811   if((hdc = BeginPaint(hwnd, &ps))) {\r
4812     if (IsIconic(hwnd)) {\r
4813       DrawIcon(hdc, 2, 2, iconCurrent);\r
4814     } else {\r
4815       if (!appData.monoMode) {\r
4816         SelectPalette(hdc, hPal, FALSE);\r
4817         RealizePalette(hdc);\r
4818       }\r
4819       HDCDrawPosition(hdc, 1, NULL);\r
4820       oldFont =\r
4821         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4822       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4823                  ETO_CLIPPED|ETO_OPAQUE,\r
4824                  &messageRect, messageText, strlen(messageText), NULL);\r
4825       SelectObject(hdc, oldFont);\r
4826       DisplayBothClocks();\r
4827     }\r
4828     EndPaint(hwnd,&ps);\r
4829   }\r
4830 \r
4831   return;\r
4832 }\r
4833 \r
4834 \r
4835 /*\r
4836  * If the user selects on a border boundary, return -1; if off the board,\r
4837  *   return -2.  Otherwise map the event coordinate to the square.\r
4838  * The offset boardRect.left or boardRect.top must already have been\r
4839  *   subtracted from x.\r
4840  */\r
4841 int\r
4842 EventToSquare(int x)\r
4843 {\r
4844   if (x <= 0)\r
4845     return -2;\r
4846   if (x < lineGap)\r
4847     return -1;\r
4848   x -= lineGap;\r
4849   if ((x % (squareSize + lineGap)) >= squareSize)\r
4850     return -1;\r
4851   x /= (squareSize + lineGap);\r
4852   if (x >= BOARD_SIZE)\r
4853     return -2;\r
4854   return x;\r
4855 }\r
4856 \r
4857 typedef struct {\r
4858   char piece;\r
4859   int command;\r
4860   char* name;\r
4861 } DropEnable;\r
4862 \r
4863 DropEnable dropEnables[] = {\r
4864   { 'P', DP_Pawn, "Pawn" },\r
4865   { 'N', DP_Knight, "Knight" },\r
4866   { 'B', DP_Bishop, "Bishop" },\r
4867   { 'R', DP_Rook, "Rook" },\r
4868   { 'Q', DP_Queen, "Queen" },\r
4869 };\r
4870 \r
4871 VOID\r
4872 SetupDropMenu(HMENU hmenu)\r
4873 {\r
4874   int i, count, enable;\r
4875   char *p;\r
4876   extern char white_holding[], black_holding[];\r
4877   char item[MSG_SIZ];\r
4878 \r
4879   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4880     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4881                dropEnables[i].piece);\r
4882     count = 0;\r
4883     while (p && *p++ == dropEnables[i].piece) count++;\r
4884     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4885     enable = count > 0 || !appData.testLegality\r
4886       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4887                       && !appData.icsActive);\r
4888     ModifyMenu(hmenu, dropEnables[i].command,\r
4889                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4890                dropEnables[i].command, item);\r
4891   }\r
4892 }\r
4893 \r
4894 /* Event handler for mouse messages */\r
4895 VOID\r
4896 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4897 {\r
4898   int x, y;\r
4899   POINT pt;\r
4900   static int recursive = 0;\r
4901   HMENU hmenu;\r
4902 //  BOOLEAN needsRedraw = FALSE;\r
4903   BOOLEAN saveAnimate;\r
4904   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4905   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4906   ChessMove moveType;\r
4907 \r
4908   if (recursive) {\r
4909     if (message == WM_MBUTTONUP) {\r
4910       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4911          to the middle button: we simulate pressing the left button too!\r
4912          */\r
4913       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4914       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4915     }\r
4916     return;\r
4917   }\r
4918   recursive++;\r
4919   \r
4920   pt.x = LOWORD(lParam);\r
4921   pt.y = HIWORD(lParam);\r
4922   x = EventToSquare(pt.x - boardRect.left);\r
4923   y = EventToSquare(pt.y - boardRect.top);\r
4924   if (!flipView && y >= 0) {\r
4925     y = BOARD_HEIGHT - 1 - y;\r
4926   }\r
4927   if (flipView && x >= 0) {\r
4928     x = BOARD_WIDTH - 1 - x;\r
4929   }\r
4930 \r
4931   switch (message) {\r
4932   case WM_LBUTTONDOWN:\r
4933     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4934         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4935         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4936         if(gameInfo.holdingsWidth && \r
4937                 (WhiteOnMove(currentMove) \r
4938                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4939                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4940             // click in right holdings, for determining promotion piece\r
4941             ChessSquare p = boards[currentMove][y][x];\r
4942             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4943             if(p != EmptySquare) {\r
4944                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4945                 fromX = fromY = -1;\r
4946                 break;\r
4947             }\r
4948         }\r
4949         DrawPosition(FALSE, boards[currentMove]);\r
4950         break;\r
4951     }\r
4952     ErrorPopDown();\r
4953     sameAgain = FALSE;\r
4954     if (y == -2) {\r
4955       /* Downclick vertically off board; check if on clock */\r
4956       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4957         if (gameMode == EditPosition) {\r
4958           SetWhiteToPlayEvent();\r
4959         } else if (gameMode == IcsPlayingBlack ||\r
4960                    gameMode == MachinePlaysWhite) {\r
4961           CallFlagEvent();\r
4962         } else if (gameMode == EditGame) {\r
4963           AdjustClock(flipClock, -1);\r
4964         }\r
4965       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4966         if (gameMode == EditPosition) {\r
4967           SetBlackToPlayEvent();\r
4968         } else if (gameMode == IcsPlayingWhite ||\r
4969                    gameMode == MachinePlaysBlack) {\r
4970           CallFlagEvent();\r
4971         } else if (gameMode == EditGame) {\r
4972           AdjustClock(!flipClock, -1);\r
4973         }\r
4974       }\r
4975       if (!appData.highlightLastMove) {\r
4976         ClearHighlights();\r
4977         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4978       }\r
4979       fromX = fromY = -1;\r
4980       dragInfo.start.x = dragInfo.start.y = -1;\r
4981       dragInfo.from = dragInfo.start;\r
4982       break;\r
4983     } else if (x < 0 || y < 0\r
4984       /* [HGM] block clicks between board and holdings */\r
4985               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4986               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4987               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4988         /* EditPosition, empty square, or different color piece;\r
4989            click-click move is possible */\r
4990                                ) {\r
4991       break;\r
4992     } else if (fromX == x && fromY == y) {\r
4993       /* Downclick on same square again */\r
4994       ClearHighlights();\r
4995       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4996       sameAgain = TRUE;  \r
4997     } else if (fromX != -1 &&\r
4998                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4999                                                                         ) {\r
5000       /* Downclick on different square. */\r
5001       /* [HGM] if on holdings file, should count as new first click ! */\r
5002       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5003         toX = x;\r
5004         toY = y;\r
5005         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5006            to make sure move is legal before showing promotion popup */\r
5007         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE);\r
5008         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5009                 fromX = fromY = -1; \r
5010                 ClearHighlights();\r
5011                 DrawPosition(FALSE, boards[currentMove]);\r
5012                 break; \r
5013         } else \r
5014         if(moveType != ImpossibleMove && moveType != Comment) {\r
5015           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5016           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5017             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5018               appData.alwaysPromoteToQueen)) {\r
5019                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5020                   if (!appData.highlightLastMove) {\r
5021                       ClearHighlights();\r
5022                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5023                   }\r
5024           } else\r
5025           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5026                   SetHighlights(fromX, fromY, toX, toY);\r
5027                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5028                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5029                      If promotion to Q is legal, all are legal! */\r
5030                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5031                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5032                     // kludge to temporarily execute move on display, without promoting yet\r
5033                     promotionChoice = TRUE;\r
5034                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5035                     boards[currentMove][toY][toX] = p;\r
5036                     DrawPosition(FALSE, boards[currentMove]);\r
5037                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5038                     boards[currentMove][toY][toX] = q;\r
5039                     DisplayMessage("Select piece from holdings", "");\r
5040                   } else\r
5041                   PromotionPopup(hwnd);\r
5042                   goto noClear;\r
5043           } else { // not a promotion. Move can be illegal if testLegality off, and should be made then.\r
5044              if (appData.animate || appData.highlightLastMove) {\r
5045                  SetHighlights(fromX, fromY, toX, toY);\r
5046              } else {\r
5047                  ClearHighlights();\r
5048              }\r
5049              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5050              if (appData.animate && !appData.highlightLastMove) {\r
5051                   ClearHighlights();\r
5052                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5053              }\r
5054           }\r
5055           fromX = fromY = -1;\r
5056         noClear:\r
5057           break;\r
5058         }\r
5059         if (gotPremove && moveType != Comment) {\r
5060             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5061 //            DrawPosition(forceFullRepaint || FALSE, NULL);\r
5062         } else ClearHighlights();\r
5063         fromX = fromY = -1;\r
5064         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5065         if(moveType != Comment) break;\r
5066     }\r
5067     /* First downclick, or restart on a square with same color piece */\r
5068     if (!frozen && OKToStartUserMove(x, y)) {\r
5069       fromX = x;\r
5070       fromY = y;\r
5071       dragInfo.lastpos = pt;\r
5072       dragInfo.from.x = fromX;\r
5073       dragInfo.from.y = fromY;\r
5074       dragInfo.start = dragInfo.from;\r
5075       SetCapture(hwndMain);\r
5076     } else {\r
5077       fromX = fromY = -1;\r
5078       dragInfo.start.x = dragInfo.start.y = -1;\r
5079       dragInfo.from = dragInfo.start;\r
5080       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5081     }\r
5082     break;\r
5083 \r
5084   case WM_LBUTTONUP:\r
5085     ReleaseCapture();\r
5086     if (fromX == -1) break;\r
5087     if (x == fromX && y == fromY) {\r
5088       dragInfo.from.x = dragInfo.from.y = -1;\r
5089       /* Upclick on same square */\r
5090       if (sameAgain) {\r
5091         /* Clicked same square twice: abort click-click move */\r
5092         fromX = fromY = -1;\r
5093         gotPremove = 0;\r
5094         ClearPremoveHighlights();\r
5095       } else {\r
5096         /* First square clicked: start click-click move */\r
5097         SetHighlights(fromX, fromY, -1, -1);\r
5098       }\r
5099       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5100     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5101       /* Errant click; ignore */\r
5102       break;\r
5103     } else {\r
5104       /* Finish drag move. */\r
5105     if (appData.debugMode) {\r
5106         fprintf(debugFP, "release\n");\r
5107     }\r
5108       dragInfo.from.x = dragInfo.from.y = -1;\r
5109       toX = x;\r
5110       toY = y;\r
5111       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5112       appData.animate = appData.animate && !appData.animateDragging;\r
5113       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, TRUE);\r
5114       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5115                 fromX = fromY = -1; \r
5116                 ClearHighlights();\r
5117                 DrawPosition(FALSE, boards[currentMove]);\r
5118                 appData.animate = saveAnimate;\r
5119                 break; \r
5120       } else \r
5121       if(moveType != ImpossibleMove) {\r
5122           /* [HGM] use move type to determine if move is promotion.\r
5123              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5124           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5125             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5126               appData.alwaysPromoteToQueen)) \r
5127                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5128           else \r
5129           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5130                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5131                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5132                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5133                     // kludge to temporarily execute move on display, wthout promotng yet\r
5134                     promotionChoice = TRUE;\r
5135                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5136                     boards[currentMove][toY][toX] = p;\r
5137                     DrawPosition(FALSE, boards[currentMove]);\r
5138                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5139                     boards[currentMove][toY][toX] = q;\r
5140                     appData.animate = saveAnimate;\r
5141                     DisplayMessage("Select piece from holdings", "");\r
5142                     break;\r
5143                   } else\r
5144                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5145           } else {\r
5146             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5147                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5148                                         moveType == WhiteCapturesEnPassant || \r
5149                                         moveType == BlackCapturesEnPassant   ) )\r
5150                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5151             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5152           }\r
5153       }\r
5154       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5155       appData.animate = saveAnimate;\r
5156       fromX = fromY = -1;\r
5157       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5158         ClearHighlights();\r
5159       }\r
5160       if (appData.animate || appData.animateDragging ||\r
5161           appData.highlightDragging || gotPremove) {\r
5162         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5163       }\r
5164     }\r
5165     dragInfo.start.x = dragInfo.start.y = -1; \r
5166     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5167     break;\r
5168 \r
5169   case WM_MOUSEMOVE:\r
5170     if ((appData.animateDragging || appData.highlightDragging)\r
5171         && (wParam & MK_LBUTTON)\r
5172         && dragInfo.from.x >= 0) \r
5173     {\r
5174       BOOL full_repaint = FALSE;\r
5175 \r
5176       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5177       if (appData.animateDragging) {\r
5178         dragInfo.pos = pt;\r
5179       }\r
5180       if (appData.highlightDragging) {\r
5181         SetHighlights(fromX, fromY, x, y);\r
5182         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5183             full_repaint = TRUE;\r
5184         }\r
5185       }\r
5186       \r
5187       DrawPosition( full_repaint, NULL);\r
5188       \r
5189       dragInfo.lastpos = dragInfo.pos;\r
5190     }\r
5191     break;\r
5192 \r
5193   case WM_MOUSEWHEEL: // [DM]\r
5194     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5195        /* Mouse Wheel is being rolled forward\r
5196         * Play moves forward\r
5197         */\r
5198        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5199                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5200        /* Mouse Wheel is being rolled backward\r
5201         * Play moves backward\r
5202         */\r
5203        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5204                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5205     }\r
5206     break;\r
5207 \r
5208   case WM_MBUTTONDOWN:\r
5209   case WM_RBUTTONDOWN:\r
5210     ErrorPopDown();\r
5211     ReleaseCapture();\r
5212     fromX = fromY = -1;\r
5213     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5214     dragInfo.start.x = dragInfo.start.y = -1;\r
5215     dragInfo.from = dragInfo.start;\r
5216     dragInfo.lastpos = dragInfo.pos;\r
5217     if (appData.highlightDragging) {\r
5218       ClearHighlights();\r
5219     }\r
5220     if(y == -2) {\r
5221       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5222       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5223           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5224       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5225           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5226       }\r
5227     }\r
5228     DrawPosition(TRUE, NULL);\r
5229 \r
5230     switch (gameMode) {\r
5231     case EditPosition:\r
5232     case IcsExamining:\r
5233       if (x < 0 || y < 0) break;\r
5234       fromX = x;\r
5235       fromY = y;\r
5236       if (message == WM_MBUTTONDOWN) {\r
5237         buttonCount = 3;  /* even if system didn't think so */\r
5238         if (wParam & MK_SHIFT) \r
5239           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5240         else\r
5241           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5242       } else { /* message == WM_RBUTTONDOWN */\r
5243         /* Just have one menu, on the right button.  Windows users don't\r
5244            think to try the middle one, and sometimes other software steals\r
5245            it, or it doesn't really exist. */\r
5246         if(gameInfo.variant != VariantShogi)\r
5247             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5248         else\r
5249             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5250       }\r
5251       break;\r
5252     case IcsPlayingWhite:\r
5253     case IcsPlayingBlack:\r
5254     case EditGame:\r
5255     case MachinePlaysWhite:\r
5256     case MachinePlaysBlack:\r
5257       if (appData.testLegality &&\r
5258           gameInfo.variant != VariantBughouse &&\r
5259           gameInfo.variant != VariantCrazyhouse) break;\r
5260       if (x < 0 || y < 0) break;\r
5261       fromX = x;\r
5262       fromY = y;\r
5263       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5264       SetupDropMenu(hmenu);\r
5265       MenuPopup(hwnd, pt, hmenu, -1);\r
5266       break;\r
5267     default:\r
5268       break;\r
5269     }\r
5270     break;\r
5271   }\r
5272 \r
5273   recursive--;\r
5274 }\r
5275 \r
5276 /* Preprocess messages for buttons in main window */\r
5277 LRESULT CALLBACK\r
5278 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5279 {\r
5280   int id = GetWindowLong(hwnd, GWL_ID);\r
5281   int i, dir;\r
5282 \r
5283   for (i=0; i<N_BUTTONS; i++) {\r
5284     if (buttonDesc[i].id == id) break;\r
5285   }\r
5286   if (i == N_BUTTONS) return 0;\r
5287   switch (message) {\r
5288   case WM_KEYDOWN:\r
5289     switch (wParam) {\r
5290     case VK_LEFT:\r
5291     case VK_RIGHT:\r
5292       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5293       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5294       return TRUE;\r
5295     }\r
5296     break;\r
5297   case WM_CHAR:\r
5298     switch (wParam) {\r
5299     case '\r':\r
5300       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5301       return TRUE;\r
5302     default:\r
5303       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5304         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5305         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5306         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5307         SetFocus(h);\r
5308         SendMessage(h, WM_CHAR, wParam, lParam);\r
5309         return TRUE;\r
5310       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5311         PopUpMoveDialog((char)wParam);\r
5312       }\r
5313       break;\r
5314     }\r
5315     break;\r
5316   }\r
5317   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5318 }\r
5319 \r
5320 /* Process messages for Promotion dialog box */\r
5321 LRESULT CALLBACK\r
5322 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5323 {\r
5324   char promoChar;\r
5325 \r
5326   switch (message) {\r
5327   case WM_INITDIALOG: /* message: initialize dialog box */\r
5328     /* Center the dialog over the application window */\r
5329     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5330     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5331       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5332        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5333                SW_SHOW : SW_HIDE);\r
5334     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5335     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5336        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5337          PieceToChar(WhiteAngel) != '~') ||\r
5338         (PieceToChar(BlackAngel) >= 'A' &&\r
5339          PieceToChar(BlackAngel) != '~')   ) ?\r
5340                SW_SHOW : SW_HIDE);\r
5341     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5342        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5343          PieceToChar(WhiteMarshall) != '~') ||\r
5344         (PieceToChar(BlackMarshall) >= 'A' &&\r
5345          PieceToChar(BlackMarshall) != '~')   ) ?\r
5346                SW_SHOW : SW_HIDE);\r
5347     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5348     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5349        gameInfo.variant != VariantShogi ?\r
5350                SW_SHOW : SW_HIDE);\r
5351     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5352        gameInfo.variant != VariantShogi ?\r
5353                SW_SHOW : SW_HIDE);\r
5354     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5355        gameInfo.variant == VariantShogi ?\r
5356                SW_SHOW : SW_HIDE);\r
5357     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5358        gameInfo.variant == VariantShogi ?\r
5359                SW_SHOW : SW_HIDE);\r
5360     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5361        gameInfo.variant == VariantSuper ?\r
5362                SW_SHOW : SW_HIDE);\r
5363     return TRUE;\r
5364 \r
5365   case WM_COMMAND: /* message: received a command */\r
5366     switch (LOWORD(wParam)) {\r
5367     case IDCANCEL:\r
5368       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5369       ClearHighlights();\r
5370       DrawPosition(FALSE, NULL);\r
5371       return TRUE;\r
5372     case PB_King:\r
5373       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5374       break;\r
5375     case PB_Queen:\r
5376       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5377       break;\r
5378     case PB_Rook:\r
5379       promoChar = PieceToChar(BlackRook);\r
5380       break;\r
5381     case PB_Bishop:\r
5382       promoChar = PieceToChar(BlackBishop);\r
5383       break;\r
5384     case PB_Chancellor:\r
5385       promoChar = PieceToChar(BlackMarshall);\r
5386       break;\r
5387     case PB_Archbishop:\r
5388       promoChar = PieceToChar(BlackAngel);\r
5389       break;\r
5390     case PB_Knight:\r
5391       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5392       break;\r
5393     default:\r
5394       return FALSE;\r
5395     }\r
5396     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5397     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5398        only show the popup when we are already sure the move is valid or\r
5399        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5400        will figure out it is a promotion from the promoChar. */\r
5401     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5402     if (!appData.highlightLastMove) {\r
5403       ClearHighlights();\r
5404       DrawPosition(FALSE, NULL);\r
5405     }\r
5406     return TRUE;\r
5407   }\r
5408   return FALSE;\r
5409 }\r
5410 \r
5411 /* Pop up promotion dialog */\r
5412 VOID\r
5413 PromotionPopup(HWND hwnd)\r
5414 {\r
5415   FARPROC lpProc;\r
5416 \r
5417   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5418   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5419     hwnd, (DLGPROC)lpProc);\r
5420   FreeProcInstance(lpProc);\r
5421 }\r
5422 \r
5423 /* Toggle ShowThinking */\r
5424 VOID\r
5425 ToggleShowThinking()\r
5426 {\r
5427   appData.showThinking = !appData.showThinking;\r
5428   ShowThinkingEvent();\r
5429 }\r
5430 \r
5431 VOID\r
5432 LoadGameDialog(HWND hwnd, char* title)\r
5433 {\r
5434   UINT number = 0;\r
5435   FILE *f;\r
5436   char fileTitle[MSG_SIZ];\r
5437   f = OpenFileDialog(hwnd, "rb", "",\r
5438                      appData.oldSaveStyle ? "gam" : "pgn",\r
5439                      GAME_FILT,\r
5440                      title, &number, fileTitle, NULL);\r
5441   if (f != NULL) {\r
5442     cmailMsgLoaded = FALSE;\r
5443     if (number == 0) {\r
5444       int error = GameListBuild(f);\r
5445       if (error) {\r
5446         DisplayError("Cannot build game list", error);\r
5447       } else if (!ListEmpty(&gameList) &&\r
5448                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5449         GameListPopUp(f, fileTitle);\r
5450         return;\r
5451       }\r
5452       GameListDestroy();\r
5453       number = 1;\r
5454     }\r
5455     LoadGame(f, number, fileTitle, FALSE);\r
5456   }\r
5457 }\r
5458 \r
5459 VOID\r
5460 ChangedConsoleFont()\r
5461 {\r
5462   CHARFORMAT cfmt;\r
5463   CHARRANGE tmpsel, sel;\r
5464   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5465   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5466   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5467   PARAFORMAT paraf;\r
5468 \r
5469   cfmt.cbSize = sizeof(CHARFORMAT);\r
5470   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5471   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5472   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5473    * size.  This was undocumented in the version of MSVC++ that I had\r
5474    * when I wrote the code, but is apparently documented now.\r
5475    */\r
5476   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5477   cfmt.bCharSet = f->lf.lfCharSet;\r
5478   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5479   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5480   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5481   /* Why are the following seemingly needed too? */\r
5482   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5483   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5484   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5485   tmpsel.cpMin = 0;\r
5486   tmpsel.cpMax = -1; /*999999?*/\r
5487   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5488   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5489   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5490    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5491    */\r
5492   paraf.cbSize = sizeof(paraf);\r
5493   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5494   paraf.dxStartIndent = 0;\r
5495   paraf.dxOffset = WRAP_INDENT;\r
5496   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5497   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5498 }\r
5499 \r
5500 /*---------------------------------------------------------------------------*\\r
5501  *\r
5502  * Window Proc for main window\r
5503  *\r
5504 \*---------------------------------------------------------------------------*/\r
5505 \r
5506 /* Process messages for main window, etc. */\r
5507 LRESULT CALLBACK\r
5508 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5509 {\r
5510   FARPROC lpProc;\r
5511   int wmId, wmEvent;\r
5512   char *defName;\r
5513   FILE *f;\r
5514   UINT number;\r
5515   char fileTitle[MSG_SIZ];\r
5516   char buf[MSG_SIZ];\r
5517   static SnapData sd;\r
5518 \r
5519   switch (message) {\r
5520 \r
5521   case WM_PAINT: /* message: repaint portion of window */\r
5522     PaintProc(hwnd);\r
5523     break;\r
5524 \r
5525   case WM_ERASEBKGND:\r
5526     if (IsIconic(hwnd)) {\r
5527       /* Cheat; change the message */\r
5528       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5529     } else {\r
5530       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5531     }\r
5532     break;\r
5533 \r
5534   case WM_LBUTTONDOWN:\r
5535   case WM_MBUTTONDOWN:\r
5536   case WM_RBUTTONDOWN:\r
5537   case WM_LBUTTONUP:\r
5538   case WM_MBUTTONUP:\r
5539   case WM_RBUTTONUP:\r
5540   case WM_MOUSEMOVE:\r
5541   case WM_MOUSEWHEEL:\r
5542     MouseEvent(hwnd, message, wParam, lParam);\r
5543     break;\r
5544 \r
5545   JAWS_KB_NAVIGATION\r
5546 \r
5547   case WM_CHAR:\r
5548     \r
5549     JAWS_ALT_INTERCEPT\r
5550 \r
5551     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5552         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5553         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5554         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5555         SetFocus(h);\r
5556         SendMessage(h, message, wParam, lParam);\r
5557     } else if(lParam != KF_REPEAT) {\r
5558         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5559                 PopUpMoveDialog((char)wParam);\r
5560         } else if((char)wParam == 003) CopyGameToClipboard();\r
5561          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5562     }\r
5563 \r
5564     break;\r
5565 \r
5566   case WM_PALETTECHANGED:\r
5567     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5568       int nnew;\r
5569       HDC hdc = GetDC(hwndMain);\r
5570       SelectPalette(hdc, hPal, TRUE);\r
5571       nnew = RealizePalette(hdc);\r
5572       if (nnew > 0) {\r
5573         paletteChanged = TRUE;\r
5574         InvalidateRect(hwnd, &boardRect, FALSE);\r
5575       }\r
5576       ReleaseDC(hwnd, hdc);\r
5577     }\r
5578     break;\r
5579 \r
5580   case WM_QUERYNEWPALETTE:\r
5581     if (!appData.monoMode /*&& paletteChanged*/) {\r
5582       int nnew;\r
5583       HDC hdc = GetDC(hwndMain);\r
5584       paletteChanged = FALSE;\r
5585       SelectPalette(hdc, hPal, FALSE);\r
5586       nnew = RealizePalette(hdc);\r
5587       if (nnew > 0) {\r
5588         InvalidateRect(hwnd, &boardRect, FALSE);\r
5589       }\r
5590       ReleaseDC(hwnd, hdc);\r
5591       return TRUE;\r
5592     }\r
5593     return FALSE;\r
5594 \r
5595   case WM_COMMAND: /* message: command from application menu */\r
5596     wmId    = LOWORD(wParam);\r
5597     wmEvent = HIWORD(wParam);\r
5598 \r
5599     switch (wmId) {\r
5600     case IDM_NewGame:\r
5601       ResetGameEvent();\r
5602       AnalysisPopDown();\r
5603       SAY("new game enter a move to play against the computer with white");\r
5604       break;\r
5605 \r
5606     case IDM_NewGameFRC:\r
5607       if( NewGameFRC() == 0 ) {\r
5608         ResetGameEvent();\r
5609         AnalysisPopDown();\r
5610       }\r
5611       break;\r
5612 \r
5613     case IDM_NewVariant:\r
5614       NewVariantPopup(hwnd);\r
5615       break;\r
5616 \r
5617     case IDM_LoadGame:\r
5618       LoadGameDialog(hwnd, "Load Game from File");\r
5619       break;\r
5620 \r
5621     case IDM_LoadNextGame:\r
5622       ReloadGame(1);\r
5623       break;\r
5624 \r
5625     case IDM_LoadPrevGame:\r
5626       ReloadGame(-1);\r
5627       break;\r
5628 \r
5629     case IDM_ReloadGame:\r
5630       ReloadGame(0);\r
5631       break;\r
5632 \r
5633     case IDM_LoadPosition:\r
5634       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5635         Reset(FALSE, TRUE);\r
5636       }\r
5637       number = 1;\r
5638       f = OpenFileDialog(hwnd, "rb", "",\r
5639                          appData.oldSaveStyle ? "pos" : "fen",\r
5640                          POSITION_FILT,\r
5641                          "Load Position from File", &number, fileTitle, NULL);\r
5642       if (f != NULL) {\r
5643         LoadPosition(f, number, fileTitle);\r
5644       }\r
5645       break;\r
5646 \r
5647     case IDM_LoadNextPosition:\r
5648       ReloadPosition(1);\r
5649       break;\r
5650 \r
5651     case IDM_LoadPrevPosition:\r
5652       ReloadPosition(-1);\r
5653       break;\r
5654 \r
5655     case IDM_ReloadPosition:\r
5656       ReloadPosition(0);\r
5657       break;\r
5658 \r
5659     case IDM_SaveGame:\r
5660       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5661       f = OpenFileDialog(hwnd, "a", defName,\r
5662                          appData.oldSaveStyle ? "gam" : "pgn",\r
5663                          GAME_FILT,\r
5664                          "Save Game to File", NULL, fileTitle, NULL);\r
5665       if (f != NULL) {\r
5666         SaveGame(f, 0, "");\r
5667       }\r
5668       break;\r
5669 \r
5670     case IDM_SavePosition:\r
5671       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5672       f = OpenFileDialog(hwnd, "a", defName,\r
5673                          appData.oldSaveStyle ? "pos" : "fen",\r
5674                          POSITION_FILT,\r
5675                          "Save Position to File", NULL, fileTitle, NULL);\r
5676       if (f != NULL) {\r
5677         SavePosition(f, 0, "");\r
5678       }\r
5679       break;\r
5680 \r
5681     case IDM_SaveDiagram:\r
5682       defName = "diagram";\r
5683       f = OpenFileDialog(hwnd, "wb", defName,\r
5684                          "bmp",\r
5685                          DIAGRAM_FILT,\r
5686                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5687       if (f != NULL) {\r
5688         SaveDiagram(f);\r
5689       }\r
5690       break;\r
5691 \r
5692     case IDM_CopyGame:\r
5693       CopyGameToClipboard();\r
5694       break;\r
5695 \r
5696     case IDM_PasteGame:\r
5697       PasteGameFromClipboard();\r
5698       break;\r
5699 \r
5700     case IDM_CopyGameListToClipboard:\r
5701       CopyGameListToClipboard();\r
5702       break;\r
5703 \r
5704     /* [AS] Autodetect FEN or PGN data */\r
5705     case IDM_PasteAny:\r
5706       PasteGameOrFENFromClipboard();\r
5707       break;\r
5708 \r
5709     /* [AS] Move history */\r
5710     case IDM_ShowMoveHistory:\r
5711         if( MoveHistoryIsUp() ) {\r
5712             MoveHistoryPopDown();\r
5713         }\r
5714         else {\r
5715             MoveHistoryPopUp();\r
5716         }\r
5717         break;\r
5718 \r
5719     /* [AS] Eval graph */\r
5720     case IDM_ShowEvalGraph:\r
5721         if( EvalGraphIsUp() ) {\r
5722             EvalGraphPopDown();\r
5723         }\r
5724         else {\r
5725             EvalGraphPopUp();\r
5726             SetFocus(hwndMain);\r
5727         }\r
5728         break;\r
5729 \r
5730     /* [AS] Engine output */\r
5731     case IDM_ShowEngineOutput:\r
5732         if( EngineOutputIsUp() ) {\r
5733             EngineOutputPopDown();\r
5734         }\r
5735         else {\r
5736             EngineOutputPopUp();\r
5737         }\r
5738         break;\r
5739 \r
5740     /* [AS] User adjudication */\r
5741     case IDM_UserAdjudication_White:\r
5742         UserAdjudicationEvent( +1 );\r
5743         break;\r
5744 \r
5745     case IDM_UserAdjudication_Black:\r
5746         UserAdjudicationEvent( -1 );\r
5747         break;\r
5748 \r
5749     case IDM_UserAdjudication_Draw:\r
5750         UserAdjudicationEvent( 0 );\r
5751         break;\r
5752 \r
5753     /* [AS] Game list options dialog */\r
5754     case IDM_GameListOptions:\r
5755       GameListOptions();\r
5756       break;\r
5757 \r
5758     case IDM_NewChat:\r
5759       ChatPopUp();\r
5760       break;\r
5761 \r
5762     case IDM_CopyPosition:\r
5763       CopyFENToClipboard();\r
5764       break;\r
5765 \r
5766     case IDM_PastePosition:\r
5767       PasteFENFromClipboard();\r
5768       break;\r
5769 \r
5770     case IDM_MailMove:\r
5771       MailMoveEvent();\r
5772       break;\r
5773 \r
5774     case IDM_ReloadCMailMsg:\r
5775       Reset(TRUE, TRUE);\r
5776       ReloadCmailMsgEvent(FALSE);\r
5777       break;\r
5778 \r
5779     case IDM_Minimize:\r
5780       ShowWindow(hwnd, SW_MINIMIZE);\r
5781       break;\r
5782 \r
5783     case IDM_Exit:\r
5784       ExitEvent(0);\r
5785       break;\r
5786 \r
5787     case IDM_MachineWhite:\r
5788       MachineWhiteEvent();\r
5789       /*\r
5790        * refresh the tags dialog only if it's visible\r
5791        */\r
5792       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5793           char *tags;\r
5794           tags = PGNTags(&gameInfo);\r
5795           TagsPopUp(tags, CmailMsg());\r
5796           free(tags);\r
5797       }\r
5798       SAY("computer starts playing white");\r
5799       break;\r
5800 \r
5801     case IDM_MachineBlack:\r
5802       MachineBlackEvent();\r
5803       /*\r
5804        * refresh the tags dialog only if it's visible\r
5805        */\r
5806       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5807           char *tags;\r
5808           tags = PGNTags(&gameInfo);\r
5809           TagsPopUp(tags, CmailMsg());\r
5810           free(tags);\r
5811       }\r
5812       SAY("computer starts playing black");\r
5813       break;\r
5814 \r
5815     case IDM_TwoMachines:\r
5816       TwoMachinesEvent();\r
5817       /*\r
5818        * refresh the tags dialog only if it's visible\r
5819        */\r
5820       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5821           char *tags;\r
5822           tags = PGNTags(&gameInfo);\r
5823           TagsPopUp(tags, CmailMsg());\r
5824           free(tags);\r
5825       }\r
5826       SAY("programs start playing each other");\r
5827       break;\r
5828 \r
5829     case IDM_AnalysisMode:\r
5830       if (!first.analysisSupport) {\r
5831         sprintf(buf, "%s does not support analysis", first.tidy);\r
5832         DisplayError(buf, 0);\r
5833       } else {\r
5834         SAY("analyzing current position");\r
5835         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5836         if (appData.icsActive) {\r
5837                if (gameMode != IcsObserving) {\r
5838                        sprintf(buf, "You are not observing a game");\r
5839                        DisplayError(buf, 0);\r
5840                        /* secure check */\r
5841                        if (appData.icsEngineAnalyze) {\r
5842                                if (appData.debugMode) \r
5843                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5844                                ExitAnalyzeMode();\r
5845                                ModeHighlight();\r
5846                                break;\r
5847                        }\r
5848                        break;\r
5849                } else {\r
5850                        /* if enable, user want disable icsEngineAnalyze */\r
5851                        if (appData.icsEngineAnalyze) {\r
5852                                ExitAnalyzeMode();\r
5853                                ModeHighlight();\r
5854                                break;\r
5855                        }\r
5856                        appData.icsEngineAnalyze = TRUE;\r
5857                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5858                }\r
5859         } \r
5860         if (!appData.showThinking) ToggleShowThinking();\r
5861         AnalyzeModeEvent();\r
5862       }\r
5863       break;\r
5864 \r
5865     case IDM_AnalyzeFile:\r
5866       if (!first.analysisSupport) {\r
5867         char buf[MSG_SIZ];\r
5868         sprintf(buf, "%s does not support analysis", first.tidy);\r
5869         DisplayError(buf, 0);\r
5870       } else {\r
5871         if (!appData.showThinking) ToggleShowThinking();\r
5872         AnalyzeFileEvent();\r
5873         LoadGameDialog(hwnd, "Analyze Game from File");\r
5874         AnalysisPeriodicEvent(1);\r
5875       }\r
5876       break;\r
5877 \r
5878     case IDM_IcsClient:\r
5879       IcsClientEvent();\r
5880       break;\r
5881 \r
5882     case IDM_EditGame:\r
5883       EditGameEvent();\r
5884       SAY("edit game");\r
5885       break;\r
5886 \r
5887     case IDM_EditPosition:\r
5888       EditPositionEvent();\r
5889       SAY("to set up a position type a FEN");\r
5890       break;\r
5891 \r
5892     case IDM_Training:\r
5893       TrainingEvent();\r
5894       break;\r
5895 \r
5896     case IDM_ShowGameList:\r
5897       ShowGameListProc();\r
5898       break;\r
5899 \r
5900     case IDM_EditTags:\r
5901       EditTagsProc();\r
5902       break;\r
5903 \r
5904     case IDM_EditComment:\r
5905       if (commentDialogUp && editComment) {\r
5906         CommentPopDown();\r
5907       } else {\r
5908         EditCommentEvent();\r
5909       }\r
5910       break;\r
5911 \r
5912     case IDM_Pause:\r
5913       PauseEvent();\r
5914       break;\r
5915 \r
5916     case IDM_Accept:\r
5917       AcceptEvent();\r
5918       break;\r
5919 \r
5920     case IDM_Decline:\r
5921       DeclineEvent();\r
5922       break;\r
5923 \r
5924     case IDM_Rematch:\r
5925       RematchEvent();\r
5926       break;\r
5927 \r
5928     case IDM_CallFlag:\r
5929       CallFlagEvent();\r
5930       break;\r
5931 \r
5932     case IDM_Draw:\r
5933       DrawEvent();\r
5934       break;\r
5935 \r
5936     case IDM_Adjourn:\r
5937       AdjournEvent();\r
5938       break;\r
5939 \r
5940     case IDM_Abort:\r
5941       AbortEvent();\r
5942       break;\r
5943 \r
5944     case IDM_Resign:\r
5945       ResignEvent();\r
5946       break;\r
5947 \r
5948     case IDM_StopObserving:\r
5949       StopObservingEvent();\r
5950       break;\r
5951 \r
5952     case IDM_StopExamining:\r
5953       StopExaminingEvent();\r
5954       break;\r
5955 \r
5956     case IDM_TypeInMove:\r
5957       PopUpMoveDialog('\000');\r
5958       break;\r
5959 \r
5960     case IDM_TypeInName:\r
5961       PopUpNameDialog('\000');\r
5962       break;\r
5963 \r
5964     case IDM_Backward:\r
5965       BackwardEvent();\r
5966       SetFocus(hwndMain);\r
5967       break;\r
5968 \r
5969     JAWS_MENU_ITEMS\r
5970 \r
5971     case IDM_Forward:\r
5972       ForwardEvent();\r
5973       SetFocus(hwndMain);\r
5974       break;\r
5975 \r
5976     case IDM_ToStart:\r
5977       ToStartEvent();\r
5978       SetFocus(hwndMain);\r
5979       break;\r
5980 \r
5981     case IDM_ToEnd:\r
5982       ToEndEvent();\r
5983       SetFocus(hwndMain);\r
5984       break;\r
5985 \r
5986     case IDM_Revert:\r
5987       RevertEvent();\r
5988       break;\r
5989 \r
5990     case IDM_TruncateGame:\r
5991       TruncateGameEvent();\r
5992       break;\r
5993 \r
5994     case IDM_MoveNow:\r
5995       MoveNowEvent();\r
5996       break;\r
5997 \r
5998     case IDM_RetractMove:\r
5999       RetractMoveEvent();\r
6000       break;\r
6001 \r
6002     case IDM_FlipView:\r
6003       flipView = !flipView;\r
6004       DrawPosition(FALSE, NULL);\r
6005       break;\r
6006 \r
6007     case IDM_FlipClock:\r
6008       flipClock = !flipClock;\r
6009       DisplayBothClocks();\r
6010       DrawPosition(FALSE, NULL);\r
6011       break;\r
6012 \r
6013     case IDM_MuteSounds:\r
6014       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6015       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6016                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6017       break;\r
6018 \r
6019     case IDM_GeneralOptions:\r
6020       GeneralOptionsPopup(hwnd);\r
6021       DrawPosition(TRUE, NULL);\r
6022       break;\r
6023 \r
6024     case IDM_BoardOptions:\r
6025       BoardOptionsPopup(hwnd);\r
6026       break;\r
6027 \r
6028     case IDM_EnginePlayOptions:\r
6029       EnginePlayOptionsPopup(hwnd);\r
6030       break;\r
6031 \r
6032     case IDM_Engine1Options:\r
6033       EngineOptionsPopup(hwnd, &first);\r
6034       break;\r
6035 \r
6036     case IDM_Engine2Options:\r
6037       EngineOptionsPopup(hwnd, &second);\r
6038       break;\r
6039 \r
6040     case IDM_OptionsUCI:\r
6041       UciOptionsPopup(hwnd);\r
6042       break;\r
6043 \r
6044     case IDM_IcsOptions:\r
6045       IcsOptionsPopup(hwnd);\r
6046       break;\r
6047 \r
6048     case IDM_Fonts:\r
6049       FontsOptionsPopup(hwnd);\r
6050       break;\r
6051 \r
6052     case IDM_Sounds:\r
6053       SoundOptionsPopup(hwnd);\r
6054       break;\r
6055 \r
6056     case IDM_CommPort:\r
6057       CommPortOptionsPopup(hwnd);\r
6058       break;\r
6059 \r
6060     case IDM_LoadOptions:\r
6061       LoadOptionsPopup(hwnd);\r
6062       break;\r
6063 \r
6064     case IDM_SaveOptions:\r
6065       SaveOptionsPopup(hwnd);\r
6066       break;\r
6067 \r
6068     case IDM_TimeControl:\r
6069       TimeControlOptionsPopup(hwnd);\r
6070       break;\r
6071 \r
6072     case IDM_SaveSettings:\r
6073       SaveSettings(settingsFileName);\r
6074       break;\r
6075 \r
6076     case IDM_SaveSettingsOnExit:\r
6077       saveSettingsOnExit = !saveSettingsOnExit;\r
6078       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6079                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6080                                          MF_CHECKED : MF_UNCHECKED));\r
6081       break;\r
6082 \r
6083     case IDM_Hint:\r
6084       HintEvent();\r
6085       break;\r
6086 \r
6087     case IDM_Book:\r
6088       BookEvent();\r
6089       break;\r
6090 \r
6091     case IDM_AboutGame:\r
6092       AboutGameEvent();\r
6093       break;\r
6094 \r
6095     case IDM_Debug:\r
6096       appData.debugMode = !appData.debugMode;\r
6097       if (appData.debugMode) {\r
6098         char dir[MSG_SIZ];\r
6099         GetCurrentDirectory(MSG_SIZ, dir);\r
6100         SetCurrentDirectory(installDir);\r
6101         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6102         SetCurrentDirectory(dir);\r
6103         setbuf(debugFP, NULL);\r
6104       } else {\r
6105         fclose(debugFP);\r
6106         debugFP = NULL;\r
6107       }\r
6108       break;\r
6109 \r
6110     case IDM_HELPCONTENTS:\r
6111       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6112           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6113           MessageBox (GetFocus(),\r
6114                     "Unable to activate help",\r
6115                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6116       }\r
6117       break;\r
6118 \r
6119     case IDM_HELPSEARCH:\r
6120         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6121             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6122         MessageBox (GetFocus(),\r
6123                     "Unable to activate help",\r
6124                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6125       }\r
6126       break;\r
6127 \r
6128     case IDM_HELPHELP:\r
6129       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6130         MessageBox (GetFocus(),\r
6131                     "Unable to activate help",\r
6132                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6133       }\r
6134       break;\r
6135 \r
6136     case IDM_ABOUT:\r
6137       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6138       DialogBox(hInst, \r
6139         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6140         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6141       FreeProcInstance(lpProc);\r
6142       break;\r
6143 \r
6144     case IDM_DirectCommand1:\r
6145       AskQuestionEvent("Direct Command",\r
6146                        "Send to chess program:", "", "1");\r
6147       break;\r
6148     case IDM_DirectCommand2:\r
6149       AskQuestionEvent("Direct Command",\r
6150                        "Send to second chess program:", "", "2");\r
6151       break;\r
6152 \r
6153     case EP_WhitePawn:\r
6154       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6155       fromX = fromY = -1;\r
6156       break;\r
6157 \r
6158     case EP_WhiteKnight:\r
6159       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6160       fromX = fromY = -1;\r
6161       break;\r
6162 \r
6163     case EP_WhiteBishop:\r
6164       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6165       fromX = fromY = -1;\r
6166       break;\r
6167 \r
6168     case EP_WhiteRook:\r
6169       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6170       fromX = fromY = -1;\r
6171       break;\r
6172 \r
6173     case EP_WhiteQueen:\r
6174       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6175       fromX = fromY = -1;\r
6176       break;\r
6177 \r
6178     case EP_WhiteFerz:\r
6179       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6180       fromX = fromY = -1;\r
6181       break;\r
6182 \r
6183     case EP_WhiteWazir:\r
6184       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6185       fromX = fromY = -1;\r
6186       break;\r
6187 \r
6188     case EP_WhiteAlfil:\r
6189       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6190       fromX = fromY = -1;\r
6191       break;\r
6192 \r
6193     case EP_WhiteCannon:\r
6194       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6195       fromX = fromY = -1;\r
6196       break;\r
6197 \r
6198     case EP_WhiteCardinal:\r
6199       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6200       fromX = fromY = -1;\r
6201       break;\r
6202 \r
6203     case EP_WhiteMarshall:\r
6204       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6205       fromX = fromY = -1;\r
6206       break;\r
6207 \r
6208     case EP_WhiteKing:\r
6209       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6210       fromX = fromY = -1;\r
6211       break;\r
6212 \r
6213     case EP_BlackPawn:\r
6214       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6215       fromX = fromY = -1;\r
6216       break;\r
6217 \r
6218     case EP_BlackKnight:\r
6219       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6220       fromX = fromY = -1;\r
6221       break;\r
6222 \r
6223     case EP_BlackBishop:\r
6224       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6225       fromX = fromY = -1;\r
6226       break;\r
6227 \r
6228     case EP_BlackRook:\r
6229       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6230       fromX = fromY = -1;\r
6231       break;\r
6232 \r
6233     case EP_BlackQueen:\r
6234       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6235       fromX = fromY = -1;\r
6236       break;\r
6237 \r
6238     case EP_BlackFerz:\r
6239       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6240       fromX = fromY = -1;\r
6241       break;\r
6242 \r
6243     case EP_BlackWazir:\r
6244       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6245       fromX = fromY = -1;\r
6246       break;\r
6247 \r
6248     case EP_BlackAlfil:\r
6249       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6250       fromX = fromY = -1;\r
6251       break;\r
6252 \r
6253     case EP_BlackCannon:\r
6254       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6255       fromX = fromY = -1;\r
6256       break;\r
6257 \r
6258     case EP_BlackCardinal:\r
6259       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6260       fromX = fromY = -1;\r
6261       break;\r
6262 \r
6263     case EP_BlackMarshall:\r
6264       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6265       fromX = fromY = -1;\r
6266       break;\r
6267 \r
6268     case EP_BlackKing:\r
6269       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6270       fromX = fromY = -1;\r
6271       break;\r
6272 \r
6273     case EP_EmptySquare:\r
6274       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6275       fromX = fromY = -1;\r
6276       break;\r
6277 \r
6278     case EP_ClearBoard:\r
6279       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6280       fromX = fromY = -1;\r
6281       break;\r
6282 \r
6283     case EP_White:\r
6284       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6285       fromX = fromY = -1;\r
6286       break;\r
6287 \r
6288     case EP_Black:\r
6289       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6290       fromX = fromY = -1;\r
6291       break;\r
6292 \r
6293     case EP_Promote:\r
6294       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6295       fromX = fromY = -1;\r
6296       break;\r
6297 \r
6298     case EP_Demote:\r
6299       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6300       fromX = fromY = -1;\r
6301       break;\r
6302 \r
6303     case DP_Pawn:\r
6304       DropMenuEvent(WhitePawn, fromX, fromY);\r
6305       fromX = fromY = -1;\r
6306       break;\r
6307 \r
6308     case DP_Knight:\r
6309       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6310       fromX = fromY = -1;\r
6311       break;\r
6312 \r
6313     case DP_Bishop:\r
6314       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6315       fromX = fromY = -1;\r
6316       break;\r
6317 \r
6318     case DP_Rook:\r
6319       DropMenuEvent(WhiteRook, fromX, fromY);\r
6320       fromX = fromY = -1;\r
6321       break;\r
6322 \r
6323     case DP_Queen:\r
6324       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6325       fromX = fromY = -1;\r
6326       break;\r
6327 \r
6328     default:\r
6329       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6330     }\r
6331     break;\r
6332 \r
6333   case WM_TIMER:\r
6334     switch (wParam) {\r
6335     case CLOCK_TIMER_ID:\r
6336       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6337       clockTimerEvent = 0;\r
6338       DecrementClocks(); /* call into back end */\r
6339       break;\r
6340     case LOAD_GAME_TIMER_ID:\r
6341       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6342       loadGameTimerEvent = 0;\r
6343       AutoPlayGameLoop(); /* call into back end */\r
6344       break;\r
6345     case ANALYSIS_TIMER_ID:\r
6346       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6347                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6348         AnalysisPeriodicEvent(0);\r
6349       } else {\r
6350         KillTimer(hwnd, analysisTimerEvent);\r
6351         analysisTimerEvent = 0;\r
6352       }\r
6353       break;\r
6354     case DELAYED_TIMER_ID:\r
6355       KillTimer(hwnd, delayedTimerEvent);\r
6356       delayedTimerEvent = 0;\r
6357       delayedTimerCallback();\r
6358       break;\r
6359     }\r
6360     break;\r
6361 \r
6362   case WM_USER_Input:\r
6363     InputEvent(hwnd, message, wParam, lParam);\r
6364     break;\r
6365 \r
6366   /* [AS] Also move "attached" child windows */\r
6367   case WM_WINDOWPOSCHANGING:\r
6368 \r
6369     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6370         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6371 \r
6372         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6373             /* Window is moving */\r
6374             RECT rcMain;\r
6375 \r
6376 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6377             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6378             rcMain.right  = boardX + winWidth;\r
6379             rcMain.top    = boardY;\r
6380             rcMain.bottom = boardY + winHeight;\r
6381             \r
6382             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6383             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6384             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6385             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6386             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6387             boardX = lpwp->x;\r
6388             boardY = lpwp->y;\r
6389         }\r
6390     }\r
6391     break;\r
6392 \r
6393   /* [AS] Snapping */\r
6394   case WM_ENTERSIZEMOVE:\r
6395     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6396     if (hwnd == hwndMain) {\r
6397       doingSizing = TRUE;\r
6398       lastSizing = 0;\r
6399     }\r
6400     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6401     break;\r
6402 \r
6403   case WM_SIZING:\r
6404     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6405     if (hwnd == hwndMain) {\r
6406       lastSizing = wParam;\r
6407     }\r
6408     break;\r
6409 \r
6410   case WM_MOVING:\r
6411     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6412       return OnMoving( &sd, hwnd, wParam, lParam );\r
6413 \r
6414   case WM_EXITSIZEMOVE:\r
6415     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6416     if (hwnd == hwndMain) {\r
6417       RECT client;\r
6418       doingSizing = FALSE;\r
6419       InvalidateRect(hwnd, &boardRect, FALSE);\r
6420       GetClientRect(hwnd, &client);\r
6421       ResizeBoard(client.right, client.bottom, lastSizing);\r
6422       lastSizing = 0;\r
6423       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6424     }\r
6425     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6426     break;\r
6427 \r
6428   case WM_DESTROY: /* message: window being destroyed */\r
6429     PostQuitMessage(0);\r
6430     break;\r
6431 \r
6432   case WM_CLOSE:\r
6433     if (hwnd == hwndMain) {\r
6434       ExitEvent(0);\r
6435     }\r
6436     break;\r
6437 \r
6438   default:      /* Passes it on if unprocessed */\r
6439     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6440   }\r
6441   return 0;\r
6442 }\r
6443 \r
6444 /*---------------------------------------------------------------------------*\\r
6445  *\r
6446  * Misc utility routines\r
6447  *\r
6448 \*---------------------------------------------------------------------------*/\r
6449 \r
6450 /*\r
6451  * Decent random number generator, at least not as bad as Windows\r
6452  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6453  */\r
6454 unsigned int randstate;\r
6455 \r
6456 int\r
6457 myrandom(void)\r
6458 {\r
6459   randstate = randstate * 1664525 + 1013904223;\r
6460   return (int) randstate & 0x7fffffff;\r
6461 }\r
6462 \r
6463 void\r
6464 mysrandom(unsigned int seed)\r
6465 {\r
6466   randstate = seed;\r
6467 }\r
6468 \r
6469 \r
6470 /* \r
6471  * returns TRUE if user selects a different color, FALSE otherwise \r
6472  */\r
6473 \r
6474 BOOL\r
6475 ChangeColor(HWND hwnd, COLORREF *which)\r
6476 {\r
6477   static BOOL firstTime = TRUE;\r
6478   static DWORD customColors[16];\r
6479   CHOOSECOLOR cc;\r
6480   COLORREF newcolor;\r
6481   int i;\r
6482   ColorClass ccl;\r
6483 \r
6484   if (firstTime) {\r
6485     /* Make initial colors in use available as custom colors */\r
6486     /* Should we put the compiled-in defaults here instead? */\r
6487     i = 0;\r
6488     customColors[i++] = lightSquareColor & 0xffffff;\r
6489     customColors[i++] = darkSquareColor & 0xffffff;\r
6490     customColors[i++] = whitePieceColor & 0xffffff;\r
6491     customColors[i++] = blackPieceColor & 0xffffff;\r
6492     customColors[i++] = highlightSquareColor & 0xffffff;\r
6493     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6494 \r
6495     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6496       customColors[i++] = textAttribs[ccl].color;\r
6497     }\r
6498     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6499     firstTime = FALSE;\r
6500   }\r
6501 \r
6502   cc.lStructSize = sizeof(cc);\r
6503   cc.hwndOwner = hwnd;\r
6504   cc.hInstance = NULL;\r
6505   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6506   cc.lpCustColors = (LPDWORD) customColors;\r
6507   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6508 \r
6509   if (!ChooseColor(&cc)) return FALSE;\r
6510 \r
6511   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6512   if (newcolor == *which) return FALSE;\r
6513   *which = newcolor;\r
6514   return TRUE;\r
6515 \r
6516   /*\r
6517   InitDrawingColors();\r
6518   InvalidateRect(hwnd, &boardRect, FALSE);\r
6519   */\r
6520 }\r
6521 \r
6522 BOOLEAN\r
6523 MyLoadSound(MySound *ms)\r
6524 {\r
6525   BOOL ok = FALSE;\r
6526   struct stat st;\r
6527   FILE *f;\r
6528 \r
6529   if (ms->data) free(ms->data);\r
6530   ms->data = NULL;\r
6531 \r
6532   switch (ms->name[0]) {\r
6533   case NULLCHAR:\r
6534     /* Silence */\r
6535     ok = TRUE;\r
6536     break;\r
6537   case '$':\r
6538     /* System sound from Control Panel.  Don't preload here. */\r
6539     ok = TRUE;\r
6540     break;\r
6541   case '!':\r
6542     if (ms->name[1] == NULLCHAR) {\r
6543       /* "!" alone = silence */\r
6544       ok = TRUE;\r
6545     } else {\r
6546       /* Builtin wave resource.  Error if not found. */\r
6547       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6548       if (h == NULL) break;\r
6549       ms->data = (void *)LoadResource(hInst, h);\r
6550       if (h == NULL) break;\r
6551       ok = TRUE;\r
6552     }\r
6553     break;\r
6554   default:\r
6555     /* .wav file.  Error if not found. */\r
6556     f = fopen(ms->name, "rb");\r
6557     if (f == NULL) break;\r
6558     if (fstat(fileno(f), &st) < 0) break;\r
6559     ms->data = malloc(st.st_size);\r
6560     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6561     fclose(f);\r
6562     ok = TRUE;\r
6563     break;\r
6564   }\r
6565   if (!ok) {\r
6566     char buf[MSG_SIZ];\r
6567     sprintf(buf, "Error loading sound %s", ms->name);\r
6568     DisplayError(buf, GetLastError());\r
6569   }\r
6570   return ok;\r
6571 }\r
6572 \r
6573 BOOLEAN\r
6574 MyPlaySound(MySound *ms)\r
6575 {\r
6576   BOOLEAN ok = FALSE;\r
6577 \r
6578   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6579   switch (ms->name[0]) {\r
6580   case NULLCHAR:\r
6581         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6582     /* Silence */\r
6583     ok = TRUE;\r
6584     break;\r
6585   case '$':\r
6586     /* System sound from Control Panel (deprecated feature).\r
6587        "$" alone or an unset sound name gets default beep (still in use). */\r
6588     if (ms->name[1]) {\r
6589       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6590     }\r
6591     if (!ok) ok = MessageBeep(MB_OK);\r
6592     break; \r
6593   case '!':\r
6594     /* Builtin wave resource, or "!" alone for silence */\r
6595     if (ms->name[1]) {\r
6596       if (ms->data == NULL) return FALSE;\r
6597       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6598     } else {\r
6599       ok = TRUE;\r
6600     }\r
6601     break;\r
6602   default:\r
6603     /* .wav file.  Error if not found. */\r
6604     if (ms->data == NULL) return FALSE;\r
6605     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6606     break;\r
6607   }\r
6608   /* Don't print an error: this can happen innocently if the sound driver\r
6609      is busy; for instance, if another instance of WinBoard is playing\r
6610      a sound at about the same time. */\r
6611   return ok;\r
6612 }\r
6613 \r
6614 \r
6615 LRESULT CALLBACK\r
6616 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6617 {\r
6618   BOOL ok;\r
6619   OPENFILENAME *ofn;\r
6620   static UINT *number; /* gross that this is static */\r
6621 \r
6622   switch (message) {\r
6623   case WM_INITDIALOG: /* message: initialize dialog box */\r
6624     /* Center the dialog over the application window */\r
6625     ofn = (OPENFILENAME *) lParam;\r
6626     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6627       number = (UINT *) ofn->lCustData;\r
6628       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6629     } else {\r
6630       number = NULL;\r
6631     }\r
6632     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6633     return FALSE;  /* Allow for further processing */\r
6634 \r
6635   case WM_COMMAND:\r
6636     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6637       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6638     }\r
6639     return FALSE;  /* Allow for further processing */\r
6640   }\r
6641   return FALSE;\r
6642 }\r
6643 \r
6644 UINT APIENTRY\r
6645 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6646 {\r
6647   static UINT *number;\r
6648   OPENFILENAME *ofname;\r
6649   OFNOTIFY *ofnot;\r
6650   switch (uiMsg) {\r
6651   case WM_INITDIALOG:\r
6652     ofname = (OPENFILENAME *)lParam;\r
6653     number = (UINT *)(ofname->lCustData);\r
6654     break;\r
6655   case WM_NOTIFY:\r
6656     ofnot = (OFNOTIFY *)lParam;\r
6657     if (ofnot->hdr.code == CDN_FILEOK) {\r
6658       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6659     }\r
6660     break;\r
6661   }\r
6662   return 0;\r
6663 }\r
6664 \r
6665 \r
6666 FILE *\r
6667 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6668                char *nameFilt, char *dlgTitle, UINT *number,\r
6669                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6670 {\r
6671   OPENFILENAME openFileName;\r
6672   char buf1[MSG_SIZ];\r
6673   FILE *f;\r
6674 \r
6675   if (fileName == NULL) fileName = buf1;\r
6676   if (defName == NULL) {\r
6677     strcpy(fileName, "*.");\r
6678     strcat(fileName, defExt);\r
6679   } else {\r
6680     strcpy(fileName, defName);\r
6681   }\r
6682   if (fileTitle) strcpy(fileTitle, "");\r
6683   if (number) *number = 0;\r
6684 \r
6685   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6686   openFileName.hwndOwner         = hwnd;\r
6687   openFileName.hInstance         = (HANDLE) hInst;\r
6688   openFileName.lpstrFilter       = nameFilt;\r
6689   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6690   openFileName.nMaxCustFilter    = 0L;\r
6691   openFileName.nFilterIndex      = 1L;\r
6692   openFileName.lpstrFile         = fileName;\r
6693   openFileName.nMaxFile          = MSG_SIZ;\r
6694   openFileName.lpstrFileTitle    = fileTitle;\r
6695   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6696   openFileName.lpstrInitialDir   = NULL;\r
6697   openFileName.lpstrTitle        = dlgTitle;\r
6698   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6699     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6700     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6701     | (oldDialog ? 0 : OFN_EXPLORER);\r
6702   openFileName.nFileOffset       = 0;\r
6703   openFileName.nFileExtension    = 0;\r
6704   openFileName.lpstrDefExt       = defExt;\r
6705   openFileName.lCustData         = (LONG) number;\r
6706   openFileName.lpfnHook          = oldDialog ?\r
6707     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6708   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6709 \r
6710   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6711                         GetOpenFileName(&openFileName)) {\r
6712     /* open the file */\r
6713     f = fopen(openFileName.lpstrFile, write);\r
6714     if (f == NULL) {\r
6715       MessageBox(hwnd, "File open failed", NULL,\r
6716                  MB_OK|MB_ICONEXCLAMATION);\r
6717       return NULL;\r
6718     }\r
6719   } else {\r
6720     int err = CommDlgExtendedError();\r
6721     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6722     return FALSE;\r
6723   }\r
6724   return f;\r
6725 }\r
6726 \r
6727 \r
6728 \r
6729 VOID APIENTRY\r
6730 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6731 {\r
6732   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6733 \r
6734   /*\r
6735    * Get the first pop-up menu in the menu template. This is the\r
6736    * menu that TrackPopupMenu displays.\r
6737    */\r
6738   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6739 \r
6740   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6741 \r
6742   /*\r
6743    * TrackPopup uses screen coordinates, so convert the\r
6744    * coordinates of the mouse click to screen coordinates.\r
6745    */\r
6746   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6747 \r
6748   /* Draw and track the floating pop-up menu. */\r
6749   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6750                  pt.x, pt.y, 0, hwnd, NULL);\r
6751 \r
6752   /* Destroy the menu.*/\r
6753   DestroyMenu(hmenu);\r
6754 }\r
6755    \r
6756 typedef struct {\r
6757   HWND hDlg, hText;\r
6758   int sizeX, sizeY, newSizeX, newSizeY;\r
6759   HDWP hdwp;\r
6760 } ResizeEditPlusButtonsClosure;\r
6761 \r
6762 BOOL CALLBACK\r
6763 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6764 {\r
6765   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6766   RECT rect;\r
6767   POINT pt;\r
6768 \r
6769   if (hChild == cl->hText) return TRUE;\r
6770   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6771   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6772   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6773   ScreenToClient(cl->hDlg, &pt);\r
6774   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6775     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6776   return TRUE;\r
6777 }\r
6778 \r
6779 /* Resize a dialog that has a (rich) edit field filling most of\r
6780    the top, with a row of buttons below */\r
6781 VOID\r
6782 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6783 {\r
6784   RECT rectText;\r
6785   int newTextHeight, newTextWidth;\r
6786   ResizeEditPlusButtonsClosure cl;\r
6787   \r
6788   /*if (IsIconic(hDlg)) return;*/\r
6789   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6790   \r
6791   cl.hdwp = BeginDeferWindowPos(8);\r
6792 \r
6793   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6794   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6795   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6796   if (newTextHeight < 0) {\r
6797     newSizeY += -newTextHeight;\r
6798     newTextHeight = 0;\r
6799   }\r
6800   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6801     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6802 \r
6803   cl.hDlg = hDlg;\r
6804   cl.hText = hText;\r
6805   cl.sizeX = sizeX;\r
6806   cl.sizeY = sizeY;\r
6807   cl.newSizeX = newSizeX;\r
6808   cl.newSizeY = newSizeY;\r
6809   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6810 \r
6811   EndDeferWindowPos(cl.hdwp);\r
6812 }\r
6813 \r
6814 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6815 {\r
6816     RECT    rChild, rParent;\r
6817     int     wChild, hChild, wParent, hParent;\r
6818     int     wScreen, hScreen, xNew, yNew;\r
6819     HDC     hdc;\r
6820 \r
6821     /* Get the Height and Width of the child window */\r
6822     GetWindowRect (hwndChild, &rChild);\r
6823     wChild = rChild.right - rChild.left;\r
6824     hChild = rChild.bottom - rChild.top;\r
6825 \r
6826     /* Get the Height and Width of the parent window */\r
6827     GetWindowRect (hwndParent, &rParent);\r
6828     wParent = rParent.right - rParent.left;\r
6829     hParent = rParent.bottom - rParent.top;\r
6830 \r
6831     /* Get the display limits */\r
6832     hdc = GetDC (hwndChild);\r
6833     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6834     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6835     ReleaseDC(hwndChild, hdc);\r
6836 \r
6837     /* Calculate new X position, then adjust for screen */\r
6838     xNew = rParent.left + ((wParent - wChild) /2);\r
6839     if (xNew < 0) {\r
6840         xNew = 0;\r
6841     } else if ((xNew+wChild) > wScreen) {\r
6842         xNew = wScreen - wChild;\r
6843     }\r
6844 \r
6845     /* Calculate new Y position, then adjust for screen */\r
6846     if( mode == 0 ) {\r
6847         yNew = rParent.top  + ((hParent - hChild) /2);\r
6848     }\r
6849     else {\r
6850         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6851     }\r
6852 \r
6853     if (yNew < 0) {\r
6854         yNew = 0;\r
6855     } else if ((yNew+hChild) > hScreen) {\r
6856         yNew = hScreen - hChild;\r
6857     }\r
6858 \r
6859     /* Set it, and return */\r
6860     return SetWindowPos (hwndChild, NULL,\r
6861                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6862 }\r
6863 \r
6864 /* Center one window over another */\r
6865 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6866 {\r
6867     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6868 }\r
6869 \r
6870 /*---------------------------------------------------------------------------*\\r
6871  *\r
6872  * Startup Dialog functions\r
6873  *\r
6874 \*---------------------------------------------------------------------------*/\r
6875 void\r
6876 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6877 {\r
6878   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6879 \r
6880   while (*cd != NULL) {\r
6881     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6882     cd++;\r
6883   }\r
6884 }\r
6885 \r
6886 void\r
6887 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6888 {\r
6889   char buf1[ARG_MAX];\r
6890   int len;\r
6891 \r
6892   if (str[0] == '@') {\r
6893     FILE* f = fopen(str + 1, "r");\r
6894     if (f == NULL) {\r
6895       DisplayFatalError(str + 1, errno, 2);\r
6896       return;\r
6897     }\r
6898     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6899     fclose(f);\r
6900     buf1[len] = NULLCHAR;\r
6901     str = buf1;\r
6902   }\r
6903 \r
6904   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6905 \r
6906   for (;;) {\r
6907     char buf[MSG_SIZ];\r
6908     char *end = strchr(str, '\n');\r
6909     if (end == NULL) return;\r
6910     memcpy(buf, str, end - str);\r
6911     buf[end - str] = NULLCHAR;\r
6912     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6913     str = end + 1;\r
6914   }\r
6915 }\r
6916 \r
6917 void\r
6918 SetStartupDialogEnables(HWND hDlg)\r
6919 {\r
6920   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6921     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6922     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6923   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6924     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6925   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6926     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6927   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6928     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6929   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6930     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6931     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6932     IsDlgButtonChecked(hDlg, OPT_View));\r
6933 }\r
6934 \r
6935 char *\r
6936 QuoteForFilename(char *filename)\r
6937 {\r
6938   int dquote, space;\r
6939   dquote = strchr(filename, '"') != NULL;\r
6940   space = strchr(filename, ' ') != NULL;\r
6941   if (dquote || space) {\r
6942     if (dquote) {\r
6943       return "'";\r
6944     } else {\r
6945       return "\"";\r
6946     }\r
6947   } else {\r
6948     return "";\r
6949   }\r
6950 }\r
6951 \r
6952 VOID\r
6953 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6954 {\r
6955   char buf[MSG_SIZ];\r
6956   char *q;\r
6957 \r
6958   InitComboStringsFromOption(hwndCombo, nthnames);\r
6959   q = QuoteForFilename(nthcp);\r
6960   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6961   if (*nthdir != NULLCHAR) {\r
6962     q = QuoteForFilename(nthdir);\r
6963     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6964   }\r
6965   if (*nthcp == NULLCHAR) {\r
6966     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6967   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6968     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6969     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6970   }\r
6971 }\r
6972 \r
6973 LRESULT CALLBACK\r
6974 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6975 {\r
6976   char buf[MSG_SIZ];\r
6977   HANDLE hwndCombo;\r
6978   char *p;\r
6979 \r
6980   switch (message) {\r
6981   case WM_INITDIALOG:\r
6982     /* Center the dialog */\r
6983     CenterWindow (hDlg, GetDesktopWindow());\r
6984     /* Initialize the dialog items */\r
6985     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6986                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6987                   firstChessProgramNames);\r
6988     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6989                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6990                   secondChessProgramNames);\r
6991     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6992     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6993     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6994     if (*appData.icsHelper != NULLCHAR) {\r
6995       char *q = QuoteForFilename(appData.icsHelper);\r
6996       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6997     }\r
6998     if (*appData.icsHost == NULLCHAR) {\r
6999       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7000       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7001     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7002       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7003       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7004     }\r
7005 \r
7006     if (appData.icsActive) {\r
7007       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7008     }\r
7009     else if (appData.noChessProgram) {\r
7010       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7011     }\r
7012     else {\r
7013       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7014     }\r
7015 \r
7016     SetStartupDialogEnables(hDlg);\r
7017     return TRUE;\r
7018 \r
7019   case WM_COMMAND:\r
7020     switch (LOWORD(wParam)) {\r
7021     case IDOK:\r
7022       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7023         strcpy(buf, "/fcp=");\r
7024         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7025         p = buf;\r
7026         ParseArgs(StringGet, &p);\r
7027         strcpy(buf, "/scp=");\r
7028         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7029         p = buf;\r
7030         ParseArgs(StringGet, &p);\r
7031         appData.noChessProgram = FALSE;\r
7032         appData.icsActive = FALSE;\r
7033       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7034         strcpy(buf, "/ics /icshost=");\r
7035         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7036         p = buf;\r
7037         ParseArgs(StringGet, &p);\r
7038         if (appData.zippyPlay) {\r
7039           strcpy(buf, "/fcp=");\r
7040           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7041           p = buf;\r
7042           ParseArgs(StringGet, &p);\r
7043         }\r
7044       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7045         appData.noChessProgram = TRUE;\r
7046         appData.icsActive = FALSE;\r
7047       } else {\r
7048         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7049                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7050         return TRUE;\r
7051       }\r
7052       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7053         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7054         p = buf;\r
7055         ParseArgs(StringGet, &p);\r
7056       }\r
7057       EndDialog(hDlg, TRUE);\r
7058       return TRUE;\r
7059 \r
7060     case IDCANCEL:\r
7061       ExitEvent(0);\r
7062       return TRUE;\r
7063 \r
7064     case IDM_HELPCONTENTS:\r
7065       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7066         MessageBox (GetFocus(),\r
7067                     "Unable to activate help",\r
7068                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7069       }\r
7070       break;\r
7071 \r
7072     default:\r
7073       SetStartupDialogEnables(hDlg);\r
7074       break;\r
7075     }\r
7076     break;\r
7077   }\r
7078   return FALSE;\r
7079 }\r
7080 \r
7081 /*---------------------------------------------------------------------------*\\r
7082  *\r
7083  * About box dialog functions\r
7084  *\r
7085 \*---------------------------------------------------------------------------*/\r
7086 \r
7087 /* Process messages for "About" dialog box */\r
7088 LRESULT CALLBACK\r
7089 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7090 {\r
7091   switch (message) {\r
7092   case WM_INITDIALOG: /* message: initialize dialog box */\r
7093     /* Center the dialog over the application window */\r
7094     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7095     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7096     JAWS_COPYRIGHT\r
7097     return (TRUE);\r
7098 \r
7099   case WM_COMMAND: /* message: received a command */\r
7100     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7101         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7102       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7103       return (TRUE);\r
7104     }\r
7105     break;\r
7106   }\r
7107   return (FALSE);\r
7108 }\r
7109 \r
7110 /*---------------------------------------------------------------------------*\\r
7111  *\r
7112  * Comment Dialog functions\r
7113  *\r
7114 \*---------------------------------------------------------------------------*/\r
7115 \r
7116 LRESULT CALLBACK\r
7117 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7118 {\r
7119   static HANDLE hwndText = NULL;\r
7120   int len, newSizeX, newSizeY, flags;\r
7121   static int sizeX, sizeY;\r
7122   char *str;\r
7123   RECT rect;\r
7124   MINMAXINFO *mmi;\r
7125 \r
7126   switch (message) {\r
7127   case WM_INITDIALOG: /* message: initialize dialog box */\r
7128     /* Initialize the dialog items */\r
7129     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7130     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7131     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7132     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7133     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7134     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7135     SetWindowText(hDlg, commentTitle);\r
7136     if (editComment) {\r
7137       SetFocus(hwndText);\r
7138     } else {\r
7139       SetFocus(GetDlgItem(hDlg, IDOK));\r
7140     }\r
7141     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7142                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7143                 MAKELPARAM(FALSE, 0));\r
7144     /* Size and position the dialog */\r
7145     if (!commentDialog) {\r
7146       commentDialog = hDlg;\r
7147       flags = SWP_NOZORDER;\r
7148       GetClientRect(hDlg, &rect);\r
7149       sizeX = rect.right;\r
7150       sizeY = rect.bottom;\r
7151       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7152           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7153         WINDOWPLACEMENT wp;\r
7154         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7155         wp.length = sizeof(WINDOWPLACEMENT);\r
7156         wp.flags = 0;\r
7157         wp.showCmd = SW_SHOW;\r
7158         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7159         wp.rcNormalPosition.left = commentX;\r
7160         wp.rcNormalPosition.right = commentX + commentW;\r
7161         wp.rcNormalPosition.top = commentY;\r
7162         wp.rcNormalPosition.bottom = commentY + commentH;\r
7163         SetWindowPlacement(hDlg, &wp);\r
7164 \r
7165         GetClientRect(hDlg, &rect);\r
7166         newSizeX = rect.right;\r
7167         newSizeY = rect.bottom;\r
7168         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7169                               newSizeX, newSizeY);\r
7170         sizeX = newSizeX;\r
7171         sizeY = newSizeY;\r
7172       }\r
7173     }\r
7174     return FALSE;\r
7175 \r
7176   case WM_COMMAND: /* message: received a command */\r
7177     switch (LOWORD(wParam)) {\r
7178     case IDOK:\r
7179       if (editComment) {\r
7180         char *p, *q;\r
7181         /* Read changed options from the dialog box */\r
7182         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7183         len = GetWindowTextLength(hwndText);\r
7184         str = (char *) malloc(len + 1);\r
7185         GetWindowText(hwndText, str, len + 1);\r
7186         p = q = str;\r
7187         while (*q) {\r
7188           if (*q == '\r')\r
7189             q++;\r
7190           else\r
7191             *p++ = *q++;\r
7192         }\r
7193         *p = NULLCHAR;\r
7194         ReplaceComment(commentIndex, str);\r
7195         free(str);\r
7196       }\r
7197       CommentPopDown();\r
7198       return TRUE;\r
7199 \r
7200     case IDCANCEL:\r
7201     case OPT_CancelComment:\r
7202       CommentPopDown();\r
7203       return TRUE;\r
7204 \r
7205     case OPT_ClearComment:\r
7206       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7207       break;\r
7208 \r
7209     case OPT_EditComment:\r
7210       EditCommentEvent();\r
7211       return TRUE;\r
7212 \r
7213     default:\r
7214       break;\r
7215     }\r
7216     break;\r
7217 \r
7218   case WM_SIZE:\r
7219     newSizeX = LOWORD(lParam);\r
7220     newSizeY = HIWORD(lParam);\r
7221     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7222     sizeX = newSizeX;\r
7223     sizeY = newSizeY;\r
7224     break;\r
7225 \r
7226   case WM_GETMINMAXINFO:\r
7227     /* Prevent resizing window too small */\r
7228     mmi = (MINMAXINFO *) lParam;\r
7229     mmi->ptMinTrackSize.x = 100;\r
7230     mmi->ptMinTrackSize.y = 100;\r
7231     break;\r
7232   }\r
7233   return FALSE;\r
7234 }\r
7235 \r
7236 VOID\r
7237 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7238 {\r
7239   FARPROC lpProc;\r
7240   char *p, *q;\r
7241 \r
7242   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7243 \r
7244   if (str == NULL) str = "";\r
7245   p = (char *) malloc(2 * strlen(str) + 2);\r
7246   q = p;\r
7247   while (*str) {\r
7248     if (*str == '\n') *q++ = '\r';\r
7249     *q++ = *str++;\r
7250   }\r
7251   *q = NULLCHAR;\r
7252   if (commentText != NULL) free(commentText);\r
7253 \r
7254   commentIndex = index;\r
7255   commentTitle = title;\r
7256   commentText = p;\r
7257   editComment = edit;\r
7258 \r
7259   if (commentDialog) {\r
7260     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7261     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7262   } else {\r
7263     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7264     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7265                  hwndMain, (DLGPROC)lpProc);\r
7266     FreeProcInstance(lpProc);\r
7267   }\r
7268   commentDialogUp = TRUE;\r
7269 }\r
7270 \r
7271 \r
7272 /*---------------------------------------------------------------------------*\\r
7273  *\r
7274  * Type-in move dialog functions\r
7275  * \r
7276 \*---------------------------------------------------------------------------*/\r
7277 \r
7278 LRESULT CALLBACK\r
7279 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7280 {\r
7281   char move[MSG_SIZ];\r
7282   HWND hInput;\r
7283   ChessMove moveType;\r
7284   int fromX, fromY, toX, toY;\r
7285   char promoChar;\r
7286 \r
7287   switch (message) {\r
7288   case WM_INITDIALOG:\r
7289     move[0] = (char) lParam;\r
7290     move[1] = NULLCHAR;\r
7291     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7292     hInput = GetDlgItem(hDlg, OPT_Move);\r
7293     SetWindowText(hInput, move);\r
7294     SetFocus(hInput);\r
7295     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7296     return FALSE;\r
7297 \r
7298   case WM_COMMAND:\r
7299     switch (LOWORD(wParam)) {\r
7300     case IDOK:\r
7301       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7302       { int n; Board board;\r
7303         // [HGM] FENedit\r
7304         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7305                 EditPositionPasteFEN(move);\r
7306                 EndDialog(hDlg, TRUE);\r
7307                 return TRUE;\r
7308         }\r
7309         // [HGM] movenum: allow move number to be typed in any mode\r
7310         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7311           currentMove = 2*n-1;\r
7312           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7313           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7314           EndDialog(hDlg, TRUE);\r
7315           DrawPosition(TRUE, boards[currentMove]);\r
7316           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7317           else DisplayMessage("", "");\r
7318           return TRUE;\r
7319         }\r
7320       }\r
7321       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7322         gameMode != Training) {\r
7323         DisplayMoveError("Displayed move is not current");\r
7324       } else {\r
7325 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7326         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7327           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7328         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7329         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7330           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7331           if (gameMode != Training)\r
7332               forwardMostMove = currentMove;\r
7333           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7334         } else {\r
7335           DisplayMoveError("Could not parse move");\r
7336         }\r
7337       }\r
7338       EndDialog(hDlg, TRUE);\r
7339       return TRUE;\r
7340     case IDCANCEL:\r
7341       EndDialog(hDlg, FALSE);\r
7342       return TRUE;\r
7343     default:\r
7344       break;\r
7345     }\r
7346     break;\r
7347   }\r
7348   return FALSE;\r
7349 }\r
7350 \r
7351 VOID\r
7352 PopUpMoveDialog(char firstchar)\r
7353 {\r
7354     FARPROC lpProc;\r
7355     \r
7356     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7357         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7358         gameMode == AnalyzeMode || gameMode == EditGame || \r
7359         gameMode == EditPosition || gameMode == IcsExamining ||\r
7360         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7361         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7362                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7363                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7364         gameMode == Training) {\r
7365       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7366       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7367         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7368       FreeProcInstance(lpProc);\r
7369     }\r
7370 }\r
7371 \r
7372 /*---------------------------------------------------------------------------*\\r
7373  *\r
7374  * Type-in name dialog functions\r
7375  * \r
7376 \*---------------------------------------------------------------------------*/\r
7377 \r
7378 LRESULT CALLBACK\r
7379 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7380 {\r
7381   char move[MSG_SIZ];\r
7382   HWND hInput;\r
7383 \r
7384   switch (message) {\r
7385   case WM_INITDIALOG:\r
7386     move[0] = (char) lParam;\r
7387     move[1] = NULLCHAR;\r
7388     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7389     hInput = GetDlgItem(hDlg, OPT_Name);\r
7390     SetWindowText(hInput, move);\r
7391     SetFocus(hInput);\r
7392     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7393     return FALSE;\r
7394 \r
7395   case WM_COMMAND:\r
7396     switch (LOWORD(wParam)) {\r
7397     case IDOK:\r
7398       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7399       appData.userName = strdup(move);\r
7400       SetUserLogo();\r
7401 \r
7402       EndDialog(hDlg, TRUE);\r
7403       return TRUE;\r
7404     case IDCANCEL:\r
7405       EndDialog(hDlg, FALSE);\r
7406       return TRUE;\r
7407     default:\r
7408       break;\r
7409     }\r
7410     break;\r
7411   }\r
7412   return FALSE;\r
7413 }\r
7414 \r
7415 VOID\r
7416 PopUpNameDialog(char firstchar)\r
7417 {\r
7418     FARPROC lpProc;\r
7419     \r
7420       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7421       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7422         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7423       FreeProcInstance(lpProc);\r
7424 }\r
7425 \r
7426 /*---------------------------------------------------------------------------*\\r
7427  *\r
7428  *  Error dialogs\r
7429  * \r
7430 \*---------------------------------------------------------------------------*/\r
7431 \r
7432 /* Nonmodal error box */\r
7433 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7434                              WPARAM wParam, LPARAM lParam);\r
7435 \r
7436 VOID\r
7437 ErrorPopUp(char *title, char *content)\r
7438 {\r
7439   FARPROC lpProc;\r
7440   char *p, *q;\r
7441   BOOLEAN modal = hwndMain == NULL;\r
7442 \r
7443   p = content;\r
7444   q = errorMessage;\r
7445   while (*p) {\r
7446     if (*p == '\n') {\r
7447       if (modal) {\r
7448         *q++ = ' ';\r
7449         p++;\r
7450       } else {\r
7451         *q++ = '\r';\r
7452         *q++ = *p++;\r
7453       }\r
7454     } else {\r
7455       *q++ = *p++;\r
7456     }\r
7457   }\r
7458   *q = NULLCHAR;\r
7459   strncpy(errorTitle, title, sizeof(errorTitle));\r
7460   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7461   \r
7462   if (modal) {\r
7463     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7464   } else {\r
7465     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7466     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7467                  hwndMain, (DLGPROC)lpProc);\r
7468     FreeProcInstance(lpProc);\r
7469   }\r
7470 }\r
7471 \r
7472 VOID\r
7473 ErrorPopDown()\r
7474 {\r
7475   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7476   if (errorDialog == NULL) return;\r
7477   DestroyWindow(errorDialog);\r
7478   errorDialog = NULL;\r
7479 }\r
7480 \r
7481 LRESULT CALLBACK\r
7482 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7483 {\r
7484   HANDLE hwndText;\r
7485   RECT rChild;\r
7486 \r
7487   switch (message) {\r
7488   case WM_INITDIALOG:\r
7489     GetWindowRect(hDlg, &rChild);\r
7490 \r
7491     /*\r
7492     SetWindowPos(hDlg, NULL, rChild.left,\r
7493       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7494       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7495     */\r
7496 \r
7497     /* \r
7498         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7499         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7500         and it doesn't work when you resize the dialog.\r
7501         For now, just give it a default position.\r
7502     */\r
7503     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7504 \r
7505     errorDialog = hDlg;\r
7506     SetWindowText(hDlg, errorTitle);\r
7507     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7508     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7509     return FALSE;\r
7510 \r
7511   case WM_COMMAND:\r
7512     switch (LOWORD(wParam)) {\r
7513     case IDOK:\r
7514     case IDCANCEL:\r
7515       if (errorDialog == hDlg) errorDialog = NULL;\r
7516       DestroyWindow(hDlg);\r
7517       return TRUE;\r
7518 \r
7519     default:\r
7520       break;\r
7521     }\r
7522     break;\r
7523   }\r
7524   return FALSE;\r
7525 }\r
7526 \r
7527 #ifdef GOTHIC\r
7528 HWND gothicDialog = NULL;\r
7529 \r
7530 LRESULT CALLBACK\r
7531 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7532 {\r
7533   HANDLE hwndText;\r
7534   RECT rChild;\r
7535   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7536 \r
7537   switch (message) {\r
7538   case WM_INITDIALOG:\r
7539     GetWindowRect(hDlg, &rChild);\r
7540 \r
7541     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7542                                                              SWP_NOZORDER);\r
7543 \r
7544     /* \r
7545         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7546         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7547         and it doesn't work when you resize the dialog.\r
7548         For now, just give it a default position.\r
7549     */\r
7550     gothicDialog = hDlg;\r
7551     SetWindowText(hDlg, errorTitle);\r
7552     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7553     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7554     return FALSE;\r
7555 \r
7556   case WM_COMMAND:\r
7557     switch (LOWORD(wParam)) {\r
7558     case IDOK:\r
7559     case IDCANCEL:\r
7560       if (errorDialog == hDlg) errorDialog = NULL;\r
7561       DestroyWindow(hDlg);\r
7562       return TRUE;\r
7563 \r
7564     default:\r
7565       break;\r
7566     }\r
7567     break;\r
7568   }\r
7569   return FALSE;\r
7570 }\r
7571 \r
7572 VOID\r
7573 GothicPopUp(char *title, VariantClass variant)\r
7574 {\r
7575   FARPROC lpProc;\r
7576   static char *lastTitle;\r
7577 \r
7578   strncpy(errorTitle, title, sizeof(errorTitle));\r
7579   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7580 \r
7581   if(lastTitle != title && gothicDialog != NULL) {\r
7582     DestroyWindow(gothicDialog);\r
7583     gothicDialog = NULL;\r
7584   }\r
7585   if(variant != VariantNormal && gothicDialog == NULL) {\r
7586     title = lastTitle;\r
7587     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7588     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7589                  hwndMain, (DLGPROC)lpProc);\r
7590     FreeProcInstance(lpProc);\r
7591   }\r
7592 }\r
7593 #endif\r
7594 \r
7595 /*---------------------------------------------------------------------------*\\r
7596  *\r
7597  *  Ics Interaction console functions\r
7598  *\r
7599 \*---------------------------------------------------------------------------*/\r
7600 \r
7601 #define HISTORY_SIZE 64\r
7602 static char *history[HISTORY_SIZE];\r
7603 int histIn = 0, histP = 0;\r
7604 \r
7605 VOID\r
7606 SaveInHistory(char *cmd)\r
7607 {\r
7608   if (history[histIn] != NULL) {\r
7609     free(history[histIn]);\r
7610     history[histIn] = NULL;\r
7611   }\r
7612   if (*cmd == NULLCHAR) return;\r
7613   history[histIn] = StrSave(cmd);\r
7614   histIn = (histIn + 1) % HISTORY_SIZE;\r
7615   if (history[histIn] != NULL) {\r
7616     free(history[histIn]);\r
7617     history[histIn] = NULL;\r
7618   }\r
7619   histP = histIn;\r
7620 }\r
7621 \r
7622 char *\r
7623 PrevInHistory(char *cmd)\r
7624 {\r
7625   int newhp;\r
7626   if (histP == histIn) {\r
7627     if (history[histIn] != NULL) free(history[histIn]);\r
7628     history[histIn] = StrSave(cmd);\r
7629   }\r
7630   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7631   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7632   histP = newhp;\r
7633   return history[histP];\r
7634 }\r
7635 \r
7636 char *\r
7637 NextInHistory()\r
7638 {\r
7639   if (histP == histIn) return NULL;\r
7640   histP = (histP + 1) % HISTORY_SIZE;\r
7641   return history[histP];\r
7642 }\r
7643 \r
7644 typedef struct {\r
7645   char *item;\r
7646   char *command;\r
7647   BOOLEAN getname;\r
7648   BOOLEAN immediate;\r
7649 } IcsTextMenuEntry;\r
7650 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7651 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7652 \r
7653 void\r
7654 ParseIcsTextMenu(char *icsTextMenuString)\r
7655 {\r
7656 //  int flags = 0;\r
7657   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7658   char *p = icsTextMenuString;\r
7659   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7660     free(e->item);\r
7661     e->item = NULL;\r
7662     if (e->command != NULL) {\r
7663       free(e->command);\r
7664       e->command = NULL;\r
7665     }\r
7666     e++;\r
7667   }\r
7668   e = icsTextMenuEntry;\r
7669   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7670     if (*p == ';' || *p == '\n') {\r
7671       e->item = strdup("-");\r
7672       e->command = NULL;\r
7673       p++;\r
7674     } else if (*p == '-') {\r
7675       e->item = strdup("-");\r
7676       e->command = NULL;\r
7677       p++;\r
7678       if (*p) p++;\r
7679     } else {\r
7680       char *q, *r, *s, *t;\r
7681       char c;\r
7682       q = strchr(p, ',');\r
7683       if (q == NULL) break;\r
7684       *q = NULLCHAR;\r
7685       r = strchr(q + 1, ',');\r
7686       if (r == NULL) break;\r
7687       *r = NULLCHAR;\r
7688       s = strchr(r + 1, ',');\r
7689       if (s == NULL) break;\r
7690       *s = NULLCHAR;\r
7691       c = ';';\r
7692       t = strchr(s + 1, c);\r
7693       if (t == NULL) {\r
7694         c = '\n';\r
7695         t = strchr(s + 1, c);\r
7696       }\r
7697       if (t != NULL) *t = NULLCHAR;\r
7698       e->item = strdup(p);\r
7699       e->command = strdup(q + 1);\r
7700       e->getname = *(r + 1) != '0';\r
7701       e->immediate = *(s + 1) != '0';\r
7702       *q = ',';\r
7703       *r = ',';\r
7704       *s = ',';\r
7705       if (t == NULL) break;\r
7706       *t = c;\r
7707       p = t + 1;\r
7708     }\r
7709     e++;\r
7710   } \r
7711 }\r
7712 \r
7713 HMENU\r
7714 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7715 {\r
7716   HMENU hmenu, h;\r
7717   int i = 0;\r
7718   hmenu = LoadMenu(hInst, "TextMenu");\r
7719   h = GetSubMenu(hmenu, 0);\r
7720   while (e->item) {\r
7721     if (strcmp(e->item, "-") == 0) {\r
7722       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7723     } else {\r
7724       if (e->item[0] == '|') {\r
7725         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7726                    IDM_CommandX + i, &e->item[1]);\r
7727       } else {\r
7728         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7729       }\r
7730     }\r
7731     e++;\r
7732     i++;\r
7733   } \r
7734   return hmenu;\r
7735 }\r
7736 \r
7737 WNDPROC consoleTextWindowProc;\r
7738 \r
7739 void\r
7740 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7741 {\r
7742   char buf[MSG_SIZ], name[MSG_SIZ];\r
7743   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7744   CHARRANGE sel;\r
7745 \r
7746   if (!getname) {\r
7747     SetWindowText(hInput, command);\r
7748     if (immediate) {\r
7749       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7750     } else {\r
7751       sel.cpMin = 999999;\r
7752       sel.cpMax = 999999;\r
7753       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7754       SetFocus(hInput);\r
7755     }\r
7756     return;\r
7757   }    \r
7758   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7759   if (sel.cpMin == sel.cpMax) {\r
7760     /* Expand to surrounding word */\r
7761     TEXTRANGE tr;\r
7762     do {\r
7763       tr.chrg.cpMax = sel.cpMin;\r
7764       tr.chrg.cpMin = --sel.cpMin;\r
7765       if (sel.cpMin < 0) break;\r
7766       tr.lpstrText = name;\r
7767       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7768     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7769     sel.cpMin++;\r
7770 \r
7771     do {\r
7772       tr.chrg.cpMin = sel.cpMax;\r
7773       tr.chrg.cpMax = ++sel.cpMax;\r
7774       tr.lpstrText = name;\r
7775       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7776     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7777     sel.cpMax--;\r
7778 \r
7779     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7780       MessageBeep(MB_ICONEXCLAMATION);\r
7781       return;\r
7782     }\r
7783     tr.chrg = sel;\r
7784     tr.lpstrText = name;\r
7785     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7786   } else {\r
7787     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7788       MessageBeep(MB_ICONEXCLAMATION);\r
7789       return;\r
7790     }\r
7791     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7792   }\r
7793   if (immediate) {\r
7794     sprintf(buf, "%s %s", command, name);\r
7795     SetWindowText(hInput, buf);\r
7796     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7797   } else {\r
7798     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7799     SetWindowText(hInput, buf);\r
7800     sel.cpMin = 999999;\r
7801     sel.cpMax = 999999;\r
7802     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7803     SetFocus(hInput);\r
7804   }\r
7805 }\r
7806 \r
7807 LRESULT CALLBACK \r
7808 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7809 {\r
7810   HWND hInput;\r
7811   CHARRANGE sel;\r
7812 \r
7813   switch (message) {\r
7814   case WM_KEYDOWN:\r
7815     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7816     switch (wParam) {\r
7817     case VK_PRIOR:\r
7818       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7819       return 0;\r
7820     case VK_NEXT:\r
7821       sel.cpMin = 999999;\r
7822       sel.cpMax = 999999;\r
7823       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7824       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7825       return 0;\r
7826     }\r
7827     break;\r
7828   case WM_CHAR:\r
7829    if(wParam != '\022') {\r
7830     if (wParam == '\t') {\r
7831       if (GetKeyState(VK_SHIFT) < 0) {\r
7832         /* shifted */\r
7833         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7834         if (buttonDesc[0].hwnd) {\r
7835           SetFocus(buttonDesc[0].hwnd);\r
7836         } else {\r
7837           SetFocus(hwndMain);\r
7838         }\r
7839       } else {\r
7840         /* unshifted */\r
7841         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7842       }\r
7843     } else {\r
7844       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7845       JAWS_DELETE( SetFocus(hInput); )\r
7846       SendMessage(hInput, message, wParam, lParam);\r
7847     }\r
7848     return 0;\r
7849    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7850   case WM_RBUTTONUP:\r
7851     if (GetKeyState(VK_SHIFT) & ~1) {\r
7852       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7853         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7854     } else {\r
7855       POINT pt;\r
7856       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7857       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7858       if (sel.cpMin == sel.cpMax) {\r
7859         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7860         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7861       }\r
7862       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7863         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7864       }\r
7865       pt.x = LOWORD(lParam);\r
7866       pt.y = HIWORD(lParam);\r
7867       MenuPopup(hwnd, pt, hmenu, -1);\r
7868     }\r
7869     return 0;\r
7870   case WM_PASTE:\r
7871     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7872     SetFocus(hInput);\r
7873     return SendMessage(hInput, message, wParam, lParam);\r
7874   case WM_MBUTTONDOWN:\r
7875     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7876   case WM_RBUTTONDOWN:\r
7877     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7878       /* Move selection here if it was empty */\r
7879       POINT pt;\r
7880       pt.x = LOWORD(lParam);\r
7881       pt.y = HIWORD(lParam);\r
7882       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7883       if (sel.cpMin == sel.cpMax) {\r
7884         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7885         sel.cpMax = sel.cpMin;\r
7886         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7887       }\r
7888       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7889     }\r
7890     return 0;\r
7891   case WM_COMMAND:\r
7892     switch (LOWORD(wParam)) {\r
7893     case IDM_QuickPaste:\r
7894       {\r
7895         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7896         if (sel.cpMin == sel.cpMax) {\r
7897           MessageBeep(MB_ICONEXCLAMATION);\r
7898           return 0;\r
7899         }\r
7900         SendMessage(hwnd, WM_COPY, 0, 0);\r
7901         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7902         SendMessage(hInput, WM_PASTE, 0, 0);\r
7903         SetFocus(hInput);\r
7904         return 0;\r
7905       }\r
7906     case IDM_Cut:\r
7907       SendMessage(hwnd, WM_CUT, 0, 0);\r
7908       return 0;\r
7909     case IDM_Paste:\r
7910       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7911       return 0;\r
7912     case IDM_Copy:\r
7913       SendMessage(hwnd, WM_COPY, 0, 0);\r
7914       return 0;\r
7915     default:\r
7916       {\r
7917         int i = LOWORD(wParam) - IDM_CommandX;\r
7918         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7919             icsTextMenuEntry[i].command != NULL) {\r
7920           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7921                    icsTextMenuEntry[i].getname,\r
7922                    icsTextMenuEntry[i].immediate);\r
7923           return 0;\r
7924         }\r
7925       }\r
7926       break;\r
7927     }\r
7928     break;\r
7929   }\r
7930   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7931 }\r
7932 \r
7933 WNDPROC consoleInputWindowProc;\r
7934 \r
7935 LRESULT CALLBACK\r
7936 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7937 {\r
7938   char buf[MSG_SIZ];\r
7939   char *p;\r
7940   static BOOL sendNextChar = FALSE;\r
7941   static BOOL quoteNextChar = FALSE;\r
7942   InputSource *is = consoleInputSource;\r
7943   CHARFORMAT cf;\r
7944   CHARRANGE sel;\r
7945 \r
7946   switch (message) {\r
7947   case WM_CHAR:\r
7948     if (!appData.localLineEditing || sendNextChar) {\r
7949       is->buf[0] = (CHAR) wParam;\r
7950       is->count = 1;\r
7951       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7952       sendNextChar = FALSE;\r
7953       return 0;\r
7954     }\r
7955     if (quoteNextChar) {\r
7956       buf[0] = (char) wParam;\r
7957       buf[1] = NULLCHAR;\r
7958       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7959       quoteNextChar = FALSE;\r
7960       return 0;\r
7961     }\r
7962     switch (wParam) {\r
7963     case '\r':   /* Enter key */\r
7964       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7965       if (consoleEcho) SaveInHistory(is->buf);\r
7966       is->buf[is->count++] = '\n';\r
7967       is->buf[is->count] = NULLCHAR;\r
7968       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7969       if (consoleEcho) {\r
7970         ConsoleOutput(is->buf, is->count, TRUE);\r
7971       } else if (appData.localLineEditing) {\r
7972         ConsoleOutput("\n", 1, TRUE);\r
7973       }\r
7974       /* fall thru */\r
7975     case '\033': /* Escape key */\r
7976       SetWindowText(hwnd, "");\r
7977       cf.cbSize = sizeof(CHARFORMAT);\r
7978       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7979       if (consoleEcho) {\r
7980         cf.crTextColor = textAttribs[ColorNormal].color;\r
7981       } else {\r
7982         cf.crTextColor = COLOR_ECHOOFF;\r
7983       }\r
7984       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7985       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7986       return 0;\r
7987     case '\t':   /* Tab key */\r
7988       if (GetKeyState(VK_SHIFT) < 0) {\r
7989         /* shifted */\r
7990         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7991       } else {\r
7992         /* unshifted */\r
7993         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7994         if (buttonDesc[0].hwnd) {\r
7995           SetFocus(buttonDesc[0].hwnd);\r
7996         } else {\r
7997           SetFocus(hwndMain);\r
7998         }\r
7999       }\r
8000       return 0;\r
8001     case '\023': /* Ctrl+S */\r
8002       sendNextChar = TRUE;\r
8003       return 0;\r
8004     case '\021': /* Ctrl+Q */\r
8005       quoteNextChar = TRUE;\r
8006       return 0;\r
8007     JAWS_REPLAY\r
8008     default:\r
8009       break;\r
8010     }\r
8011     break;\r
8012   case WM_KEYDOWN:\r
8013     switch (wParam) {\r
8014     case VK_UP:\r
8015       GetWindowText(hwnd, buf, MSG_SIZ);\r
8016       p = PrevInHistory(buf);\r
8017       if (p != NULL) {\r
8018         SetWindowText(hwnd, p);\r
8019         sel.cpMin = 999999;\r
8020         sel.cpMax = 999999;\r
8021         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8022         return 0;\r
8023       }\r
8024       break;\r
8025     case VK_DOWN:\r
8026       p = NextInHistory();\r
8027       if (p != NULL) {\r
8028         SetWindowText(hwnd, p);\r
8029         sel.cpMin = 999999;\r
8030         sel.cpMax = 999999;\r
8031         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8032         return 0;\r
8033       }\r
8034       break;\r
8035     case VK_HOME:\r
8036     case VK_END:\r
8037       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8038       /* fall thru */\r
8039     case VK_PRIOR:\r
8040     case VK_NEXT:\r
8041       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8042       return 0;\r
8043     }\r
8044     break;\r
8045   case WM_MBUTTONDOWN:\r
8046     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8047       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8048     break;\r
8049   case WM_RBUTTONUP:\r
8050     if (GetKeyState(VK_SHIFT) & ~1) {\r
8051       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8052         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8053     } else {\r
8054       POINT pt;\r
8055       HMENU hmenu;\r
8056       hmenu = LoadMenu(hInst, "InputMenu");\r
8057       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8058       if (sel.cpMin == sel.cpMax) {\r
8059         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8060         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8061       }\r
8062       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8063         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8064       }\r
8065       pt.x = LOWORD(lParam);\r
8066       pt.y = HIWORD(lParam);\r
8067       MenuPopup(hwnd, pt, hmenu, -1);\r
8068     }\r
8069     return 0;\r
8070   case WM_COMMAND:\r
8071     switch (LOWORD(wParam)) { \r
8072     case IDM_Undo:\r
8073       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8074       return 0;\r
8075     case IDM_SelectAll:\r
8076       sel.cpMin = 0;\r
8077       sel.cpMax = -1; /*999999?*/\r
8078       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8079       return 0;\r
8080     case IDM_Cut:\r
8081       SendMessage(hwnd, WM_CUT, 0, 0);\r
8082       return 0;\r
8083     case IDM_Paste:\r
8084       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8085       return 0;\r
8086     case IDM_Copy:\r
8087       SendMessage(hwnd, WM_COPY, 0, 0);\r
8088       return 0;\r
8089     }\r
8090     break;\r
8091   }\r
8092   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8093 }\r
8094 \r
8095 #define CO_MAX  100000\r
8096 #define CO_TRIM   1000\r
8097 \r
8098 LRESULT CALLBACK\r
8099 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8100 {\r
8101   static SnapData sd;\r
8102   static HWND hText, hInput /*, hFocus*/;\r
8103 //  InputSource *is = consoleInputSource;\r
8104   RECT rect;\r
8105   static int sizeX, sizeY;\r
8106   int newSizeX, newSizeY;\r
8107   MINMAXINFO *mmi;\r
8108   WORD wMask;\r
8109 \r
8110   switch (message) {\r
8111   case WM_NOTIFY:\r
8112     if (((NMHDR*)lParam)->code == EN_LINK)\r
8113     {\r
8114       ENLINK *pLink = (ENLINK*)lParam;\r
8115       if (pLink->msg == WM_LBUTTONUP)\r
8116       {\r
8117         TEXTRANGE tr;\r
8118 \r
8119         tr.chrg = pLink->chrg;\r
8120         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8121         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8122         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8123         free(tr.lpstrText);\r
8124       }\r
8125     }\r
8126     break;\r
8127   case WM_INITDIALOG: /* message: initialize dialog box */\r
8128     hwndConsole = hDlg;\r
8129     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8130     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8131     SetFocus(hInput);\r
8132     consoleTextWindowProc = (WNDPROC)\r
8133       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8134     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8135     consoleInputWindowProc = (WNDPROC)\r
8136       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8137     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8138     Colorize(ColorNormal, TRUE);\r
8139     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8140     ChangedConsoleFont();\r
8141     GetClientRect(hDlg, &rect);\r
8142     sizeX = rect.right;\r
8143     sizeY = rect.bottom;\r
8144     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8145         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8146       WINDOWPLACEMENT wp;\r
8147       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8148       wp.length = sizeof(WINDOWPLACEMENT);\r
8149       wp.flags = 0;\r
8150       wp.showCmd = SW_SHOW;\r
8151       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8152       wp.rcNormalPosition.left = wpConsole.x;\r
8153       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8154       wp.rcNormalPosition.top = wpConsole.y;\r
8155       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8156       SetWindowPlacement(hDlg, &wp);\r
8157     }\r
8158 \r
8159    // [HGM] Chessknight's change 2004-07-13\r
8160    else { /* Determine Defaults */\r
8161        WINDOWPLACEMENT wp;\r
8162        wpConsole.x = winWidth + 1;\r
8163        wpConsole.y = boardY;\r
8164        wpConsole.width = screenWidth -  winWidth;\r
8165        wpConsole.height = winHeight;\r
8166        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8167        wp.length = sizeof(WINDOWPLACEMENT);\r
8168        wp.flags = 0;\r
8169        wp.showCmd = SW_SHOW;\r
8170        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8171        wp.rcNormalPosition.left = wpConsole.x;\r
8172        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8173        wp.rcNormalPosition.top = wpConsole.y;\r
8174        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8175        SetWindowPlacement(hDlg, &wp);\r
8176     }\r
8177 \r
8178    // Allow hText to highlight URLs and send notifications on them\r
8179    wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8180    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8181    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8182 \r
8183     return FALSE;\r
8184 \r
8185   case WM_SETFOCUS:\r
8186     SetFocus(hInput);\r
8187     return 0;\r
8188 \r
8189   case WM_CLOSE:\r
8190     ExitEvent(0);\r
8191     /* not reached */\r
8192     break;\r
8193 \r
8194   case WM_SIZE:\r
8195     if (IsIconic(hDlg)) break;\r
8196     newSizeX = LOWORD(lParam);\r
8197     newSizeY = HIWORD(lParam);\r
8198     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8199       RECT rectText, rectInput;\r
8200       POINT pt;\r
8201       int newTextHeight, newTextWidth;\r
8202       GetWindowRect(hText, &rectText);\r
8203       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8204       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8205       if (newTextHeight < 0) {\r
8206         newSizeY += -newTextHeight;\r
8207         newTextHeight = 0;\r
8208       }\r
8209       SetWindowPos(hText, NULL, 0, 0,\r
8210         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8211       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8212       pt.x = rectInput.left;\r
8213       pt.y = rectInput.top + newSizeY - sizeY;\r
8214       ScreenToClient(hDlg, &pt);\r
8215       SetWindowPos(hInput, NULL, \r
8216         pt.x, pt.y, /* needs client coords */   \r
8217         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8218         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8219     }\r
8220     sizeX = newSizeX;\r
8221     sizeY = newSizeY;\r
8222     break;\r
8223 \r
8224   case WM_GETMINMAXINFO:\r
8225     /* Prevent resizing window too small */\r
8226     mmi = (MINMAXINFO *) lParam;\r
8227     mmi->ptMinTrackSize.x = 100;\r
8228     mmi->ptMinTrackSize.y = 100;\r
8229     break;\r
8230 \r
8231   /* [AS] Snapping */\r
8232   case WM_ENTERSIZEMOVE:\r
8233     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8234 \r
8235   case WM_SIZING:\r
8236     return OnSizing( &sd, hDlg, wParam, lParam );\r
8237 \r
8238   case WM_MOVING:\r
8239     return OnMoving( &sd, hDlg, wParam, lParam );\r
8240 \r
8241   case WM_EXITSIZEMOVE:\r
8242     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8243   }\r
8244 \r
8245   return DefWindowProc(hDlg, message, wParam, lParam);\r
8246 }\r
8247 \r
8248 \r
8249 VOID\r
8250 ConsoleCreate()\r
8251 {\r
8252   HWND hCons;\r
8253   if (hwndConsole) return;\r
8254   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8255   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8256 }\r
8257 \r
8258 \r
8259 VOID\r
8260 ConsoleOutput(char* data, int length, int forceVisible)\r
8261 {\r
8262   HWND hText;\r
8263   int trim, exlen;\r
8264   char *p, *q;\r
8265   char buf[CO_MAX+1];\r
8266   POINT pEnd;\r
8267   RECT rect;\r
8268   static int delayLF = 0;\r
8269   CHARRANGE savesel, sel;\r
8270 \r
8271   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8272   p = data;\r
8273   q = buf;\r
8274   if (delayLF) {\r
8275     *q++ = '\r';\r
8276     *q++ = '\n';\r
8277     delayLF = 0;\r
8278   }\r
8279   while (length--) {\r
8280     if (*p == '\n') {\r
8281       if (*++p) {\r
8282         *q++ = '\r';\r
8283         *q++ = '\n';\r
8284       } else {\r
8285         delayLF = 1;\r
8286       }\r
8287     } else if (*p == '\007') {\r
8288        MyPlaySound(&sounds[(int)SoundBell]);\r
8289        p++;\r
8290     } else {\r
8291       *q++ = *p++;\r
8292     }\r
8293   }\r
8294   *q = NULLCHAR;\r
8295   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8296   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8297   /* Save current selection */\r
8298   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8299   exlen = GetWindowTextLength(hText);\r
8300   /* Find out whether current end of text is visible */\r
8301   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8302   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8303   /* Trim existing text if it's too long */\r
8304   if (exlen + (q - buf) > CO_MAX) {\r
8305     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8306     sel.cpMin = 0;\r
8307     sel.cpMax = trim;\r
8308     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8309     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8310     exlen -= trim;\r
8311     savesel.cpMin -= trim;\r
8312     savesel.cpMax -= trim;\r
8313     if (exlen < 0) exlen = 0;\r
8314     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8315     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8316   }\r
8317   /* Append the new text */\r
8318   sel.cpMin = exlen;\r
8319   sel.cpMax = exlen;\r
8320   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8321   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8322   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8323   if (forceVisible || exlen == 0 ||\r
8324       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8325        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8326     /* Scroll to make new end of text visible if old end of text\r
8327        was visible or new text is an echo of user typein */\r
8328     sel.cpMin = 9999999;\r
8329     sel.cpMax = 9999999;\r
8330     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8331     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8332     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8333     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8334   }\r
8335   if (savesel.cpMax == exlen || forceVisible) {\r
8336     /* Move insert point to new end of text if it was at the old\r
8337        end of text or if the new text is an echo of user typein */\r
8338     sel.cpMin = 9999999;\r
8339     sel.cpMax = 9999999;\r
8340     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8341   } else {\r
8342     /* Restore previous selection */\r
8343     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8344   }\r
8345   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8346 }\r
8347 \r
8348 /*---------*/\r
8349 \r
8350 \r
8351 void\r
8352 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8353 {\r
8354   char buf[100];\r
8355   char *str;\r
8356   COLORREF oldFg, oldBg;\r
8357   HFONT oldFont;\r
8358   RECT rect;\r
8359 \r
8360   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8361 \r
8362   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8363   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8364   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8365 \r
8366   rect.left = x;\r
8367   rect.right = x + squareSize;\r
8368   rect.top  = y;\r
8369   rect.bottom = y + squareSize;\r
8370   str = buf;\r
8371 \r
8372   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8373                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8374              y, ETO_CLIPPED|ETO_OPAQUE,\r
8375              &rect, str, strlen(str), NULL);\r
8376 \r
8377   (void) SetTextColor(hdc, oldFg);\r
8378   (void) SetBkColor(hdc, oldBg);\r
8379   (void) SelectObject(hdc, oldFont);\r
8380 }\r
8381 \r
8382 void\r
8383 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8384               RECT *rect, char *color, char *flagFell)\r
8385 {\r
8386   char buf[100];\r
8387   char *str;\r
8388   COLORREF oldFg, oldBg;\r
8389   HFONT oldFont;\r
8390 \r
8391   if (appData.clockMode) {\r
8392     if (tinyLayout)\r
8393       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8394     else\r
8395       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8396     str = buf;\r
8397   } else {\r
8398     str = color;\r
8399   }\r
8400 \r
8401   if (highlight) {\r
8402     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8403     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8404   } else {\r
8405     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8406     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8407   }\r
8408   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8409 \r
8410   JAWS_SILENCE\r
8411 \r
8412   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8413              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8414              rect, str, strlen(str), NULL);\r
8415   if(logoHeight > 0 && appData.clockMode) {\r
8416       RECT r;\r
8417       sprintf(buf, "%s %s", buf+7, flagFell);\r
8418       r.top = rect->top + logoHeight/2;\r
8419       r.left = rect->left;\r
8420       r.right = rect->right;\r
8421       r.bottom = rect->bottom;\r
8422       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8423                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8424                  &r, str, strlen(str), NULL);\r
8425   }\r
8426   (void) SetTextColor(hdc, oldFg);\r
8427   (void) SetBkColor(hdc, oldBg);\r
8428   (void) SelectObject(hdc, oldFont);\r
8429 }\r
8430 \r
8431 \r
8432 int\r
8433 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8434            OVERLAPPED *ovl)\r
8435 {\r
8436   int ok, err;\r
8437 \r
8438   /* [AS]  */\r
8439   if( count <= 0 ) {\r
8440     if (appData.debugMode) {\r
8441       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8442     }\r
8443 \r
8444     return ERROR_INVALID_USER_BUFFER;\r
8445   }\r
8446 \r
8447   ResetEvent(ovl->hEvent);\r
8448   ovl->Offset = ovl->OffsetHigh = 0;\r
8449   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8450   if (ok) {\r
8451     err = NO_ERROR;\r
8452   } else {\r
8453     err = GetLastError();\r
8454     if (err == ERROR_IO_PENDING) {\r
8455       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8456       if (ok)\r
8457         err = NO_ERROR;\r
8458       else\r
8459         err = GetLastError();\r
8460     }\r
8461   }\r
8462   return err;\r
8463 }\r
8464 \r
8465 int\r
8466 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8467             OVERLAPPED *ovl)\r
8468 {\r
8469   int ok, err;\r
8470 \r
8471   ResetEvent(ovl->hEvent);\r
8472   ovl->Offset = ovl->OffsetHigh = 0;\r
8473   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8474   if (ok) {\r
8475     err = NO_ERROR;\r
8476   } else {\r
8477     err = GetLastError();\r
8478     if (err == ERROR_IO_PENDING) {\r
8479       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8480       if (ok)\r
8481         err = NO_ERROR;\r
8482       else\r
8483         err = GetLastError();\r
8484     }\r
8485   }\r
8486   return err;\r
8487 }\r
8488 \r
8489 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8490 void CheckForInputBufferFull( InputSource * is )\r
8491 {\r
8492     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8493         /* Look for end of line */\r
8494         char * p = is->buf;\r
8495         \r
8496         while( p < is->next && *p != '\n' ) {\r
8497             p++;\r
8498         }\r
8499 \r
8500         if( p >= is->next ) {\r
8501             if (appData.debugMode) {\r
8502                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8503             }\r
8504 \r
8505             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8506             is->count = (DWORD) -1;\r
8507             is->next = is->buf;\r
8508         }\r
8509     }\r
8510 }\r
8511 \r
8512 DWORD\r
8513 InputThread(LPVOID arg)\r
8514 {\r
8515   InputSource *is;\r
8516   OVERLAPPED ovl;\r
8517 \r
8518   is = (InputSource *) arg;\r
8519   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8520   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8521   while (is->hThread != NULL) {\r
8522     is->error = DoReadFile(is->hFile, is->next,\r
8523                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8524                            &is->count, &ovl);\r
8525     if (is->error == NO_ERROR) {\r
8526       is->next += is->count;\r
8527     } else {\r
8528       if (is->error == ERROR_BROKEN_PIPE) {\r
8529         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8530         is->count = 0;\r
8531       } else {\r
8532         is->count = (DWORD) -1;\r
8533         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8534         break; \r
8535       }\r
8536     }\r
8537 \r
8538     CheckForInputBufferFull( is );\r
8539 \r
8540     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8541 \r
8542     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8543 \r
8544     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8545   }\r
8546 \r
8547   CloseHandle(ovl.hEvent);\r
8548   CloseHandle(is->hFile);\r
8549 \r
8550   if (appData.debugMode) {\r
8551     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8552   }\r
8553 \r
8554   return 0;\r
8555 }\r
8556 \r
8557 \r
8558 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8559 DWORD\r
8560 NonOvlInputThread(LPVOID arg)\r
8561 {\r
8562   InputSource *is;\r
8563   char *p, *q;\r
8564   int i;\r
8565   char prev;\r
8566 \r
8567   is = (InputSource *) arg;\r
8568   while (is->hThread != NULL) {\r
8569     is->error = ReadFile(is->hFile, is->next,\r
8570                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8571                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8572     if (is->error == NO_ERROR) {\r
8573       /* Change CRLF to LF */\r
8574       if (is->next > is->buf) {\r
8575         p = is->next - 1;\r
8576         i = is->count + 1;\r
8577       } else {\r
8578         p = is->next;\r
8579         i = is->count;\r
8580       }\r
8581       q = p;\r
8582       prev = NULLCHAR;\r
8583       while (i > 0) {\r
8584         if (prev == '\r' && *p == '\n') {\r
8585           *(q-1) = '\n';\r
8586           is->count--;\r
8587         } else { \r
8588           *q++ = *p;\r
8589         }\r
8590         prev = *p++;\r
8591         i--;\r
8592       }\r
8593       *q = NULLCHAR;\r
8594       is->next = q;\r
8595     } else {\r
8596       if (is->error == ERROR_BROKEN_PIPE) {\r
8597         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8598         is->count = 0; \r
8599       } else {\r
8600         is->count = (DWORD) -1;\r
8601       }\r
8602     }\r
8603 \r
8604     CheckForInputBufferFull( is );\r
8605 \r
8606     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8607 \r
8608     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8609 \r
8610     if (is->count < 0) break;  /* Quit on error */\r
8611   }\r
8612   CloseHandle(is->hFile);\r
8613   return 0;\r
8614 }\r
8615 \r
8616 DWORD\r
8617 SocketInputThread(LPVOID arg)\r
8618 {\r
8619   InputSource *is;\r
8620 \r
8621   is = (InputSource *) arg;\r
8622   while (is->hThread != NULL) {\r
8623     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8624     if ((int)is->count == SOCKET_ERROR) {\r
8625       is->count = (DWORD) -1;\r
8626       is->error = WSAGetLastError();\r
8627     } else {\r
8628       is->error = NO_ERROR;\r
8629       is->next += is->count;\r
8630       if (is->count == 0 && is->second == is) {\r
8631         /* End of file on stderr; quit with no message */\r
8632         break;\r
8633       }\r
8634     }\r
8635     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8636 \r
8637     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8638 \r
8639     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8640   }\r
8641   return 0;\r
8642 }\r
8643 \r
8644 VOID\r
8645 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8646 {\r
8647   InputSource *is;\r
8648 \r
8649   is = (InputSource *) lParam;\r
8650   if (is->lineByLine) {\r
8651     /* Feed in lines one by one */\r
8652     char *p = is->buf;\r
8653     char *q = p;\r
8654     while (q < is->next) {\r
8655       if (*q++ == '\n') {\r
8656         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8657         p = q;\r
8658       }\r
8659     }\r
8660     \r
8661     /* Move any partial line to the start of the buffer */\r
8662     q = is->buf;\r
8663     while (p < is->next) {\r
8664       *q++ = *p++;\r
8665     }\r
8666     is->next = q;\r
8667 \r
8668     if (is->error != NO_ERROR || is->count == 0) {\r
8669       /* Notify backend of the error.  Note: If there was a partial\r
8670          line at the end, it is not flushed through. */\r
8671       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8672     }\r
8673   } else {\r
8674     /* Feed in the whole chunk of input at once */\r
8675     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8676     is->next = is->buf;\r
8677   }\r
8678 }\r
8679 \r
8680 /*---------------------------------------------------------------------------*\\r
8681  *\r
8682  *  Menu enables. Used when setting various modes.\r
8683  *\r
8684 \*---------------------------------------------------------------------------*/\r
8685 \r
8686 typedef struct {\r
8687   int item;\r
8688   int flags;\r
8689 } Enables;\r
8690 \r
8691 VOID\r
8692 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8693 {\r
8694   while (enab->item > 0) {\r
8695     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8696     enab++;\r
8697   }\r
8698 }\r
8699 \r
8700 Enables gnuEnables[] = {\r
8701   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8702   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8703   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8704   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8705   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8706   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8707   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8708   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8709   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8710   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8711   { -1, -1 }\r
8712 };\r
8713 \r
8714 Enables icsEnables[] = {\r
8715   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8716   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8717   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8718   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8719   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8720   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8721   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8722   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8723   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8724   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8725   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8726   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8727   { -1, -1 }\r
8728 };\r
8729 \r
8730 #ifdef ZIPPY\r
8731 Enables zippyEnables[] = {\r
8732   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8733   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8734   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8735   { -1, -1 }\r
8736 };\r
8737 #endif\r
8738 \r
8739 Enables ncpEnables[] = {\r
8740   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8741   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8742   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8743   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8744   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8745   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8746   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8747   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8748   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8749   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8750   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8751   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8752   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8753   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8754   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8755   { -1, -1 }\r
8756 };\r
8757 \r
8758 Enables trainingOnEnables[] = {\r
8759   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8760   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8761   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8762   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8763   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8764   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8765   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8766   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8767   { -1, -1 }\r
8768 };\r
8769 \r
8770 Enables trainingOffEnables[] = {\r
8771   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8772   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8773   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8774   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8775   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8776   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8777   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8778   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8779   { -1, -1 }\r
8780 };\r
8781 \r
8782 /* These modify either ncpEnables or gnuEnables */\r
8783 Enables cmailEnables[] = {\r
8784   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8785   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8786   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8787   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8789   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8791   { -1, -1 }\r
8792 };\r
8793 \r
8794 Enables machineThinkingEnables[] = {\r
8795   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8800   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8802   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8803   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8805   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8806   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8807   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8808   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8809   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8810   { -1, -1 }\r
8811 };\r
8812 \r
8813 Enables userThinkingEnables[] = {\r
8814   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8815   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8816   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8817   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8818   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8819   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8820   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8821   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8822   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8823   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8824   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8825   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8826   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8827   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8828   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8829   { -1, -1 }\r
8830 };\r
8831 \r
8832 /*---------------------------------------------------------------------------*\\r
8833  *\r
8834  *  Front-end interface functions exported by XBoard.\r
8835  *  Functions appear in same order as prototypes in frontend.h.\r
8836  * \r
8837 \*---------------------------------------------------------------------------*/\r
8838 VOID\r
8839 ModeHighlight()\r
8840 {\r
8841   static UINT prevChecked = 0;\r
8842   static int prevPausing = 0;\r
8843   UINT nowChecked;\r
8844 \r
8845   if (pausing != prevPausing) {\r
8846     prevPausing = pausing;\r
8847     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8848                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8849     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8850   }\r
8851 \r
8852   switch (gameMode) {\r
8853   case BeginningOfGame:\r
8854     if (appData.icsActive)\r
8855       nowChecked = IDM_IcsClient;\r
8856     else if (appData.noChessProgram)\r
8857       nowChecked = IDM_EditGame;\r
8858     else\r
8859       nowChecked = IDM_MachineBlack;\r
8860     break;\r
8861   case MachinePlaysBlack:\r
8862     nowChecked = IDM_MachineBlack;\r
8863     break;\r
8864   case MachinePlaysWhite:\r
8865     nowChecked = IDM_MachineWhite;\r
8866     break;\r
8867   case TwoMachinesPlay:\r
8868     nowChecked = IDM_TwoMachines;\r
8869     break;\r
8870   case AnalyzeMode:\r
8871     nowChecked = IDM_AnalysisMode;\r
8872     break;\r
8873   case AnalyzeFile:\r
8874     nowChecked = IDM_AnalyzeFile;\r
8875     break;\r
8876   case EditGame:\r
8877     nowChecked = IDM_EditGame;\r
8878     break;\r
8879   case PlayFromGameFile:\r
8880     nowChecked = IDM_LoadGame;\r
8881     break;\r
8882   case EditPosition:\r
8883     nowChecked = IDM_EditPosition;\r
8884     break;\r
8885   case Training:\r
8886     nowChecked = IDM_Training;\r
8887     break;\r
8888   case IcsPlayingWhite:\r
8889   case IcsPlayingBlack:\r
8890   case IcsObserving:\r
8891   case IcsIdle:\r
8892     nowChecked = IDM_IcsClient;\r
8893     break;\r
8894   default:\r
8895   case EndOfGame:\r
8896     nowChecked = 0;\r
8897     break;\r
8898   }\r
8899   if (prevChecked != 0)\r
8900     (void) CheckMenuItem(GetMenu(hwndMain),\r
8901                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8902   if (nowChecked != 0)\r
8903     (void) CheckMenuItem(GetMenu(hwndMain),\r
8904                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8905 \r
8906   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8907     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8908                           MF_BYCOMMAND|MF_ENABLED);\r
8909   } else {\r
8910     (void) EnableMenuItem(GetMenu(hwndMain), \r
8911                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8912   }\r
8913 \r
8914   prevChecked = nowChecked;\r
8915 \r
8916   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8917   if (appData.icsActive) {\r
8918        if (appData.icsEngineAnalyze) {\r
8919                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8920                        MF_BYCOMMAND|MF_CHECKED);\r
8921        } else {\r
8922                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8923                        MF_BYCOMMAND|MF_UNCHECKED);\r
8924        }\r
8925   }\r
8926 }\r
8927 \r
8928 VOID\r
8929 SetICSMode()\r
8930 {\r
8931   HMENU hmenu = GetMenu(hwndMain);\r
8932   SetMenuEnables(hmenu, icsEnables);\r
8933   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8934     MF_BYPOSITION|MF_ENABLED);\r
8935 #ifdef ZIPPY\r
8936   if (appData.zippyPlay) {\r
8937     SetMenuEnables(hmenu, zippyEnables);\r
8938     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8939          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8940           MF_BYCOMMAND|MF_ENABLED);\r
8941   }\r
8942 #endif\r
8943 }\r
8944 \r
8945 VOID\r
8946 SetGNUMode()\r
8947 {\r
8948   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8949 }\r
8950 \r
8951 VOID\r
8952 SetNCPMode()\r
8953 {\r
8954   HMENU hmenu = GetMenu(hwndMain);\r
8955   SetMenuEnables(hmenu, ncpEnables);\r
8956   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8957     MF_BYPOSITION|MF_GRAYED);\r
8958     DrawMenuBar(hwndMain);\r
8959 }\r
8960 \r
8961 VOID\r
8962 SetCmailMode()\r
8963 {\r
8964   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8965 }\r
8966 \r
8967 VOID \r
8968 SetTrainingModeOn()\r
8969 {\r
8970   int i;\r
8971   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8972   for (i = 0; i < N_BUTTONS; i++) {\r
8973     if (buttonDesc[i].hwnd != NULL)\r
8974       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8975   }\r
8976   CommentPopDown();\r
8977 }\r
8978 \r
8979 VOID SetTrainingModeOff()\r
8980 {\r
8981   int i;\r
8982   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8983   for (i = 0; i < N_BUTTONS; i++) {\r
8984     if (buttonDesc[i].hwnd != NULL)\r
8985       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8986   }\r
8987 }\r
8988 \r
8989 \r
8990 VOID\r
8991 SetUserThinkingEnables()\r
8992 {\r
8993   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8994 }\r
8995 \r
8996 VOID\r
8997 SetMachineThinkingEnables()\r
8998 {\r
8999   HMENU hMenu = GetMenu(hwndMain);\r
9000   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9001 \r
9002   SetMenuEnables(hMenu, machineThinkingEnables);\r
9003 \r
9004   if (gameMode == MachinePlaysBlack) {\r
9005     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9006   } else if (gameMode == MachinePlaysWhite) {\r
9007     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9008   } else if (gameMode == TwoMachinesPlay) {\r
9009     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9010   }\r
9011 }\r
9012 \r
9013 \r
9014 VOID\r
9015 DisplayTitle(char *str)\r
9016 {\r
9017   char title[MSG_SIZ], *host;\r
9018   if (str[0] != NULLCHAR) {\r
9019     strcpy(title, str);\r
9020   } else if (appData.icsActive) {\r
9021     if (appData.icsCommPort[0] != NULLCHAR)\r
9022       host = "ICS";\r
9023     else \r
9024       host = appData.icsHost;\r
9025     sprintf(title, "%s: %s", szTitle, host);\r
9026   } else if (appData.noChessProgram) {\r
9027     strcpy(title, szTitle);\r
9028   } else {\r
9029     strcpy(title, szTitle);\r
9030     strcat(title, ": ");\r
9031     strcat(title, first.tidy);\r
9032   }\r
9033   SetWindowText(hwndMain, title);\r
9034 }\r
9035 \r
9036 \r
9037 VOID\r
9038 DisplayMessage(char *str1, char *str2)\r
9039 {\r
9040   HDC hdc;\r
9041   HFONT oldFont;\r
9042   int remain = MESSAGE_TEXT_MAX - 1;\r
9043   int len;\r
9044 \r
9045   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9046   messageText[0] = NULLCHAR;\r
9047   if (*str1) {\r
9048     len = strlen(str1);\r
9049     if (len > remain) len = remain;\r
9050     strncpy(messageText, str1, len);\r
9051     messageText[len] = NULLCHAR;\r
9052     remain -= len;\r
9053   }\r
9054   if (*str2 && remain >= 2) {\r
9055     if (*str1) {\r
9056       strcat(messageText, "  ");\r
9057       remain -= 2;\r
9058     }\r
9059     len = strlen(str2);\r
9060     if (len > remain) len = remain;\r
9061     strncat(messageText, str2, len);\r
9062   }\r
9063   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9064 \r
9065   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9066 \r
9067   SAYMACHINEMOVE();\r
9068 \r
9069   hdc = GetDC(hwndMain);\r
9070   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9071   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9072              &messageRect, messageText, strlen(messageText), NULL);\r
9073   (void) SelectObject(hdc, oldFont);\r
9074   (void) ReleaseDC(hwndMain, hdc);\r
9075 }\r
9076 \r
9077 VOID\r
9078 DisplayError(char *str, int error)\r
9079 {\r
9080   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9081   int len;\r
9082 \r
9083   if (error == 0) {\r
9084     strcpy(buf, str);\r
9085   } else {\r
9086     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9087                         NULL, error, LANG_NEUTRAL,\r
9088                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9089     if (len > 0) {\r
9090       sprintf(buf, "%s:\n%s", str, buf2);\r
9091     } else {\r
9092       ErrorMap *em = errmap;\r
9093       while (em->err != 0 && em->err != error) em++;\r
9094       if (em->err != 0) {\r
9095         sprintf(buf, "%s:\n%s", str, em->msg);\r
9096       } else {\r
9097         sprintf(buf, "%s:\nError code %d", str, error);\r
9098       }\r
9099     }\r
9100   }\r
9101   \r
9102   ErrorPopUp("Error", buf);\r
9103 }\r
9104 \r
9105 \r
9106 VOID\r
9107 DisplayMoveError(char *str)\r
9108 {\r
9109   fromX = fromY = -1;\r
9110   ClearHighlights();\r
9111   DrawPosition(FALSE, NULL);\r
9112   if (appData.popupMoveErrors) {\r
9113     ErrorPopUp("Error", str);\r
9114   } else {\r
9115     DisplayMessage(str, "");\r
9116     moveErrorMessageUp = TRUE;\r
9117   }\r
9118 }\r
9119 \r
9120 VOID\r
9121 DisplayFatalError(char *str, int error, int exitStatus)\r
9122 {\r
9123   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9124   int len;\r
9125   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9126 \r
9127   if (error != 0) {\r
9128     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9129                         NULL, error, LANG_NEUTRAL,\r
9130                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9131     if (len > 0) {\r
9132       sprintf(buf, "%s:\n%s", str, buf2);\r
9133     } else {\r
9134       ErrorMap *em = errmap;\r
9135       while (em->err != 0 && em->err != error) em++;\r
9136       if (em->err != 0) {\r
9137         sprintf(buf, "%s:\n%s", str, em->msg);\r
9138       } else {\r
9139         sprintf(buf, "%s:\nError code %d", str, error);\r
9140       }\r
9141     }\r
9142     str = buf;\r
9143   }\r
9144   if (appData.debugMode) {\r
9145     fprintf(debugFP, "%s: %s\n", label, str);\r
9146   }\r
9147   if (appData.popupExitMessage) {\r
9148     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9149                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9150   }\r
9151   ExitEvent(exitStatus);\r
9152 }\r
9153 \r
9154 \r
9155 VOID\r
9156 DisplayInformation(char *str)\r
9157 {\r
9158   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9159 }\r
9160 \r
9161 \r
9162 VOID\r
9163 DisplayNote(char *str)\r
9164 {\r
9165   ErrorPopUp("Note", str);\r
9166 }\r
9167 \r
9168 \r
9169 typedef struct {\r
9170   char *title, *question, *replyPrefix;\r
9171   ProcRef pr;\r
9172 } QuestionParams;\r
9173 \r
9174 LRESULT CALLBACK\r
9175 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9176 {\r
9177   static QuestionParams *qp;\r
9178   char reply[MSG_SIZ];\r
9179   int len, err;\r
9180 \r
9181   switch (message) {\r
9182   case WM_INITDIALOG:\r
9183     qp = (QuestionParams *) lParam;\r
9184     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9185     SetWindowText(hDlg, qp->title);\r
9186     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9187     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9188     return FALSE;\r
9189 \r
9190   case WM_COMMAND:\r
9191     switch (LOWORD(wParam)) {\r
9192     case IDOK:\r
9193       strcpy(reply, qp->replyPrefix);\r
9194       if (*reply) strcat(reply, " ");\r
9195       len = strlen(reply);\r
9196       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9197       strcat(reply, "\n");\r
9198       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9199       EndDialog(hDlg, TRUE);\r
9200       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9201       return TRUE;\r
9202     case IDCANCEL:\r
9203       EndDialog(hDlg, FALSE);\r
9204       return TRUE;\r
9205     default:\r
9206       break;\r
9207     }\r
9208     break;\r
9209   }\r
9210   return FALSE;\r
9211 }\r
9212 \r
9213 VOID\r
9214 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9215 {\r
9216     QuestionParams qp;\r
9217     FARPROC lpProc;\r
9218     \r
9219     qp.title = title;\r
9220     qp.question = question;\r
9221     qp.replyPrefix = replyPrefix;\r
9222     qp.pr = pr;\r
9223     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9224     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9225       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9226     FreeProcInstance(lpProc);\r
9227 }\r
9228 \r
9229 /* [AS] Pick FRC position */\r
9230 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9231 {\r
9232     static int * lpIndexFRC;\r
9233     BOOL index_is_ok;\r
9234     char buf[16];\r
9235 \r
9236     switch( message )\r
9237     {\r
9238     case WM_INITDIALOG:\r
9239         lpIndexFRC = (int *) lParam;\r
9240 \r
9241         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9242 \r
9243         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9244         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9245         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9246         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9247 \r
9248         break;\r
9249 \r
9250     case WM_COMMAND:\r
9251         switch( LOWORD(wParam) ) {\r
9252         case IDOK:\r
9253             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9254             EndDialog( hDlg, 0 );\r
9255             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9256             return TRUE;\r
9257         case IDCANCEL:\r
9258             EndDialog( hDlg, 1 );   \r
9259             return TRUE;\r
9260         case IDC_NFG_Edit:\r
9261             if( HIWORD(wParam) == EN_CHANGE ) {\r
9262                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9263 \r
9264                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9265             }\r
9266             return TRUE;\r
9267         case IDC_NFG_Random:\r
9268             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9269             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9270             return TRUE;\r
9271         }\r
9272 \r
9273         break;\r
9274     }\r
9275 \r
9276     return FALSE;\r
9277 }\r
9278 \r
9279 int NewGameFRC()\r
9280 {\r
9281     int result;\r
9282     int index = appData.defaultFrcPosition;\r
9283     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9284 \r
9285     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9286 \r
9287     if( result == 0 ) {\r
9288         appData.defaultFrcPosition = index;\r
9289     }\r
9290 \r
9291     return result;\r
9292 }\r
9293 \r
9294 /* [AS] Game list options */\r
9295 typedef struct {\r
9296     char id;\r
9297     char * name;\r
9298 } GLT_Item;\r
9299 \r
9300 static GLT_Item GLT_ItemInfo[] = {\r
9301     { GLT_EVENT,      "Event" },\r
9302     { GLT_SITE,       "Site" },\r
9303     { GLT_DATE,       "Date" },\r
9304     { GLT_ROUND,      "Round" },\r
9305     { GLT_PLAYERS,    "Players" },\r
9306     { GLT_RESULT,     "Result" },\r
9307     { GLT_WHITE_ELO,  "White Rating" },\r
9308     { GLT_BLACK_ELO,  "Black Rating" },\r
9309     { GLT_TIME_CONTROL,"Time Control" },\r
9310     { GLT_VARIANT,    "Variant" },\r
9311     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9312     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9313     { 0, 0 }\r
9314 };\r
9315 \r
9316 const char * GLT_FindItem( char id )\r
9317 {\r
9318     const char * result = 0;\r
9319 \r
9320     GLT_Item * list = GLT_ItemInfo;\r
9321 \r
9322     while( list->id != 0 ) {\r
9323         if( list->id == id ) {\r
9324             result = list->name;\r
9325             break;\r
9326         }\r
9327 \r
9328         list++;\r
9329     }\r
9330 \r
9331     return result;\r
9332 }\r
9333 \r
9334 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9335 {\r
9336     const char * name = GLT_FindItem( id );\r
9337 \r
9338     if( name != 0 ) {\r
9339         if( index >= 0 ) {\r
9340             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9341         }\r
9342         else {\r
9343             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9344         }\r
9345     }\r
9346 }\r
9347 \r
9348 void GLT_TagsToList( HWND hDlg, char * tags )\r
9349 {\r
9350     char * pc = tags;\r
9351 \r
9352     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9353 \r
9354     while( *pc ) {\r
9355         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9356         pc++;\r
9357     }\r
9358 \r
9359     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9360 \r
9361     pc = GLT_ALL_TAGS;\r
9362 \r
9363     while( *pc ) {\r
9364         if( strchr( tags, *pc ) == 0 ) {\r
9365             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9366         }\r
9367         pc++;\r
9368     }\r
9369 \r
9370     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9371 }\r
9372 \r
9373 char GLT_ListItemToTag( HWND hDlg, int index )\r
9374 {\r
9375     char result = '\0';\r
9376     char name[128];\r
9377 \r
9378     GLT_Item * list = GLT_ItemInfo;\r
9379 \r
9380     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9381         while( list->id != 0 ) {\r
9382             if( strcmp( list->name, name ) == 0 ) {\r
9383                 result = list->id;\r
9384                 break;\r
9385             }\r
9386 \r
9387             list++;\r
9388         }\r
9389     }\r
9390 \r
9391     return result;\r
9392 }\r
9393 \r
9394 void GLT_MoveSelection( HWND hDlg, int delta )\r
9395 {\r
9396     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9397     int idx2 = idx1 + delta;\r
9398     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9399 \r
9400     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9401         char buf[128];\r
9402 \r
9403         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9404         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9405         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9406         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9407     }\r
9408 }\r
9409 \r
9410 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9411 {\r
9412     static char glt[64];\r
9413     static char * lpUserGLT;\r
9414 \r
9415     switch( message )\r
9416     {\r
9417     case WM_INITDIALOG:\r
9418         lpUserGLT = (char *) lParam;\r
9419         \r
9420         strcpy( glt, lpUserGLT );\r
9421 \r
9422         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9423 \r
9424         /* Initialize list */\r
9425         GLT_TagsToList( hDlg, glt );\r
9426 \r
9427         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9428 \r
9429         break;\r
9430 \r
9431     case WM_COMMAND:\r
9432         switch( LOWORD(wParam) ) {\r
9433         case IDOK:\r
9434             {\r
9435                 char * pc = lpUserGLT;\r
9436                 int idx = 0;\r
9437 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9438                 char id;\r
9439 \r
9440                 do {\r
9441                     id = GLT_ListItemToTag( hDlg, idx );\r
9442 \r
9443                     *pc++ = id;\r
9444                     idx++;\r
9445                 } while( id != '\0' );\r
9446             }\r
9447             EndDialog( hDlg, 0 );\r
9448             return TRUE;\r
9449         case IDCANCEL:\r
9450             EndDialog( hDlg, 1 );\r
9451             return TRUE;\r
9452 \r
9453         case IDC_GLT_Default:\r
9454             strcpy( glt, GLT_DEFAULT_TAGS );\r
9455             GLT_TagsToList( hDlg, glt );\r
9456             return TRUE;\r
9457 \r
9458         case IDC_GLT_Restore:\r
9459             strcpy( glt, lpUserGLT );\r
9460             GLT_TagsToList( hDlg, glt );\r
9461             return TRUE;\r
9462 \r
9463         case IDC_GLT_Up:\r
9464             GLT_MoveSelection( hDlg, -1 );\r
9465             return TRUE;\r
9466 \r
9467         case IDC_GLT_Down:\r
9468             GLT_MoveSelection( hDlg, +1 );\r
9469             return TRUE;\r
9470         }\r
9471 \r
9472         break;\r
9473     }\r
9474 \r
9475     return FALSE;\r
9476 }\r
9477 \r
9478 int GameListOptions()\r
9479 {\r
9480     char glt[64];\r
9481     int result;\r
9482     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9483 \r
9484     strcpy( glt, appData.gameListTags );\r
9485 \r
9486     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9487 \r
9488     if( result == 0 ) {\r
9489         /* [AS] Memory leak here! */\r
9490         appData.gameListTags = strdup( glt ); \r
9491     }\r
9492 \r
9493     return result;\r
9494 }\r
9495 \r
9496 \r
9497 VOID\r
9498 DisplayIcsInteractionTitle(char *str)\r
9499 {\r
9500   char consoleTitle[MSG_SIZ];\r
9501 \r
9502   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9503   SetWindowText(hwndConsole, consoleTitle);\r
9504 }\r
9505 \r
9506 void\r
9507 DrawPosition(int fullRedraw, Board board)\r
9508 {\r
9509   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9510 }\r
9511 \r
9512 \r
9513 VOID\r
9514 ResetFrontEnd()\r
9515 {\r
9516   fromX = fromY = -1;\r
9517   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9518     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9519     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9520     dragInfo.lastpos = dragInfo.pos;\r
9521     dragInfo.start.x = dragInfo.start.y = -1;\r
9522     dragInfo.from = dragInfo.start;\r
9523     ReleaseCapture();\r
9524     DrawPosition(TRUE, NULL);\r
9525   }\r
9526 }\r
9527 \r
9528 \r
9529 VOID\r
9530 CommentPopUp(char *title, char *str)\r
9531 {\r
9532   HWND hwnd = GetActiveWindow();\r
9533   EitherCommentPopUp(0, title, str, FALSE);\r
9534   SAY(str);\r
9535   SetActiveWindow(hwnd);\r
9536 }\r
9537 \r
9538 VOID\r
9539 CommentPopDown(void)\r
9540 {\r
9541   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9542   if (commentDialog) {\r
9543     ShowWindow(commentDialog, SW_HIDE);\r
9544   }\r
9545   commentDialogUp = FALSE;\r
9546 }\r
9547 \r
9548 VOID\r
9549 EditCommentPopUp(int index, char *title, char *str)\r
9550 {\r
9551   EitherCommentPopUp(index, title, str, TRUE);\r
9552 }\r
9553 \r
9554 \r
9555 VOID\r
9556 RingBell()\r
9557 {\r
9558   MyPlaySound(&sounds[(int)SoundMove]);\r
9559 }\r
9560 \r
9561 VOID PlayIcsWinSound()\r
9562 {\r
9563   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9564 }\r
9565 \r
9566 VOID PlayIcsLossSound()\r
9567 {\r
9568   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9569 }\r
9570 \r
9571 VOID PlayIcsDrawSound()\r
9572 {\r
9573   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9574 }\r
9575 \r
9576 VOID PlayIcsUnfinishedSound()\r
9577 {\r
9578   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9579 }\r
9580 \r
9581 VOID\r
9582 PlayAlarmSound()\r
9583 {\r
9584   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9585 }\r
9586 \r
9587 \r
9588 VOID\r
9589 EchoOn()\r
9590 {\r
9591   HWND hInput;\r
9592   consoleEcho = TRUE;\r
9593   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9594   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9595   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9596 }\r
9597 \r
9598 \r
9599 VOID\r
9600 EchoOff()\r
9601 {\r
9602   CHARFORMAT cf;\r
9603   HWND hInput;\r
9604   consoleEcho = FALSE;\r
9605   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9606   /* This works OK: set text and background both to the same color */\r
9607   cf = consoleCF;\r
9608   cf.crTextColor = COLOR_ECHOOFF;\r
9609   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9610   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9611 }\r
9612 \r
9613 /* No Raw()...? */\r
9614 \r
9615 void Colorize(ColorClass cc, int continuation)\r
9616 {\r
9617   currentColorClass = cc;\r
9618   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9619   consoleCF.crTextColor = textAttribs[cc].color;\r
9620   consoleCF.dwEffects = textAttribs[cc].effects;\r
9621   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9622 }\r
9623 \r
9624 char *\r
9625 UserName()\r
9626 {\r
9627   static char buf[MSG_SIZ];\r
9628   DWORD bufsiz = MSG_SIZ;\r
9629 \r
9630   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9631         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9632   }\r
9633   if (!GetUserName(buf, &bufsiz)) {\r
9634     /*DisplayError("Error getting user name", GetLastError());*/\r
9635     strcpy(buf, "User");\r
9636   }\r
9637   return buf;\r
9638 }\r
9639 \r
9640 char *\r
9641 HostName()\r
9642 {\r
9643   static char buf[MSG_SIZ];\r
9644   DWORD bufsiz = MSG_SIZ;\r
9645 \r
9646   if (!GetComputerName(buf, &bufsiz)) {\r
9647     /*DisplayError("Error getting host name", GetLastError());*/\r
9648     strcpy(buf, "Unknown");\r
9649   }\r
9650   return buf;\r
9651 }\r
9652 \r
9653 \r
9654 int\r
9655 ClockTimerRunning()\r
9656 {\r
9657   return clockTimerEvent != 0;\r
9658 }\r
9659 \r
9660 int\r
9661 StopClockTimer()\r
9662 {\r
9663   if (clockTimerEvent == 0) return FALSE;\r
9664   KillTimer(hwndMain, clockTimerEvent);\r
9665   clockTimerEvent = 0;\r
9666   return TRUE;\r
9667 }\r
9668 \r
9669 void\r
9670 StartClockTimer(long millisec)\r
9671 {\r
9672   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9673                              (UINT) millisec, NULL);\r
9674 }\r
9675 \r
9676 void\r
9677 DisplayWhiteClock(long timeRemaining, int highlight)\r
9678 {\r
9679   HDC hdc;\r
9680   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9681 \r
9682   if(appData.noGUI) return;\r
9683   hdc = GetDC(hwndMain);\r
9684   if (!IsIconic(hwndMain)) {\r
9685     DisplayAClock(hdc, timeRemaining, highlight, \r
9686                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9687   }\r
9688   if (highlight && iconCurrent == iconBlack) {\r
9689     iconCurrent = iconWhite;\r
9690     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9691     if (IsIconic(hwndMain)) {\r
9692       DrawIcon(hdc, 2, 2, iconCurrent);\r
9693     }\r
9694   }\r
9695   (void) ReleaseDC(hwndMain, hdc);\r
9696   if (hwndConsole)\r
9697     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9698 }\r
9699 \r
9700 void\r
9701 DisplayBlackClock(long timeRemaining, int highlight)\r
9702 {\r
9703   HDC hdc;\r
9704   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9705 \r
9706   if(appData.noGUI) return;\r
9707   hdc = GetDC(hwndMain);\r
9708   if (!IsIconic(hwndMain)) {\r
9709     DisplayAClock(hdc, timeRemaining, highlight, \r
9710                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9711   }\r
9712   if (highlight && iconCurrent == iconWhite) {\r
9713     iconCurrent = iconBlack;\r
9714     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9715     if (IsIconic(hwndMain)) {\r
9716       DrawIcon(hdc, 2, 2, iconCurrent);\r
9717     }\r
9718   }\r
9719   (void) ReleaseDC(hwndMain, hdc);\r
9720   if (hwndConsole)\r
9721     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9722 }\r
9723 \r
9724 \r
9725 int\r
9726 LoadGameTimerRunning()\r
9727 {\r
9728   return loadGameTimerEvent != 0;\r
9729 }\r
9730 \r
9731 int\r
9732 StopLoadGameTimer()\r
9733 {\r
9734   if (loadGameTimerEvent == 0) return FALSE;\r
9735   KillTimer(hwndMain, loadGameTimerEvent);\r
9736   loadGameTimerEvent = 0;\r
9737   return TRUE;\r
9738 }\r
9739 \r
9740 void\r
9741 StartLoadGameTimer(long millisec)\r
9742 {\r
9743   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9744                                 (UINT) millisec, NULL);\r
9745 }\r
9746 \r
9747 void\r
9748 AutoSaveGame()\r
9749 {\r
9750   char *defName;\r
9751   FILE *f;\r
9752   char fileTitle[MSG_SIZ];\r
9753 \r
9754   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9755   f = OpenFileDialog(hwndMain, "a", defName,\r
9756                      appData.oldSaveStyle ? "gam" : "pgn",\r
9757                      GAME_FILT, \r
9758                      "Save Game to File", NULL, fileTitle, NULL);\r
9759   if (f != NULL) {\r
9760     SaveGame(f, 0, "");\r
9761     fclose(f);\r
9762   }\r
9763 }\r
9764 \r
9765 \r
9766 void\r
9767 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9768 {\r
9769   if (delayedTimerEvent != 0) {\r
9770     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9771       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9772     }\r
9773     KillTimer(hwndMain, delayedTimerEvent);\r
9774     delayedTimerEvent = 0;\r
9775     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9776     delayedTimerCallback();\r
9777   }\r
9778   delayedTimerCallback = cb;\r
9779   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9780                                 (UINT) millisec, NULL);\r
9781 }\r
9782 \r
9783 DelayedEventCallback\r
9784 GetDelayedEvent()\r
9785 {\r
9786   if (delayedTimerEvent) {\r
9787     return delayedTimerCallback;\r
9788   } else {\r
9789     return NULL;\r
9790   }\r
9791 }\r
9792 \r
9793 void\r
9794 CancelDelayedEvent()\r
9795 {\r
9796   if (delayedTimerEvent) {\r
9797     KillTimer(hwndMain, delayedTimerEvent);\r
9798     delayedTimerEvent = 0;\r
9799   }\r
9800 }\r
9801 \r
9802 DWORD GetWin32Priority(int nice)\r
9803 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9804 /*\r
9805 REALTIME_PRIORITY_CLASS     0x00000100\r
9806 HIGH_PRIORITY_CLASS         0x00000080\r
9807 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9808 NORMAL_PRIORITY_CLASS       0x00000020\r
9809 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9810 IDLE_PRIORITY_CLASS         0x00000040\r
9811 */\r
9812         if (nice < -15) return 0x00000080;\r
9813         if (nice < 0)   return 0x00008000;\r
9814         if (nice == 0)  return 0x00000020;\r
9815         if (nice < 15)  return 0x00004000;\r
9816         return 0x00000040;\r
9817 }\r
9818 \r
9819 /* Start a child process running the given program.\r
9820    The process's standard output can be read from "from", and its\r
9821    standard input can be written to "to".\r
9822    Exit with fatal error if anything goes wrong.\r
9823    Returns an opaque pointer that can be used to destroy the process\r
9824    later.\r
9825 */\r
9826 int\r
9827 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9828 {\r
9829 #define BUFSIZE 4096\r
9830 \r
9831   HANDLE hChildStdinRd, hChildStdinWr,\r
9832     hChildStdoutRd, hChildStdoutWr;\r
9833   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9834   SECURITY_ATTRIBUTES saAttr;\r
9835   BOOL fSuccess;\r
9836   PROCESS_INFORMATION piProcInfo;\r
9837   STARTUPINFO siStartInfo;\r
9838   ChildProc *cp;\r
9839   char buf[MSG_SIZ];\r
9840   DWORD err;\r
9841 \r
9842   if (appData.debugMode) {\r
9843     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9844   }\r
9845 \r
9846   *pr = NoProc;\r
9847 \r
9848   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9849   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9850   saAttr.bInheritHandle = TRUE;\r
9851   saAttr.lpSecurityDescriptor = NULL;\r
9852 \r
9853   /*\r
9854    * The steps for redirecting child's STDOUT:\r
9855    *     1. Create anonymous pipe to be STDOUT for child.\r
9856    *     2. Create a noninheritable duplicate of read handle,\r
9857    *         and close the inheritable read handle.\r
9858    */\r
9859 \r
9860   /* Create a pipe for the child's STDOUT. */\r
9861   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9862     return GetLastError();\r
9863   }\r
9864 \r
9865   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9866   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9867                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9868                              FALSE,     /* not inherited */\r
9869                              DUPLICATE_SAME_ACCESS);\r
9870   if (! fSuccess) {\r
9871     return GetLastError();\r
9872   }\r
9873   CloseHandle(hChildStdoutRd);\r
9874 \r
9875   /*\r
9876    * The steps for redirecting child's STDIN:\r
9877    *     1. Create anonymous pipe to be STDIN for child.\r
9878    *     2. Create a noninheritable duplicate of write handle,\r
9879    *         and close the inheritable write handle.\r
9880    */\r
9881 \r
9882   /* Create a pipe for the child's STDIN. */\r
9883   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9884     return GetLastError();\r
9885   }\r
9886 \r
9887   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9888   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9889                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9890                              FALSE,     /* not inherited */\r
9891                              DUPLICATE_SAME_ACCESS);\r
9892   if (! fSuccess) {\r
9893     return GetLastError();\r
9894   }\r
9895   CloseHandle(hChildStdinWr);\r
9896 \r
9897   /* Arrange to (1) look in dir for the child .exe file, and\r
9898    * (2) have dir be the child's working directory.  Interpret\r
9899    * dir relative to the directory WinBoard loaded from. */\r
9900   GetCurrentDirectory(MSG_SIZ, buf);\r
9901   SetCurrentDirectory(installDir);\r
9902   SetCurrentDirectory(dir);\r
9903 \r
9904   /* Now create the child process. */\r
9905 \r
9906   siStartInfo.cb = sizeof(STARTUPINFO);\r
9907   siStartInfo.lpReserved = NULL;\r
9908   siStartInfo.lpDesktop = NULL;\r
9909   siStartInfo.lpTitle = NULL;\r
9910   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9911   siStartInfo.cbReserved2 = 0;\r
9912   siStartInfo.lpReserved2 = NULL;\r
9913   siStartInfo.hStdInput = hChildStdinRd;\r
9914   siStartInfo.hStdOutput = hChildStdoutWr;\r
9915   siStartInfo.hStdError = hChildStdoutWr;\r
9916 \r
9917   fSuccess = CreateProcess(NULL,\r
9918                            cmdLine,        /* command line */\r
9919                            NULL,           /* process security attributes */\r
9920                            NULL,           /* primary thread security attrs */\r
9921                            TRUE,           /* handles are inherited */\r
9922                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9923                            NULL,           /* use parent's environment */\r
9924                            NULL,\r
9925                            &siStartInfo, /* STARTUPINFO pointer */\r
9926                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9927 \r
9928   err = GetLastError();\r
9929   SetCurrentDirectory(buf); /* return to prev directory */\r
9930   if (! fSuccess) {\r
9931     return err;\r
9932   }\r
9933 \r
9934   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9935     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9936     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9937   }\r
9938 \r
9939   /* Close the handles we don't need in the parent */\r
9940   CloseHandle(piProcInfo.hThread);\r
9941   CloseHandle(hChildStdinRd);\r
9942   CloseHandle(hChildStdoutWr);\r
9943 \r
9944   /* Prepare return value */\r
9945   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9946   cp->kind = CPReal;\r
9947   cp->hProcess = piProcInfo.hProcess;\r
9948   cp->pid = piProcInfo.dwProcessId;\r
9949   cp->hFrom = hChildStdoutRdDup;\r
9950   cp->hTo = hChildStdinWrDup;\r
9951 \r
9952   *pr = (void *) cp;\r
9953 \r
9954   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9955      2000 where engines sometimes don't see the initial command(s)\r
9956      from WinBoard and hang.  I don't understand how that can happen,\r
9957      but the Sleep is harmless, so I've put it in.  Others have also\r
9958      reported what may be the same problem, so hopefully this will fix\r
9959      it for them too.  */\r
9960   Sleep(500);\r
9961 \r
9962   return NO_ERROR;\r
9963 }\r
9964 \r
9965 \r
9966 void\r
9967 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9968 {\r
9969   ChildProc *cp; int result;\r
9970 \r
9971   cp = (ChildProc *) pr;\r
9972   if (cp == NULL) return;\r
9973 \r
9974   switch (cp->kind) {\r
9975   case CPReal:\r
9976     /* TerminateProcess is considered harmful, so... */\r
9977     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9978     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9979     /* The following doesn't work because the chess program\r
9980        doesn't "have the same console" as WinBoard.  Maybe\r
9981        we could arrange for this even though neither WinBoard\r
9982        nor the chess program uses a console for stdio? */\r
9983     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9984 \r
9985     /* [AS] Special termination modes for misbehaving programs... */\r
9986     if( signal == 9 ) { \r
9987         result = TerminateProcess( cp->hProcess, 0 );\r
9988 \r
9989         if ( appData.debugMode) {\r
9990             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9991         }\r
9992     }\r
9993     else if( signal == 10 ) {\r
9994         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9995 \r
9996         if( dw != WAIT_OBJECT_0 ) {\r
9997             result = TerminateProcess( cp->hProcess, 0 );\r
9998 \r
9999             if ( appData.debugMode) {\r
10000                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10001             }\r
10002 \r
10003         }\r
10004     }\r
10005 \r
10006     CloseHandle(cp->hProcess);\r
10007     break;\r
10008 \r
10009   case CPComm:\r
10010     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10011     break;\r
10012 \r
10013   case CPSock:\r
10014     closesocket(cp->sock);\r
10015     WSACleanup();\r
10016     break;\r
10017 \r
10018   case CPRcmd:\r
10019     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10020     closesocket(cp->sock);\r
10021     closesocket(cp->sock2);\r
10022     WSACleanup();\r
10023     break;\r
10024   }\r
10025   free(cp);\r
10026 }\r
10027 \r
10028 void\r
10029 InterruptChildProcess(ProcRef pr)\r
10030 {\r
10031   ChildProc *cp;\r
10032 \r
10033   cp = (ChildProc *) pr;\r
10034   if (cp == NULL) return;\r
10035   switch (cp->kind) {\r
10036   case CPReal:\r
10037     /* The following doesn't work because the chess program\r
10038        doesn't "have the same console" as WinBoard.  Maybe\r
10039        we could arrange for this even though neither WinBoard\r
10040        nor the chess program uses a console for stdio */\r
10041     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10042     break;\r
10043 \r
10044   case CPComm:\r
10045   case CPSock:\r
10046     /* Can't interrupt */\r
10047     break;\r
10048 \r
10049   case CPRcmd:\r
10050     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10051     break;\r
10052   }\r
10053 }\r
10054 \r
10055 \r
10056 int\r
10057 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10058 {\r
10059   char cmdLine[MSG_SIZ];\r
10060 \r
10061   if (port[0] == NULLCHAR) {\r
10062     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10063   } else {\r
10064     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10065   }\r
10066   return StartChildProcess(cmdLine, "", pr);\r
10067 }\r
10068 \r
10069 \r
10070 /* Code to open TCP sockets */\r
10071 \r
10072 int\r
10073 OpenTCP(char *host, char *port, ProcRef *pr)\r
10074 {\r
10075   ChildProc *cp;\r
10076   int err;\r
10077   SOCKET s;\r
10078   struct sockaddr_in sa, mysa;\r
10079   struct hostent FAR *hp;\r
10080   unsigned short uport;\r
10081   WORD wVersionRequested;\r
10082   WSADATA wsaData;\r
10083 \r
10084   /* Initialize socket DLL */\r
10085   wVersionRequested = MAKEWORD(1, 1);\r
10086   err = WSAStartup(wVersionRequested, &wsaData);\r
10087   if (err != 0) return err;\r
10088 \r
10089   /* Make socket */\r
10090   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10091     err = WSAGetLastError();\r
10092     WSACleanup();\r
10093     return err;\r
10094   }\r
10095 \r
10096   /* Bind local address using (mostly) don't-care values.\r
10097    */\r
10098   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10099   mysa.sin_family = AF_INET;\r
10100   mysa.sin_addr.s_addr = INADDR_ANY;\r
10101   uport = (unsigned short) 0;\r
10102   mysa.sin_port = htons(uport);\r
10103   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10104       == SOCKET_ERROR) {\r
10105     err = WSAGetLastError();\r
10106     WSACleanup();\r
10107     return err;\r
10108   }\r
10109 \r
10110   /* Resolve remote host name */\r
10111   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10112   if (!(hp = gethostbyname(host))) {\r
10113     unsigned int b0, b1, b2, b3;\r
10114 \r
10115     err = WSAGetLastError();\r
10116 \r
10117     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10118       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10119       hp->h_addrtype = AF_INET;\r
10120       hp->h_length = 4;\r
10121       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10122       hp->h_addr_list[0] = (char *) malloc(4);\r
10123       hp->h_addr_list[0][0] = (char) b0;\r
10124       hp->h_addr_list[0][1] = (char) b1;\r
10125       hp->h_addr_list[0][2] = (char) b2;\r
10126       hp->h_addr_list[0][3] = (char) b3;\r
10127     } else {\r
10128       WSACleanup();\r
10129       return err;\r
10130     }\r
10131   }\r
10132   sa.sin_family = hp->h_addrtype;\r
10133   uport = (unsigned short) atoi(port);\r
10134   sa.sin_port = htons(uport);\r
10135   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10136 \r
10137   /* Make connection */\r
10138   if (connect(s, (struct sockaddr *) &sa,\r
10139               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10140     err = WSAGetLastError();\r
10141     WSACleanup();\r
10142     return err;\r
10143   }\r
10144 \r
10145   /* Prepare return value */\r
10146   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10147   cp->kind = CPSock;\r
10148   cp->sock = s;\r
10149   *pr = (ProcRef *) cp;\r
10150 \r
10151   return NO_ERROR;\r
10152 }\r
10153 \r
10154 int\r
10155 OpenCommPort(char *name, ProcRef *pr)\r
10156 {\r
10157   HANDLE h;\r
10158   COMMTIMEOUTS ct;\r
10159   ChildProc *cp;\r
10160   char fullname[MSG_SIZ];\r
10161 \r
10162   if (*name != '\\')\r
10163     sprintf(fullname, "\\\\.\\%s", name);\r
10164   else\r
10165     strcpy(fullname, name);\r
10166 \r
10167   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10168                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10169   if (h == (HANDLE) -1) {\r
10170     return GetLastError();\r
10171   }\r
10172   hCommPort = h;\r
10173 \r
10174   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10175 \r
10176   /* Accumulate characters until a 100ms pause, then parse */\r
10177   ct.ReadIntervalTimeout = 100;\r
10178   ct.ReadTotalTimeoutMultiplier = 0;\r
10179   ct.ReadTotalTimeoutConstant = 0;\r
10180   ct.WriteTotalTimeoutMultiplier = 0;\r
10181   ct.WriteTotalTimeoutConstant = 0;\r
10182   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10183 \r
10184   /* Prepare return value */\r
10185   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10186   cp->kind = CPComm;\r
10187   cp->hFrom = h;\r
10188   cp->hTo = h;\r
10189   *pr = (ProcRef *) cp;\r
10190 \r
10191   return NO_ERROR;\r
10192 }\r
10193 \r
10194 int\r
10195 OpenLoopback(ProcRef *pr)\r
10196 {\r
10197   DisplayFatalError("Not implemented", 0, 1);\r
10198   return NO_ERROR;\r
10199 }\r
10200 \r
10201 \r
10202 int\r
10203 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10204 {\r
10205   ChildProc *cp;\r
10206   int err;\r
10207   SOCKET s, s2, s3;\r
10208   struct sockaddr_in sa, mysa;\r
10209   struct hostent FAR *hp;\r
10210   unsigned short uport;\r
10211   WORD wVersionRequested;\r
10212   WSADATA wsaData;\r
10213   int fromPort;\r
10214   char stderrPortStr[MSG_SIZ];\r
10215 \r
10216   /* Initialize socket DLL */\r
10217   wVersionRequested = MAKEWORD(1, 1);\r
10218   err = WSAStartup(wVersionRequested, &wsaData);\r
10219   if (err != 0) return err;\r
10220 \r
10221   /* Resolve remote host name */\r
10222   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10223   if (!(hp = gethostbyname(host))) {\r
10224     unsigned int b0, b1, b2, b3;\r
10225 \r
10226     err = WSAGetLastError();\r
10227 \r
10228     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10229       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10230       hp->h_addrtype = AF_INET;\r
10231       hp->h_length = 4;\r
10232       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10233       hp->h_addr_list[0] = (char *) malloc(4);\r
10234       hp->h_addr_list[0][0] = (char) b0;\r
10235       hp->h_addr_list[0][1] = (char) b1;\r
10236       hp->h_addr_list[0][2] = (char) b2;\r
10237       hp->h_addr_list[0][3] = (char) b3;\r
10238     } else {\r
10239       WSACleanup();\r
10240       return err;\r
10241     }\r
10242   }\r
10243   sa.sin_family = hp->h_addrtype;\r
10244   uport = (unsigned short) 514;\r
10245   sa.sin_port = htons(uport);\r
10246   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10247 \r
10248   /* Bind local socket to unused "privileged" port address\r
10249    */\r
10250   s = INVALID_SOCKET;\r
10251   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10252   mysa.sin_family = AF_INET;\r
10253   mysa.sin_addr.s_addr = INADDR_ANY;\r
10254   for (fromPort = 1023;; fromPort--) {\r
10255     if (fromPort < 0) {\r
10256       WSACleanup();\r
10257       return WSAEADDRINUSE;\r
10258     }\r
10259     if (s == INVALID_SOCKET) {\r
10260       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10261         err = WSAGetLastError();\r
10262         WSACleanup();\r
10263         return err;\r
10264       }\r
10265     }\r
10266     uport = (unsigned short) fromPort;\r
10267     mysa.sin_port = htons(uport);\r
10268     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10269         == SOCKET_ERROR) {\r
10270       err = WSAGetLastError();\r
10271       if (err == WSAEADDRINUSE) continue;\r
10272       WSACleanup();\r
10273       return err;\r
10274     }\r
10275     if (connect(s, (struct sockaddr *) &sa,\r
10276       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10277       err = WSAGetLastError();\r
10278       if (err == WSAEADDRINUSE) {\r
10279         closesocket(s);\r
10280         s = -1;\r
10281         continue;\r
10282       }\r
10283       WSACleanup();\r
10284       return err;\r
10285     }\r
10286     break;\r
10287   }\r
10288 \r
10289   /* Bind stderr local socket to unused "privileged" port address\r
10290    */\r
10291   s2 = INVALID_SOCKET;\r
10292   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10293   mysa.sin_family = AF_INET;\r
10294   mysa.sin_addr.s_addr = INADDR_ANY;\r
10295   for (fromPort = 1023;; fromPort--) {\r
10296     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10297     if (fromPort < 0) {\r
10298       (void) closesocket(s);\r
10299       WSACleanup();\r
10300       return WSAEADDRINUSE;\r
10301     }\r
10302     if (s2 == INVALID_SOCKET) {\r
10303       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10304         err = WSAGetLastError();\r
10305         closesocket(s);\r
10306         WSACleanup();\r
10307         return err;\r
10308       }\r
10309     }\r
10310     uport = (unsigned short) fromPort;\r
10311     mysa.sin_port = htons(uport);\r
10312     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10313         == SOCKET_ERROR) {\r
10314       err = WSAGetLastError();\r
10315       if (err == WSAEADDRINUSE) continue;\r
10316       (void) closesocket(s);\r
10317       WSACleanup();\r
10318       return err;\r
10319     }\r
10320     if (listen(s2, 1) == SOCKET_ERROR) {\r
10321       err = WSAGetLastError();\r
10322       if (err == WSAEADDRINUSE) {\r
10323         closesocket(s2);\r
10324         s2 = INVALID_SOCKET;\r
10325         continue;\r
10326       }\r
10327       (void) closesocket(s);\r
10328       (void) closesocket(s2);\r
10329       WSACleanup();\r
10330       return err;\r
10331     }\r
10332     break;\r
10333   }\r
10334   prevStderrPort = fromPort; // remember port used\r
10335   sprintf(stderrPortStr, "%d", fromPort);\r
10336 \r
10337   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10338     err = WSAGetLastError();\r
10339     (void) closesocket(s);\r
10340     (void) closesocket(s2);\r
10341     WSACleanup();\r
10342     return err;\r
10343   }\r
10344 \r
10345   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10346     err = WSAGetLastError();\r
10347     (void) closesocket(s);\r
10348     (void) closesocket(s2);\r
10349     WSACleanup();\r
10350     return err;\r
10351   }\r
10352   if (*user == NULLCHAR) user = UserName();\r
10353   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10354     err = WSAGetLastError();\r
10355     (void) closesocket(s);\r
10356     (void) closesocket(s2);\r
10357     WSACleanup();\r
10358     return err;\r
10359   }\r
10360   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10361     err = WSAGetLastError();\r
10362     (void) closesocket(s);\r
10363     (void) closesocket(s2);\r
10364     WSACleanup();\r
10365     return err;\r
10366   }\r
10367 \r
10368   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10369     err = WSAGetLastError();\r
10370     (void) closesocket(s);\r
10371     (void) closesocket(s2);\r
10372     WSACleanup();\r
10373     return err;\r
10374   }\r
10375   (void) closesocket(s2);  /* Stop listening */\r
10376 \r
10377   /* Prepare return value */\r
10378   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10379   cp->kind = CPRcmd;\r
10380   cp->sock = s;\r
10381   cp->sock2 = s3;\r
10382   *pr = (ProcRef *) cp;\r
10383 \r
10384   return NO_ERROR;\r
10385 }\r
10386 \r
10387 \r
10388 InputSourceRef\r
10389 AddInputSource(ProcRef pr, int lineByLine,\r
10390                InputCallback func, VOIDSTAR closure)\r
10391 {\r
10392   InputSource *is, *is2 = NULL;\r
10393   ChildProc *cp = (ChildProc *) pr;\r
10394 \r
10395   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10396   is->lineByLine = lineByLine;\r
10397   is->func = func;\r
10398   is->closure = closure;\r
10399   is->second = NULL;\r
10400   is->next = is->buf;\r
10401   if (pr == NoProc) {\r
10402     is->kind = CPReal;\r
10403     consoleInputSource = is;\r
10404   } else {\r
10405     is->kind = cp->kind;\r
10406     /* \r
10407         [AS] Try to avoid a race condition if the thread is given control too early:\r
10408         we create all threads suspended so that the is->hThread variable can be\r
10409         safely assigned, then let the threads start with ResumeThread.\r
10410     */\r
10411     switch (cp->kind) {\r
10412     case CPReal:\r
10413       is->hFile = cp->hFrom;\r
10414       cp->hFrom = NULL; /* now owned by InputThread */\r
10415       is->hThread =\r
10416         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10417                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10418       break;\r
10419 \r
10420     case CPComm:\r
10421       is->hFile = cp->hFrom;\r
10422       cp->hFrom = NULL; /* now owned by InputThread */\r
10423       is->hThread =\r
10424         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10425                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10426       break;\r
10427 \r
10428     case CPSock:\r
10429       is->sock = cp->sock;\r
10430       is->hThread =\r
10431         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10432                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10433       break;\r
10434 \r
10435     case CPRcmd:\r
10436       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10437       *is2 = *is;\r
10438       is->sock = cp->sock;\r
10439       is->second = is2;\r
10440       is2->sock = cp->sock2;\r
10441       is2->second = is2;\r
10442       is->hThread =\r
10443         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10444                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10445       is2->hThread =\r
10446         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10447                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10448       break;\r
10449     }\r
10450 \r
10451     if( is->hThread != NULL ) {\r
10452         ResumeThread( is->hThread );\r
10453     }\r
10454 \r
10455     if( is2 != NULL && is2->hThread != NULL ) {\r
10456         ResumeThread( is2->hThread );\r
10457     }\r
10458   }\r
10459 \r
10460   return (InputSourceRef) is;\r
10461 }\r
10462 \r
10463 void\r
10464 RemoveInputSource(InputSourceRef isr)\r
10465 {\r
10466   InputSource *is;\r
10467 \r
10468   is = (InputSource *) isr;\r
10469   is->hThread = NULL;  /* tell thread to stop */\r
10470   CloseHandle(is->hThread);\r
10471   if (is->second != NULL) {\r
10472     is->second->hThread = NULL;\r
10473     CloseHandle(is->second->hThread);\r
10474   }\r
10475 }\r
10476 \r
10477 \r
10478 int\r
10479 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10480 {\r
10481   DWORD dOutCount;\r
10482   int outCount = SOCKET_ERROR;\r
10483   ChildProc *cp = (ChildProc *) pr;\r
10484   static OVERLAPPED ovl;\r
10485 \r
10486   if (pr == NoProc) {\r
10487     ConsoleOutput(message, count, FALSE);\r
10488     return count;\r
10489   } \r
10490 \r
10491   if (ovl.hEvent == NULL) {\r
10492     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10493   }\r
10494   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10495 \r
10496   switch (cp->kind) {\r
10497   case CPSock:\r
10498   case CPRcmd:\r
10499     outCount = send(cp->sock, message, count, 0);\r
10500     if (outCount == SOCKET_ERROR) {\r
10501       *outError = WSAGetLastError();\r
10502     } else {\r
10503       *outError = NO_ERROR;\r
10504     }\r
10505     break;\r
10506 \r
10507   case CPReal:\r
10508     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10509                   &dOutCount, NULL)) {\r
10510       *outError = NO_ERROR;\r
10511       outCount = (int) dOutCount;\r
10512     } else {\r
10513       *outError = GetLastError();\r
10514     }\r
10515     break;\r
10516 \r
10517   case CPComm:\r
10518     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10519                             &dOutCount, &ovl);\r
10520     if (*outError == NO_ERROR) {\r
10521       outCount = (int) dOutCount;\r
10522     }\r
10523     break;\r
10524   }\r
10525   return outCount;\r
10526 }\r
10527 \r
10528 int\r
10529 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10530                        long msdelay)\r
10531 {\r
10532   /* Ignore delay, not implemented for WinBoard */\r
10533   return OutputToProcess(pr, message, count, outError);\r
10534 }\r
10535 \r
10536 \r
10537 void\r
10538 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10539                         char *buf, int count, int error)\r
10540 {\r
10541   DisplayFatalError("Not implemented", 0, 1);\r
10542 }\r
10543 \r
10544 /* see wgamelist.c for Game List functions */\r
10545 /* see wedittags.c for Edit Tags functions */\r
10546 \r
10547 \r
10548 VOID\r
10549 ICSInitScript()\r
10550 {\r
10551   FILE *f;\r
10552   char buf[MSG_SIZ];\r
10553   char *dummy;\r
10554 \r
10555   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10556     f = fopen(buf, "r");\r
10557     if (f != NULL) {\r
10558       ProcessICSInitScript(f);\r
10559       fclose(f);\r
10560     }\r
10561   }\r
10562 }\r
10563 \r
10564 \r
10565 VOID\r
10566 StartAnalysisClock()\r
10567 {\r
10568   if (analysisTimerEvent) return;\r
10569   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10570                                         (UINT) 2000, NULL);\r
10571 }\r
10572 \r
10573 LRESULT CALLBACK\r
10574 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10575 {\r
10576   static HANDLE hwndText;\r
10577   RECT rect;\r
10578   static int sizeX, sizeY;\r
10579   int newSizeX, newSizeY, flags;\r
10580   MINMAXINFO *mmi;\r
10581 \r
10582   switch (message) {\r
10583   case WM_INITDIALOG: /* message: initialize dialog box */\r
10584     /* Initialize the dialog items */\r
10585     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10586     SetWindowText(hDlg, analysisTitle);\r
10587     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10588     /* Size and position the dialog */\r
10589     if (!analysisDialog) {\r
10590       analysisDialog = hDlg;\r
10591       flags = SWP_NOZORDER;\r
10592       GetClientRect(hDlg, &rect);\r
10593       sizeX = rect.right;\r
10594       sizeY = rect.bottom;\r
10595       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10596           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10597         WINDOWPLACEMENT wp;\r
10598         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10599         wp.length = sizeof(WINDOWPLACEMENT);\r
10600         wp.flags = 0;\r
10601         wp.showCmd = SW_SHOW;\r
10602         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10603         wp.rcNormalPosition.left = analysisX;\r
10604         wp.rcNormalPosition.right = analysisX + analysisW;\r
10605         wp.rcNormalPosition.top = analysisY;\r
10606         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10607         SetWindowPlacement(hDlg, &wp);\r
10608 \r
10609         GetClientRect(hDlg, &rect);\r
10610         newSizeX = rect.right;\r
10611         newSizeY = rect.bottom;\r
10612         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10613                               newSizeX, newSizeY);\r
10614         sizeX = newSizeX;\r
10615         sizeY = newSizeY;\r
10616       }\r
10617     }\r
10618     return FALSE;\r
10619 \r
10620   case WM_COMMAND: /* message: received a command */\r
10621     switch (LOWORD(wParam)) {\r
10622     case IDCANCEL:\r
10623       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10624           ExitAnalyzeMode();\r
10625           ModeHighlight();\r
10626           return TRUE;\r
10627       }\r
10628       EditGameEvent();\r
10629       return TRUE;\r
10630     default:\r
10631       break;\r
10632     }\r
10633     break;\r
10634 \r
10635   case WM_SIZE:\r
10636     newSizeX = LOWORD(lParam);\r
10637     newSizeY = HIWORD(lParam);\r
10638     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10639     sizeX = newSizeX;\r
10640     sizeY = newSizeY;\r
10641     break;\r
10642 \r
10643   case WM_GETMINMAXINFO:\r
10644     /* Prevent resizing window too small */\r
10645     mmi = (MINMAXINFO *) lParam;\r
10646     mmi->ptMinTrackSize.x = 100;\r
10647     mmi->ptMinTrackSize.y = 100;\r
10648     break;\r
10649   }\r
10650   return FALSE;\r
10651 }\r
10652 \r
10653 VOID\r
10654 AnalysisPopUp(char* title, char* str)\r
10655 {\r
10656   FARPROC lpProc;\r
10657   char *p, *q;\r
10658 \r
10659   /* [AS] */\r
10660   EngineOutputPopUp();\r
10661   return;\r
10662 \r
10663   if (str == NULL) str = "";\r
10664   p = (char *) malloc(2 * strlen(str) + 2);\r
10665   q = p;\r
10666   while (*str) {\r
10667     if (*str == '\n') *q++ = '\r';\r
10668     *q++ = *str++;\r
10669   }\r
10670   *q = NULLCHAR;\r
10671   if (analysisText != NULL) free(analysisText);\r
10672   analysisText = p;\r
10673 \r
10674   if (analysisDialog) {\r
10675     SetWindowText(analysisDialog, title);\r
10676     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10677     ShowWindow(analysisDialog, SW_SHOW);\r
10678   } else {\r
10679     analysisTitle = title;\r
10680     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10681     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10682                  hwndMain, (DLGPROC)lpProc);\r
10683     FreeProcInstance(lpProc);\r
10684   }\r
10685   analysisDialogUp = TRUE;  \r
10686 }\r
10687 \r
10688 VOID\r
10689 AnalysisPopDown()\r
10690 {\r
10691   if (analysisDialog) {\r
10692     ShowWindow(analysisDialog, SW_HIDE);\r
10693   }\r
10694   analysisDialogUp = FALSE;  \r
10695 }\r
10696 \r
10697 \r
10698 VOID\r
10699 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10700 {\r
10701   highlightInfo.sq[0].x = fromX;\r
10702   highlightInfo.sq[0].y = fromY;\r
10703   highlightInfo.sq[1].x = toX;\r
10704   highlightInfo.sq[1].y = toY;\r
10705 }\r
10706 \r
10707 VOID\r
10708 ClearHighlights()\r
10709 {\r
10710   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10711     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10712 }\r
10713 \r
10714 VOID\r
10715 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10716 {\r
10717   premoveHighlightInfo.sq[0].x = fromX;\r
10718   premoveHighlightInfo.sq[0].y = fromY;\r
10719   premoveHighlightInfo.sq[1].x = toX;\r
10720   premoveHighlightInfo.sq[1].y = toY;\r
10721 }\r
10722 \r
10723 VOID\r
10724 ClearPremoveHighlights()\r
10725 {\r
10726   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10727     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10728 }\r
10729 \r
10730 VOID\r
10731 ShutDownFrontEnd()\r
10732 {\r
10733   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10734   DeleteClipboardTempFiles();\r
10735 }\r
10736 \r
10737 void\r
10738 BoardToTop()\r
10739 {\r
10740     if (IsIconic(hwndMain))\r
10741       ShowWindow(hwndMain, SW_RESTORE);\r
10742 \r
10743     SetActiveWindow(hwndMain);\r
10744 }\r
10745 \r
10746 /*\r
10747  * Prototypes for animation support routines\r
10748  */\r
10749 static void ScreenSquare(int column, int row, POINT * pt);\r
10750 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10751      POINT frames[], int * nFrames);\r
10752 \r
10753 \r
10754 void\r
10755 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10756 {       // [HGM] atomic: animate blast wave\r
10757         int i;\r
10758 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10759         explodeInfo.fromX = fromX;\r
10760         explodeInfo.fromY = fromY;\r
10761         explodeInfo.toX = toX;\r
10762         explodeInfo.toY = toY;\r
10763         for(i=1; i<nFrames; i++) {\r
10764             explodeInfo.radius = (i*180)/(nFrames-1);\r
10765             DrawPosition(FALSE, NULL);\r
10766             Sleep(appData.animSpeed);\r
10767         }\r
10768         explodeInfo.radius = 0;\r
10769         DrawPosition(TRUE, NULL);\r
10770 }\r
10771 \r
10772 #define kFactor 4\r
10773 \r
10774 void\r
10775 AnimateMove(board, fromX, fromY, toX, toY)\r
10776      Board board;\r
10777      int fromX;\r
10778      int fromY;\r
10779      int toX;\r
10780      int toY;\r
10781 {\r
10782   ChessSquare piece;\r
10783   POINT start, finish, mid;\r
10784   POINT frames[kFactor * 2 + 1];\r
10785   int nFrames, n;\r
10786 \r
10787   if (!appData.animate) return;\r
10788   if (doingSizing) return;\r
10789   if (fromY < 0 || fromX < 0) return;\r
10790   piece = board[fromY][fromX];\r
10791   if (piece >= EmptySquare) return;\r
10792 \r
10793   ScreenSquare(fromX, fromY, &start);\r
10794   ScreenSquare(toX, toY, &finish);\r
10795 \r
10796   /* All pieces except knights move in straight line */\r
10797   if (piece != WhiteKnight && piece != BlackKnight) {\r
10798     mid.x = start.x + (finish.x - start.x) / 2;\r
10799     mid.y = start.y + (finish.y - start.y) / 2;\r
10800   } else {\r
10801     /* Knight: make diagonal movement then straight */\r
10802     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10803        mid.x = start.x + (finish.x - start.x) / 2;\r
10804        mid.y = finish.y;\r
10805      } else {\r
10806        mid.x = finish.x;\r
10807        mid.y = start.y + (finish.y - start.y) / 2;\r
10808      }\r
10809   }\r
10810   \r
10811   /* Don't use as many frames for very short moves */\r
10812   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10813     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10814   else\r
10815     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10816 \r
10817   animInfo.from.x = fromX;\r
10818   animInfo.from.y = fromY;\r
10819   animInfo.to.x = toX;\r
10820   animInfo.to.y = toY;\r
10821   animInfo.lastpos = start;\r
10822   animInfo.piece = piece;\r
10823   for (n = 0; n < nFrames; n++) {\r
10824     animInfo.pos = frames[n];\r
10825     DrawPosition(FALSE, NULL);\r
10826     animInfo.lastpos = animInfo.pos;\r
10827     Sleep(appData.animSpeed);\r
10828   }\r
10829   animInfo.pos = finish;\r
10830   DrawPosition(FALSE, NULL);\r
10831   animInfo.piece = EmptySquare;\r
10832   if(gameInfo.variant == VariantAtomic && \r
10833      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10834         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10835 }\r
10836 \r
10837 /*      Convert board position to corner of screen rect and color       */\r
10838 \r
10839 static void\r
10840 ScreenSquare(column, row, pt)\r
10841      int column; int row; POINT * pt;\r
10842 {\r
10843   if (flipView) {\r
10844     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10845     pt->y = lineGap + row * (squareSize + lineGap);\r
10846   } else {\r
10847     pt->x = lineGap + column * (squareSize + lineGap);\r
10848     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10849   }\r
10850 }\r
10851 \r
10852 /*      Generate a series of frame coords from start->mid->finish.\r
10853         The movement rate doubles until the half way point is\r
10854         reached, then halves back down to the final destination,\r
10855         which gives a nice slow in/out effect. The algorithmn\r
10856         may seem to generate too many intermediates for short\r
10857         moves, but remember that the purpose is to attract the\r
10858         viewers attention to the piece about to be moved and\r
10859         then to where it ends up. Too few frames would be less\r
10860         noticeable.                                             */\r
10861 \r
10862 static void\r
10863 Tween(start, mid, finish, factor, frames, nFrames)\r
10864      POINT * start; POINT * mid;\r
10865      POINT * finish; int factor;\r
10866      POINT frames[]; int * nFrames;\r
10867 {\r
10868   int n, fraction = 1, count = 0;\r
10869 \r
10870   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10871   for (n = 0; n < factor; n++)\r
10872     fraction *= 2;\r
10873   for (n = 0; n < factor; n++) {\r
10874     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10875     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10876     count ++;\r
10877     fraction = fraction / 2;\r
10878   }\r
10879   \r
10880   /* Midpoint */\r
10881   frames[count] = *mid;\r
10882   count ++;\r
10883   \r
10884   /* Slow out, stepping 1/2, then 1/4, ... */\r
10885   fraction = 2;\r
10886   for (n = 0; n < factor; n++) {\r
10887     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10888     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10889     count ++;\r
10890     fraction = fraction * 2;\r
10891   }\r
10892   *nFrames = count;\r
10893 }\r
10894 \r
10895 void\r
10896 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10897 {\r
10898     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10899 \r
10900     EvalGraphSet( first, last, current, pvInfoList );\r
10901 }\r
10902 \r
10903 void SetProgramStats( FrontEndProgramStats * stats )\r
10904 {\r
10905     EngineOutputUpdate( stats );\r
10906 }\r