cleanup: removed "#if 0" from source
[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 #if 1\r
4707         wb = b.bmWidthBytes;\r
4708         // count colors\r
4709         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4710                 int k = ((int*) pData)[i];\r
4711                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4712                 if(j >= 16) break;\r
4713                 color[j] = k;\r
4714                 if(j >= nrColors) nrColors = j+1;\r
4715         }\r
4716         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4717                 INT p = 0;\r
4718                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4719                     for(w=0; w<(wb>>2); w+=2) {\r
4720                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4721                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4722                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4723                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4724                         pData[p++] = m | j<<4;\r
4725                     }\r
4726                     while(p&3) pData[p++] = 0;\r
4727                 }\r
4728                 fac = 3;\r
4729                 wb = ((wb+31)>>5)<<2;\r
4730         }\r
4731         // write BITMAPFILEHEADER\r
4732         fprintf(diagFile, "BM");\r
4733         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4734         fputDW(diagFile, 0);\r
4735         fputDW(diagFile, 0x36 + (fac?64:0));\r
4736         // write BITMAPINFOHEADER\r
4737         fputDW(diagFile, 40);\r
4738         fputDW(diagFile, b.bmWidth);\r
4739         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4740         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4741         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\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         fputDW(diagFile, 0);\r
4748         // write color table\r
4749         if(fac)\r
4750         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4751         // write bitmap data\r
4752         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4753                 fputc(pData[i], diagFile);\r
4754 #endif\r
4755      }\r
4756   }\r
4757 \r
4758   SelectObject(tmphdc, oldBitmap);\r
4759 \r
4760   /* Massive cleanup */\r
4761   for (x = 0; x < num_clips; x++)\r
4762     DeleteObject(clips[x]);\r
4763 \r
4764   DeleteDC(tmphdc);\r
4765   DeleteObject(bufferBitmap);\r
4766 \r
4767   if (releaseDC) \r
4768     ReleaseDC(hwndMain, hdc);\r
4769   \r
4770   if (lastDrawnFlipView != flipView) {\r
4771     if (flipView)\r
4772       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4773     else\r
4774       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4775   }\r
4776 \r
4777 /*  CopyBoard(lastDrawn, board);*/\r
4778   lastDrawnHighlight = highlightInfo;\r
4779   lastDrawnPremove   = premoveHighlightInfo;\r
4780   lastDrawnFlipView = flipView;\r
4781   lastDrawnValid = 1;\r
4782 }\r
4783 \r
4784 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4785 int\r
4786 SaveDiagram(f)\r
4787      FILE *f;\r
4788 {\r
4789     saveDiagFlag = 1; diagFile = f;\r
4790     HDCDrawPosition(NULL, TRUE, NULL);\r
4791 \r
4792     saveDiagFlag = 0;\r
4793 \r
4794 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4795     \r
4796     fclose(f);\r
4797     return TRUE;\r
4798 }\r
4799 \r
4800 \r
4801 /*---------------------------------------------------------------------------*\\r
4802 | CLIENT PAINT PROCEDURE\r
4803 |   This is the main event-handler for the WM_PAINT message.\r
4804 |\r
4805 \*---------------------------------------------------------------------------*/\r
4806 VOID\r
4807 PaintProc(HWND hwnd)\r
4808 {\r
4809   HDC         hdc;\r
4810   PAINTSTRUCT ps;\r
4811   HFONT       oldFont;\r
4812 \r
4813   if((hdc = BeginPaint(hwnd, &ps))) {\r
4814     if (IsIconic(hwnd)) {\r
4815       DrawIcon(hdc, 2, 2, iconCurrent);\r
4816     } else {\r
4817       if (!appData.monoMode) {\r
4818         SelectPalette(hdc, hPal, FALSE);\r
4819         RealizePalette(hdc);\r
4820       }\r
4821       HDCDrawPosition(hdc, 1, NULL);\r
4822       oldFont =\r
4823         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4824       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4825                  ETO_CLIPPED|ETO_OPAQUE,\r
4826                  &messageRect, messageText, strlen(messageText), NULL);\r
4827       SelectObject(hdc, oldFont);\r
4828       DisplayBothClocks();\r
4829     }\r
4830     EndPaint(hwnd,&ps);\r
4831   }\r
4832 \r
4833   return;\r
4834 }\r
4835 \r
4836 \r
4837 /*\r
4838  * If the user selects on a border boundary, return -1; if off the board,\r
4839  *   return -2.  Otherwise map the event coordinate to the square.\r
4840  * The offset boardRect.left or boardRect.top must already have been\r
4841  *   subtracted from x.\r
4842  */\r
4843 int\r
4844 EventToSquare(int x)\r
4845 {\r
4846   if (x <= 0)\r
4847     return -2;\r
4848   if (x < lineGap)\r
4849     return -1;\r
4850   x -= lineGap;\r
4851   if ((x % (squareSize + lineGap)) >= squareSize)\r
4852     return -1;\r
4853   x /= (squareSize + lineGap);\r
4854   if (x >= BOARD_SIZE)\r
4855     return -2;\r
4856   return x;\r
4857 }\r
4858 \r
4859 typedef struct {\r
4860   char piece;\r
4861   int command;\r
4862   char* name;\r
4863 } DropEnable;\r
4864 \r
4865 DropEnable dropEnables[] = {\r
4866   { 'P', DP_Pawn, "Pawn" },\r
4867   { 'N', DP_Knight, "Knight" },\r
4868   { 'B', DP_Bishop, "Bishop" },\r
4869   { 'R', DP_Rook, "Rook" },\r
4870   { 'Q', DP_Queen, "Queen" },\r
4871 };\r
4872 \r
4873 VOID\r
4874 SetupDropMenu(HMENU hmenu)\r
4875 {\r
4876   int i, count, enable;\r
4877   char *p;\r
4878   extern char white_holding[], black_holding[];\r
4879   char item[MSG_SIZ];\r
4880 \r
4881   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4882     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4883                dropEnables[i].piece);\r
4884     count = 0;\r
4885     while (p && *p++ == dropEnables[i].piece) count++;\r
4886     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4887     enable = count > 0 || !appData.testLegality\r
4888       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4889                       && !appData.icsActive);\r
4890     ModifyMenu(hmenu, dropEnables[i].command,\r
4891                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4892                dropEnables[i].command, item);\r
4893   }\r
4894 }\r
4895 \r
4896 /* Event handler for mouse messages */\r
4897 VOID\r
4898 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4899 {\r
4900   int x, y;\r
4901   POINT pt;\r
4902   static int recursive = 0;\r
4903   HMENU hmenu;\r
4904 //  BOOLEAN needsRedraw = FALSE;\r
4905   BOOLEAN saveAnimate;\r
4906   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4907   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4908   ChessMove moveType;\r
4909 \r
4910   if (recursive) {\r
4911     if (message == WM_MBUTTONUP) {\r
4912       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4913          to the middle button: we simulate pressing the left button too!\r
4914          */\r
4915       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4916       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4917     }\r
4918     return;\r
4919   }\r
4920   recursive++;\r
4921   \r
4922   pt.x = LOWORD(lParam);\r
4923   pt.y = HIWORD(lParam);\r
4924   x = EventToSquare(pt.x - boardRect.left);\r
4925   y = EventToSquare(pt.y - boardRect.top);\r
4926   if (!flipView && y >= 0) {\r
4927     y = BOARD_HEIGHT - 1 - y;\r
4928   }\r
4929   if (flipView && x >= 0) {\r
4930     x = BOARD_WIDTH - 1 - x;\r
4931   }\r
4932 \r
4933   switch (message) {\r
4934   case WM_LBUTTONDOWN:\r
4935     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4936         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4937         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4938         if(gameInfo.holdingsWidth && \r
4939                 (WhiteOnMove(currentMove) \r
4940                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4941                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4942             // click in right holdings, for determining promotion piece\r
4943             ChessSquare p = boards[currentMove][y][x];\r
4944             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4945             if(p != EmptySquare) {\r
4946                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4947                 fromX = fromY = -1;\r
4948                 break;\r
4949             }\r
4950         }\r
4951         DrawPosition(FALSE, boards[currentMove]);\r
4952         break;\r
4953     }\r
4954     ErrorPopDown();\r
4955     sameAgain = FALSE;\r
4956     if (y == -2) {\r
4957       /* Downclick vertically off board; check if on clock */\r
4958       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4959         if (gameMode == EditPosition) {\r
4960           SetWhiteToPlayEvent();\r
4961         } else if (gameMode == IcsPlayingBlack ||\r
4962                    gameMode == MachinePlaysWhite) {\r
4963           CallFlagEvent();\r
4964         } else if (gameMode == EditGame) {\r
4965           AdjustClock(flipClock, -1);\r
4966         }\r
4967       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4968         if (gameMode == EditPosition) {\r
4969           SetBlackToPlayEvent();\r
4970         } else if (gameMode == IcsPlayingWhite ||\r
4971                    gameMode == MachinePlaysBlack) {\r
4972           CallFlagEvent();\r
4973         } else if (gameMode == EditGame) {\r
4974           AdjustClock(!flipClock, -1);\r
4975         }\r
4976       }\r
4977       if (!appData.highlightLastMove) {\r
4978         ClearHighlights();\r
4979         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4980       }\r
4981       fromX = fromY = -1;\r
4982       dragInfo.start.x = dragInfo.start.y = -1;\r
4983       dragInfo.from = dragInfo.start;\r
4984       break;\r
4985     } else if (x < 0 || y < 0\r
4986       /* [HGM] block clicks between board and holdings */\r
4987               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4988               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4989               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4990         /* EditPosition, empty square, or different color piece;\r
4991            click-click move is possible */\r
4992                                ) {\r
4993       break;\r
4994     } else if (fromX == x && fromY == y) {\r
4995       /* Downclick on same square again */\r
4996       ClearHighlights();\r
4997       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4998       sameAgain = TRUE;  \r
4999     } else if (fromX != -1 &&\r
5000                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5001                                                                         ) {\r
5002       /* Downclick on different square. */\r
5003       /* [HGM] if on holdings file, should count as new first click ! */\r
5004       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5005         toX = x;\r
5006         toY = y;\r
5007         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5008            to make sure move is legal before showing promotion popup */\r
5009         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE);\r
5010         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5011                 fromX = fromY = -1; \r
5012                 ClearHighlights();\r
5013                 DrawPosition(FALSE, boards[currentMove]);\r
5014                 break; \r
5015         } else \r
5016         if(moveType != ImpossibleMove && moveType != Comment) {\r
5017           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5018           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5019             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5020               appData.alwaysPromoteToQueen)) {\r
5021                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5022                   if (!appData.highlightLastMove) {\r
5023                       ClearHighlights();\r
5024                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5025                   }\r
5026           } else\r
5027           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5028                   SetHighlights(fromX, fromY, toX, toY);\r
5029                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5030                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5031                      If promotion to Q is legal, all are legal! */\r
5032                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5033                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5034                     // kludge to temporarily execute move on display, without promoting yet\r
5035                     promotionChoice = TRUE;\r
5036                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5037                     boards[currentMove][toY][toX] = p;\r
5038                     DrawPosition(FALSE, boards[currentMove]);\r
5039                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5040                     boards[currentMove][toY][toX] = q;\r
5041                     DisplayMessage("Select piece from holdings", "");\r
5042                   } else\r
5043                   PromotionPopup(hwnd);\r
5044                   goto noClear;\r
5045           } else { // not a promotion. Move can be illegal if testLegality off, and should be made then.\r
5046              if (appData.animate || appData.highlightLastMove) {\r
5047                  SetHighlights(fromX, fromY, toX, toY);\r
5048              } else {\r
5049                  ClearHighlights();\r
5050              }\r
5051              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5052              if (appData.animate && !appData.highlightLastMove) {\r
5053                   ClearHighlights();\r
5054                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5055              }\r
5056           }\r
5057           fromX = fromY = -1;\r
5058         noClear:\r
5059           break;\r
5060         }\r
5061         if (gotPremove && moveType != Comment) {\r
5062             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5063 //            DrawPosition(forceFullRepaint || FALSE, NULL);\r
5064         } else ClearHighlights();\r
5065         fromX = fromY = -1;\r
5066         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5067         if(moveType != Comment) break;\r
5068     }\r
5069     /* First downclick, or restart on a square with same color piece */\r
5070     if (!frozen && OKToStartUserMove(x, y)) {\r
5071       fromX = x;\r
5072       fromY = y;\r
5073       dragInfo.lastpos = pt;\r
5074       dragInfo.from.x = fromX;\r
5075       dragInfo.from.y = fromY;\r
5076       dragInfo.start = dragInfo.from;\r
5077       SetCapture(hwndMain);\r
5078     } else {\r
5079       fromX = fromY = -1;\r
5080       dragInfo.start.x = dragInfo.start.y = -1;\r
5081       dragInfo.from = dragInfo.start;\r
5082       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5083     }\r
5084     break;\r
5085 \r
5086   case WM_LBUTTONUP:\r
5087     ReleaseCapture();\r
5088     if (fromX == -1) break;\r
5089     if (x == fromX && y == fromY) {\r
5090       dragInfo.from.x = dragInfo.from.y = -1;\r
5091       /* Upclick on same square */\r
5092       if (sameAgain) {\r
5093         /* Clicked same square twice: abort click-click move */\r
5094         fromX = fromY = -1;\r
5095         gotPremove = 0;\r
5096         ClearPremoveHighlights();\r
5097       } else {\r
5098         /* First square clicked: start click-click move */\r
5099         SetHighlights(fromX, fromY, -1, -1);\r
5100       }\r
5101       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5102     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5103       /* Errant click; ignore */\r
5104       break;\r
5105     } else {\r
5106       /* Finish drag move. */\r
5107     if (appData.debugMode) {\r
5108         fprintf(debugFP, "release\n");\r
5109     }\r
5110       dragInfo.from.x = dragInfo.from.y = -1;\r
5111       toX = x;\r
5112       toY = y;\r
5113       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5114       appData.animate = appData.animate && !appData.animateDragging;\r
5115       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, TRUE);\r
5116       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5117                 fromX = fromY = -1; \r
5118                 ClearHighlights();\r
5119                 DrawPosition(FALSE, boards[currentMove]);\r
5120                 appData.animate = saveAnimate;\r
5121                 break; \r
5122       } else \r
5123       if(moveType != ImpossibleMove) {\r
5124           /* [HGM] use move type to determine if move is promotion.\r
5125              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5126           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5127             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5128               appData.alwaysPromoteToQueen)) \r
5129                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5130           else \r
5131           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5132                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5133                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5134                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5135                     // kludge to temporarily execute move on display, wthout promotng yet\r
5136                     promotionChoice = TRUE;\r
5137                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5138                     boards[currentMove][toY][toX] = p;\r
5139                     DrawPosition(FALSE, boards[currentMove]);\r
5140                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5141                     boards[currentMove][toY][toX] = q;\r
5142                     appData.animate = saveAnimate;\r
5143                     DisplayMessage("Select piece from holdings", "");\r
5144                     break;\r
5145                   } else\r
5146                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5147           } else {\r
5148             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5149                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5150                                         moveType == WhiteCapturesEnPassant || \r
5151                                         moveType == BlackCapturesEnPassant   ) )\r
5152                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5153             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5154           }\r
5155       }\r
5156       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5157       appData.animate = saveAnimate;\r
5158       fromX = fromY = -1;\r
5159       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5160         ClearHighlights();\r
5161       }\r
5162       if (appData.animate || appData.animateDragging ||\r
5163           appData.highlightDragging || gotPremove) {\r
5164         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5165       }\r
5166     }\r
5167     dragInfo.start.x = dragInfo.start.y = -1; \r
5168     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5169     break;\r
5170 \r
5171   case WM_MOUSEMOVE:\r
5172     if ((appData.animateDragging || appData.highlightDragging)\r
5173         && (wParam & MK_LBUTTON)\r
5174         && dragInfo.from.x >= 0) \r
5175     {\r
5176       BOOL full_repaint = FALSE;\r
5177 \r
5178       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5179       if (appData.animateDragging) {\r
5180         dragInfo.pos = pt;\r
5181       }\r
5182       if (appData.highlightDragging) {\r
5183         SetHighlights(fromX, fromY, x, y);\r
5184         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5185             full_repaint = TRUE;\r
5186         }\r
5187       }\r
5188       \r
5189       DrawPosition( full_repaint, NULL);\r
5190       \r
5191       dragInfo.lastpos = dragInfo.pos;\r
5192     }\r
5193     break;\r
5194 \r
5195   case WM_MOUSEWHEEL: // [DM]\r
5196     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5197        /* Mouse Wheel is being rolled forward\r
5198         * Play moves forward\r
5199         */\r
5200        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5201                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5202        /* Mouse Wheel is being rolled backward\r
5203         * Play moves backward\r
5204         */\r
5205        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5206                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5207     }\r
5208     break;\r
5209 \r
5210   case WM_MBUTTONDOWN:\r
5211   case WM_RBUTTONDOWN:\r
5212     ErrorPopDown();\r
5213     ReleaseCapture();\r
5214     fromX = fromY = -1;\r
5215     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5216     dragInfo.start.x = dragInfo.start.y = -1;\r
5217     dragInfo.from = dragInfo.start;\r
5218     dragInfo.lastpos = dragInfo.pos;\r
5219     if (appData.highlightDragging) {\r
5220       ClearHighlights();\r
5221     }\r
5222     if(y == -2) {\r
5223       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5224       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5225           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5226       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5227           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5228       }\r
5229     }\r
5230     DrawPosition(TRUE, NULL);\r
5231 \r
5232     switch (gameMode) {\r
5233     case EditPosition:\r
5234     case IcsExamining:\r
5235       if (x < 0 || y < 0) break;\r
5236       fromX = x;\r
5237       fromY = y;\r
5238       if (message == WM_MBUTTONDOWN) {\r
5239         buttonCount = 3;  /* even if system didn't think so */\r
5240         if (wParam & MK_SHIFT) \r
5241           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5242         else\r
5243           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5244       } else { /* message == WM_RBUTTONDOWN */\r
5245         /* Just have one menu, on the right button.  Windows users don't\r
5246            think to try the middle one, and sometimes other software steals\r
5247            it, or it doesn't really exist. */\r
5248         if(gameInfo.variant != VariantShogi)\r
5249             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5250         else\r
5251             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5252       }\r
5253       break;\r
5254     case IcsPlayingWhite:\r
5255     case IcsPlayingBlack:\r
5256     case EditGame:\r
5257     case MachinePlaysWhite:\r
5258     case MachinePlaysBlack:\r
5259       if (appData.testLegality &&\r
5260           gameInfo.variant != VariantBughouse &&\r
5261           gameInfo.variant != VariantCrazyhouse) break;\r
5262       if (x < 0 || y < 0) break;\r
5263       fromX = x;\r
5264       fromY = y;\r
5265       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5266       SetupDropMenu(hmenu);\r
5267       MenuPopup(hwnd, pt, hmenu, -1);\r
5268       break;\r
5269     default:\r
5270       break;\r
5271     }\r
5272     break;\r
5273   }\r
5274 \r
5275   recursive--;\r
5276 }\r
5277 \r
5278 /* Preprocess messages for buttons in main window */\r
5279 LRESULT CALLBACK\r
5280 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5281 {\r
5282   int id = GetWindowLong(hwnd, GWL_ID);\r
5283   int i, dir;\r
5284 \r
5285   for (i=0; i<N_BUTTONS; i++) {\r
5286     if (buttonDesc[i].id == id) break;\r
5287   }\r
5288   if (i == N_BUTTONS) return 0;\r
5289   switch (message) {\r
5290   case WM_KEYDOWN:\r
5291     switch (wParam) {\r
5292     case VK_LEFT:\r
5293     case VK_RIGHT:\r
5294       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5295       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5296       return TRUE;\r
5297     }\r
5298     break;\r
5299   case WM_CHAR:\r
5300     switch (wParam) {\r
5301     case '\r':\r
5302       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5303       return TRUE;\r
5304     default:\r
5305       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5306         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5307         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5308         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5309         SetFocus(h);\r
5310         SendMessage(h, WM_CHAR, wParam, lParam);\r
5311         return TRUE;\r
5312       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5313         PopUpMoveDialog((char)wParam);\r
5314       }\r
5315       break;\r
5316     }\r
5317     break;\r
5318   }\r
5319   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5320 }\r
5321 \r
5322 /* Process messages for Promotion dialog box */\r
5323 LRESULT CALLBACK\r
5324 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5325 {\r
5326   char promoChar;\r
5327 \r
5328   switch (message) {\r
5329   case WM_INITDIALOG: /* message: initialize dialog box */\r
5330     /* Center the dialog over the application window */\r
5331     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5332     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5333       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5334        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5335                SW_SHOW : SW_HIDE);\r
5336     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5337     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5338        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5339          PieceToChar(WhiteAngel) != '~') ||\r
5340         (PieceToChar(BlackAngel) >= 'A' &&\r
5341          PieceToChar(BlackAngel) != '~')   ) ?\r
5342                SW_SHOW : SW_HIDE);\r
5343     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5344        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5345          PieceToChar(WhiteMarshall) != '~') ||\r
5346         (PieceToChar(BlackMarshall) >= 'A' &&\r
5347          PieceToChar(BlackMarshall) != '~')   ) ?\r
5348                SW_SHOW : SW_HIDE);\r
5349     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5350     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5351        gameInfo.variant != VariantShogi ?\r
5352                SW_SHOW : SW_HIDE);\r
5353     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5354        gameInfo.variant != VariantShogi ?\r
5355                SW_SHOW : SW_HIDE);\r
5356     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5357        gameInfo.variant == VariantShogi ?\r
5358                SW_SHOW : SW_HIDE);\r
5359     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5360        gameInfo.variant == VariantShogi ?\r
5361                SW_SHOW : SW_HIDE);\r
5362     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5363        gameInfo.variant == VariantSuper ?\r
5364                SW_SHOW : SW_HIDE);\r
5365     return TRUE;\r
5366 \r
5367   case WM_COMMAND: /* message: received a command */\r
5368     switch (LOWORD(wParam)) {\r
5369     case IDCANCEL:\r
5370       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5371       ClearHighlights();\r
5372       DrawPosition(FALSE, NULL);\r
5373       return TRUE;\r
5374     case PB_King:\r
5375       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5376       break;\r
5377     case PB_Queen:\r
5378       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5379       break;\r
5380     case PB_Rook:\r
5381       promoChar = PieceToChar(BlackRook);\r
5382       break;\r
5383     case PB_Bishop:\r
5384       promoChar = PieceToChar(BlackBishop);\r
5385       break;\r
5386     case PB_Chancellor:\r
5387       promoChar = PieceToChar(BlackMarshall);\r
5388       break;\r
5389     case PB_Archbishop:\r
5390       promoChar = PieceToChar(BlackAngel);\r
5391       break;\r
5392     case PB_Knight:\r
5393       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5394       break;\r
5395     default:\r
5396       return FALSE;\r
5397     }\r
5398     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5399     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5400        only show the popup when we are already sure the move is valid or\r
5401        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5402        will figure out it is a promotion from the promoChar. */\r
5403     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5404     if (!appData.highlightLastMove) {\r
5405       ClearHighlights();\r
5406       DrawPosition(FALSE, NULL);\r
5407     }\r
5408     return TRUE;\r
5409   }\r
5410   return FALSE;\r
5411 }\r
5412 \r
5413 /* Pop up promotion dialog */\r
5414 VOID\r
5415 PromotionPopup(HWND hwnd)\r
5416 {\r
5417   FARPROC lpProc;\r
5418 \r
5419   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5420   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5421     hwnd, (DLGPROC)lpProc);\r
5422   FreeProcInstance(lpProc);\r
5423 }\r
5424 \r
5425 /* Toggle ShowThinking */\r
5426 VOID\r
5427 ToggleShowThinking()\r
5428 {\r
5429   appData.showThinking = !appData.showThinking;\r
5430   ShowThinkingEvent();\r
5431 }\r
5432 \r
5433 VOID\r
5434 LoadGameDialog(HWND hwnd, char* title)\r
5435 {\r
5436   UINT number = 0;\r
5437   FILE *f;\r
5438   char fileTitle[MSG_SIZ];\r
5439   f = OpenFileDialog(hwnd, "rb", "",\r
5440                      appData.oldSaveStyle ? "gam" : "pgn",\r
5441                      GAME_FILT,\r
5442                      title, &number, fileTitle, NULL);\r
5443   if (f != NULL) {\r
5444     cmailMsgLoaded = FALSE;\r
5445     if (number == 0) {\r
5446       int error = GameListBuild(f);\r
5447       if (error) {\r
5448         DisplayError("Cannot build game list", error);\r
5449       } else if (!ListEmpty(&gameList) &&\r
5450                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5451         GameListPopUp(f, fileTitle);\r
5452         return;\r
5453       }\r
5454       GameListDestroy();\r
5455       number = 1;\r
5456     }\r
5457     LoadGame(f, number, fileTitle, FALSE);\r
5458   }\r
5459 }\r
5460 \r
5461 VOID\r
5462 ChangedConsoleFont()\r
5463 {\r
5464   CHARFORMAT cfmt;\r
5465   CHARRANGE tmpsel, sel;\r
5466   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5467   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5468   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5469   PARAFORMAT paraf;\r
5470 \r
5471   cfmt.cbSize = sizeof(CHARFORMAT);\r
5472   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5473   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5474   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5475    * size.  This was undocumented in the version of MSVC++ that I had\r
5476    * when I wrote the code, but is apparently documented now.\r
5477    */\r
5478   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5479   cfmt.bCharSet = f->lf.lfCharSet;\r
5480   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5481   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5482   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5483   /* Why are the following seemingly needed too? */\r
5484   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5485   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5486   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5487   tmpsel.cpMin = 0;\r
5488   tmpsel.cpMax = -1; /*999999?*/\r
5489   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5490   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5491   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5492    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5493    */\r
5494   paraf.cbSize = sizeof(paraf);\r
5495   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5496   paraf.dxStartIndent = 0;\r
5497   paraf.dxOffset = WRAP_INDENT;\r
5498   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5499   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5500 }\r
5501 \r
5502 /*---------------------------------------------------------------------------*\\r
5503  *\r
5504  * Window Proc for main window\r
5505  *\r
5506 \*---------------------------------------------------------------------------*/\r
5507 \r
5508 /* Process messages for main window, etc. */\r
5509 LRESULT CALLBACK\r
5510 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5511 {\r
5512   FARPROC lpProc;\r
5513   int wmId, wmEvent;\r
5514   char *defName;\r
5515   FILE *f;\r
5516   UINT number;\r
5517   char fileTitle[MSG_SIZ];\r
5518   char buf[MSG_SIZ];\r
5519   static SnapData sd;\r
5520 \r
5521   switch (message) {\r
5522 \r
5523   case WM_PAINT: /* message: repaint portion of window */\r
5524     PaintProc(hwnd);\r
5525     break;\r
5526 \r
5527   case WM_ERASEBKGND:\r
5528     if (IsIconic(hwnd)) {\r
5529       /* Cheat; change the message */\r
5530       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5531     } else {\r
5532       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5533     }\r
5534     break;\r
5535 \r
5536   case WM_LBUTTONDOWN:\r
5537   case WM_MBUTTONDOWN:\r
5538   case WM_RBUTTONDOWN:\r
5539   case WM_LBUTTONUP:\r
5540   case WM_MBUTTONUP:\r
5541   case WM_RBUTTONUP:\r
5542   case WM_MOUSEMOVE:\r
5543   case WM_MOUSEWHEEL:\r
5544     MouseEvent(hwnd, message, wParam, lParam);\r
5545     break;\r
5546 \r
5547   JAWS_KB_NAVIGATION\r
5548 \r
5549   case WM_CHAR:\r
5550     \r
5551     JAWS_ALT_INTERCEPT\r
5552 \r
5553     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5554         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5555         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5556         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5557         SetFocus(h);\r
5558         SendMessage(h, message, wParam, lParam);\r
5559     } else if(lParam != KF_REPEAT) {\r
5560         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5561                 PopUpMoveDialog((char)wParam);\r
5562         } else if((char)wParam == 003) CopyGameToClipboard();\r
5563          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5564     }\r
5565 \r
5566     break;\r
5567 \r
5568   case WM_PALETTECHANGED:\r
5569     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5570       int nnew;\r
5571       HDC hdc = GetDC(hwndMain);\r
5572       SelectPalette(hdc, hPal, TRUE);\r
5573       nnew = RealizePalette(hdc);\r
5574       if (nnew > 0) {\r
5575         paletteChanged = TRUE;\r
5576         InvalidateRect(hwnd, &boardRect, FALSE);\r
5577       }\r
5578       ReleaseDC(hwnd, hdc);\r
5579     }\r
5580     break;\r
5581 \r
5582   case WM_QUERYNEWPALETTE:\r
5583     if (!appData.monoMode /*&& paletteChanged*/) {\r
5584       int nnew;\r
5585       HDC hdc = GetDC(hwndMain);\r
5586       paletteChanged = FALSE;\r
5587       SelectPalette(hdc, hPal, FALSE);\r
5588       nnew = RealizePalette(hdc);\r
5589       if (nnew > 0) {\r
5590         InvalidateRect(hwnd, &boardRect, FALSE);\r
5591       }\r
5592       ReleaseDC(hwnd, hdc);\r
5593       return TRUE;\r
5594     }\r
5595     return FALSE;\r
5596 \r
5597   case WM_COMMAND: /* message: command from application menu */\r
5598     wmId    = LOWORD(wParam);\r
5599     wmEvent = HIWORD(wParam);\r
5600 \r
5601     switch (wmId) {\r
5602     case IDM_NewGame:\r
5603       ResetGameEvent();\r
5604       AnalysisPopDown();\r
5605       SAY("new game enter a move to play against the computer with white");\r
5606       break;\r
5607 \r
5608     case IDM_NewGameFRC:\r
5609       if( NewGameFRC() == 0 ) {\r
5610         ResetGameEvent();\r
5611         AnalysisPopDown();\r
5612       }\r
5613       break;\r
5614 \r
5615     case IDM_NewVariant:\r
5616       NewVariantPopup(hwnd);\r
5617       break;\r
5618 \r
5619     case IDM_LoadGame:\r
5620       LoadGameDialog(hwnd, "Load Game from File");\r
5621       break;\r
5622 \r
5623     case IDM_LoadNextGame:\r
5624       ReloadGame(1);\r
5625       break;\r
5626 \r
5627     case IDM_LoadPrevGame:\r
5628       ReloadGame(-1);\r
5629       break;\r
5630 \r
5631     case IDM_ReloadGame:\r
5632       ReloadGame(0);\r
5633       break;\r
5634 \r
5635     case IDM_LoadPosition:\r
5636       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5637         Reset(FALSE, TRUE);\r
5638       }\r
5639       number = 1;\r
5640       f = OpenFileDialog(hwnd, "rb", "",\r
5641                          appData.oldSaveStyle ? "pos" : "fen",\r
5642                          POSITION_FILT,\r
5643                          "Load Position from File", &number, fileTitle, NULL);\r
5644       if (f != NULL) {\r
5645         LoadPosition(f, number, fileTitle);\r
5646       }\r
5647       break;\r
5648 \r
5649     case IDM_LoadNextPosition:\r
5650       ReloadPosition(1);\r
5651       break;\r
5652 \r
5653     case IDM_LoadPrevPosition:\r
5654       ReloadPosition(-1);\r
5655       break;\r
5656 \r
5657     case IDM_ReloadPosition:\r
5658       ReloadPosition(0);\r
5659       break;\r
5660 \r
5661     case IDM_SaveGame:\r
5662       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5663       f = OpenFileDialog(hwnd, "a", defName,\r
5664                          appData.oldSaveStyle ? "gam" : "pgn",\r
5665                          GAME_FILT,\r
5666                          "Save Game to File", NULL, fileTitle, NULL);\r
5667       if (f != NULL) {\r
5668         SaveGame(f, 0, "");\r
5669       }\r
5670       break;\r
5671 \r
5672     case IDM_SavePosition:\r
5673       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5674       f = OpenFileDialog(hwnd, "a", defName,\r
5675                          appData.oldSaveStyle ? "pos" : "fen",\r
5676                          POSITION_FILT,\r
5677                          "Save Position to File", NULL, fileTitle, NULL);\r
5678       if (f != NULL) {\r
5679         SavePosition(f, 0, "");\r
5680       }\r
5681       break;\r
5682 \r
5683     case IDM_SaveDiagram:\r
5684       defName = "diagram";\r
5685       f = OpenFileDialog(hwnd, "wb", defName,\r
5686                          "bmp",\r
5687                          DIAGRAM_FILT,\r
5688                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5689       if (f != NULL) {\r
5690         SaveDiagram(f);\r
5691       }\r
5692       break;\r
5693 \r
5694     case IDM_CopyGame:\r
5695       CopyGameToClipboard();\r
5696       break;\r
5697 \r
5698     case IDM_PasteGame:\r
5699       PasteGameFromClipboard();\r
5700       break;\r
5701 \r
5702     case IDM_CopyGameListToClipboard:\r
5703       CopyGameListToClipboard();\r
5704       break;\r
5705 \r
5706     /* [AS] Autodetect FEN or PGN data */\r
5707     case IDM_PasteAny:\r
5708       PasteGameOrFENFromClipboard();\r
5709       break;\r
5710 \r
5711     /* [AS] Move history */\r
5712     case IDM_ShowMoveHistory:\r
5713         if( MoveHistoryIsUp() ) {\r
5714             MoveHistoryPopDown();\r
5715         }\r
5716         else {\r
5717             MoveHistoryPopUp();\r
5718         }\r
5719         break;\r
5720 \r
5721     /* [AS] Eval graph */\r
5722     case IDM_ShowEvalGraph:\r
5723         if( EvalGraphIsUp() ) {\r
5724             EvalGraphPopDown();\r
5725         }\r
5726         else {\r
5727             EvalGraphPopUp();\r
5728             SetFocus(hwndMain);\r
5729         }\r
5730         break;\r
5731 \r
5732     /* [AS] Engine output */\r
5733     case IDM_ShowEngineOutput:\r
5734         if( EngineOutputIsUp() ) {\r
5735             EngineOutputPopDown();\r
5736         }\r
5737         else {\r
5738             EngineOutputPopUp();\r
5739         }\r
5740         break;\r
5741 \r
5742     /* [AS] User adjudication */\r
5743     case IDM_UserAdjudication_White:\r
5744         UserAdjudicationEvent( +1 );\r
5745         break;\r
5746 \r
5747     case IDM_UserAdjudication_Black:\r
5748         UserAdjudicationEvent( -1 );\r
5749         break;\r
5750 \r
5751     case IDM_UserAdjudication_Draw:\r
5752         UserAdjudicationEvent( 0 );\r
5753         break;\r
5754 \r
5755     /* [AS] Game list options dialog */\r
5756     case IDM_GameListOptions:\r
5757       GameListOptions();\r
5758       break;\r
5759 \r
5760     case IDM_NewChat:\r
5761       ChatPopUp();\r
5762       break;\r
5763 \r
5764     case IDM_CopyPosition:\r
5765       CopyFENToClipboard();\r
5766       break;\r
5767 \r
5768     case IDM_PastePosition:\r
5769       PasteFENFromClipboard();\r
5770       break;\r
5771 \r
5772     case IDM_MailMove:\r
5773       MailMoveEvent();\r
5774       break;\r
5775 \r
5776     case IDM_ReloadCMailMsg:\r
5777       Reset(TRUE, TRUE);\r
5778       ReloadCmailMsgEvent(FALSE);\r
5779       break;\r
5780 \r
5781     case IDM_Minimize:\r
5782       ShowWindow(hwnd, SW_MINIMIZE);\r
5783       break;\r
5784 \r
5785     case IDM_Exit:\r
5786       ExitEvent(0);\r
5787       break;\r
5788 \r
5789     case IDM_MachineWhite:\r
5790       MachineWhiteEvent();\r
5791       /*\r
5792        * refresh the tags dialog only if it's visible\r
5793        */\r
5794       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5795           char *tags;\r
5796           tags = PGNTags(&gameInfo);\r
5797           TagsPopUp(tags, CmailMsg());\r
5798           free(tags);\r
5799       }\r
5800       SAY("computer starts playing white");\r
5801       break;\r
5802 \r
5803     case IDM_MachineBlack:\r
5804       MachineBlackEvent();\r
5805       /*\r
5806        * refresh the tags dialog only if it's visible\r
5807        */\r
5808       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5809           char *tags;\r
5810           tags = PGNTags(&gameInfo);\r
5811           TagsPopUp(tags, CmailMsg());\r
5812           free(tags);\r
5813       }\r
5814       SAY("computer starts playing black");\r
5815       break;\r
5816 \r
5817     case IDM_TwoMachines:\r
5818       TwoMachinesEvent();\r
5819       /*\r
5820        * refresh the tags dialog only if it's visible\r
5821        */\r
5822       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5823           char *tags;\r
5824           tags = PGNTags(&gameInfo);\r
5825           TagsPopUp(tags, CmailMsg());\r
5826           free(tags);\r
5827       }\r
5828       SAY("programs start playing each other");\r
5829       break;\r
5830 \r
5831     case IDM_AnalysisMode:\r
5832       if (!first.analysisSupport) {\r
5833         sprintf(buf, "%s does not support analysis", first.tidy);\r
5834         DisplayError(buf, 0);\r
5835       } else {\r
5836         SAY("analyzing current position");\r
5837         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5838         if (appData.icsActive) {\r
5839                if (gameMode != IcsObserving) {\r
5840                        sprintf(buf, "You are not observing a game");\r
5841                        DisplayError(buf, 0);\r
5842                        /* secure check */\r
5843                        if (appData.icsEngineAnalyze) {\r
5844                                if (appData.debugMode) \r
5845                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5846                                ExitAnalyzeMode();\r
5847                                ModeHighlight();\r
5848                                break;\r
5849                        }\r
5850                        break;\r
5851                } else {\r
5852                        /* if enable, user want disable icsEngineAnalyze */\r
5853                        if (appData.icsEngineAnalyze) {\r
5854                                ExitAnalyzeMode();\r
5855                                ModeHighlight();\r
5856                                break;\r
5857                        }\r
5858                        appData.icsEngineAnalyze = TRUE;\r
5859                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5860                }\r
5861         } \r
5862         if (!appData.showThinking) ToggleShowThinking();\r
5863         AnalyzeModeEvent();\r
5864       }\r
5865       break;\r
5866 \r
5867     case IDM_AnalyzeFile:\r
5868       if (!first.analysisSupport) {\r
5869         char buf[MSG_SIZ];\r
5870         sprintf(buf, "%s does not support analysis", first.tidy);\r
5871         DisplayError(buf, 0);\r
5872       } else {\r
5873         if (!appData.showThinking) ToggleShowThinking();\r
5874         AnalyzeFileEvent();\r
5875         LoadGameDialog(hwnd, "Analyze Game from File");\r
5876         AnalysisPeriodicEvent(1);\r
5877       }\r
5878       break;\r
5879 \r
5880     case IDM_IcsClient:\r
5881       IcsClientEvent();\r
5882       break;\r
5883 \r
5884     case IDM_EditGame:\r
5885       EditGameEvent();\r
5886       SAY("edit game");\r
5887       break;\r
5888 \r
5889     case IDM_EditPosition:\r
5890       EditPositionEvent();\r
5891       SAY("to set up a position type a FEN");\r
5892       break;\r
5893 \r
5894     case IDM_Training:\r
5895       TrainingEvent();\r
5896       break;\r
5897 \r
5898     case IDM_ShowGameList:\r
5899       ShowGameListProc();\r
5900       break;\r
5901 \r
5902     case IDM_EditTags:\r
5903       EditTagsProc();\r
5904       break;\r
5905 \r
5906     case IDM_EditComment:\r
5907       if (commentDialogUp && editComment) {\r
5908         CommentPopDown();\r
5909       } else {\r
5910         EditCommentEvent();\r
5911       }\r
5912       break;\r
5913 \r
5914     case IDM_Pause:\r
5915       PauseEvent();\r
5916       break;\r
5917 \r
5918     case IDM_Accept:\r
5919       AcceptEvent();\r
5920       break;\r
5921 \r
5922     case IDM_Decline:\r
5923       DeclineEvent();\r
5924       break;\r
5925 \r
5926     case IDM_Rematch:\r
5927       RematchEvent();\r
5928       break;\r
5929 \r
5930     case IDM_CallFlag:\r
5931       CallFlagEvent();\r
5932       break;\r
5933 \r
5934     case IDM_Draw:\r
5935       DrawEvent();\r
5936       break;\r
5937 \r
5938     case IDM_Adjourn:\r
5939       AdjournEvent();\r
5940       break;\r
5941 \r
5942     case IDM_Abort:\r
5943       AbortEvent();\r
5944       break;\r
5945 \r
5946     case IDM_Resign:\r
5947       ResignEvent();\r
5948       break;\r
5949 \r
5950     case IDM_StopObserving:\r
5951       StopObservingEvent();\r
5952       break;\r
5953 \r
5954     case IDM_StopExamining:\r
5955       StopExaminingEvent();\r
5956       break;\r
5957 \r
5958     case IDM_TypeInMove:\r
5959       PopUpMoveDialog('\000');\r
5960       break;\r
5961 \r
5962     case IDM_TypeInName:\r
5963       PopUpNameDialog('\000');\r
5964       break;\r
5965 \r
5966     case IDM_Backward:\r
5967       BackwardEvent();\r
5968       SetFocus(hwndMain);\r
5969       break;\r
5970 \r
5971     JAWS_MENU_ITEMS\r
5972 \r
5973     case IDM_Forward:\r
5974       ForwardEvent();\r
5975       SetFocus(hwndMain);\r
5976       break;\r
5977 \r
5978     case IDM_ToStart:\r
5979       ToStartEvent();\r
5980       SetFocus(hwndMain);\r
5981       break;\r
5982 \r
5983     case IDM_ToEnd:\r
5984       ToEndEvent();\r
5985       SetFocus(hwndMain);\r
5986       break;\r
5987 \r
5988     case IDM_Revert:\r
5989       RevertEvent();\r
5990       break;\r
5991 \r
5992     case IDM_TruncateGame:\r
5993       TruncateGameEvent();\r
5994       break;\r
5995 \r
5996     case IDM_MoveNow:\r
5997       MoveNowEvent();\r
5998       break;\r
5999 \r
6000     case IDM_RetractMove:\r
6001       RetractMoveEvent();\r
6002       break;\r
6003 \r
6004     case IDM_FlipView:\r
6005       flipView = !flipView;\r
6006       DrawPosition(FALSE, NULL);\r
6007       break;\r
6008 \r
6009     case IDM_FlipClock:\r
6010       flipClock = !flipClock;\r
6011       DisplayBothClocks();\r
6012       DrawPosition(FALSE, NULL);\r
6013       break;\r
6014 \r
6015     case IDM_MuteSounds:\r
6016       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6017       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6018                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6019       break;\r
6020 \r
6021     case IDM_GeneralOptions:\r
6022       GeneralOptionsPopup(hwnd);\r
6023       DrawPosition(TRUE, NULL);\r
6024       break;\r
6025 \r
6026     case IDM_BoardOptions:\r
6027       BoardOptionsPopup(hwnd);\r
6028       break;\r
6029 \r
6030     case IDM_EnginePlayOptions:\r
6031       EnginePlayOptionsPopup(hwnd);\r
6032       break;\r
6033 \r
6034     case IDM_Engine1Options:\r
6035       EngineOptionsPopup(hwnd, &first);\r
6036       break;\r
6037 \r
6038     case IDM_Engine2Options:\r
6039       EngineOptionsPopup(hwnd, &second);\r
6040       break;\r
6041 \r
6042     case IDM_OptionsUCI:\r
6043       UciOptionsPopup(hwnd);\r
6044       break;\r
6045 \r
6046     case IDM_IcsOptions:\r
6047       IcsOptionsPopup(hwnd);\r
6048       break;\r
6049 \r
6050     case IDM_Fonts:\r
6051       FontsOptionsPopup(hwnd);\r
6052       break;\r
6053 \r
6054     case IDM_Sounds:\r
6055       SoundOptionsPopup(hwnd);\r
6056       break;\r
6057 \r
6058     case IDM_CommPort:\r
6059       CommPortOptionsPopup(hwnd);\r
6060       break;\r
6061 \r
6062     case IDM_LoadOptions:\r
6063       LoadOptionsPopup(hwnd);\r
6064       break;\r
6065 \r
6066     case IDM_SaveOptions:\r
6067       SaveOptionsPopup(hwnd);\r
6068       break;\r
6069 \r
6070     case IDM_TimeControl:\r
6071       TimeControlOptionsPopup(hwnd);\r
6072       break;\r
6073 \r
6074     case IDM_SaveSettings:\r
6075       SaveSettings(settingsFileName);\r
6076       break;\r
6077 \r
6078     case IDM_SaveSettingsOnExit:\r
6079       saveSettingsOnExit = !saveSettingsOnExit;\r
6080       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6081                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6082                                          MF_CHECKED : MF_UNCHECKED));\r
6083       break;\r
6084 \r
6085     case IDM_Hint:\r
6086       HintEvent();\r
6087       break;\r
6088 \r
6089     case IDM_Book:\r
6090       BookEvent();\r
6091       break;\r
6092 \r
6093     case IDM_AboutGame:\r
6094       AboutGameEvent();\r
6095       break;\r
6096 \r
6097     case IDM_Debug:\r
6098       appData.debugMode = !appData.debugMode;\r
6099       if (appData.debugMode) {\r
6100         char dir[MSG_SIZ];\r
6101         GetCurrentDirectory(MSG_SIZ, dir);\r
6102         SetCurrentDirectory(installDir);\r
6103         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6104         SetCurrentDirectory(dir);\r
6105         setbuf(debugFP, NULL);\r
6106       } else {\r
6107         fclose(debugFP);\r
6108         debugFP = NULL;\r
6109       }\r
6110       break;\r
6111 \r
6112     case IDM_HELPCONTENTS:\r
6113       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6114           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6115           MessageBox (GetFocus(),\r
6116                     "Unable to activate help",\r
6117                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6118       }\r
6119       break;\r
6120 \r
6121     case IDM_HELPSEARCH:\r
6122         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6123             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6124         MessageBox (GetFocus(),\r
6125                     "Unable to activate help",\r
6126                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6127       }\r
6128       break;\r
6129 \r
6130     case IDM_HELPHELP:\r
6131       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6132         MessageBox (GetFocus(),\r
6133                     "Unable to activate help",\r
6134                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6135       }\r
6136       break;\r
6137 \r
6138     case IDM_ABOUT:\r
6139       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6140       DialogBox(hInst, \r
6141         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6142         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6143       FreeProcInstance(lpProc);\r
6144       break;\r
6145 \r
6146     case IDM_DirectCommand1:\r
6147       AskQuestionEvent("Direct Command",\r
6148                        "Send to chess program:", "", "1");\r
6149       break;\r
6150     case IDM_DirectCommand2:\r
6151       AskQuestionEvent("Direct Command",\r
6152                        "Send to second chess program:", "", "2");\r
6153       break;\r
6154 \r
6155     case EP_WhitePawn:\r
6156       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6157       fromX = fromY = -1;\r
6158       break;\r
6159 \r
6160     case EP_WhiteKnight:\r
6161       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6162       fromX = fromY = -1;\r
6163       break;\r
6164 \r
6165     case EP_WhiteBishop:\r
6166       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6167       fromX = fromY = -1;\r
6168       break;\r
6169 \r
6170     case EP_WhiteRook:\r
6171       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6172       fromX = fromY = -1;\r
6173       break;\r
6174 \r
6175     case EP_WhiteQueen:\r
6176       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6177       fromX = fromY = -1;\r
6178       break;\r
6179 \r
6180     case EP_WhiteFerz:\r
6181       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6182       fromX = fromY = -1;\r
6183       break;\r
6184 \r
6185     case EP_WhiteWazir:\r
6186       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6187       fromX = fromY = -1;\r
6188       break;\r
6189 \r
6190     case EP_WhiteAlfil:\r
6191       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6192       fromX = fromY = -1;\r
6193       break;\r
6194 \r
6195     case EP_WhiteCannon:\r
6196       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6197       fromX = fromY = -1;\r
6198       break;\r
6199 \r
6200     case EP_WhiteCardinal:\r
6201       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6202       fromX = fromY = -1;\r
6203       break;\r
6204 \r
6205     case EP_WhiteMarshall:\r
6206       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6207       fromX = fromY = -1;\r
6208       break;\r
6209 \r
6210     case EP_WhiteKing:\r
6211       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6212       fromX = fromY = -1;\r
6213       break;\r
6214 \r
6215     case EP_BlackPawn:\r
6216       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6217       fromX = fromY = -1;\r
6218       break;\r
6219 \r
6220     case EP_BlackKnight:\r
6221       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6222       fromX = fromY = -1;\r
6223       break;\r
6224 \r
6225     case EP_BlackBishop:\r
6226       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6227       fromX = fromY = -1;\r
6228       break;\r
6229 \r
6230     case EP_BlackRook:\r
6231       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6232       fromX = fromY = -1;\r
6233       break;\r
6234 \r
6235     case EP_BlackQueen:\r
6236       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6237       fromX = fromY = -1;\r
6238       break;\r
6239 \r
6240     case EP_BlackFerz:\r
6241       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6242       fromX = fromY = -1;\r
6243       break;\r
6244 \r
6245     case EP_BlackWazir:\r
6246       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6247       fromX = fromY = -1;\r
6248       break;\r
6249 \r
6250     case EP_BlackAlfil:\r
6251       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6252       fromX = fromY = -1;\r
6253       break;\r
6254 \r
6255     case EP_BlackCannon:\r
6256       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6257       fromX = fromY = -1;\r
6258       break;\r
6259 \r
6260     case EP_BlackCardinal:\r
6261       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6262       fromX = fromY = -1;\r
6263       break;\r
6264 \r
6265     case EP_BlackMarshall:\r
6266       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6267       fromX = fromY = -1;\r
6268       break;\r
6269 \r
6270     case EP_BlackKing:\r
6271       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6272       fromX = fromY = -1;\r
6273       break;\r
6274 \r
6275     case EP_EmptySquare:\r
6276       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6277       fromX = fromY = -1;\r
6278       break;\r
6279 \r
6280     case EP_ClearBoard:\r
6281       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6282       fromX = fromY = -1;\r
6283       break;\r
6284 \r
6285     case EP_White:\r
6286       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6287       fromX = fromY = -1;\r
6288       break;\r
6289 \r
6290     case EP_Black:\r
6291       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6292       fromX = fromY = -1;\r
6293       break;\r
6294 \r
6295     case EP_Promote:\r
6296       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6297       fromX = fromY = -1;\r
6298       break;\r
6299 \r
6300     case EP_Demote:\r
6301       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6302       fromX = fromY = -1;\r
6303       break;\r
6304 \r
6305     case DP_Pawn:\r
6306       DropMenuEvent(WhitePawn, fromX, fromY);\r
6307       fromX = fromY = -1;\r
6308       break;\r
6309 \r
6310     case DP_Knight:\r
6311       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6312       fromX = fromY = -1;\r
6313       break;\r
6314 \r
6315     case DP_Bishop:\r
6316       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6317       fromX = fromY = -1;\r
6318       break;\r
6319 \r
6320     case DP_Rook:\r
6321       DropMenuEvent(WhiteRook, fromX, fromY);\r
6322       fromX = fromY = -1;\r
6323       break;\r
6324 \r
6325     case DP_Queen:\r
6326       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6327       fromX = fromY = -1;\r
6328       break;\r
6329 \r
6330     default:\r
6331       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6332     }\r
6333     break;\r
6334 \r
6335   case WM_TIMER:\r
6336     switch (wParam) {\r
6337     case CLOCK_TIMER_ID:\r
6338       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6339       clockTimerEvent = 0;\r
6340       DecrementClocks(); /* call into back end */\r
6341       break;\r
6342     case LOAD_GAME_TIMER_ID:\r
6343       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6344       loadGameTimerEvent = 0;\r
6345       AutoPlayGameLoop(); /* call into back end */\r
6346       break;\r
6347     case ANALYSIS_TIMER_ID:\r
6348       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6349                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6350         AnalysisPeriodicEvent(0);\r
6351       } else {\r
6352         KillTimer(hwnd, analysisTimerEvent);\r
6353         analysisTimerEvent = 0;\r
6354       }\r
6355       break;\r
6356     case DELAYED_TIMER_ID:\r
6357       KillTimer(hwnd, delayedTimerEvent);\r
6358       delayedTimerEvent = 0;\r
6359       delayedTimerCallback();\r
6360       break;\r
6361     }\r
6362     break;\r
6363 \r
6364   case WM_USER_Input:\r
6365     InputEvent(hwnd, message, wParam, lParam);\r
6366     break;\r
6367 \r
6368   /* [AS] Also move "attached" child windows */\r
6369   case WM_WINDOWPOSCHANGING:\r
6370 \r
6371     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6372         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6373 \r
6374         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6375             /* Window is moving */\r
6376             RECT rcMain;\r
6377 \r
6378 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6379             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6380             rcMain.right  = boardX + winWidth;\r
6381             rcMain.top    = boardY;\r
6382             rcMain.bottom = boardY + winHeight;\r
6383             \r
6384             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6385             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6386             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6387             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6388             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6389             boardX = lpwp->x;\r
6390             boardY = lpwp->y;\r
6391         }\r
6392     }\r
6393     break;\r
6394 \r
6395   /* [AS] Snapping */\r
6396   case WM_ENTERSIZEMOVE:\r
6397     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6398     if (hwnd == hwndMain) {\r
6399       doingSizing = TRUE;\r
6400       lastSizing = 0;\r
6401     }\r
6402     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6403     break;\r
6404 \r
6405   case WM_SIZING:\r
6406     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6407     if (hwnd == hwndMain) {\r
6408       lastSizing = wParam;\r
6409     }\r
6410     break;\r
6411 \r
6412   case WM_MOVING:\r
6413     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6414       return OnMoving( &sd, hwnd, wParam, lParam );\r
6415 \r
6416   case WM_EXITSIZEMOVE:\r
6417     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6418     if (hwnd == hwndMain) {\r
6419       RECT client;\r
6420       doingSizing = FALSE;\r
6421       InvalidateRect(hwnd, &boardRect, FALSE);\r
6422       GetClientRect(hwnd, &client);\r
6423       ResizeBoard(client.right, client.bottom, lastSizing);\r
6424       lastSizing = 0;\r
6425       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6426     }\r
6427     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6428     break;\r
6429 \r
6430   case WM_DESTROY: /* message: window being destroyed */\r
6431     PostQuitMessage(0);\r
6432     break;\r
6433 \r
6434   case WM_CLOSE:\r
6435     if (hwnd == hwndMain) {\r
6436       ExitEvent(0);\r
6437     }\r
6438     break;\r
6439 \r
6440   default:      /* Passes it on if unprocessed */\r
6441     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6442   }\r
6443   return 0;\r
6444 }\r
6445 \r
6446 /*---------------------------------------------------------------------------*\\r
6447  *\r
6448  * Misc utility routines\r
6449  *\r
6450 \*---------------------------------------------------------------------------*/\r
6451 \r
6452 /*\r
6453  * Decent random number generator, at least not as bad as Windows\r
6454  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6455  */\r
6456 unsigned int randstate;\r
6457 \r
6458 int\r
6459 myrandom(void)\r
6460 {\r
6461   randstate = randstate * 1664525 + 1013904223;\r
6462   return (int) randstate & 0x7fffffff;\r
6463 }\r
6464 \r
6465 void\r
6466 mysrandom(unsigned int seed)\r
6467 {\r
6468   randstate = seed;\r
6469 }\r
6470 \r
6471 \r
6472 /* \r
6473  * returns TRUE if user selects a different color, FALSE otherwise \r
6474  */\r
6475 \r
6476 BOOL\r
6477 ChangeColor(HWND hwnd, COLORREF *which)\r
6478 {\r
6479   static BOOL firstTime = TRUE;\r
6480   static DWORD customColors[16];\r
6481   CHOOSECOLOR cc;\r
6482   COLORREF newcolor;\r
6483   int i;\r
6484   ColorClass ccl;\r
6485 \r
6486   if (firstTime) {\r
6487     /* Make initial colors in use available as custom colors */\r
6488     /* Should we put the compiled-in defaults here instead? */\r
6489     i = 0;\r
6490     customColors[i++] = lightSquareColor & 0xffffff;\r
6491     customColors[i++] = darkSquareColor & 0xffffff;\r
6492     customColors[i++] = whitePieceColor & 0xffffff;\r
6493     customColors[i++] = blackPieceColor & 0xffffff;\r
6494     customColors[i++] = highlightSquareColor & 0xffffff;\r
6495     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6496 \r
6497     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6498       customColors[i++] = textAttribs[ccl].color;\r
6499     }\r
6500     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6501     firstTime = FALSE;\r
6502   }\r
6503 \r
6504   cc.lStructSize = sizeof(cc);\r
6505   cc.hwndOwner = hwnd;\r
6506   cc.hInstance = NULL;\r
6507   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6508   cc.lpCustColors = (LPDWORD) customColors;\r
6509   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6510 \r
6511   if (!ChooseColor(&cc)) return FALSE;\r
6512 \r
6513   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6514   if (newcolor == *which) return FALSE;\r
6515   *which = newcolor;\r
6516   return TRUE;\r
6517 \r
6518   /*\r
6519   InitDrawingColors();\r
6520   InvalidateRect(hwnd, &boardRect, FALSE);\r
6521   */\r
6522 }\r
6523 \r
6524 BOOLEAN\r
6525 MyLoadSound(MySound *ms)\r
6526 {\r
6527   BOOL ok = FALSE;\r
6528   struct stat st;\r
6529   FILE *f;\r
6530 \r
6531   if (ms->data) free(ms->data);\r
6532   ms->data = NULL;\r
6533 \r
6534   switch (ms->name[0]) {\r
6535   case NULLCHAR:\r
6536     /* Silence */\r
6537     ok = TRUE;\r
6538     break;\r
6539   case '$':\r
6540     /* System sound from Control Panel.  Don't preload here. */\r
6541     ok = TRUE;\r
6542     break;\r
6543   case '!':\r
6544     if (ms->name[1] == NULLCHAR) {\r
6545       /* "!" alone = silence */\r
6546       ok = TRUE;\r
6547     } else {\r
6548       /* Builtin wave resource.  Error if not found. */\r
6549       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6550       if (h == NULL) break;\r
6551       ms->data = (void *)LoadResource(hInst, h);\r
6552       if (h == NULL) break;\r
6553       ok = TRUE;\r
6554     }\r
6555     break;\r
6556   default:\r
6557     /* .wav file.  Error if not found. */\r
6558     f = fopen(ms->name, "rb");\r
6559     if (f == NULL) break;\r
6560     if (fstat(fileno(f), &st) < 0) break;\r
6561     ms->data = malloc(st.st_size);\r
6562     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6563     fclose(f);\r
6564     ok = TRUE;\r
6565     break;\r
6566   }\r
6567   if (!ok) {\r
6568     char buf[MSG_SIZ];\r
6569     sprintf(buf, "Error loading sound %s", ms->name);\r
6570     DisplayError(buf, GetLastError());\r
6571   }\r
6572   return ok;\r
6573 }\r
6574 \r
6575 BOOLEAN\r
6576 MyPlaySound(MySound *ms)\r
6577 {\r
6578   BOOLEAN ok = FALSE;\r
6579 \r
6580   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6581   switch (ms->name[0]) {\r
6582   case NULLCHAR:\r
6583         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6584     /* Silence */\r
6585     ok = TRUE;\r
6586     break;\r
6587   case '$':\r
6588     /* System sound from Control Panel (deprecated feature).\r
6589        "$" alone or an unset sound name gets default beep (still in use). */\r
6590     if (ms->name[1]) {\r
6591       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6592     }\r
6593     if (!ok) ok = MessageBeep(MB_OK);\r
6594     break; \r
6595   case '!':\r
6596     /* Builtin wave resource, or "!" alone for silence */\r
6597     if (ms->name[1]) {\r
6598       if (ms->data == NULL) return FALSE;\r
6599       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6600     } else {\r
6601       ok = TRUE;\r
6602     }\r
6603     break;\r
6604   default:\r
6605     /* .wav file.  Error if not found. */\r
6606     if (ms->data == NULL) return FALSE;\r
6607     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6608     break;\r
6609   }\r
6610   /* Don't print an error: this can happen innocently if the sound driver\r
6611      is busy; for instance, if another instance of WinBoard is playing\r
6612      a sound at about the same time. */\r
6613   return ok;\r
6614 }\r
6615 \r
6616 \r
6617 LRESULT CALLBACK\r
6618 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6619 {\r
6620   BOOL ok;\r
6621   OPENFILENAME *ofn;\r
6622   static UINT *number; /* gross that this is static */\r
6623 \r
6624   switch (message) {\r
6625   case WM_INITDIALOG: /* message: initialize dialog box */\r
6626     /* Center the dialog over the application window */\r
6627     ofn = (OPENFILENAME *) lParam;\r
6628     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6629       number = (UINT *) ofn->lCustData;\r
6630       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6631     } else {\r
6632       number = NULL;\r
6633     }\r
6634     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6635     return FALSE;  /* Allow for further processing */\r
6636 \r
6637   case WM_COMMAND:\r
6638     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6639       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6640     }\r
6641     return FALSE;  /* Allow for further processing */\r
6642   }\r
6643   return FALSE;\r
6644 }\r
6645 \r
6646 UINT APIENTRY\r
6647 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6648 {\r
6649   static UINT *number;\r
6650   OPENFILENAME *ofname;\r
6651   OFNOTIFY *ofnot;\r
6652   switch (uiMsg) {\r
6653   case WM_INITDIALOG:\r
6654     ofname = (OPENFILENAME *)lParam;\r
6655     number = (UINT *)(ofname->lCustData);\r
6656     break;\r
6657   case WM_NOTIFY:\r
6658     ofnot = (OFNOTIFY *)lParam;\r
6659     if (ofnot->hdr.code == CDN_FILEOK) {\r
6660       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6661     }\r
6662     break;\r
6663   }\r
6664   return 0;\r
6665 }\r
6666 \r
6667 \r
6668 FILE *\r
6669 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6670                char *nameFilt, char *dlgTitle, UINT *number,\r
6671                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6672 {\r
6673   OPENFILENAME openFileName;\r
6674   char buf1[MSG_SIZ];\r
6675   FILE *f;\r
6676 \r
6677   if (fileName == NULL) fileName = buf1;\r
6678   if (defName == NULL) {\r
6679     strcpy(fileName, "*.");\r
6680     strcat(fileName, defExt);\r
6681   } else {\r
6682     strcpy(fileName, defName);\r
6683   }\r
6684   if (fileTitle) strcpy(fileTitle, "");\r
6685   if (number) *number = 0;\r
6686 \r
6687   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6688   openFileName.hwndOwner         = hwnd;\r
6689   openFileName.hInstance         = (HANDLE) hInst;\r
6690   openFileName.lpstrFilter       = nameFilt;\r
6691   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6692   openFileName.nMaxCustFilter    = 0L;\r
6693   openFileName.nFilterIndex      = 1L;\r
6694   openFileName.lpstrFile         = fileName;\r
6695   openFileName.nMaxFile          = MSG_SIZ;\r
6696   openFileName.lpstrFileTitle    = fileTitle;\r
6697   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6698   openFileName.lpstrInitialDir   = NULL;\r
6699   openFileName.lpstrTitle        = dlgTitle;\r
6700   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6701     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6702     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6703     | (oldDialog ? 0 : OFN_EXPLORER);\r
6704   openFileName.nFileOffset       = 0;\r
6705   openFileName.nFileExtension    = 0;\r
6706   openFileName.lpstrDefExt       = defExt;\r
6707   openFileName.lCustData         = (LONG) number;\r
6708   openFileName.lpfnHook          = oldDialog ?\r
6709     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6710   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6711 \r
6712   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6713                         GetOpenFileName(&openFileName)) {\r
6714     /* open the file */\r
6715     f = fopen(openFileName.lpstrFile, write);\r
6716     if (f == NULL) {\r
6717       MessageBox(hwnd, "File open failed", NULL,\r
6718                  MB_OK|MB_ICONEXCLAMATION);\r
6719       return NULL;\r
6720     }\r
6721   } else {\r
6722     int err = CommDlgExtendedError();\r
6723     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6724     return FALSE;\r
6725   }\r
6726   return f;\r
6727 }\r
6728 \r
6729 \r
6730 \r
6731 VOID APIENTRY\r
6732 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6733 {\r
6734   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6735 \r
6736   /*\r
6737    * Get the first pop-up menu in the menu template. This is the\r
6738    * menu that TrackPopupMenu displays.\r
6739    */\r
6740   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6741 \r
6742   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6743 \r
6744   /*\r
6745    * TrackPopup uses screen coordinates, so convert the\r
6746    * coordinates of the mouse click to screen coordinates.\r
6747    */\r
6748   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6749 \r
6750   /* Draw and track the floating pop-up menu. */\r
6751   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6752                  pt.x, pt.y, 0, hwnd, NULL);\r
6753 \r
6754   /* Destroy the menu.*/\r
6755   DestroyMenu(hmenu);\r
6756 }\r
6757    \r
6758 typedef struct {\r
6759   HWND hDlg, hText;\r
6760   int sizeX, sizeY, newSizeX, newSizeY;\r
6761   HDWP hdwp;\r
6762 } ResizeEditPlusButtonsClosure;\r
6763 \r
6764 BOOL CALLBACK\r
6765 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6766 {\r
6767   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6768   RECT rect;\r
6769   POINT pt;\r
6770 \r
6771   if (hChild == cl->hText) return TRUE;\r
6772   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6773   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6774   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6775   ScreenToClient(cl->hDlg, &pt);\r
6776   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6777     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6778   return TRUE;\r
6779 }\r
6780 \r
6781 /* Resize a dialog that has a (rich) edit field filling most of\r
6782    the top, with a row of buttons below */\r
6783 VOID\r
6784 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6785 {\r
6786   RECT rectText;\r
6787   int newTextHeight, newTextWidth;\r
6788   ResizeEditPlusButtonsClosure cl;\r
6789   \r
6790   /*if (IsIconic(hDlg)) return;*/\r
6791   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6792   \r
6793   cl.hdwp = BeginDeferWindowPos(8);\r
6794 \r
6795   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6796   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6797   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6798   if (newTextHeight < 0) {\r
6799     newSizeY += -newTextHeight;\r
6800     newTextHeight = 0;\r
6801   }\r
6802   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6803     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6804 \r
6805   cl.hDlg = hDlg;\r
6806   cl.hText = hText;\r
6807   cl.sizeX = sizeX;\r
6808   cl.sizeY = sizeY;\r
6809   cl.newSizeX = newSizeX;\r
6810   cl.newSizeY = newSizeY;\r
6811   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6812 \r
6813   EndDeferWindowPos(cl.hdwp);\r
6814 }\r
6815 \r
6816 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6817 {\r
6818     RECT    rChild, rParent;\r
6819     int     wChild, hChild, wParent, hParent;\r
6820     int     wScreen, hScreen, xNew, yNew;\r
6821     HDC     hdc;\r
6822 \r
6823     /* Get the Height and Width of the child window */\r
6824     GetWindowRect (hwndChild, &rChild);\r
6825     wChild = rChild.right - rChild.left;\r
6826     hChild = rChild.bottom - rChild.top;\r
6827 \r
6828     /* Get the Height and Width of the parent window */\r
6829     GetWindowRect (hwndParent, &rParent);\r
6830     wParent = rParent.right - rParent.left;\r
6831     hParent = rParent.bottom - rParent.top;\r
6832 \r
6833     /* Get the display limits */\r
6834     hdc = GetDC (hwndChild);\r
6835     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6836     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6837     ReleaseDC(hwndChild, hdc);\r
6838 \r
6839     /* Calculate new X position, then adjust for screen */\r
6840     xNew = rParent.left + ((wParent - wChild) /2);\r
6841     if (xNew < 0) {\r
6842         xNew = 0;\r
6843     } else if ((xNew+wChild) > wScreen) {\r
6844         xNew = wScreen - wChild;\r
6845     }\r
6846 \r
6847     /* Calculate new Y position, then adjust for screen */\r
6848     if( mode == 0 ) {\r
6849         yNew = rParent.top  + ((hParent - hChild) /2);\r
6850     }\r
6851     else {\r
6852         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6853     }\r
6854 \r
6855     if (yNew < 0) {\r
6856         yNew = 0;\r
6857     } else if ((yNew+hChild) > hScreen) {\r
6858         yNew = hScreen - hChild;\r
6859     }\r
6860 \r
6861     /* Set it, and return */\r
6862     return SetWindowPos (hwndChild, NULL,\r
6863                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6864 }\r
6865 \r
6866 /* Center one window over another */\r
6867 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6868 {\r
6869     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6870 }\r
6871 \r
6872 /*---------------------------------------------------------------------------*\\r
6873  *\r
6874  * Startup Dialog functions\r
6875  *\r
6876 \*---------------------------------------------------------------------------*/\r
6877 void\r
6878 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6879 {\r
6880   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6881 \r
6882   while (*cd != NULL) {\r
6883     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6884     cd++;\r
6885   }\r
6886 }\r
6887 \r
6888 void\r
6889 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6890 {\r
6891   char buf1[ARG_MAX];\r
6892   int len;\r
6893 \r
6894   if (str[0] == '@') {\r
6895     FILE* f = fopen(str + 1, "r");\r
6896     if (f == NULL) {\r
6897       DisplayFatalError(str + 1, errno, 2);\r
6898       return;\r
6899     }\r
6900     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6901     fclose(f);\r
6902     buf1[len] = NULLCHAR;\r
6903     str = buf1;\r
6904   }\r
6905 \r
6906   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6907 \r
6908   for (;;) {\r
6909     char buf[MSG_SIZ];\r
6910     char *end = strchr(str, '\n');\r
6911     if (end == NULL) return;\r
6912     memcpy(buf, str, end - str);\r
6913     buf[end - str] = NULLCHAR;\r
6914     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6915     str = end + 1;\r
6916   }\r
6917 }\r
6918 \r
6919 void\r
6920 SetStartupDialogEnables(HWND hDlg)\r
6921 {\r
6922   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6923     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6924     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6925   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6926     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6927   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6928     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6929   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6930     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6931   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6932     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6933     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6934     IsDlgButtonChecked(hDlg, OPT_View));\r
6935 }\r
6936 \r
6937 char *\r
6938 QuoteForFilename(char *filename)\r
6939 {\r
6940   int dquote, space;\r
6941   dquote = strchr(filename, '"') != NULL;\r
6942   space = strchr(filename, ' ') != NULL;\r
6943   if (dquote || space) {\r
6944     if (dquote) {\r
6945       return "'";\r
6946     } else {\r
6947       return "\"";\r
6948     }\r
6949   } else {\r
6950     return "";\r
6951   }\r
6952 }\r
6953 \r
6954 VOID\r
6955 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6956 {\r
6957   char buf[MSG_SIZ];\r
6958   char *q;\r
6959 \r
6960   InitComboStringsFromOption(hwndCombo, nthnames);\r
6961   q = QuoteForFilename(nthcp);\r
6962   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6963   if (*nthdir != NULLCHAR) {\r
6964     q = QuoteForFilename(nthdir);\r
6965     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6966   }\r
6967   if (*nthcp == NULLCHAR) {\r
6968     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6969   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6970     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6971     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6972   }\r
6973 }\r
6974 \r
6975 LRESULT CALLBACK\r
6976 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6977 {\r
6978   char buf[MSG_SIZ];\r
6979   HANDLE hwndCombo;\r
6980   char *p;\r
6981 \r
6982   switch (message) {\r
6983   case WM_INITDIALOG:\r
6984     /* Center the dialog */\r
6985     CenterWindow (hDlg, GetDesktopWindow());\r
6986     /* Initialize the dialog items */\r
6987     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6988                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6989                   firstChessProgramNames);\r
6990     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6991                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6992                   secondChessProgramNames);\r
6993     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6994     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6995     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6996     if (*appData.icsHelper != NULLCHAR) {\r
6997       char *q = QuoteForFilename(appData.icsHelper);\r
6998       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6999     }\r
7000     if (*appData.icsHost == NULLCHAR) {\r
7001       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7002       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7003     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7004       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7005       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7006     }\r
7007 \r
7008     if (appData.icsActive) {\r
7009       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7010     }\r
7011     else if (appData.noChessProgram) {\r
7012       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7013     }\r
7014     else {\r
7015       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7016     }\r
7017 \r
7018     SetStartupDialogEnables(hDlg);\r
7019     return TRUE;\r
7020 \r
7021   case WM_COMMAND:\r
7022     switch (LOWORD(wParam)) {\r
7023     case IDOK:\r
7024       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7025         strcpy(buf, "/fcp=");\r
7026         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7027         p = buf;\r
7028         ParseArgs(StringGet, &p);\r
7029         strcpy(buf, "/scp=");\r
7030         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7031         p = buf;\r
7032         ParseArgs(StringGet, &p);\r
7033         appData.noChessProgram = FALSE;\r
7034         appData.icsActive = FALSE;\r
7035       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7036         strcpy(buf, "/ics /icshost=");\r
7037         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7038         p = buf;\r
7039         ParseArgs(StringGet, &p);\r
7040         if (appData.zippyPlay) {\r
7041           strcpy(buf, "/fcp=");\r
7042           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7043           p = buf;\r
7044           ParseArgs(StringGet, &p);\r
7045         }\r
7046       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7047         appData.noChessProgram = TRUE;\r
7048         appData.icsActive = FALSE;\r
7049       } else {\r
7050         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7051                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7052         return TRUE;\r
7053       }\r
7054       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7055         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7056         p = buf;\r
7057         ParseArgs(StringGet, &p);\r
7058       }\r
7059       EndDialog(hDlg, TRUE);\r
7060       return TRUE;\r
7061 \r
7062     case IDCANCEL:\r
7063       ExitEvent(0);\r
7064       return TRUE;\r
7065 \r
7066     case IDM_HELPCONTENTS:\r
7067       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7068         MessageBox (GetFocus(),\r
7069                     "Unable to activate help",\r
7070                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7071       }\r
7072       break;\r
7073 \r
7074     default:\r
7075       SetStartupDialogEnables(hDlg);\r
7076       break;\r
7077     }\r
7078     break;\r
7079   }\r
7080   return FALSE;\r
7081 }\r
7082 \r
7083 /*---------------------------------------------------------------------------*\\r
7084  *\r
7085  * About box dialog functions\r
7086  *\r
7087 \*---------------------------------------------------------------------------*/\r
7088 \r
7089 /* Process messages for "About" dialog box */\r
7090 LRESULT CALLBACK\r
7091 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7092 {\r
7093   switch (message) {\r
7094   case WM_INITDIALOG: /* message: initialize dialog box */\r
7095     /* Center the dialog over the application window */\r
7096     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7097     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7098     JAWS_COPYRIGHT\r
7099     return (TRUE);\r
7100 \r
7101   case WM_COMMAND: /* message: received a command */\r
7102     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7103         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7104       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7105       return (TRUE);\r
7106     }\r
7107     break;\r
7108   }\r
7109   return (FALSE);\r
7110 }\r
7111 \r
7112 /*---------------------------------------------------------------------------*\\r
7113  *\r
7114  * Comment Dialog functions\r
7115  *\r
7116 \*---------------------------------------------------------------------------*/\r
7117 \r
7118 LRESULT CALLBACK\r
7119 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7120 {\r
7121   static HANDLE hwndText = NULL;\r
7122   int len, newSizeX, newSizeY, flags;\r
7123   static int sizeX, sizeY;\r
7124   char *str;\r
7125   RECT rect;\r
7126   MINMAXINFO *mmi;\r
7127 \r
7128   switch (message) {\r
7129   case WM_INITDIALOG: /* message: initialize dialog box */\r
7130     /* Initialize the dialog items */\r
7131     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7132     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7133     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7134     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7135     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7136     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7137     SetWindowText(hDlg, commentTitle);\r
7138     if (editComment) {\r
7139       SetFocus(hwndText);\r
7140     } else {\r
7141       SetFocus(GetDlgItem(hDlg, IDOK));\r
7142     }\r
7143     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7144                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7145                 MAKELPARAM(FALSE, 0));\r
7146     /* Size and position the dialog */\r
7147     if (!commentDialog) {\r
7148       commentDialog = hDlg;\r
7149       flags = SWP_NOZORDER;\r
7150       GetClientRect(hDlg, &rect);\r
7151       sizeX = rect.right;\r
7152       sizeY = rect.bottom;\r
7153       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7154           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7155         WINDOWPLACEMENT wp;\r
7156         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7157         wp.length = sizeof(WINDOWPLACEMENT);\r
7158         wp.flags = 0;\r
7159         wp.showCmd = SW_SHOW;\r
7160         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7161         wp.rcNormalPosition.left = commentX;\r
7162         wp.rcNormalPosition.right = commentX + commentW;\r
7163         wp.rcNormalPosition.top = commentY;\r
7164         wp.rcNormalPosition.bottom = commentY + commentH;\r
7165         SetWindowPlacement(hDlg, &wp);\r
7166 \r
7167         GetClientRect(hDlg, &rect);\r
7168         newSizeX = rect.right;\r
7169         newSizeY = rect.bottom;\r
7170         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7171                               newSizeX, newSizeY);\r
7172         sizeX = newSizeX;\r
7173         sizeY = newSizeY;\r
7174       }\r
7175     }\r
7176     return FALSE;\r
7177 \r
7178   case WM_COMMAND: /* message: received a command */\r
7179     switch (LOWORD(wParam)) {\r
7180     case IDOK:\r
7181       if (editComment) {\r
7182         char *p, *q;\r
7183         /* Read changed options from the dialog box */\r
7184         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7185         len = GetWindowTextLength(hwndText);\r
7186         str = (char *) malloc(len + 1);\r
7187         GetWindowText(hwndText, str, len + 1);\r
7188         p = q = str;\r
7189         while (*q) {\r
7190           if (*q == '\r')\r
7191             q++;\r
7192           else\r
7193             *p++ = *q++;\r
7194         }\r
7195         *p = NULLCHAR;\r
7196         ReplaceComment(commentIndex, str);\r
7197         free(str);\r
7198       }\r
7199       CommentPopDown();\r
7200       return TRUE;\r
7201 \r
7202     case IDCANCEL:\r
7203     case OPT_CancelComment:\r
7204       CommentPopDown();\r
7205       return TRUE;\r
7206 \r
7207     case OPT_ClearComment:\r
7208       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7209       break;\r
7210 \r
7211     case OPT_EditComment:\r
7212       EditCommentEvent();\r
7213       return TRUE;\r
7214 \r
7215     default:\r
7216       break;\r
7217     }\r
7218     break;\r
7219 \r
7220   case WM_SIZE:\r
7221     newSizeX = LOWORD(lParam);\r
7222     newSizeY = HIWORD(lParam);\r
7223     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7224     sizeX = newSizeX;\r
7225     sizeY = newSizeY;\r
7226     break;\r
7227 \r
7228   case WM_GETMINMAXINFO:\r
7229     /* Prevent resizing window too small */\r
7230     mmi = (MINMAXINFO *) lParam;\r
7231     mmi->ptMinTrackSize.x = 100;\r
7232     mmi->ptMinTrackSize.y = 100;\r
7233     break;\r
7234   }\r
7235   return FALSE;\r
7236 }\r
7237 \r
7238 VOID\r
7239 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7240 {\r
7241   FARPROC lpProc;\r
7242   char *p, *q;\r
7243 \r
7244   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7245 \r
7246   if (str == NULL) str = "";\r
7247   p = (char *) malloc(2 * strlen(str) + 2);\r
7248   q = p;\r
7249   while (*str) {\r
7250     if (*str == '\n') *q++ = '\r';\r
7251     *q++ = *str++;\r
7252   }\r
7253   *q = NULLCHAR;\r
7254   if (commentText != NULL) free(commentText);\r
7255 \r
7256   commentIndex = index;\r
7257   commentTitle = title;\r
7258   commentText = p;\r
7259   editComment = edit;\r
7260 \r
7261   if (commentDialog) {\r
7262     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7263     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7264   } else {\r
7265     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7266     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7267                  hwndMain, (DLGPROC)lpProc);\r
7268     FreeProcInstance(lpProc);\r
7269   }\r
7270   commentDialogUp = TRUE;\r
7271 }\r
7272 \r
7273 \r
7274 /*---------------------------------------------------------------------------*\\r
7275  *\r
7276  * Type-in move dialog functions\r
7277  * \r
7278 \*---------------------------------------------------------------------------*/\r
7279 \r
7280 LRESULT CALLBACK\r
7281 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7282 {\r
7283   char move[MSG_SIZ];\r
7284   HWND hInput;\r
7285   ChessMove moveType;\r
7286   int fromX, fromY, toX, toY;\r
7287   char promoChar;\r
7288 \r
7289   switch (message) {\r
7290   case WM_INITDIALOG:\r
7291     move[0] = (char) lParam;\r
7292     move[1] = NULLCHAR;\r
7293     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7294     hInput = GetDlgItem(hDlg, OPT_Move);\r
7295     SetWindowText(hInput, move);\r
7296     SetFocus(hInput);\r
7297     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7298     return FALSE;\r
7299 \r
7300   case WM_COMMAND:\r
7301     switch (LOWORD(wParam)) {\r
7302     case IDOK:\r
7303       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7304       { int n; Board board;\r
7305         // [HGM] FENedit\r
7306         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7307                 EditPositionPasteFEN(move);\r
7308                 EndDialog(hDlg, TRUE);\r
7309                 return TRUE;\r
7310         }\r
7311         // [HGM] movenum: allow move number to be typed in any mode\r
7312         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7313           currentMove = 2*n-1;\r
7314           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7315           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7316           EndDialog(hDlg, TRUE);\r
7317           DrawPosition(TRUE, boards[currentMove]);\r
7318           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7319           else DisplayMessage("", "");\r
7320           return TRUE;\r
7321         }\r
7322       }\r
7323       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7324         gameMode != Training) {\r
7325         DisplayMoveError("Displayed move is not current");\r
7326       } else {\r
7327 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7328         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7329           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7330         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7331         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7332           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7333           if (gameMode != Training)\r
7334               forwardMostMove = currentMove;\r
7335           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7336         } else {\r
7337           DisplayMoveError("Could not parse move");\r
7338         }\r
7339       }\r
7340       EndDialog(hDlg, TRUE);\r
7341       return TRUE;\r
7342     case IDCANCEL:\r
7343       EndDialog(hDlg, FALSE);\r
7344       return TRUE;\r
7345     default:\r
7346       break;\r
7347     }\r
7348     break;\r
7349   }\r
7350   return FALSE;\r
7351 }\r
7352 \r
7353 VOID\r
7354 PopUpMoveDialog(char firstchar)\r
7355 {\r
7356     FARPROC lpProc;\r
7357     \r
7358     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7359         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7360         gameMode == AnalyzeMode || gameMode == EditGame || \r
7361         gameMode == EditPosition || gameMode == IcsExamining ||\r
7362         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7363         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7364                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7365                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7366         gameMode == Training) {\r
7367       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7368       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7369         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7370       FreeProcInstance(lpProc);\r
7371     }\r
7372 }\r
7373 \r
7374 /*---------------------------------------------------------------------------*\\r
7375  *\r
7376  * Type-in name dialog functions\r
7377  * \r
7378 \*---------------------------------------------------------------------------*/\r
7379 \r
7380 LRESULT CALLBACK\r
7381 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7382 {\r
7383   char move[MSG_SIZ];\r
7384   HWND hInput;\r
7385 \r
7386   switch (message) {\r
7387   case WM_INITDIALOG:\r
7388     move[0] = (char) lParam;\r
7389     move[1] = NULLCHAR;\r
7390     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7391     hInput = GetDlgItem(hDlg, OPT_Name);\r
7392     SetWindowText(hInput, move);\r
7393     SetFocus(hInput);\r
7394     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7395     return FALSE;\r
7396 \r
7397   case WM_COMMAND:\r
7398     switch (LOWORD(wParam)) {\r
7399     case IDOK:\r
7400       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7401       appData.userName = strdup(move);\r
7402       SetUserLogo();\r
7403 \r
7404       EndDialog(hDlg, TRUE);\r
7405       return TRUE;\r
7406     case IDCANCEL:\r
7407       EndDialog(hDlg, FALSE);\r
7408       return TRUE;\r
7409     default:\r
7410       break;\r
7411     }\r
7412     break;\r
7413   }\r
7414   return FALSE;\r
7415 }\r
7416 \r
7417 VOID\r
7418 PopUpNameDialog(char firstchar)\r
7419 {\r
7420     FARPROC lpProc;\r
7421     \r
7422       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7423       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7424         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7425       FreeProcInstance(lpProc);\r
7426 }\r
7427 \r
7428 /*---------------------------------------------------------------------------*\\r
7429  *\r
7430  *  Error dialogs\r
7431  * \r
7432 \*---------------------------------------------------------------------------*/\r
7433 \r
7434 /* Nonmodal error box */\r
7435 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7436                              WPARAM wParam, LPARAM lParam);\r
7437 \r
7438 VOID\r
7439 ErrorPopUp(char *title, char *content)\r
7440 {\r
7441   FARPROC lpProc;\r
7442   char *p, *q;\r
7443   BOOLEAN modal = hwndMain == NULL;\r
7444 \r
7445   p = content;\r
7446   q = errorMessage;\r
7447   while (*p) {\r
7448     if (*p == '\n') {\r
7449       if (modal) {\r
7450         *q++ = ' ';\r
7451         p++;\r
7452       } else {\r
7453         *q++ = '\r';\r
7454         *q++ = *p++;\r
7455       }\r
7456     } else {\r
7457       *q++ = *p++;\r
7458     }\r
7459   }\r
7460   *q = NULLCHAR;\r
7461   strncpy(errorTitle, title, sizeof(errorTitle));\r
7462   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7463   \r
7464   if (modal) {\r
7465     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7466   } else {\r
7467     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7468     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7469                  hwndMain, (DLGPROC)lpProc);\r
7470     FreeProcInstance(lpProc);\r
7471   }\r
7472 }\r
7473 \r
7474 VOID\r
7475 ErrorPopDown()\r
7476 {\r
7477   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7478   if (errorDialog == NULL) return;\r
7479   DestroyWindow(errorDialog);\r
7480   errorDialog = NULL;\r
7481 }\r
7482 \r
7483 LRESULT CALLBACK\r
7484 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7485 {\r
7486   HANDLE hwndText;\r
7487   RECT rChild;\r
7488 \r
7489   switch (message) {\r
7490   case WM_INITDIALOG:\r
7491     GetWindowRect(hDlg, &rChild);\r
7492 \r
7493     /*\r
7494     SetWindowPos(hDlg, NULL, rChild.left,\r
7495       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7496       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7497     */\r
7498 \r
7499     /* \r
7500         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7501         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7502         and it doesn't work when you resize the dialog.\r
7503         For now, just give it a default position.\r
7504     */\r
7505     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7506 \r
7507     errorDialog = hDlg;\r
7508     SetWindowText(hDlg, errorTitle);\r
7509     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7510     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7511     return FALSE;\r
7512 \r
7513   case WM_COMMAND:\r
7514     switch (LOWORD(wParam)) {\r
7515     case IDOK:\r
7516     case IDCANCEL:\r
7517       if (errorDialog == hDlg) errorDialog = NULL;\r
7518       DestroyWindow(hDlg);\r
7519       return TRUE;\r
7520 \r
7521     default:\r
7522       break;\r
7523     }\r
7524     break;\r
7525   }\r
7526   return FALSE;\r
7527 }\r
7528 \r
7529 #ifdef GOTHIC\r
7530 HWND gothicDialog = NULL;\r
7531 \r
7532 LRESULT CALLBACK\r
7533 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7534 {\r
7535   HANDLE hwndText;\r
7536   RECT rChild;\r
7537   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7538 \r
7539   switch (message) {\r
7540   case WM_INITDIALOG:\r
7541     GetWindowRect(hDlg, &rChild);\r
7542 \r
7543     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7544                                                              SWP_NOZORDER);\r
7545 \r
7546     /* \r
7547         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7548         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7549         and it doesn't work when you resize the dialog.\r
7550         For now, just give it a default position.\r
7551     */\r
7552     gothicDialog = hDlg;\r
7553     SetWindowText(hDlg, errorTitle);\r
7554     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7555     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7556     return FALSE;\r
7557 \r
7558   case WM_COMMAND:\r
7559     switch (LOWORD(wParam)) {\r
7560     case IDOK:\r
7561     case IDCANCEL:\r
7562       if (errorDialog == hDlg) errorDialog = NULL;\r
7563       DestroyWindow(hDlg);\r
7564       return TRUE;\r
7565 \r
7566     default:\r
7567       break;\r
7568     }\r
7569     break;\r
7570   }\r
7571   return FALSE;\r
7572 }\r
7573 \r
7574 VOID\r
7575 GothicPopUp(char *title, VariantClass variant)\r
7576 {\r
7577   FARPROC lpProc;\r
7578   static char *lastTitle;\r
7579 \r
7580   strncpy(errorTitle, title, sizeof(errorTitle));\r
7581   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7582 \r
7583   if(lastTitle != title && gothicDialog != NULL) {\r
7584     DestroyWindow(gothicDialog);\r
7585     gothicDialog = NULL;\r
7586   }\r
7587   if(variant != VariantNormal && gothicDialog == NULL) {\r
7588     title = lastTitle;\r
7589     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7590     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7591                  hwndMain, (DLGPROC)lpProc);\r
7592     FreeProcInstance(lpProc);\r
7593   }\r
7594 }\r
7595 #endif\r
7596 \r
7597 /*---------------------------------------------------------------------------*\\r
7598  *\r
7599  *  Ics Interaction console functions\r
7600  *\r
7601 \*---------------------------------------------------------------------------*/\r
7602 \r
7603 #define HISTORY_SIZE 64\r
7604 static char *history[HISTORY_SIZE];\r
7605 int histIn = 0, histP = 0;\r
7606 \r
7607 VOID\r
7608 SaveInHistory(char *cmd)\r
7609 {\r
7610   if (history[histIn] != NULL) {\r
7611     free(history[histIn]);\r
7612     history[histIn] = NULL;\r
7613   }\r
7614   if (*cmd == NULLCHAR) return;\r
7615   history[histIn] = StrSave(cmd);\r
7616   histIn = (histIn + 1) % HISTORY_SIZE;\r
7617   if (history[histIn] != NULL) {\r
7618     free(history[histIn]);\r
7619     history[histIn] = NULL;\r
7620   }\r
7621   histP = histIn;\r
7622 }\r
7623 \r
7624 char *\r
7625 PrevInHistory(char *cmd)\r
7626 {\r
7627   int newhp;\r
7628   if (histP == histIn) {\r
7629     if (history[histIn] != NULL) free(history[histIn]);\r
7630     history[histIn] = StrSave(cmd);\r
7631   }\r
7632   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7633   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7634   histP = newhp;\r
7635   return history[histP];\r
7636 }\r
7637 \r
7638 char *\r
7639 NextInHistory()\r
7640 {\r
7641   if (histP == histIn) return NULL;\r
7642   histP = (histP + 1) % HISTORY_SIZE;\r
7643   return history[histP];\r
7644 }\r
7645 \r
7646 typedef struct {\r
7647   char *item;\r
7648   char *command;\r
7649   BOOLEAN getname;\r
7650   BOOLEAN immediate;\r
7651 } IcsTextMenuEntry;\r
7652 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7653 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7654 \r
7655 void\r
7656 ParseIcsTextMenu(char *icsTextMenuString)\r
7657 {\r
7658 //  int flags = 0;\r
7659   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7660   char *p = icsTextMenuString;\r
7661   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7662     free(e->item);\r
7663     e->item = NULL;\r
7664     if (e->command != NULL) {\r
7665       free(e->command);\r
7666       e->command = NULL;\r
7667     }\r
7668     e++;\r
7669   }\r
7670   e = icsTextMenuEntry;\r
7671   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7672     if (*p == ';' || *p == '\n') {\r
7673       e->item = strdup("-");\r
7674       e->command = NULL;\r
7675       p++;\r
7676     } else if (*p == '-') {\r
7677       e->item = strdup("-");\r
7678       e->command = NULL;\r
7679       p++;\r
7680       if (*p) p++;\r
7681     } else {\r
7682       char *q, *r, *s, *t;\r
7683       char c;\r
7684       q = strchr(p, ',');\r
7685       if (q == NULL) break;\r
7686       *q = NULLCHAR;\r
7687       r = strchr(q + 1, ',');\r
7688       if (r == NULL) break;\r
7689       *r = NULLCHAR;\r
7690       s = strchr(r + 1, ',');\r
7691       if (s == NULL) break;\r
7692       *s = NULLCHAR;\r
7693       c = ';';\r
7694       t = strchr(s + 1, c);\r
7695       if (t == NULL) {\r
7696         c = '\n';\r
7697         t = strchr(s + 1, c);\r
7698       }\r
7699       if (t != NULL) *t = NULLCHAR;\r
7700       e->item = strdup(p);\r
7701       e->command = strdup(q + 1);\r
7702       e->getname = *(r + 1) != '0';\r
7703       e->immediate = *(s + 1) != '0';\r
7704       *q = ',';\r
7705       *r = ',';\r
7706       *s = ',';\r
7707       if (t == NULL) break;\r
7708       *t = c;\r
7709       p = t + 1;\r
7710     }\r
7711     e++;\r
7712   } \r
7713 }\r
7714 \r
7715 HMENU\r
7716 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7717 {\r
7718   HMENU hmenu, h;\r
7719   int i = 0;\r
7720   hmenu = LoadMenu(hInst, "TextMenu");\r
7721   h = GetSubMenu(hmenu, 0);\r
7722   while (e->item) {\r
7723     if (strcmp(e->item, "-") == 0) {\r
7724       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7725     } else {\r
7726       if (e->item[0] == '|') {\r
7727         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7728                    IDM_CommandX + i, &e->item[1]);\r
7729       } else {\r
7730         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7731       }\r
7732     }\r
7733     e++;\r
7734     i++;\r
7735   } \r
7736   return hmenu;\r
7737 }\r
7738 \r
7739 WNDPROC consoleTextWindowProc;\r
7740 \r
7741 void\r
7742 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7743 {\r
7744   char buf[MSG_SIZ], name[MSG_SIZ];\r
7745   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7746   CHARRANGE sel;\r
7747 \r
7748   if (!getname) {\r
7749     SetWindowText(hInput, command);\r
7750     if (immediate) {\r
7751       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7752     } else {\r
7753       sel.cpMin = 999999;\r
7754       sel.cpMax = 999999;\r
7755       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7756       SetFocus(hInput);\r
7757     }\r
7758     return;\r
7759   }    \r
7760   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7761   if (sel.cpMin == sel.cpMax) {\r
7762     /* Expand to surrounding word */\r
7763     TEXTRANGE tr;\r
7764     do {\r
7765       tr.chrg.cpMax = sel.cpMin;\r
7766       tr.chrg.cpMin = --sel.cpMin;\r
7767       if (sel.cpMin < 0) break;\r
7768       tr.lpstrText = name;\r
7769       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7770     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7771     sel.cpMin++;\r
7772 \r
7773     do {\r
7774       tr.chrg.cpMin = sel.cpMax;\r
7775       tr.chrg.cpMax = ++sel.cpMax;\r
7776       tr.lpstrText = name;\r
7777       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7778     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7779     sel.cpMax--;\r
7780 \r
7781     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7782       MessageBeep(MB_ICONEXCLAMATION);\r
7783       return;\r
7784     }\r
7785     tr.chrg = sel;\r
7786     tr.lpstrText = name;\r
7787     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7788   } else {\r
7789     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7790       MessageBeep(MB_ICONEXCLAMATION);\r
7791       return;\r
7792     }\r
7793     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7794   }\r
7795   if (immediate) {\r
7796     sprintf(buf, "%s %s", command, name);\r
7797     SetWindowText(hInput, buf);\r
7798     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7799   } else {\r
7800     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7801     SetWindowText(hInput, buf);\r
7802     sel.cpMin = 999999;\r
7803     sel.cpMax = 999999;\r
7804     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7805     SetFocus(hInput);\r
7806   }\r
7807 }\r
7808 \r
7809 LRESULT CALLBACK \r
7810 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7811 {\r
7812   HWND hInput;\r
7813   CHARRANGE sel;\r
7814 \r
7815   switch (message) {\r
7816   case WM_KEYDOWN:\r
7817     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7818     switch (wParam) {\r
7819     case VK_PRIOR:\r
7820       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7821       return 0;\r
7822     case VK_NEXT:\r
7823       sel.cpMin = 999999;\r
7824       sel.cpMax = 999999;\r
7825       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7826       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7827       return 0;\r
7828     }\r
7829     break;\r
7830   case WM_CHAR:\r
7831    if(wParam != '\022') {\r
7832     if (wParam == '\t') {\r
7833       if (GetKeyState(VK_SHIFT) < 0) {\r
7834         /* shifted */\r
7835         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7836         if (buttonDesc[0].hwnd) {\r
7837           SetFocus(buttonDesc[0].hwnd);\r
7838         } else {\r
7839           SetFocus(hwndMain);\r
7840         }\r
7841       } else {\r
7842         /* unshifted */\r
7843         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7844       }\r
7845     } else {\r
7846       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7847       JAWS_DELETE( SetFocus(hInput); )\r
7848       SendMessage(hInput, message, wParam, lParam);\r
7849     }\r
7850     return 0;\r
7851    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7852   case WM_RBUTTONUP:\r
7853     if (GetKeyState(VK_SHIFT) & ~1) {\r
7854       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7855         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7856     } else {\r
7857       POINT pt;\r
7858       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7859       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7860       if (sel.cpMin == sel.cpMax) {\r
7861         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7862         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7863       }\r
7864       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7865         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7866       }\r
7867       pt.x = LOWORD(lParam);\r
7868       pt.y = HIWORD(lParam);\r
7869       MenuPopup(hwnd, pt, hmenu, -1);\r
7870     }\r
7871     return 0;\r
7872   case WM_PASTE:\r
7873     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7874     SetFocus(hInput);\r
7875     return SendMessage(hInput, message, wParam, lParam);\r
7876   case WM_MBUTTONDOWN:\r
7877     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7878   case WM_RBUTTONDOWN:\r
7879     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7880       /* Move selection here if it was empty */\r
7881       POINT pt;\r
7882       pt.x = LOWORD(lParam);\r
7883       pt.y = HIWORD(lParam);\r
7884       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7885       if (sel.cpMin == sel.cpMax) {\r
7886         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7887         sel.cpMax = sel.cpMin;\r
7888         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7889       }\r
7890       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7891     }\r
7892     return 0;\r
7893   case WM_COMMAND:\r
7894     switch (LOWORD(wParam)) {\r
7895     case IDM_QuickPaste:\r
7896       {\r
7897         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7898         if (sel.cpMin == sel.cpMax) {\r
7899           MessageBeep(MB_ICONEXCLAMATION);\r
7900           return 0;\r
7901         }\r
7902         SendMessage(hwnd, WM_COPY, 0, 0);\r
7903         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7904         SendMessage(hInput, WM_PASTE, 0, 0);\r
7905         SetFocus(hInput);\r
7906         return 0;\r
7907       }\r
7908     case IDM_Cut:\r
7909       SendMessage(hwnd, WM_CUT, 0, 0);\r
7910       return 0;\r
7911     case IDM_Paste:\r
7912       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7913       return 0;\r
7914     case IDM_Copy:\r
7915       SendMessage(hwnd, WM_COPY, 0, 0);\r
7916       return 0;\r
7917     default:\r
7918       {\r
7919         int i = LOWORD(wParam) - IDM_CommandX;\r
7920         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7921             icsTextMenuEntry[i].command != NULL) {\r
7922           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7923                    icsTextMenuEntry[i].getname,\r
7924                    icsTextMenuEntry[i].immediate);\r
7925           return 0;\r
7926         }\r
7927       }\r
7928       break;\r
7929     }\r
7930     break;\r
7931   }\r
7932   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7933 }\r
7934 \r
7935 WNDPROC consoleInputWindowProc;\r
7936 \r
7937 LRESULT CALLBACK\r
7938 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7939 {\r
7940   char buf[MSG_SIZ];\r
7941   char *p;\r
7942   static BOOL sendNextChar = FALSE;\r
7943   static BOOL quoteNextChar = FALSE;\r
7944   InputSource *is = consoleInputSource;\r
7945   CHARFORMAT cf;\r
7946   CHARRANGE sel;\r
7947 \r
7948   switch (message) {\r
7949   case WM_CHAR:\r
7950     if (!appData.localLineEditing || sendNextChar) {\r
7951       is->buf[0] = (CHAR) wParam;\r
7952       is->count = 1;\r
7953       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7954       sendNextChar = FALSE;\r
7955       return 0;\r
7956     }\r
7957     if (quoteNextChar) {\r
7958       buf[0] = (char) wParam;\r
7959       buf[1] = NULLCHAR;\r
7960       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7961       quoteNextChar = FALSE;\r
7962       return 0;\r
7963     }\r
7964     switch (wParam) {\r
7965     case '\r':   /* Enter key */\r
7966       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7967       if (consoleEcho) SaveInHistory(is->buf);\r
7968       is->buf[is->count++] = '\n';\r
7969       is->buf[is->count] = NULLCHAR;\r
7970       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7971       if (consoleEcho) {\r
7972         ConsoleOutput(is->buf, is->count, TRUE);\r
7973       } else if (appData.localLineEditing) {\r
7974         ConsoleOutput("\n", 1, TRUE);\r
7975       }\r
7976       /* fall thru */\r
7977     case '\033': /* Escape key */\r
7978       SetWindowText(hwnd, "");\r
7979       cf.cbSize = sizeof(CHARFORMAT);\r
7980       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7981       if (consoleEcho) {\r
7982         cf.crTextColor = textAttribs[ColorNormal].color;\r
7983       } else {\r
7984         cf.crTextColor = COLOR_ECHOOFF;\r
7985       }\r
7986       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7987       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7988       return 0;\r
7989     case '\t':   /* Tab key */\r
7990       if (GetKeyState(VK_SHIFT) < 0) {\r
7991         /* shifted */\r
7992         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7993       } else {\r
7994         /* unshifted */\r
7995         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7996         if (buttonDesc[0].hwnd) {\r
7997           SetFocus(buttonDesc[0].hwnd);\r
7998         } else {\r
7999           SetFocus(hwndMain);\r
8000         }\r
8001       }\r
8002       return 0;\r
8003     case '\023': /* Ctrl+S */\r
8004       sendNextChar = TRUE;\r
8005       return 0;\r
8006     case '\021': /* Ctrl+Q */\r
8007       quoteNextChar = TRUE;\r
8008       return 0;\r
8009     JAWS_REPLAY\r
8010     default:\r
8011       break;\r
8012     }\r
8013     break;\r
8014   case WM_KEYDOWN:\r
8015     switch (wParam) {\r
8016     case VK_UP:\r
8017       GetWindowText(hwnd, buf, MSG_SIZ);\r
8018       p = PrevInHistory(buf);\r
8019       if (p != NULL) {\r
8020         SetWindowText(hwnd, p);\r
8021         sel.cpMin = 999999;\r
8022         sel.cpMax = 999999;\r
8023         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8024         return 0;\r
8025       }\r
8026       break;\r
8027     case VK_DOWN:\r
8028       p = NextInHistory();\r
8029       if (p != NULL) {\r
8030         SetWindowText(hwnd, p);\r
8031         sel.cpMin = 999999;\r
8032         sel.cpMax = 999999;\r
8033         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8034         return 0;\r
8035       }\r
8036       break;\r
8037     case VK_HOME:\r
8038     case VK_END:\r
8039       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8040       /* fall thru */\r
8041     case VK_PRIOR:\r
8042     case VK_NEXT:\r
8043       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8044       return 0;\r
8045     }\r
8046     break;\r
8047   case WM_MBUTTONDOWN:\r
8048     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8049       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8050     break;\r
8051   case WM_RBUTTONUP:\r
8052     if (GetKeyState(VK_SHIFT) & ~1) {\r
8053       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8054         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8055     } else {\r
8056       POINT pt;\r
8057       HMENU hmenu;\r
8058       hmenu = LoadMenu(hInst, "InputMenu");\r
8059       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8060       if (sel.cpMin == sel.cpMax) {\r
8061         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8062         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8063       }\r
8064       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8065         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8066       }\r
8067       pt.x = LOWORD(lParam);\r
8068       pt.y = HIWORD(lParam);\r
8069       MenuPopup(hwnd, pt, hmenu, -1);\r
8070     }\r
8071     return 0;\r
8072   case WM_COMMAND:\r
8073     switch (LOWORD(wParam)) { \r
8074     case IDM_Undo:\r
8075       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8076       return 0;\r
8077     case IDM_SelectAll:\r
8078       sel.cpMin = 0;\r
8079       sel.cpMax = -1; /*999999?*/\r
8080       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8081       return 0;\r
8082     case IDM_Cut:\r
8083       SendMessage(hwnd, WM_CUT, 0, 0);\r
8084       return 0;\r
8085     case IDM_Paste:\r
8086       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8087       return 0;\r
8088     case IDM_Copy:\r
8089       SendMessage(hwnd, WM_COPY, 0, 0);\r
8090       return 0;\r
8091     }\r
8092     break;\r
8093   }\r
8094   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8095 }\r
8096 \r
8097 #define CO_MAX  100000\r
8098 #define CO_TRIM   1000\r
8099 \r
8100 LRESULT CALLBACK\r
8101 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8102 {\r
8103   static SnapData sd;\r
8104   static HWND hText, hInput /*, hFocus*/;\r
8105 //  InputSource *is = consoleInputSource;\r
8106   RECT rect;\r
8107   static int sizeX, sizeY;\r
8108   int newSizeX, newSizeY;\r
8109   MINMAXINFO *mmi;\r
8110 \r
8111   switch (message) {\r
8112   case WM_NOTIFY:\r
8113     if (((NMHDR*)lParam)->code == EN_LINK)\r
8114     {\r
8115       ENLINK *pLink = (ENLINK*)lParam;\r
8116       if (pLink->msg == WM_LBUTTONUP)\r
8117       {\r
8118           TEXTRANGE tr;\r
8119 \r
8120           tr.chrg = pLink->chrg;\r
8121           tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8122           hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8123           SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8124           ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8125           free(tr.lpstrText);\r
8126       }\r
8127     }\r
8128     break;\r
8129   case WM_INITDIALOG: /* message: initialize dialog box */\r
8130     hwndConsole = hDlg;\r
8131     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8132     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8133     SetFocus(hInput);\r
8134     consoleTextWindowProc = (WNDPROC)\r
8135       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8136     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8137     consoleInputWindowProc = (WNDPROC)\r
8138       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8139     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8140     Colorize(ColorNormal, TRUE);\r
8141     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8142     ChangedConsoleFont();\r
8143     GetClientRect(hDlg, &rect);\r
8144     sizeX = rect.right;\r
8145     sizeY = rect.bottom;\r
8146     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8147         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8148       WINDOWPLACEMENT wp;\r
8149       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8150       wp.length = sizeof(WINDOWPLACEMENT);\r
8151       wp.flags = 0;\r
8152       wp.showCmd = SW_SHOW;\r
8153       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8154       wp.rcNormalPosition.left = wpConsole.x;\r
8155       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8156       wp.rcNormalPosition.top = wpConsole.y;\r
8157       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8158       SetWindowPlacement(hDlg, &wp);\r
8159     }\r
8160 #if 1\r
8161    // [HGM] Chessknight's change 2004-07-13\r
8162    else { /* Determine Defaults */\r
8163        WINDOWPLACEMENT wp;\r
8164        wpConsole.x = winWidth + 1;\r
8165        wpConsole.y = boardY;\r
8166        wpConsole.width = screenWidth -  winWidth;\r
8167        wpConsole.height = winHeight;\r
8168        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8169        wp.length = sizeof(WINDOWPLACEMENT);\r
8170        wp.flags = 0;\r
8171        wp.showCmd = SW_SHOW;\r
8172        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8173        wp.rcNormalPosition.left = wpConsole.x;\r
8174        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8175        wp.rcNormalPosition.top = wpConsole.y;\r
8176        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8177        SetWindowPlacement(hDlg, &wp);\r
8178     }\r
8179 #endif\r
8180     return FALSE;\r
8181 \r
8182   case WM_SETFOCUS:\r
8183     SetFocus(hInput);\r
8184     return 0;\r
8185 \r
8186   case WM_CLOSE:\r
8187     ExitEvent(0);\r
8188     /* not reached */\r
8189     break;\r
8190 \r
8191   case WM_SIZE:\r
8192     if (IsIconic(hDlg)) break;\r
8193     newSizeX = LOWORD(lParam);\r
8194     newSizeY = HIWORD(lParam);\r
8195     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8196       RECT rectText, rectInput;\r
8197       POINT pt;\r
8198       int newTextHeight, newTextWidth;\r
8199       GetWindowRect(hText, &rectText);\r
8200       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8201       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8202       if (newTextHeight < 0) {\r
8203         newSizeY += -newTextHeight;\r
8204         newTextHeight = 0;\r
8205       }\r
8206       SetWindowPos(hText, NULL, 0, 0,\r
8207         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8208       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8209       pt.x = rectInput.left;\r
8210       pt.y = rectInput.top + newSizeY - sizeY;\r
8211       ScreenToClient(hDlg, &pt);\r
8212       SetWindowPos(hInput, NULL, \r
8213         pt.x, pt.y, /* needs client coords */   \r
8214         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8215         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8216     }\r
8217     sizeX = newSizeX;\r
8218     sizeY = newSizeY;\r
8219     break;\r
8220 \r
8221   case WM_GETMINMAXINFO:\r
8222     /* Prevent resizing window too small */\r
8223     mmi = (MINMAXINFO *) lParam;\r
8224     mmi->ptMinTrackSize.x = 100;\r
8225     mmi->ptMinTrackSize.y = 100;\r
8226     break;\r
8227 \r
8228   /* [AS] Snapping */\r
8229   case WM_ENTERSIZEMOVE:\r
8230     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8231 \r
8232   case WM_SIZING:\r
8233     return OnSizing( &sd, hDlg, wParam, lParam );\r
8234 \r
8235   case WM_MOVING:\r
8236     return OnMoving( &sd, hDlg, wParam, lParam );\r
8237 \r
8238   case WM_EXITSIZEMOVE:\r
8239     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8240   }\r
8241 \r
8242   return DefWindowProc(hDlg, message, wParam, lParam);\r
8243 }\r
8244 \r
8245 \r
8246 VOID\r
8247 ConsoleCreate()\r
8248 {\r
8249   HWND hCons, hText;\r
8250   WORD wMask;\r
8251   if (hwndConsole) return;\r
8252   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8253   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8254 \r
8255 \r
8256   // make the text item in the console do URL links\r
8257   hText = GetDlgItem(hCons, OPT_ConsoleText);\r
8258   wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8259   SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8260   SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8261 }\r
8262 \r
8263 \r
8264 VOID\r
8265 ConsoleOutput(char* data, int length, int forceVisible)\r
8266 {\r
8267   HWND hText;\r
8268   int trim, exlen;\r
8269   char *p, *q;\r
8270   char buf[CO_MAX+1];\r
8271   POINT pEnd;\r
8272   RECT rect;\r
8273   static int delayLF = 0;\r
8274   CHARRANGE savesel, sel;\r
8275 \r
8276   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8277   p = data;\r
8278   q = buf;\r
8279   if (delayLF) {\r
8280     *q++ = '\r';\r
8281     *q++ = '\n';\r
8282     delayLF = 0;\r
8283   }\r
8284   while (length--) {\r
8285     if (*p == '\n') {\r
8286       if (*++p) {\r
8287         *q++ = '\r';\r
8288         *q++ = '\n';\r
8289       } else {\r
8290         delayLF = 1;\r
8291       }\r
8292     } else if (*p == '\007') {\r
8293        MyPlaySound(&sounds[(int)SoundBell]);\r
8294        p++;\r
8295     } else {\r
8296       *q++ = *p++;\r
8297     }\r
8298   }\r
8299   *q = NULLCHAR;\r
8300   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8301   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8302   /* Save current selection */\r
8303   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8304   exlen = GetWindowTextLength(hText);\r
8305   /* Find out whether current end of text is visible */\r
8306   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8307   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8308   /* Trim existing text if it's too long */\r
8309   if (exlen + (q - buf) > CO_MAX) {\r
8310     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8311     sel.cpMin = 0;\r
8312     sel.cpMax = trim;\r
8313     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8314     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8315     exlen -= trim;\r
8316     savesel.cpMin -= trim;\r
8317     savesel.cpMax -= trim;\r
8318     if (exlen < 0) exlen = 0;\r
8319     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8320     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8321   }\r
8322   /* Append the new text */\r
8323   sel.cpMin = exlen;\r
8324   sel.cpMax = exlen;\r
8325   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8326   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8327   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8328   if (forceVisible || exlen == 0 ||\r
8329       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8330        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8331     /* Scroll to make new end of text visible if old end of text\r
8332        was visible or new text is an echo of user typein */\r
8333     sel.cpMin = 9999999;\r
8334     sel.cpMax = 9999999;\r
8335     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8336     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8337     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8338     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8339   }\r
8340   if (savesel.cpMax == exlen || forceVisible) {\r
8341     /* Move insert point to new end of text if it was at the old\r
8342        end of text or if the new text is an echo of user typein */\r
8343     sel.cpMin = 9999999;\r
8344     sel.cpMax = 9999999;\r
8345     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8346   } else {\r
8347     /* Restore previous selection */\r
8348     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8349   }\r
8350   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8351 }\r
8352 \r
8353 /*---------*/\r
8354 \r
8355 \r
8356 void\r
8357 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8358 {\r
8359   char buf[100];\r
8360   char *str;\r
8361   COLORREF oldFg, oldBg;\r
8362   HFONT oldFont;\r
8363   RECT rect;\r
8364 \r
8365   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8366 \r
8367   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8368   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8369   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8370 \r
8371   rect.left = x;\r
8372   rect.right = x + squareSize;\r
8373   rect.top  = y;\r
8374   rect.bottom = y + squareSize;\r
8375   str = buf;\r
8376 \r
8377   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8378                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8379              y, ETO_CLIPPED|ETO_OPAQUE,\r
8380              &rect, str, strlen(str), NULL);\r
8381 \r
8382   (void) SetTextColor(hdc, oldFg);\r
8383   (void) SetBkColor(hdc, oldBg);\r
8384   (void) SelectObject(hdc, oldFont);\r
8385 }\r
8386 \r
8387 void\r
8388 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8389               RECT *rect, char *color, char *flagFell)\r
8390 {\r
8391   char buf[100];\r
8392   char *str;\r
8393   COLORREF oldFg, oldBg;\r
8394   HFONT oldFont;\r
8395 \r
8396   if (appData.clockMode) {\r
8397     if (tinyLayout)\r
8398       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8399     else\r
8400       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8401     str = buf;\r
8402   } else {\r
8403     str = color;\r
8404   }\r
8405 \r
8406   if (highlight) {\r
8407     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8408     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8409   } else {\r
8410     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8411     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8412   }\r
8413   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8414 \r
8415   JAWS_SILENCE\r
8416 \r
8417   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8418              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8419              rect, str, strlen(str), NULL);\r
8420   if(logoHeight > 0 && appData.clockMode) {\r
8421       RECT r;\r
8422       sprintf(buf, "%s %s", buf+7, flagFell);\r
8423       r.top = rect->top + logoHeight/2;\r
8424       r.left = rect->left;\r
8425       r.right = rect->right;\r
8426       r.bottom = rect->bottom;\r
8427       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8428                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8429                  &r, str, strlen(str), NULL);\r
8430   }\r
8431   (void) SetTextColor(hdc, oldFg);\r
8432   (void) SetBkColor(hdc, oldBg);\r
8433   (void) SelectObject(hdc, oldFont);\r
8434 }\r
8435 \r
8436 \r
8437 int\r
8438 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8439            OVERLAPPED *ovl)\r
8440 {\r
8441   int ok, err;\r
8442 \r
8443   /* [AS]  */\r
8444   if( count <= 0 ) {\r
8445     if (appData.debugMode) {\r
8446       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8447     }\r
8448 \r
8449     return ERROR_INVALID_USER_BUFFER;\r
8450   }\r
8451 \r
8452   ResetEvent(ovl->hEvent);\r
8453   ovl->Offset = ovl->OffsetHigh = 0;\r
8454   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8455   if (ok) {\r
8456     err = NO_ERROR;\r
8457   } else {\r
8458     err = GetLastError();\r
8459     if (err == ERROR_IO_PENDING) {\r
8460       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8461       if (ok)\r
8462         err = NO_ERROR;\r
8463       else\r
8464         err = GetLastError();\r
8465     }\r
8466   }\r
8467   return err;\r
8468 }\r
8469 \r
8470 int\r
8471 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8472             OVERLAPPED *ovl)\r
8473 {\r
8474   int ok, err;\r
8475 \r
8476   ResetEvent(ovl->hEvent);\r
8477   ovl->Offset = ovl->OffsetHigh = 0;\r
8478   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8479   if (ok) {\r
8480     err = NO_ERROR;\r
8481   } else {\r
8482     err = GetLastError();\r
8483     if (err == ERROR_IO_PENDING) {\r
8484       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8485       if (ok)\r
8486         err = NO_ERROR;\r
8487       else\r
8488         err = GetLastError();\r
8489     }\r
8490   }\r
8491   return err;\r
8492 }\r
8493 \r
8494 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8495 void CheckForInputBufferFull( InputSource * is )\r
8496 {\r
8497     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8498         /* Look for end of line */\r
8499         char * p = is->buf;\r
8500         \r
8501         while( p < is->next && *p != '\n' ) {\r
8502             p++;\r
8503         }\r
8504 \r
8505         if( p >= is->next ) {\r
8506             if (appData.debugMode) {\r
8507                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8508             }\r
8509 \r
8510             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8511             is->count = (DWORD) -1;\r
8512             is->next = is->buf;\r
8513         }\r
8514     }\r
8515 }\r
8516 \r
8517 DWORD\r
8518 InputThread(LPVOID arg)\r
8519 {\r
8520   InputSource *is;\r
8521   OVERLAPPED ovl;\r
8522 \r
8523   is = (InputSource *) arg;\r
8524   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8525   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8526   while (is->hThread != NULL) {\r
8527     is->error = DoReadFile(is->hFile, is->next,\r
8528                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8529                            &is->count, &ovl);\r
8530     if (is->error == NO_ERROR) {\r
8531       is->next += is->count;\r
8532     } else {\r
8533       if (is->error == ERROR_BROKEN_PIPE) {\r
8534         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8535         is->count = 0;\r
8536       } else {\r
8537         is->count = (DWORD) -1;\r
8538         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8539         break; \r
8540       }\r
8541     }\r
8542 \r
8543     CheckForInputBufferFull( is );\r
8544 \r
8545     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8546 \r
8547     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8548 \r
8549     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8550   }\r
8551 \r
8552   CloseHandle(ovl.hEvent);\r
8553   CloseHandle(is->hFile);\r
8554 \r
8555   if (appData.debugMode) {\r
8556     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8557   }\r
8558 \r
8559   return 0;\r
8560 }\r
8561 \r
8562 \r
8563 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8564 DWORD\r
8565 NonOvlInputThread(LPVOID arg)\r
8566 {\r
8567   InputSource *is;\r
8568   char *p, *q;\r
8569   int i;\r
8570   char prev;\r
8571 \r
8572   is = (InputSource *) arg;\r
8573   while (is->hThread != NULL) {\r
8574     is->error = ReadFile(is->hFile, is->next,\r
8575                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8576                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8577     if (is->error == NO_ERROR) {\r
8578       /* Change CRLF to LF */\r
8579       if (is->next > is->buf) {\r
8580         p = is->next - 1;\r
8581         i = is->count + 1;\r
8582       } else {\r
8583         p = is->next;\r
8584         i = is->count;\r
8585       }\r
8586       q = p;\r
8587       prev = NULLCHAR;\r
8588       while (i > 0) {\r
8589         if (prev == '\r' && *p == '\n') {\r
8590           *(q-1) = '\n';\r
8591           is->count--;\r
8592         } else { \r
8593           *q++ = *p;\r
8594         }\r
8595         prev = *p++;\r
8596         i--;\r
8597       }\r
8598       *q = NULLCHAR;\r
8599       is->next = q;\r
8600     } else {\r
8601       if (is->error == ERROR_BROKEN_PIPE) {\r
8602         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8603         is->count = 0; \r
8604       } else {\r
8605         is->count = (DWORD) -1;\r
8606       }\r
8607     }\r
8608 \r
8609     CheckForInputBufferFull( is );\r
8610 \r
8611     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8612 \r
8613     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8614 \r
8615     if (is->count < 0) break;  /* Quit on error */\r
8616   }\r
8617   CloseHandle(is->hFile);\r
8618   return 0;\r
8619 }\r
8620 \r
8621 DWORD\r
8622 SocketInputThread(LPVOID arg)\r
8623 {\r
8624   InputSource *is;\r
8625 \r
8626   is = (InputSource *) arg;\r
8627   while (is->hThread != NULL) {\r
8628     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8629     if ((int)is->count == SOCKET_ERROR) {\r
8630       is->count = (DWORD) -1;\r
8631       is->error = WSAGetLastError();\r
8632     } else {\r
8633       is->error = NO_ERROR;\r
8634       is->next += is->count;\r
8635       if (is->count == 0 && is->second == is) {\r
8636         /* End of file on stderr; quit with no message */\r
8637         break;\r
8638       }\r
8639     }\r
8640     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8641 \r
8642     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8643 \r
8644     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8645   }\r
8646   return 0;\r
8647 }\r
8648 \r
8649 VOID\r
8650 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8651 {\r
8652   InputSource *is;\r
8653 \r
8654   is = (InputSource *) lParam;\r
8655   if (is->lineByLine) {\r
8656     /* Feed in lines one by one */\r
8657     char *p = is->buf;\r
8658     char *q = p;\r
8659     while (q < is->next) {\r
8660       if (*q++ == '\n') {\r
8661         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8662         p = q;\r
8663       }\r
8664     }\r
8665     \r
8666     /* Move any partial line to the start of the buffer */\r
8667     q = is->buf;\r
8668     while (p < is->next) {\r
8669       *q++ = *p++;\r
8670     }\r
8671     is->next = q;\r
8672 \r
8673     if (is->error != NO_ERROR || is->count == 0) {\r
8674       /* Notify backend of the error.  Note: If there was a partial\r
8675          line at the end, it is not flushed through. */\r
8676       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8677     }\r
8678   } else {\r
8679     /* Feed in the whole chunk of input at once */\r
8680     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8681     is->next = is->buf;\r
8682   }\r
8683 }\r
8684 \r
8685 /*---------------------------------------------------------------------------*\\r
8686  *\r
8687  *  Menu enables. Used when setting various modes.\r
8688  *\r
8689 \*---------------------------------------------------------------------------*/\r
8690 \r
8691 typedef struct {\r
8692   int item;\r
8693   int flags;\r
8694 } Enables;\r
8695 \r
8696 VOID\r
8697 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8698 {\r
8699   while (enab->item > 0) {\r
8700     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8701     enab++;\r
8702   }\r
8703 }\r
8704 \r
8705 Enables gnuEnables[] = {\r
8706   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8707   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8708   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8709   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8710   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8711   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8712   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8713   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8714   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8715   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8716   { -1, -1 }\r
8717 };\r
8718 \r
8719 Enables icsEnables[] = {\r
8720   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8721   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8722   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8723   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8724   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8725   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8726   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8727   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8728   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8729   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8730   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8731   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8732   { -1, -1 }\r
8733 };\r
8734 \r
8735 #ifdef ZIPPY\r
8736 Enables zippyEnables[] = {\r
8737   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8738   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8739   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8740   { -1, -1 }\r
8741 };\r
8742 #endif\r
8743 \r
8744 Enables ncpEnables[] = {\r
8745   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8746   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8747   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8748   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8749   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8750   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8751   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8752   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8753   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8754   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8755   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8756   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8757   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8758   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8759   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8760   { -1, -1 }\r
8761 };\r
8762 \r
8763 Enables trainingOnEnables[] = {\r
8764   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8765   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8766   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8767   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8768   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8769   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8770   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8771   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8772   { -1, -1 }\r
8773 };\r
8774 \r
8775 Enables trainingOffEnables[] = {\r
8776   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8777   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8778   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8779   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8780   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8781   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8782   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8783   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8784   { -1, -1 }\r
8785 };\r
8786 \r
8787 /* These modify either ncpEnables or gnuEnables */\r
8788 Enables cmailEnables[] = {\r
8789   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8790   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8791   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8792   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8794   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8796   { -1, -1 }\r
8797 };\r
8798 \r
8799 Enables machineThinkingEnables[] = {\r
8800   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8802   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8803   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8805   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8806   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8807   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8808   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8809   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8810   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8811   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8812   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8813   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8814   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8815   { -1, -1 }\r
8816 };\r
8817 \r
8818 Enables userThinkingEnables[] = {\r
8819   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8820   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8821   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8822   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8823   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8824   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8825   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8826   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8827   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8828   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8829   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8830   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8831   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8832   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8833   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8834   { -1, -1 }\r
8835 };\r
8836 \r
8837 /*---------------------------------------------------------------------------*\\r
8838  *\r
8839  *  Front-end interface functions exported by XBoard.\r
8840  *  Functions appear in same order as prototypes in frontend.h.\r
8841  * \r
8842 \*---------------------------------------------------------------------------*/\r
8843 VOID\r
8844 ModeHighlight()\r
8845 {\r
8846   static UINT prevChecked = 0;\r
8847   static int prevPausing = 0;\r
8848   UINT nowChecked;\r
8849 \r
8850   if (pausing != prevPausing) {\r
8851     prevPausing = pausing;\r
8852     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8853                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8854     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8855   }\r
8856 \r
8857   switch (gameMode) {\r
8858   case BeginningOfGame:\r
8859     if (appData.icsActive)\r
8860       nowChecked = IDM_IcsClient;\r
8861     else if (appData.noChessProgram)\r
8862       nowChecked = IDM_EditGame;\r
8863     else\r
8864       nowChecked = IDM_MachineBlack;\r
8865     break;\r
8866   case MachinePlaysBlack:\r
8867     nowChecked = IDM_MachineBlack;\r
8868     break;\r
8869   case MachinePlaysWhite:\r
8870     nowChecked = IDM_MachineWhite;\r
8871     break;\r
8872   case TwoMachinesPlay:\r
8873     nowChecked = IDM_TwoMachines;\r
8874     break;\r
8875   case AnalyzeMode:\r
8876     nowChecked = IDM_AnalysisMode;\r
8877     break;\r
8878   case AnalyzeFile:\r
8879     nowChecked = IDM_AnalyzeFile;\r
8880     break;\r
8881   case EditGame:\r
8882     nowChecked = IDM_EditGame;\r
8883     break;\r
8884   case PlayFromGameFile:\r
8885     nowChecked = IDM_LoadGame;\r
8886     break;\r
8887   case EditPosition:\r
8888     nowChecked = IDM_EditPosition;\r
8889     break;\r
8890   case Training:\r
8891     nowChecked = IDM_Training;\r
8892     break;\r
8893   case IcsPlayingWhite:\r
8894   case IcsPlayingBlack:\r
8895   case IcsObserving:\r
8896   case IcsIdle:\r
8897     nowChecked = IDM_IcsClient;\r
8898     break;\r
8899   default:\r
8900   case EndOfGame:\r
8901     nowChecked = 0;\r
8902     break;\r
8903   }\r
8904   if (prevChecked != 0)\r
8905     (void) CheckMenuItem(GetMenu(hwndMain),\r
8906                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8907   if (nowChecked != 0)\r
8908     (void) CheckMenuItem(GetMenu(hwndMain),\r
8909                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8910 \r
8911   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8912     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8913                           MF_BYCOMMAND|MF_ENABLED);\r
8914   } else {\r
8915     (void) EnableMenuItem(GetMenu(hwndMain), \r
8916                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8917   }\r
8918 \r
8919   prevChecked = nowChecked;\r
8920 \r
8921   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8922   if (appData.icsActive) {\r
8923        if (appData.icsEngineAnalyze) {\r
8924                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8925                        MF_BYCOMMAND|MF_CHECKED);\r
8926        } else {\r
8927                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8928                        MF_BYCOMMAND|MF_UNCHECKED);\r
8929        }\r
8930   }\r
8931 }\r
8932 \r
8933 VOID\r
8934 SetICSMode()\r
8935 {\r
8936   HMENU hmenu = GetMenu(hwndMain);\r
8937   SetMenuEnables(hmenu, icsEnables);\r
8938   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8939     MF_BYPOSITION|MF_ENABLED);\r
8940 #ifdef ZIPPY\r
8941   if (appData.zippyPlay) {\r
8942     SetMenuEnables(hmenu, zippyEnables);\r
8943     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8944          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8945           MF_BYCOMMAND|MF_ENABLED);\r
8946   }\r
8947 #endif\r
8948 }\r
8949 \r
8950 VOID\r
8951 SetGNUMode()\r
8952 {\r
8953   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8954 }\r
8955 \r
8956 VOID\r
8957 SetNCPMode()\r
8958 {\r
8959   HMENU hmenu = GetMenu(hwndMain);\r
8960   SetMenuEnables(hmenu, ncpEnables);\r
8961   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8962     MF_BYPOSITION|MF_GRAYED);\r
8963     DrawMenuBar(hwndMain);\r
8964 }\r
8965 \r
8966 VOID\r
8967 SetCmailMode()\r
8968 {\r
8969   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8970 }\r
8971 \r
8972 VOID \r
8973 SetTrainingModeOn()\r
8974 {\r
8975   int i;\r
8976   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8977   for (i = 0; i < N_BUTTONS; i++) {\r
8978     if (buttonDesc[i].hwnd != NULL)\r
8979       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8980   }\r
8981   CommentPopDown();\r
8982 }\r
8983 \r
8984 VOID SetTrainingModeOff()\r
8985 {\r
8986   int i;\r
8987   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8988   for (i = 0; i < N_BUTTONS; i++) {\r
8989     if (buttonDesc[i].hwnd != NULL)\r
8990       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8991   }\r
8992 }\r
8993 \r
8994 \r
8995 VOID\r
8996 SetUserThinkingEnables()\r
8997 {\r
8998   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8999 }\r
9000 \r
9001 VOID\r
9002 SetMachineThinkingEnables()\r
9003 {\r
9004   HMENU hMenu = GetMenu(hwndMain);\r
9005   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9006 \r
9007   SetMenuEnables(hMenu, machineThinkingEnables);\r
9008 \r
9009   if (gameMode == MachinePlaysBlack) {\r
9010     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9011   } else if (gameMode == MachinePlaysWhite) {\r
9012     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9013   } else if (gameMode == TwoMachinesPlay) {\r
9014     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9015   }\r
9016 }\r
9017 \r
9018 \r
9019 VOID\r
9020 DisplayTitle(char *str)\r
9021 {\r
9022   char title[MSG_SIZ], *host;\r
9023   if (str[0] != NULLCHAR) {\r
9024     strcpy(title, str);\r
9025   } else if (appData.icsActive) {\r
9026     if (appData.icsCommPort[0] != NULLCHAR)\r
9027       host = "ICS";\r
9028     else \r
9029       host = appData.icsHost;\r
9030     sprintf(title, "%s: %s", szTitle, host);\r
9031   } else if (appData.noChessProgram) {\r
9032     strcpy(title, szTitle);\r
9033   } else {\r
9034     strcpy(title, szTitle);\r
9035     strcat(title, ": ");\r
9036     strcat(title, first.tidy);\r
9037   }\r
9038   SetWindowText(hwndMain, title);\r
9039 }\r
9040 \r
9041 \r
9042 VOID\r
9043 DisplayMessage(char *str1, char *str2)\r
9044 {\r
9045   HDC hdc;\r
9046   HFONT oldFont;\r
9047   int remain = MESSAGE_TEXT_MAX - 1;\r
9048   int len;\r
9049 \r
9050   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9051   messageText[0] = NULLCHAR;\r
9052   if (*str1) {\r
9053     len = strlen(str1);\r
9054     if (len > remain) len = remain;\r
9055     strncpy(messageText, str1, len);\r
9056     messageText[len] = NULLCHAR;\r
9057     remain -= len;\r
9058   }\r
9059   if (*str2 && remain >= 2) {\r
9060     if (*str1) {\r
9061       strcat(messageText, "  ");\r
9062       remain -= 2;\r
9063     }\r
9064     len = strlen(str2);\r
9065     if (len > remain) len = remain;\r
9066     strncat(messageText, str2, len);\r
9067   }\r
9068   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9069 \r
9070   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9071 \r
9072   SAYMACHINEMOVE();\r
9073 \r
9074   hdc = GetDC(hwndMain);\r
9075   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9076   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9077              &messageRect, messageText, strlen(messageText), NULL);\r
9078   (void) SelectObject(hdc, oldFont);\r
9079   (void) ReleaseDC(hwndMain, hdc);\r
9080 }\r
9081 \r
9082 VOID\r
9083 DisplayError(char *str, int error)\r
9084 {\r
9085   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9086   int len;\r
9087 \r
9088   if (error == 0) {\r
9089     strcpy(buf, str);\r
9090   } else {\r
9091     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9092                         NULL, error, LANG_NEUTRAL,\r
9093                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9094     if (len > 0) {\r
9095       sprintf(buf, "%s:\n%s", str, buf2);\r
9096     } else {\r
9097       ErrorMap *em = errmap;\r
9098       while (em->err != 0 && em->err != error) em++;\r
9099       if (em->err != 0) {\r
9100         sprintf(buf, "%s:\n%s", str, em->msg);\r
9101       } else {\r
9102         sprintf(buf, "%s:\nError code %d", str, error);\r
9103       }\r
9104     }\r
9105   }\r
9106   \r
9107   ErrorPopUp("Error", buf);\r
9108 }\r
9109 \r
9110 \r
9111 VOID\r
9112 DisplayMoveError(char *str)\r
9113 {\r
9114   fromX = fromY = -1;\r
9115   ClearHighlights();\r
9116   DrawPosition(FALSE, NULL);\r
9117   if (appData.popupMoveErrors) {\r
9118     ErrorPopUp("Error", str);\r
9119   } else {\r
9120     DisplayMessage(str, "");\r
9121     moveErrorMessageUp = TRUE;\r
9122   }\r
9123 }\r
9124 \r
9125 VOID\r
9126 DisplayFatalError(char *str, int error, int exitStatus)\r
9127 {\r
9128   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9129   int len;\r
9130   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9131 \r
9132   if (error != 0) {\r
9133     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9134                         NULL, error, LANG_NEUTRAL,\r
9135                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9136     if (len > 0) {\r
9137       sprintf(buf, "%s:\n%s", str, buf2);\r
9138     } else {\r
9139       ErrorMap *em = errmap;\r
9140       while (em->err != 0 && em->err != error) em++;\r
9141       if (em->err != 0) {\r
9142         sprintf(buf, "%s:\n%s", str, em->msg);\r
9143       } else {\r
9144         sprintf(buf, "%s:\nError code %d", str, error);\r
9145       }\r
9146     }\r
9147     str = buf;\r
9148   }\r
9149   if (appData.debugMode) {\r
9150     fprintf(debugFP, "%s: %s\n", label, str);\r
9151   }\r
9152   if (appData.popupExitMessage) {\r
9153     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9154                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9155   }\r
9156   ExitEvent(exitStatus);\r
9157 }\r
9158 \r
9159 \r
9160 VOID\r
9161 DisplayInformation(char *str)\r
9162 {\r
9163   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9164 }\r
9165 \r
9166 \r
9167 VOID\r
9168 DisplayNote(char *str)\r
9169 {\r
9170   ErrorPopUp("Note", str);\r
9171 }\r
9172 \r
9173 \r
9174 typedef struct {\r
9175   char *title, *question, *replyPrefix;\r
9176   ProcRef pr;\r
9177 } QuestionParams;\r
9178 \r
9179 LRESULT CALLBACK\r
9180 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9181 {\r
9182   static QuestionParams *qp;\r
9183   char reply[MSG_SIZ];\r
9184   int len, err;\r
9185 \r
9186   switch (message) {\r
9187   case WM_INITDIALOG:\r
9188     qp = (QuestionParams *) lParam;\r
9189     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9190     SetWindowText(hDlg, qp->title);\r
9191     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9192     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9193     return FALSE;\r
9194 \r
9195   case WM_COMMAND:\r
9196     switch (LOWORD(wParam)) {\r
9197     case IDOK:\r
9198       strcpy(reply, qp->replyPrefix);\r
9199       if (*reply) strcat(reply, " ");\r
9200       len = strlen(reply);\r
9201       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9202       strcat(reply, "\n");\r
9203       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9204       EndDialog(hDlg, TRUE);\r
9205       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9206       return TRUE;\r
9207     case IDCANCEL:\r
9208       EndDialog(hDlg, FALSE);\r
9209       return TRUE;\r
9210     default:\r
9211       break;\r
9212     }\r
9213     break;\r
9214   }\r
9215   return FALSE;\r
9216 }\r
9217 \r
9218 VOID\r
9219 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9220 {\r
9221     QuestionParams qp;\r
9222     FARPROC lpProc;\r
9223     \r
9224     qp.title = title;\r
9225     qp.question = question;\r
9226     qp.replyPrefix = replyPrefix;\r
9227     qp.pr = pr;\r
9228     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9229     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9230       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9231     FreeProcInstance(lpProc);\r
9232 }\r
9233 \r
9234 /* [AS] Pick FRC position */\r
9235 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9236 {\r
9237     static int * lpIndexFRC;\r
9238     BOOL index_is_ok;\r
9239     char buf[16];\r
9240 \r
9241     switch( message )\r
9242     {\r
9243     case WM_INITDIALOG:\r
9244         lpIndexFRC = (int *) lParam;\r
9245 \r
9246         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9247 \r
9248         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9249         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9250         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9251         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9252 \r
9253         break;\r
9254 \r
9255     case WM_COMMAND:\r
9256         switch( LOWORD(wParam) ) {\r
9257         case IDOK:\r
9258             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9259             EndDialog( hDlg, 0 );\r
9260             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9261             return TRUE;\r
9262         case IDCANCEL:\r
9263             EndDialog( hDlg, 1 );   \r
9264             return TRUE;\r
9265         case IDC_NFG_Edit:\r
9266             if( HIWORD(wParam) == EN_CHANGE ) {\r
9267                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9268 \r
9269                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9270             }\r
9271             return TRUE;\r
9272         case IDC_NFG_Random:\r
9273             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9274             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9275             return TRUE;\r
9276         }\r
9277 \r
9278         break;\r
9279     }\r
9280 \r
9281     return FALSE;\r
9282 }\r
9283 \r
9284 int NewGameFRC()\r
9285 {\r
9286     int result;\r
9287     int index = appData.defaultFrcPosition;\r
9288     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9289 \r
9290     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9291 \r
9292     if( result == 0 ) {\r
9293         appData.defaultFrcPosition = index;\r
9294     }\r
9295 \r
9296     return result;\r
9297 }\r
9298 \r
9299 /* [AS] Game list options */\r
9300 typedef struct {\r
9301     char id;\r
9302     char * name;\r
9303 } GLT_Item;\r
9304 \r
9305 static GLT_Item GLT_ItemInfo[] = {\r
9306     { GLT_EVENT,      "Event" },\r
9307     { GLT_SITE,       "Site" },\r
9308     { GLT_DATE,       "Date" },\r
9309     { GLT_ROUND,      "Round" },\r
9310     { GLT_PLAYERS,    "Players" },\r
9311     { GLT_RESULT,     "Result" },\r
9312     { GLT_WHITE_ELO,  "White Rating" },\r
9313     { GLT_BLACK_ELO,  "Black Rating" },\r
9314     { GLT_TIME_CONTROL,"Time Control" },\r
9315     { GLT_VARIANT,    "Variant" },\r
9316     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9317     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9318     { 0, 0 }\r
9319 };\r
9320 \r
9321 const char * GLT_FindItem( char id )\r
9322 {\r
9323     const char * result = 0;\r
9324 \r
9325     GLT_Item * list = GLT_ItemInfo;\r
9326 \r
9327     while( list->id != 0 ) {\r
9328         if( list->id == id ) {\r
9329             result = list->name;\r
9330             break;\r
9331         }\r
9332 \r
9333         list++;\r
9334     }\r
9335 \r
9336     return result;\r
9337 }\r
9338 \r
9339 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9340 {\r
9341     const char * name = GLT_FindItem( id );\r
9342 \r
9343     if( name != 0 ) {\r
9344         if( index >= 0 ) {\r
9345             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9346         }\r
9347         else {\r
9348             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9349         }\r
9350     }\r
9351 }\r
9352 \r
9353 void GLT_TagsToList( HWND hDlg, char * tags )\r
9354 {\r
9355     char * pc = tags;\r
9356 \r
9357     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9358 \r
9359     while( *pc ) {\r
9360         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9361         pc++;\r
9362     }\r
9363 \r
9364     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9365 \r
9366     pc = GLT_ALL_TAGS;\r
9367 \r
9368     while( *pc ) {\r
9369         if( strchr( tags, *pc ) == 0 ) {\r
9370             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9371         }\r
9372         pc++;\r
9373     }\r
9374 \r
9375     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9376 }\r
9377 \r
9378 char GLT_ListItemToTag( HWND hDlg, int index )\r
9379 {\r
9380     char result = '\0';\r
9381     char name[128];\r
9382 \r
9383     GLT_Item * list = GLT_ItemInfo;\r
9384 \r
9385     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9386         while( list->id != 0 ) {\r
9387             if( strcmp( list->name, name ) == 0 ) {\r
9388                 result = list->id;\r
9389                 break;\r
9390             }\r
9391 \r
9392             list++;\r
9393         }\r
9394     }\r
9395 \r
9396     return result;\r
9397 }\r
9398 \r
9399 void GLT_MoveSelection( HWND hDlg, int delta )\r
9400 {\r
9401     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9402     int idx2 = idx1 + delta;\r
9403     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9404 \r
9405     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9406         char buf[128];\r
9407 \r
9408         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9409         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9410         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9411         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9412     }\r
9413 }\r
9414 \r
9415 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9416 {\r
9417     static char glt[64];\r
9418     static char * lpUserGLT;\r
9419 \r
9420     switch( message )\r
9421     {\r
9422     case WM_INITDIALOG:\r
9423         lpUserGLT = (char *) lParam;\r
9424         \r
9425         strcpy( glt, lpUserGLT );\r
9426 \r
9427         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9428 \r
9429         /* Initialize list */\r
9430         GLT_TagsToList( hDlg, glt );\r
9431 \r
9432         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9433 \r
9434         break;\r
9435 \r
9436     case WM_COMMAND:\r
9437         switch( LOWORD(wParam) ) {\r
9438         case IDOK:\r
9439             {\r
9440                 char * pc = lpUserGLT;\r
9441                 int idx = 0;\r
9442 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9443                 char id;\r
9444 \r
9445                 do {\r
9446                     id = GLT_ListItemToTag( hDlg, idx );\r
9447 \r
9448                     *pc++ = id;\r
9449                     idx++;\r
9450                 } while( id != '\0' );\r
9451             }\r
9452             EndDialog( hDlg, 0 );\r
9453             return TRUE;\r
9454         case IDCANCEL:\r
9455             EndDialog( hDlg, 1 );\r
9456             return TRUE;\r
9457 \r
9458         case IDC_GLT_Default:\r
9459             strcpy( glt, GLT_DEFAULT_TAGS );\r
9460             GLT_TagsToList( hDlg, glt );\r
9461             return TRUE;\r
9462 \r
9463         case IDC_GLT_Restore:\r
9464             strcpy( glt, lpUserGLT );\r
9465             GLT_TagsToList( hDlg, glt );\r
9466             return TRUE;\r
9467 \r
9468         case IDC_GLT_Up:\r
9469             GLT_MoveSelection( hDlg, -1 );\r
9470             return TRUE;\r
9471 \r
9472         case IDC_GLT_Down:\r
9473             GLT_MoveSelection( hDlg, +1 );\r
9474             return TRUE;\r
9475         }\r
9476 \r
9477         break;\r
9478     }\r
9479 \r
9480     return FALSE;\r
9481 }\r
9482 \r
9483 int GameListOptions()\r
9484 {\r
9485     char glt[64];\r
9486     int result;\r
9487     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9488 \r
9489     strcpy( glt, appData.gameListTags );\r
9490 \r
9491     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9492 \r
9493     if( result == 0 ) {\r
9494         /* [AS] Memory leak here! */\r
9495         appData.gameListTags = strdup( glt ); \r
9496     }\r
9497 \r
9498     return result;\r
9499 }\r
9500 \r
9501 \r
9502 VOID\r
9503 DisplayIcsInteractionTitle(char *str)\r
9504 {\r
9505   char consoleTitle[MSG_SIZ];\r
9506 \r
9507   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9508   SetWindowText(hwndConsole, consoleTitle);\r
9509 }\r
9510 \r
9511 void\r
9512 DrawPosition(int fullRedraw, Board board)\r
9513 {\r
9514   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9515 }\r
9516 \r
9517 \r
9518 VOID\r
9519 ResetFrontEnd()\r
9520 {\r
9521   fromX = fromY = -1;\r
9522   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9523     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9524     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9525     dragInfo.lastpos = dragInfo.pos;\r
9526     dragInfo.start.x = dragInfo.start.y = -1;\r
9527     dragInfo.from = dragInfo.start;\r
9528     ReleaseCapture();\r
9529     DrawPosition(TRUE, NULL);\r
9530   }\r
9531 }\r
9532 \r
9533 \r
9534 VOID\r
9535 CommentPopUp(char *title, char *str)\r
9536 {\r
9537   HWND hwnd = GetActiveWindow();\r
9538   EitherCommentPopUp(0, title, str, FALSE);\r
9539   SAY(str);\r
9540   SetActiveWindow(hwnd);\r
9541 }\r
9542 \r
9543 VOID\r
9544 CommentPopDown(void)\r
9545 {\r
9546   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9547   if (commentDialog) {\r
9548     ShowWindow(commentDialog, SW_HIDE);\r
9549   }\r
9550   commentDialogUp = FALSE;\r
9551 }\r
9552 \r
9553 VOID\r
9554 EditCommentPopUp(int index, char *title, char *str)\r
9555 {\r
9556   EitherCommentPopUp(index, title, str, TRUE);\r
9557 }\r
9558 \r
9559 \r
9560 VOID\r
9561 RingBell()\r
9562 {\r
9563   MyPlaySound(&sounds[(int)SoundMove]);\r
9564 }\r
9565 \r
9566 VOID PlayIcsWinSound()\r
9567 {\r
9568   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9569 }\r
9570 \r
9571 VOID PlayIcsLossSound()\r
9572 {\r
9573   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9574 }\r
9575 \r
9576 VOID PlayIcsDrawSound()\r
9577 {\r
9578   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9579 }\r
9580 \r
9581 VOID PlayIcsUnfinishedSound()\r
9582 {\r
9583   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9584 }\r
9585 \r
9586 VOID\r
9587 PlayAlarmSound()\r
9588 {\r
9589   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9590 }\r
9591 \r
9592 \r
9593 VOID\r
9594 EchoOn()\r
9595 {\r
9596   HWND hInput;\r
9597   consoleEcho = TRUE;\r
9598   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9599   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9600   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9601 }\r
9602 \r
9603 \r
9604 VOID\r
9605 EchoOff()\r
9606 {\r
9607   CHARFORMAT cf;\r
9608   HWND hInput;\r
9609   consoleEcho = FALSE;\r
9610   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9611   /* This works OK: set text and background both to the same color */\r
9612   cf = consoleCF;\r
9613   cf.crTextColor = COLOR_ECHOOFF;\r
9614   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9615   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9616 }\r
9617 \r
9618 /* No Raw()...? */\r
9619 \r
9620 void Colorize(ColorClass cc, int continuation)\r
9621 {\r
9622   currentColorClass = cc;\r
9623   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9624   consoleCF.crTextColor = textAttribs[cc].color;\r
9625   consoleCF.dwEffects = textAttribs[cc].effects;\r
9626   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9627 }\r
9628 \r
9629 char *\r
9630 UserName()\r
9631 {\r
9632   static char buf[MSG_SIZ];\r
9633   DWORD bufsiz = MSG_SIZ;\r
9634 \r
9635   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9636         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9637   }\r
9638   if (!GetUserName(buf, &bufsiz)) {\r
9639     /*DisplayError("Error getting user name", GetLastError());*/\r
9640     strcpy(buf, "User");\r
9641   }\r
9642   return buf;\r
9643 }\r
9644 \r
9645 char *\r
9646 HostName()\r
9647 {\r
9648   static char buf[MSG_SIZ];\r
9649   DWORD bufsiz = MSG_SIZ;\r
9650 \r
9651   if (!GetComputerName(buf, &bufsiz)) {\r
9652     /*DisplayError("Error getting host name", GetLastError());*/\r
9653     strcpy(buf, "Unknown");\r
9654   }\r
9655   return buf;\r
9656 }\r
9657 \r
9658 \r
9659 int\r
9660 ClockTimerRunning()\r
9661 {\r
9662   return clockTimerEvent != 0;\r
9663 }\r
9664 \r
9665 int\r
9666 StopClockTimer()\r
9667 {\r
9668   if (clockTimerEvent == 0) return FALSE;\r
9669   KillTimer(hwndMain, clockTimerEvent);\r
9670   clockTimerEvent = 0;\r
9671   return TRUE;\r
9672 }\r
9673 \r
9674 void\r
9675 StartClockTimer(long millisec)\r
9676 {\r
9677   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9678                              (UINT) millisec, NULL);\r
9679 }\r
9680 \r
9681 void\r
9682 DisplayWhiteClock(long timeRemaining, int highlight)\r
9683 {\r
9684   HDC hdc;\r
9685   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9686 \r
9687   if(appData.noGUI) return;\r
9688   hdc = GetDC(hwndMain);\r
9689   if (!IsIconic(hwndMain)) {\r
9690     DisplayAClock(hdc, timeRemaining, highlight, \r
9691                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9692   }\r
9693   if (highlight && iconCurrent == iconBlack) {\r
9694     iconCurrent = iconWhite;\r
9695     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9696     if (IsIconic(hwndMain)) {\r
9697       DrawIcon(hdc, 2, 2, iconCurrent);\r
9698     }\r
9699   }\r
9700   (void) ReleaseDC(hwndMain, hdc);\r
9701   if (hwndConsole)\r
9702     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9703 }\r
9704 \r
9705 void\r
9706 DisplayBlackClock(long timeRemaining, int highlight)\r
9707 {\r
9708   HDC hdc;\r
9709   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9710 \r
9711   if(appData.noGUI) return;\r
9712   hdc = GetDC(hwndMain);\r
9713   if (!IsIconic(hwndMain)) {\r
9714     DisplayAClock(hdc, timeRemaining, highlight, \r
9715                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9716   }\r
9717   if (highlight && iconCurrent == iconWhite) {\r
9718     iconCurrent = iconBlack;\r
9719     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9720     if (IsIconic(hwndMain)) {\r
9721       DrawIcon(hdc, 2, 2, iconCurrent);\r
9722     }\r
9723   }\r
9724   (void) ReleaseDC(hwndMain, hdc);\r
9725   if (hwndConsole)\r
9726     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9727 }\r
9728 \r
9729 \r
9730 int\r
9731 LoadGameTimerRunning()\r
9732 {\r
9733   return loadGameTimerEvent != 0;\r
9734 }\r
9735 \r
9736 int\r
9737 StopLoadGameTimer()\r
9738 {\r
9739   if (loadGameTimerEvent == 0) return FALSE;\r
9740   KillTimer(hwndMain, loadGameTimerEvent);\r
9741   loadGameTimerEvent = 0;\r
9742   return TRUE;\r
9743 }\r
9744 \r
9745 void\r
9746 StartLoadGameTimer(long millisec)\r
9747 {\r
9748   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9749                                 (UINT) millisec, NULL);\r
9750 }\r
9751 \r
9752 void\r
9753 AutoSaveGame()\r
9754 {\r
9755   char *defName;\r
9756   FILE *f;\r
9757   char fileTitle[MSG_SIZ];\r
9758 \r
9759   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9760   f = OpenFileDialog(hwndMain, "a", defName,\r
9761                      appData.oldSaveStyle ? "gam" : "pgn",\r
9762                      GAME_FILT, \r
9763                      "Save Game to File", NULL, fileTitle, NULL);\r
9764   if (f != NULL) {\r
9765     SaveGame(f, 0, "");\r
9766     fclose(f);\r
9767   }\r
9768 }\r
9769 \r
9770 \r
9771 void\r
9772 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9773 {\r
9774   if (delayedTimerEvent != 0) {\r
9775     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9776       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9777     }\r
9778     KillTimer(hwndMain, delayedTimerEvent);\r
9779     delayedTimerEvent = 0;\r
9780     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9781     delayedTimerCallback();\r
9782   }\r
9783   delayedTimerCallback = cb;\r
9784   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9785                                 (UINT) millisec, NULL);\r
9786 }\r
9787 \r
9788 DelayedEventCallback\r
9789 GetDelayedEvent()\r
9790 {\r
9791   if (delayedTimerEvent) {\r
9792     return delayedTimerCallback;\r
9793   } else {\r
9794     return NULL;\r
9795   }\r
9796 }\r
9797 \r
9798 void\r
9799 CancelDelayedEvent()\r
9800 {\r
9801   if (delayedTimerEvent) {\r
9802     KillTimer(hwndMain, delayedTimerEvent);\r
9803     delayedTimerEvent = 0;\r
9804   }\r
9805 }\r
9806 \r
9807 DWORD GetWin32Priority(int nice)\r
9808 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9809 /*\r
9810 REALTIME_PRIORITY_CLASS     0x00000100\r
9811 HIGH_PRIORITY_CLASS         0x00000080\r
9812 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9813 NORMAL_PRIORITY_CLASS       0x00000020\r
9814 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9815 IDLE_PRIORITY_CLASS         0x00000040\r
9816 */\r
9817         if (nice < -15) return 0x00000080;\r
9818         if (nice < 0)   return 0x00008000;\r
9819         if (nice == 0)  return 0x00000020;\r
9820         if (nice < 15)  return 0x00004000;\r
9821         return 0x00000040;\r
9822 }\r
9823 \r
9824 /* Start a child process running the given program.\r
9825    The process's standard output can be read from "from", and its\r
9826    standard input can be written to "to".\r
9827    Exit with fatal error if anything goes wrong.\r
9828    Returns an opaque pointer that can be used to destroy the process\r
9829    later.\r
9830 */\r
9831 int\r
9832 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9833 {\r
9834 #define BUFSIZE 4096\r
9835 \r
9836   HANDLE hChildStdinRd, hChildStdinWr,\r
9837     hChildStdoutRd, hChildStdoutWr;\r
9838   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9839   SECURITY_ATTRIBUTES saAttr;\r
9840   BOOL fSuccess;\r
9841   PROCESS_INFORMATION piProcInfo;\r
9842   STARTUPINFO siStartInfo;\r
9843   ChildProc *cp;\r
9844   char buf[MSG_SIZ];\r
9845   DWORD err;\r
9846 \r
9847   if (appData.debugMode) {\r
9848     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9849   }\r
9850 \r
9851   *pr = NoProc;\r
9852 \r
9853   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9854   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9855   saAttr.bInheritHandle = TRUE;\r
9856   saAttr.lpSecurityDescriptor = NULL;\r
9857 \r
9858   /*\r
9859    * The steps for redirecting child's STDOUT:\r
9860    *     1. Create anonymous pipe to be STDOUT for child.\r
9861    *     2. Create a noninheritable duplicate of read handle,\r
9862    *         and close the inheritable read handle.\r
9863    */\r
9864 \r
9865   /* Create a pipe for the child's STDOUT. */\r
9866   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9867     return GetLastError();\r
9868   }\r
9869 \r
9870   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9871   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9872                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9873                              FALSE,     /* not inherited */\r
9874                              DUPLICATE_SAME_ACCESS);\r
9875   if (! fSuccess) {\r
9876     return GetLastError();\r
9877   }\r
9878   CloseHandle(hChildStdoutRd);\r
9879 \r
9880   /*\r
9881    * The steps for redirecting child's STDIN:\r
9882    *     1. Create anonymous pipe to be STDIN for child.\r
9883    *     2. Create a noninheritable duplicate of write handle,\r
9884    *         and close the inheritable write handle.\r
9885    */\r
9886 \r
9887   /* Create a pipe for the child's STDIN. */\r
9888   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9889     return GetLastError();\r
9890   }\r
9891 \r
9892   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9893   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9894                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9895                              FALSE,     /* not inherited */\r
9896                              DUPLICATE_SAME_ACCESS);\r
9897   if (! fSuccess) {\r
9898     return GetLastError();\r
9899   }\r
9900   CloseHandle(hChildStdinWr);\r
9901 \r
9902   /* Arrange to (1) look in dir for the child .exe file, and\r
9903    * (2) have dir be the child's working directory.  Interpret\r
9904    * dir relative to the directory WinBoard loaded from. */\r
9905   GetCurrentDirectory(MSG_SIZ, buf);\r
9906   SetCurrentDirectory(installDir);\r
9907   SetCurrentDirectory(dir);\r
9908 \r
9909   /* Now create the child process. */\r
9910 \r
9911   siStartInfo.cb = sizeof(STARTUPINFO);\r
9912   siStartInfo.lpReserved = NULL;\r
9913   siStartInfo.lpDesktop = NULL;\r
9914   siStartInfo.lpTitle = NULL;\r
9915   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9916   siStartInfo.cbReserved2 = 0;\r
9917   siStartInfo.lpReserved2 = NULL;\r
9918   siStartInfo.hStdInput = hChildStdinRd;\r
9919   siStartInfo.hStdOutput = hChildStdoutWr;\r
9920   siStartInfo.hStdError = hChildStdoutWr;\r
9921 \r
9922   fSuccess = CreateProcess(NULL,\r
9923                            cmdLine,        /* command line */\r
9924                            NULL,           /* process security attributes */\r
9925                            NULL,           /* primary thread security attrs */\r
9926                            TRUE,           /* handles are inherited */\r
9927                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9928                            NULL,           /* use parent's environment */\r
9929                            NULL,\r
9930                            &siStartInfo, /* STARTUPINFO pointer */\r
9931                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9932 \r
9933   err = GetLastError();\r
9934   SetCurrentDirectory(buf); /* return to prev directory */\r
9935   if (! fSuccess) {\r
9936     return err;\r
9937   }\r
9938 \r
9939   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9940     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9941     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9942   }\r
9943 \r
9944   /* Close the handles we don't need in the parent */\r
9945   CloseHandle(piProcInfo.hThread);\r
9946   CloseHandle(hChildStdinRd);\r
9947   CloseHandle(hChildStdoutWr);\r
9948 \r
9949   /* Prepare return value */\r
9950   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9951   cp->kind = CPReal;\r
9952   cp->hProcess = piProcInfo.hProcess;\r
9953   cp->pid = piProcInfo.dwProcessId;\r
9954   cp->hFrom = hChildStdoutRdDup;\r
9955   cp->hTo = hChildStdinWrDup;\r
9956 \r
9957   *pr = (void *) cp;\r
9958 \r
9959   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9960      2000 where engines sometimes don't see the initial command(s)\r
9961      from WinBoard and hang.  I don't understand how that can happen,\r
9962      but the Sleep is harmless, so I've put it in.  Others have also\r
9963      reported what may be the same problem, so hopefully this will fix\r
9964      it for them too.  */\r
9965   Sleep(500);\r
9966 \r
9967   return NO_ERROR;\r
9968 }\r
9969 \r
9970 \r
9971 void\r
9972 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9973 {\r
9974   ChildProc *cp; int result;\r
9975 \r
9976   cp = (ChildProc *) pr;\r
9977   if (cp == NULL) return;\r
9978 \r
9979   switch (cp->kind) {\r
9980   case CPReal:\r
9981     /* TerminateProcess is considered harmful, so... */\r
9982     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9983     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9984     /* The following doesn't work because the chess program\r
9985        doesn't "have the same console" as WinBoard.  Maybe\r
9986        we could arrange for this even though neither WinBoard\r
9987        nor the chess program uses a console for stdio? */\r
9988     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9989 \r
9990     /* [AS] Special termination modes for misbehaving programs... */\r
9991     if( signal == 9 ) { \r
9992         result = TerminateProcess( cp->hProcess, 0 );\r
9993 \r
9994         if ( appData.debugMode) {\r
9995             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9996         }\r
9997     }\r
9998     else if( signal == 10 ) {\r
9999         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10000 \r
10001         if( dw != WAIT_OBJECT_0 ) {\r
10002             result = TerminateProcess( cp->hProcess, 0 );\r
10003 \r
10004             if ( appData.debugMode) {\r
10005                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10006             }\r
10007 \r
10008         }\r
10009     }\r
10010 \r
10011     CloseHandle(cp->hProcess);\r
10012     break;\r
10013 \r
10014   case CPComm:\r
10015     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10016     break;\r
10017 \r
10018   case CPSock:\r
10019     closesocket(cp->sock);\r
10020     WSACleanup();\r
10021     break;\r
10022 \r
10023   case CPRcmd:\r
10024     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10025     closesocket(cp->sock);\r
10026     closesocket(cp->sock2);\r
10027     WSACleanup();\r
10028     break;\r
10029   }\r
10030   free(cp);\r
10031 }\r
10032 \r
10033 void\r
10034 InterruptChildProcess(ProcRef pr)\r
10035 {\r
10036   ChildProc *cp;\r
10037 \r
10038   cp = (ChildProc *) pr;\r
10039   if (cp == NULL) return;\r
10040   switch (cp->kind) {\r
10041   case CPReal:\r
10042     /* The following doesn't work because the chess program\r
10043        doesn't "have the same console" as WinBoard.  Maybe\r
10044        we could arrange for this even though neither WinBoard\r
10045        nor the chess program uses a console for stdio */\r
10046     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10047     break;\r
10048 \r
10049   case CPComm:\r
10050   case CPSock:\r
10051     /* Can't interrupt */\r
10052     break;\r
10053 \r
10054   case CPRcmd:\r
10055     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10056     break;\r
10057   }\r
10058 }\r
10059 \r
10060 \r
10061 int\r
10062 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10063 {\r
10064   char cmdLine[MSG_SIZ];\r
10065 \r
10066   if (port[0] == NULLCHAR) {\r
10067     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10068   } else {\r
10069     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10070   }\r
10071   return StartChildProcess(cmdLine, "", pr);\r
10072 }\r
10073 \r
10074 \r
10075 /* Code to open TCP sockets */\r
10076 \r
10077 int\r
10078 OpenTCP(char *host, char *port, ProcRef *pr)\r
10079 {\r
10080   ChildProc *cp;\r
10081   int err;\r
10082   SOCKET s;\r
10083   struct sockaddr_in sa, mysa;\r
10084   struct hostent FAR *hp;\r
10085   unsigned short uport;\r
10086   WORD wVersionRequested;\r
10087   WSADATA wsaData;\r
10088 \r
10089   /* Initialize socket DLL */\r
10090   wVersionRequested = MAKEWORD(1, 1);\r
10091   err = WSAStartup(wVersionRequested, &wsaData);\r
10092   if (err != 0) return err;\r
10093 \r
10094   /* Make socket */\r
10095   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10096     err = WSAGetLastError();\r
10097     WSACleanup();\r
10098     return err;\r
10099   }\r
10100 \r
10101   /* Bind local address using (mostly) don't-care values.\r
10102    */\r
10103   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10104   mysa.sin_family = AF_INET;\r
10105   mysa.sin_addr.s_addr = INADDR_ANY;\r
10106   uport = (unsigned short) 0;\r
10107   mysa.sin_port = htons(uport);\r
10108   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10109       == SOCKET_ERROR) {\r
10110     err = WSAGetLastError();\r
10111     WSACleanup();\r
10112     return err;\r
10113   }\r
10114 \r
10115   /* Resolve remote host name */\r
10116   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10117   if (!(hp = gethostbyname(host))) {\r
10118     unsigned int b0, b1, b2, b3;\r
10119 \r
10120     err = WSAGetLastError();\r
10121 \r
10122     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10123       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10124       hp->h_addrtype = AF_INET;\r
10125       hp->h_length = 4;\r
10126       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10127       hp->h_addr_list[0] = (char *) malloc(4);\r
10128       hp->h_addr_list[0][0] = (char) b0;\r
10129       hp->h_addr_list[0][1] = (char) b1;\r
10130       hp->h_addr_list[0][2] = (char) b2;\r
10131       hp->h_addr_list[0][3] = (char) b3;\r
10132     } else {\r
10133       WSACleanup();\r
10134       return err;\r
10135     }\r
10136   }\r
10137   sa.sin_family = hp->h_addrtype;\r
10138   uport = (unsigned short) atoi(port);\r
10139   sa.sin_port = htons(uport);\r
10140   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10141 \r
10142   /* Make connection */\r
10143   if (connect(s, (struct sockaddr *) &sa,\r
10144               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10145     err = WSAGetLastError();\r
10146     WSACleanup();\r
10147     return err;\r
10148   }\r
10149 \r
10150   /* Prepare return value */\r
10151   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10152   cp->kind = CPSock;\r
10153   cp->sock = s;\r
10154   *pr = (ProcRef *) cp;\r
10155 \r
10156   return NO_ERROR;\r
10157 }\r
10158 \r
10159 int\r
10160 OpenCommPort(char *name, ProcRef *pr)\r
10161 {\r
10162   HANDLE h;\r
10163   COMMTIMEOUTS ct;\r
10164   ChildProc *cp;\r
10165   char fullname[MSG_SIZ];\r
10166 \r
10167   if (*name != '\\')\r
10168     sprintf(fullname, "\\\\.\\%s", name);\r
10169   else\r
10170     strcpy(fullname, name);\r
10171 \r
10172   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10173                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10174   if (h == (HANDLE) -1) {\r
10175     return GetLastError();\r
10176   }\r
10177   hCommPort = h;\r
10178 \r
10179   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10180 \r
10181   /* Accumulate characters until a 100ms pause, then parse */\r
10182   ct.ReadIntervalTimeout = 100;\r
10183   ct.ReadTotalTimeoutMultiplier = 0;\r
10184   ct.ReadTotalTimeoutConstant = 0;\r
10185   ct.WriteTotalTimeoutMultiplier = 0;\r
10186   ct.WriteTotalTimeoutConstant = 0;\r
10187   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10188 \r
10189   /* Prepare return value */\r
10190   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10191   cp->kind = CPComm;\r
10192   cp->hFrom = h;\r
10193   cp->hTo = h;\r
10194   *pr = (ProcRef *) cp;\r
10195 \r
10196   return NO_ERROR;\r
10197 }\r
10198 \r
10199 int\r
10200 OpenLoopback(ProcRef *pr)\r
10201 {\r
10202   DisplayFatalError("Not implemented", 0, 1);\r
10203   return NO_ERROR;\r
10204 }\r
10205 \r
10206 \r
10207 int\r
10208 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10209 {\r
10210   ChildProc *cp;\r
10211   int err;\r
10212   SOCKET s, s2, s3;\r
10213   struct sockaddr_in sa, mysa;\r
10214   struct hostent FAR *hp;\r
10215   unsigned short uport;\r
10216   WORD wVersionRequested;\r
10217   WSADATA wsaData;\r
10218   int fromPort;\r
10219   char stderrPortStr[MSG_SIZ];\r
10220 \r
10221   /* Initialize socket DLL */\r
10222   wVersionRequested = MAKEWORD(1, 1);\r
10223   err = WSAStartup(wVersionRequested, &wsaData);\r
10224   if (err != 0) return err;\r
10225 \r
10226   /* Resolve remote host name */\r
10227   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10228   if (!(hp = gethostbyname(host))) {\r
10229     unsigned int b0, b1, b2, b3;\r
10230 \r
10231     err = WSAGetLastError();\r
10232 \r
10233     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10234       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10235       hp->h_addrtype = AF_INET;\r
10236       hp->h_length = 4;\r
10237       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10238       hp->h_addr_list[0] = (char *) malloc(4);\r
10239       hp->h_addr_list[0][0] = (char) b0;\r
10240       hp->h_addr_list[0][1] = (char) b1;\r
10241       hp->h_addr_list[0][2] = (char) b2;\r
10242       hp->h_addr_list[0][3] = (char) b3;\r
10243     } else {\r
10244       WSACleanup();\r
10245       return err;\r
10246     }\r
10247   }\r
10248   sa.sin_family = hp->h_addrtype;\r
10249   uport = (unsigned short) 514;\r
10250   sa.sin_port = htons(uport);\r
10251   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10252 \r
10253   /* Bind local socket to unused "privileged" port address\r
10254    */\r
10255   s = INVALID_SOCKET;\r
10256   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10257   mysa.sin_family = AF_INET;\r
10258   mysa.sin_addr.s_addr = INADDR_ANY;\r
10259   for (fromPort = 1023;; fromPort--) {\r
10260     if (fromPort < 0) {\r
10261       WSACleanup();\r
10262       return WSAEADDRINUSE;\r
10263     }\r
10264     if (s == INVALID_SOCKET) {\r
10265       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10266         err = WSAGetLastError();\r
10267         WSACleanup();\r
10268         return err;\r
10269       }\r
10270     }\r
10271     uport = (unsigned short) fromPort;\r
10272     mysa.sin_port = htons(uport);\r
10273     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10274         == SOCKET_ERROR) {\r
10275       err = WSAGetLastError();\r
10276       if (err == WSAEADDRINUSE) continue;\r
10277       WSACleanup();\r
10278       return err;\r
10279     }\r
10280     if (connect(s, (struct sockaddr *) &sa,\r
10281       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10282       err = WSAGetLastError();\r
10283       if (err == WSAEADDRINUSE) {\r
10284         closesocket(s);\r
10285         s = -1;\r
10286         continue;\r
10287       }\r
10288       WSACleanup();\r
10289       return err;\r
10290     }\r
10291     break;\r
10292   }\r
10293 \r
10294   /* Bind stderr local socket to unused "privileged" port address\r
10295    */\r
10296   s2 = INVALID_SOCKET;\r
10297   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10298   mysa.sin_family = AF_INET;\r
10299   mysa.sin_addr.s_addr = INADDR_ANY;\r
10300   for (fromPort = 1023;; fromPort--) {\r
10301     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10302     if (fromPort < 0) {\r
10303       (void) closesocket(s);\r
10304       WSACleanup();\r
10305       return WSAEADDRINUSE;\r
10306     }\r
10307     if (s2 == INVALID_SOCKET) {\r
10308       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10309         err = WSAGetLastError();\r
10310         closesocket(s);\r
10311         WSACleanup();\r
10312         return err;\r
10313       }\r
10314     }\r
10315     uport = (unsigned short) fromPort;\r
10316     mysa.sin_port = htons(uport);\r
10317     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10318         == SOCKET_ERROR) {\r
10319       err = WSAGetLastError();\r
10320       if (err == WSAEADDRINUSE) continue;\r
10321       (void) closesocket(s);\r
10322       WSACleanup();\r
10323       return err;\r
10324     }\r
10325     if (listen(s2, 1) == SOCKET_ERROR) {\r
10326       err = WSAGetLastError();\r
10327       if (err == WSAEADDRINUSE) {\r
10328         closesocket(s2);\r
10329         s2 = INVALID_SOCKET;\r
10330         continue;\r
10331       }\r
10332       (void) closesocket(s);\r
10333       (void) closesocket(s2);\r
10334       WSACleanup();\r
10335       return err;\r
10336     }\r
10337     break;\r
10338   }\r
10339   prevStderrPort = fromPort; // remember port used\r
10340   sprintf(stderrPortStr, "%d", fromPort);\r
10341 \r
10342   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10343     err = WSAGetLastError();\r
10344     (void) closesocket(s);\r
10345     (void) closesocket(s2);\r
10346     WSACleanup();\r
10347     return err;\r
10348   }\r
10349 \r
10350   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10351     err = WSAGetLastError();\r
10352     (void) closesocket(s);\r
10353     (void) closesocket(s2);\r
10354     WSACleanup();\r
10355     return err;\r
10356   }\r
10357   if (*user == NULLCHAR) user = UserName();\r
10358   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10359     err = WSAGetLastError();\r
10360     (void) closesocket(s);\r
10361     (void) closesocket(s2);\r
10362     WSACleanup();\r
10363     return err;\r
10364   }\r
10365   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10366     err = WSAGetLastError();\r
10367     (void) closesocket(s);\r
10368     (void) closesocket(s2);\r
10369     WSACleanup();\r
10370     return err;\r
10371   }\r
10372 \r
10373   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10374     err = WSAGetLastError();\r
10375     (void) closesocket(s);\r
10376     (void) closesocket(s2);\r
10377     WSACleanup();\r
10378     return err;\r
10379   }\r
10380   (void) closesocket(s2);  /* Stop listening */\r
10381 \r
10382   /* Prepare return value */\r
10383   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10384   cp->kind = CPRcmd;\r
10385   cp->sock = s;\r
10386   cp->sock2 = s3;\r
10387   *pr = (ProcRef *) cp;\r
10388 \r
10389   return NO_ERROR;\r
10390 }\r
10391 \r
10392 \r
10393 InputSourceRef\r
10394 AddInputSource(ProcRef pr, int lineByLine,\r
10395                InputCallback func, VOIDSTAR closure)\r
10396 {\r
10397   InputSource *is, *is2 = NULL;\r
10398   ChildProc *cp = (ChildProc *) pr;\r
10399 \r
10400   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10401   is->lineByLine = lineByLine;\r
10402   is->func = func;\r
10403   is->closure = closure;\r
10404   is->second = NULL;\r
10405   is->next = is->buf;\r
10406   if (pr == NoProc) {\r
10407     is->kind = CPReal;\r
10408     consoleInputSource = is;\r
10409   } else {\r
10410     is->kind = cp->kind;\r
10411     /* \r
10412         [AS] Try to avoid a race condition if the thread is given control too early:\r
10413         we create all threads suspended so that the is->hThread variable can be\r
10414         safely assigned, then let the threads start with ResumeThread.\r
10415     */\r
10416     switch (cp->kind) {\r
10417     case CPReal:\r
10418       is->hFile = cp->hFrom;\r
10419       cp->hFrom = NULL; /* now owned by InputThread */\r
10420       is->hThread =\r
10421         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10422                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10423       break;\r
10424 \r
10425     case CPComm:\r
10426       is->hFile = cp->hFrom;\r
10427       cp->hFrom = NULL; /* now owned by InputThread */\r
10428       is->hThread =\r
10429         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10430                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10431       break;\r
10432 \r
10433     case CPSock:\r
10434       is->sock = cp->sock;\r
10435       is->hThread =\r
10436         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10437                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10438       break;\r
10439 \r
10440     case CPRcmd:\r
10441       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10442       *is2 = *is;\r
10443       is->sock = cp->sock;\r
10444       is->second = is2;\r
10445       is2->sock = cp->sock2;\r
10446       is2->second = is2;\r
10447       is->hThread =\r
10448         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10449                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10450       is2->hThread =\r
10451         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10452                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10453       break;\r
10454     }\r
10455 \r
10456     if( is->hThread != NULL ) {\r
10457         ResumeThread( is->hThread );\r
10458     }\r
10459 \r
10460     if( is2 != NULL && is2->hThread != NULL ) {\r
10461         ResumeThread( is2->hThread );\r
10462     }\r
10463   }\r
10464 \r
10465   return (InputSourceRef) is;\r
10466 }\r
10467 \r
10468 void\r
10469 RemoveInputSource(InputSourceRef isr)\r
10470 {\r
10471   InputSource *is;\r
10472 \r
10473   is = (InputSource *) isr;\r
10474   is->hThread = NULL;  /* tell thread to stop */\r
10475   CloseHandle(is->hThread);\r
10476   if (is->second != NULL) {\r
10477     is->second->hThread = NULL;\r
10478     CloseHandle(is->second->hThread);\r
10479   }\r
10480 }\r
10481 \r
10482 \r
10483 int\r
10484 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10485 {\r
10486   DWORD dOutCount;\r
10487   int outCount = SOCKET_ERROR;\r
10488   ChildProc *cp = (ChildProc *) pr;\r
10489   static OVERLAPPED ovl;\r
10490 \r
10491   if (pr == NoProc) {\r
10492     ConsoleOutput(message, count, FALSE);\r
10493     return count;\r
10494   } \r
10495 \r
10496   if (ovl.hEvent == NULL) {\r
10497     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10498   }\r
10499   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10500 \r
10501   switch (cp->kind) {\r
10502   case CPSock:\r
10503   case CPRcmd:\r
10504     outCount = send(cp->sock, message, count, 0);\r
10505     if (outCount == SOCKET_ERROR) {\r
10506       *outError = WSAGetLastError();\r
10507     } else {\r
10508       *outError = NO_ERROR;\r
10509     }\r
10510     break;\r
10511 \r
10512   case CPReal:\r
10513     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10514                   &dOutCount, NULL)) {\r
10515       *outError = NO_ERROR;\r
10516       outCount = (int) dOutCount;\r
10517     } else {\r
10518       *outError = GetLastError();\r
10519     }\r
10520     break;\r
10521 \r
10522   case CPComm:\r
10523     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10524                             &dOutCount, &ovl);\r
10525     if (*outError == NO_ERROR) {\r
10526       outCount = (int) dOutCount;\r
10527     }\r
10528     break;\r
10529   }\r
10530   return outCount;\r
10531 }\r
10532 \r
10533 int\r
10534 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10535                        long msdelay)\r
10536 {\r
10537   /* Ignore delay, not implemented for WinBoard */\r
10538   return OutputToProcess(pr, message, count, outError);\r
10539 }\r
10540 \r
10541 \r
10542 void\r
10543 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10544                         char *buf, int count, int error)\r
10545 {\r
10546   DisplayFatalError("Not implemented", 0, 1);\r
10547 }\r
10548 \r
10549 /* see wgamelist.c for Game List functions */\r
10550 /* see wedittags.c for Edit Tags functions */\r
10551 \r
10552 \r
10553 VOID\r
10554 ICSInitScript()\r
10555 {\r
10556   FILE *f;\r
10557   char buf[MSG_SIZ];\r
10558   char *dummy;\r
10559 \r
10560   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10561     f = fopen(buf, "r");\r
10562     if (f != NULL) {\r
10563       ProcessICSInitScript(f);\r
10564       fclose(f);\r
10565     }\r
10566   }\r
10567 }\r
10568 \r
10569 \r
10570 VOID\r
10571 StartAnalysisClock()\r
10572 {\r
10573   if (analysisTimerEvent) return;\r
10574   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10575                                         (UINT) 2000, NULL);\r
10576 }\r
10577 \r
10578 LRESULT CALLBACK\r
10579 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10580 {\r
10581   static HANDLE hwndText;\r
10582   RECT rect;\r
10583   static int sizeX, sizeY;\r
10584   int newSizeX, newSizeY, flags;\r
10585   MINMAXINFO *mmi;\r
10586 \r
10587   switch (message) {\r
10588   case WM_INITDIALOG: /* message: initialize dialog box */\r
10589     /* Initialize the dialog items */\r
10590     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10591     SetWindowText(hDlg, analysisTitle);\r
10592     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10593     /* Size and position the dialog */\r
10594     if (!analysisDialog) {\r
10595       analysisDialog = hDlg;\r
10596       flags = SWP_NOZORDER;\r
10597       GetClientRect(hDlg, &rect);\r
10598       sizeX = rect.right;\r
10599       sizeY = rect.bottom;\r
10600       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10601           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10602         WINDOWPLACEMENT wp;\r
10603         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10604         wp.length = sizeof(WINDOWPLACEMENT);\r
10605         wp.flags = 0;\r
10606         wp.showCmd = SW_SHOW;\r
10607         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10608         wp.rcNormalPosition.left = analysisX;\r
10609         wp.rcNormalPosition.right = analysisX + analysisW;\r
10610         wp.rcNormalPosition.top = analysisY;\r
10611         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10612         SetWindowPlacement(hDlg, &wp);\r
10613 \r
10614         GetClientRect(hDlg, &rect);\r
10615         newSizeX = rect.right;\r
10616         newSizeY = rect.bottom;\r
10617         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10618                               newSizeX, newSizeY);\r
10619         sizeX = newSizeX;\r
10620         sizeY = newSizeY;\r
10621       }\r
10622     }\r
10623     return FALSE;\r
10624 \r
10625   case WM_COMMAND: /* message: received a command */\r
10626     switch (LOWORD(wParam)) {\r
10627     case IDCANCEL:\r
10628       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10629           ExitAnalyzeMode();\r
10630           ModeHighlight();\r
10631           return TRUE;\r
10632       }\r
10633       EditGameEvent();\r
10634       return TRUE;\r
10635     default:\r
10636       break;\r
10637     }\r
10638     break;\r
10639 \r
10640   case WM_SIZE:\r
10641     newSizeX = LOWORD(lParam);\r
10642     newSizeY = HIWORD(lParam);\r
10643     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10644     sizeX = newSizeX;\r
10645     sizeY = newSizeY;\r
10646     break;\r
10647 \r
10648   case WM_GETMINMAXINFO:\r
10649     /* Prevent resizing window too small */\r
10650     mmi = (MINMAXINFO *) lParam;\r
10651     mmi->ptMinTrackSize.x = 100;\r
10652     mmi->ptMinTrackSize.y = 100;\r
10653     break;\r
10654   }\r
10655   return FALSE;\r
10656 }\r
10657 \r
10658 VOID\r
10659 AnalysisPopUp(char* title, char* str)\r
10660 {\r
10661   FARPROC lpProc;\r
10662   char *p, *q;\r
10663 \r
10664   /* [AS] */\r
10665   EngineOutputPopUp();\r
10666   return;\r
10667 \r
10668   if (str == NULL) str = "";\r
10669   p = (char *) malloc(2 * strlen(str) + 2);\r
10670   q = p;\r
10671   while (*str) {\r
10672     if (*str == '\n') *q++ = '\r';\r
10673     *q++ = *str++;\r
10674   }\r
10675   *q = NULLCHAR;\r
10676   if (analysisText != NULL) free(analysisText);\r
10677   analysisText = p;\r
10678 \r
10679   if (analysisDialog) {\r
10680     SetWindowText(analysisDialog, title);\r
10681     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10682     ShowWindow(analysisDialog, SW_SHOW);\r
10683   } else {\r
10684     analysisTitle = title;\r
10685     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10686     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10687                  hwndMain, (DLGPROC)lpProc);\r
10688     FreeProcInstance(lpProc);\r
10689   }\r
10690   analysisDialogUp = TRUE;  \r
10691 }\r
10692 \r
10693 VOID\r
10694 AnalysisPopDown()\r
10695 {\r
10696   if (analysisDialog) {\r
10697     ShowWindow(analysisDialog, SW_HIDE);\r
10698   }\r
10699   analysisDialogUp = FALSE;  \r
10700 }\r
10701 \r
10702 \r
10703 VOID\r
10704 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10705 {\r
10706   highlightInfo.sq[0].x = fromX;\r
10707   highlightInfo.sq[0].y = fromY;\r
10708   highlightInfo.sq[1].x = toX;\r
10709   highlightInfo.sq[1].y = toY;\r
10710 }\r
10711 \r
10712 VOID\r
10713 ClearHighlights()\r
10714 {\r
10715   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10716     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10717 }\r
10718 \r
10719 VOID\r
10720 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10721 {\r
10722   premoveHighlightInfo.sq[0].x = fromX;\r
10723   premoveHighlightInfo.sq[0].y = fromY;\r
10724   premoveHighlightInfo.sq[1].x = toX;\r
10725   premoveHighlightInfo.sq[1].y = toY;\r
10726 }\r
10727 \r
10728 VOID\r
10729 ClearPremoveHighlights()\r
10730 {\r
10731   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10732     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10733 }\r
10734 \r
10735 VOID\r
10736 ShutDownFrontEnd()\r
10737 {\r
10738   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10739   DeleteClipboardTempFiles();\r
10740 }\r
10741 \r
10742 void\r
10743 BoardToTop()\r
10744 {\r
10745     if (IsIconic(hwndMain))\r
10746       ShowWindow(hwndMain, SW_RESTORE);\r
10747 \r
10748     SetActiveWindow(hwndMain);\r
10749 }\r
10750 \r
10751 /*\r
10752  * Prototypes for animation support routines\r
10753  */\r
10754 static void ScreenSquare(int column, int row, POINT * pt);\r
10755 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10756      POINT frames[], int * nFrames);\r
10757 \r
10758 \r
10759 void\r
10760 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10761 {       // [HGM] atomic: animate blast wave\r
10762         int i;\r
10763 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10764         explodeInfo.fromX = fromX;\r
10765         explodeInfo.fromY = fromY;\r
10766         explodeInfo.toX = toX;\r
10767         explodeInfo.toY = toY;\r
10768         for(i=1; i<nFrames; i++) {\r
10769             explodeInfo.radius = (i*180)/(nFrames-1);\r
10770             DrawPosition(FALSE, NULL);\r
10771             Sleep(appData.animSpeed);\r
10772         }\r
10773         explodeInfo.radius = 0;\r
10774         DrawPosition(TRUE, NULL);\r
10775 }\r
10776 \r
10777 #define kFactor 4\r
10778 \r
10779 void\r
10780 AnimateMove(board, fromX, fromY, toX, toY)\r
10781      Board board;\r
10782      int fromX;\r
10783      int fromY;\r
10784      int toX;\r
10785      int toY;\r
10786 {\r
10787   ChessSquare piece;\r
10788   POINT start, finish, mid;\r
10789   POINT frames[kFactor * 2 + 1];\r
10790   int nFrames, n;\r
10791 \r
10792   if (!appData.animate) return;\r
10793   if (doingSizing) return;\r
10794   if (fromY < 0 || fromX < 0) return;\r
10795   piece = board[fromY][fromX];\r
10796   if (piece >= EmptySquare) return;\r
10797 \r
10798   ScreenSquare(fromX, fromY, &start);\r
10799   ScreenSquare(toX, toY, &finish);\r
10800 \r
10801   /* All pieces except knights move in straight line */\r
10802   if (piece != WhiteKnight && piece != BlackKnight) {\r
10803     mid.x = start.x + (finish.x - start.x) / 2;\r
10804     mid.y = start.y + (finish.y - start.y) / 2;\r
10805   } else {\r
10806     /* Knight: make diagonal movement then straight */\r
10807     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10808        mid.x = start.x + (finish.x - start.x) / 2;\r
10809        mid.y = finish.y;\r
10810      } else {\r
10811        mid.x = finish.x;\r
10812        mid.y = start.y + (finish.y - start.y) / 2;\r
10813      }\r
10814   }\r
10815   \r
10816   /* Don't use as many frames for very short moves */\r
10817   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10818     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10819   else\r
10820     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10821 \r
10822   animInfo.from.x = fromX;\r
10823   animInfo.from.y = fromY;\r
10824   animInfo.to.x = toX;\r
10825   animInfo.to.y = toY;\r
10826   animInfo.lastpos = start;\r
10827   animInfo.piece = piece;\r
10828   for (n = 0; n < nFrames; n++) {\r
10829     animInfo.pos = frames[n];\r
10830     DrawPosition(FALSE, NULL);\r
10831     animInfo.lastpos = animInfo.pos;\r
10832     Sleep(appData.animSpeed);\r
10833   }\r
10834   animInfo.pos = finish;\r
10835   DrawPosition(FALSE, NULL);\r
10836   animInfo.piece = EmptySquare;\r
10837   if(gameInfo.variant == VariantAtomic && \r
10838      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10839         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10840 }\r
10841 \r
10842 /*      Convert board position to corner of screen rect and color       */\r
10843 \r
10844 static void\r
10845 ScreenSquare(column, row, pt)\r
10846      int column; int row; POINT * pt;\r
10847 {\r
10848   if (flipView) {\r
10849     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10850     pt->y = lineGap + row * (squareSize + lineGap);\r
10851   } else {\r
10852     pt->x = lineGap + column * (squareSize + lineGap);\r
10853     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10854   }\r
10855 }\r
10856 \r
10857 /*      Generate a series of frame coords from start->mid->finish.\r
10858         The movement rate doubles until the half way point is\r
10859         reached, then halves back down to the final destination,\r
10860         which gives a nice slow in/out effect. The algorithmn\r
10861         may seem to generate too many intermediates for short\r
10862         moves, but remember that the purpose is to attract the\r
10863         viewers attention to the piece about to be moved and\r
10864         then to where it ends up. Too few frames would be less\r
10865         noticeable.                                             */\r
10866 \r
10867 static void\r
10868 Tween(start, mid, finish, factor, frames, nFrames)\r
10869      POINT * start; POINT * mid;\r
10870      POINT * finish; int factor;\r
10871      POINT frames[]; int * nFrames;\r
10872 {\r
10873   int n, fraction = 1, count = 0;\r
10874 \r
10875   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10876   for (n = 0; n < factor; n++)\r
10877     fraction *= 2;\r
10878   for (n = 0; n < factor; n++) {\r
10879     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10880     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10881     count ++;\r
10882     fraction = fraction / 2;\r
10883   }\r
10884   \r
10885   /* Midpoint */\r
10886   frames[count] = *mid;\r
10887   count ++;\r
10888   \r
10889   /* Slow out, stepping 1/2, then 1/4, ... */\r
10890   fraction = 2;\r
10891   for (n = 0; n < factor; n++) {\r
10892     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10893     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10894     count ++;\r
10895     fraction = fraction * 2;\r
10896   }\r
10897   *nFrames = count;\r
10898 }\r
10899 \r
10900 void\r
10901 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10902 {\r
10903     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10904 \r
10905     EvalGraphSet( first, last, current, pvInfoList );\r
10906 }\r
10907 \r
10908 void SetProgramStats( FrontEndProgramStats * stats )\r
10909 {\r
10910     EngineOutputUpdate( stats );\r
10911 }\r