252e9b032fa681cc99a23ab182d00a5949e8d127
[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 #if 0\r
1109   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1110   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1111 #endif\r
1112   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1113   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1114   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1115   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1116   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1117   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1118   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1119   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1120   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1121   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1122   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1123   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1124   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1125   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1126   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1127   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1128   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1129   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1130   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1131   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1132   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1133   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1134   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1135   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1136   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1137   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1138   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1139   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1140   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1141   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1142   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1143   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1144   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1145   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1146   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1147   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1148   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1149   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1150   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1151   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1152   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1153   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1154   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1155   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1156   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1157   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1158   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1159   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1160   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1161   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1162   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1163   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1164   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1165   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1166   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1167   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1168   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1169   { "highlightLastMove", ArgBoolean,\r
1170     (LPVOID) &appData.highlightLastMove, TRUE },\r
1171   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1172   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1173   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1174   { "highlightDragging", ArgBoolean,\r
1175     (LPVOID) &appData.highlightDragging, TRUE },\r
1176   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1177   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1178   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1179   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1180   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1181   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1182   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1183   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1184   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1185   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1186   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1187   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1188   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1189   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1190   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1191   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1192   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1193   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1194   { "soundShout", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1196   { "soundSShout", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1198   { "soundChannel1", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1200   { "soundChannel", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1202   { "soundKibitz", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1204   { "soundTell", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1206   { "soundChallenge", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1208   { "soundRequest", ArgFilename,\r
1209     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1210   { "soundSeek", ArgFilename,\r
1211     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1212   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1213   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1214   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1215   { "soundIcsLoss", ArgFilename,\r
1216     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1217   { "soundIcsDraw", ArgFilename,\r
1218     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1219   { "soundIcsUnfinished", ArgFilename,\r
1220     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1221   { "soundIcsAlarm", ArgFilename,\r
1222     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1223   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1224   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1225   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1226   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1227   { "reuseChessPrograms", ArgBoolean,\r
1228     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1229   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1230   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1231   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1232   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1233   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1234   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1235   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1236   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1237   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1238   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1239   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1240   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1241   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1242   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1243   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1244     TRUE },\r
1245   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1246     TRUE },\r
1247   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1248   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1249   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1250   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1251   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1252   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1253   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1254   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1255   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1256   /* [AS] New features */\r
1257   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1258   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1259   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1260   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1261   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1262   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1263   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1264   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1265   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1266   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1267   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1268   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1269   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1270   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1271   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1272   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1273   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1274   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1275   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1276   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1277   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1278   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1279   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1280   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1281   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1282   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1283   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1284   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1285   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1286   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1287   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1288   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1289   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1290   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1291   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1292   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1293   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1294   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1295   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1296   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1297   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1298   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1299   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1300   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1301   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1302   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1303   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1304   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE },\r
1305   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1306   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1307 \r
1308   /* [HGM] board-size, adjudication and misc. options */\r
1309   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1310   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1311   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1312   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1313   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1314   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1315   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1316   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1317   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1318   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1319   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1320   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1321   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1322   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1323   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1324   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1325   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1326   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1327   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1328   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1329   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1330   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1331   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1332   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1333   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1334   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1335   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1336   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1337   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1338   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1339   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1340   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1341   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1342   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1343 \r
1344 #ifdef ZIPPY\r
1345   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1346   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1347   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1348   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1349   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1350   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1351   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1352   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1353   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1354   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1355   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1356   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1357   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1358     FALSE },\r
1359   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1360   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1361   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1362   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1363   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1364   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1365   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1366     FALSE },\r
1367   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1368   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1369   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1370   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1371   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1372   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1373   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1374   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1375   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1376   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1377   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1378   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1379   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1380   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1381   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1382   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1383   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1384   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1385   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1386 #endif\r
1387   /* [HGM] options for broadcasting and time odds */\r
1388   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1389   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1390   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1391   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1392   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1393   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1394   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1395   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1396   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1397   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1398   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1399 \r
1400   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1401   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1402   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1403   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1404   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1405   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1406   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1407   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1408   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1409   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1410   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1411   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1412   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1413   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1414   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1415   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1416   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1417   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1418   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1419   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1420   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1421   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1422   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1423   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1424   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1425   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1426   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1427   /* [AS] Layout stuff */\r
1428   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1429   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1430   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1431   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1432   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1433 \r
1434   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1435   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1436   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1437   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1438   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1439 \r
1440   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1441   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1442   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1443   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1444   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1445 \r
1446   { NULL, ArgNone, NULL, FALSE }\r
1447 };\r
1448 \r
1449 \r
1450 /* Kludge for indirection files on command line */\r
1451 char* lastIndirectionFilename;\r
1452 ArgDescriptor argDescriptorIndirection =\r
1453 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1454 \r
1455 \r
1456 VOID\r
1457 ExitArgError(char *msg, char *badArg)\r
1458 {\r
1459   char buf[MSG_SIZ];\r
1460 \r
1461   sprintf(buf, "%s %s", msg, badArg);\r
1462   DisplayFatalError(buf, 0, 2);\r
1463   exit(2);\r
1464 }\r
1465 \r
1466 /* Command line font name parser.  NULL name means do nothing.\r
1467    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1468    For backward compatibility, syntax without the colon is also\r
1469    accepted, but font names with digits in them won't work in that case.\r
1470 */\r
1471 VOID\r
1472 ParseFontName(char *name, MyFontParams *mfp)\r
1473 {\r
1474   char *p, *q;\r
1475   if (name == NULL) return;\r
1476   p = name;\r
1477   q = strchr(p, ':');\r
1478   if (q) {\r
1479     if (q - p >= sizeof(mfp->faceName))\r
1480       ExitArgError("Font name too long:", name);\r
1481     memcpy(mfp->faceName, p, q - p);\r
1482     mfp->faceName[q - p] = NULLCHAR;\r
1483     p = q + 1;\r
1484   } else {\r
1485     q = mfp->faceName;\r
1486     while (*p && !isdigit(*p)) {\r
1487       *q++ = *p++;\r
1488       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1489     ExitArgError("Font name too long:", name);\r
1490     }\r
1491     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1492     *q = NULLCHAR;\r
1493   }\r
1494   if (!*p) ExitArgError("Font point size missing:", name);\r
1495   mfp->pointSize = (float) atof(p);\r
1496   mfp->bold = (strchr(p, 'b') != NULL);\r
1497   mfp->italic = (strchr(p, 'i') != NULL);\r
1498   mfp->underline = (strchr(p, 'u') != NULL);\r
1499   mfp->strikeout = (strchr(p, 's') != NULL);\r
1500 }\r
1501 \r
1502 /* Color name parser.\r
1503    X version accepts X color names, but this one\r
1504    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1505 COLORREF\r
1506 ParseColorName(char *name)\r
1507 {\r
1508   int red, green, blue, count;\r
1509   char buf[MSG_SIZ];\r
1510 \r
1511   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1512   if (count != 3) {\r
1513     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",\r
1514       &red, &green, &blue);\r
1515   }\r
1516   if (count != 3) {\r
1517     sprintf(buf, "Can't parse color name %s", name);\r
1518     DisplayError(buf, 0);\r
1519     return RGB(0, 0, 0);\r
1520   }\r
1521   return PALETTERGB(red, green, blue);\r
1522 }\r
1523 \r
1524 \r
1525 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1526 {\r
1527   char *e = argValue;\r
1528   int eff = 0;\r
1529 \r
1530   while (*e) {\r
1531     if (*e == 'b')      eff |= CFE_BOLD;\r
1532     else if (*e == 'i') eff |= CFE_ITALIC;\r
1533     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1534     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1535     else if (*e == '#' || isdigit(*e)) break;\r
1536     e++;\r
1537   }\r
1538   *effects = eff;\r
1539   *color   = ParseColorName(e);\r
1540 }\r
1541 \r
1542 \r
1543 BoardSize\r
1544 ParseBoardSize(char *name)\r
1545 {\r
1546   BoardSize bs = SizeTiny;\r
1547   while (sizeInfo[bs].name != NULL) {\r
1548     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1549     bs++;\r
1550   }\r
1551   ExitArgError("Unrecognized board size value", name);\r
1552   return bs; /* not reached */\r
1553 }\r
1554 \r
1555 \r
1556 char\r
1557 StringGet(void *getClosure)\r
1558 {\r
1559   char **p = (char **) getClosure;\r
1560   return *((*p)++);\r
1561 }\r
1562 \r
1563 char\r
1564 FileGet(void *getClosure)\r
1565 {\r
1566   int c;\r
1567   FILE* f = (FILE*) getClosure;\r
1568 \r
1569   c = getc(f);\r
1570   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1571   if (c == EOF)\r
1572     return NULLCHAR;\r
1573   else\r
1574     return (char) c;\r
1575 }\r
1576 \r
1577 /* Parse settings file named "name". If file found, return the\r
1578    full name in fullname and return TRUE; else return FALSE */\r
1579 BOOLEAN\r
1580 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1581 {\r
1582   char *dummy;\r
1583   FILE *f;\r
1584   int ok; char buf[MSG_SIZ];\r
1585 \r
1586   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1587   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1588     sprintf(buf, "%s.ini", name);\r
1589     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1590   }\r
1591   if (ok) {\r
1592     f = fopen(fullname, "r");\r
1593     if (f != NULL) {\r
1594       ParseArgs(FileGet, f);\r
1595       fclose(f);\r
1596       return TRUE;\r
1597     }\r
1598   }\r
1599   return FALSE;\r
1600 }\r
1601 \r
1602 VOID\r
1603 ParseArgs(GetFunc get, void *cl)\r
1604 {\r
1605   char argName[ARG_MAX];\r
1606   char argValue[ARG_MAX];\r
1607   ArgDescriptor *ad;\r
1608   char start;\r
1609   char *q;\r
1610   int i, octval;\r
1611   char ch;\r
1612   int posarg = 0;\r
1613 \r
1614   ch = get(cl);\r
1615   for (;;) {\r
1616     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1617     if (ch == NULLCHAR) break;\r
1618     if (ch == ';') {\r
1619       /* Comment to end of line */\r
1620       ch = get(cl);\r
1621       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1622       continue;\r
1623     } else if (ch == '/' || ch == '-') {\r
1624       /* Switch */\r
1625       q = argName;\r
1626       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1627          ch != '\n' && ch != '\t') {\r
1628     *q++ = ch;\r
1629     ch = get(cl);\r
1630       }\r
1631       *q = NULLCHAR;\r
1632 \r
1633       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1634     if (strcmp(ad->argName, argName + 1) == 0) break;\r
1635 \r
1636       if (ad->argName == NULL)\r
1637     ExitArgError("Unrecognized argument", argName);\r
1638 \r
1639     } else if (ch == '@') {\r
1640       /* Indirection file */\r
1641       ad = &argDescriptorIndirection;\r
1642       ch = get(cl);\r
1643     } else {\r
1644       /* Positional argument */\r
1645       ad = &argDescriptors[posarg++];\r
1646       strcpy(argName, ad->argName);\r
1647     }\r
1648 \r
1649     if (ad->argType == ArgTrue) {\r
1650       *(Boolean *) ad->argLoc = TRUE;\r
1651       continue;\r
1652     }\r
1653     if (ad->argType == ArgFalse) {\r
1654       *(Boolean *) ad->argLoc = FALSE;\r
1655       continue;\r
1656     }\r
1657 \r
1658     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1659     if (ch == NULLCHAR || ch == '\n') {\r
1660       ExitArgError("No value provided for argument", argName);\r
1661     }\r
1662     q = argValue;\r
1663     if (ch == '{') {\r
1664       // Quoting with { }.  No characters have to (or can) be escaped.\r
1665       // Thus the string cannot contain a '}' character.\r
1666       start = ch;\r
1667       ch = get(cl);\r
1668       while (start) {\r
1669     switch (ch) {\r
1670     case NULLCHAR:\r
1671       start = NULLCHAR;\r
1672       break;\r
1673 \r
1674     case '}':\r
1675       ch = get(cl);\r
1676       start = NULLCHAR;\r
1677       break;\r
1678 \r
1679     default:\r
1680       *q++ = ch;\r
1681       ch = get(cl);\r
1682       break;\r
1683     }\r
1684       }\r
1685     } else if (ch == '\'' || ch == '"') {\r
1686       // Quoting with ' ' or " ", with \ as escape character.\r
1687       // Inconvenient for long strings that may contain Windows filenames.\r
1688       start = ch;\r
1689       ch = get(cl);\r
1690       while (start) {\r
1691     switch (ch) {\r
1692     case NULLCHAR:\r
1693       start = NULLCHAR;\r
1694       break;\r
1695 \r
1696     default:\r
1697         not_special:\r
1698       *q++ = ch;\r
1699       ch = get(cl);\r
1700       break;\r
1701 \r
1702     case '\'':\r
1703     case '\"':\r
1704       if (ch == start) {\r
1705         ch = get(cl);\r
1706         start = NULLCHAR;\r
1707         break;\r
1708       } else {\r
1709         goto not_special;\r
1710       }\r
1711 \r
1712     case '\\':\r
1713           if (ad->argType == ArgFilename\r
1714           || ad->argType == ArgSettingsFilename) {\r
1715           goto not_special;\r
1716       }\r
1717       ch = get(cl);\r
1718       switch (ch) {\r
1719       case NULLCHAR:\r
1720         ExitArgError("Incomplete \\ escape in value for", argName);\r
1721         break;\r
1722       case 'n':\r
1723         *q++ = '\n';\r
1724         ch = get(cl);\r
1725         break;\r
1726       case 'r':\r
1727         *q++ = '\r';\r
1728         ch = get(cl);\r
1729         break;\r
1730       case 't':\r
1731         *q++ = '\t';\r
1732         ch = get(cl);\r
1733         break;\r
1734       case 'b':\r
1735         *q++ = '\b';\r
1736         ch = get(cl);\r
1737         break;\r
1738       case 'f':\r
1739         *q++ = '\f';\r
1740         ch = get(cl);\r
1741         break;\r
1742       default:\r
1743         octval = 0;\r
1744         for (i = 0; i < 3; i++) {\r
1745           if (ch >= '0' && ch <= '7') {\r
1746         octval = octval*8 + (ch - '0');\r
1747         ch = get(cl);\r
1748           } else {\r
1749         break;\r
1750           }\r
1751         }\r
1752         if (i > 0) {\r
1753           *q++ = (char) octval;\r
1754         } else {\r
1755           *q++ = ch;\r
1756           ch = get(cl);\r
1757         }\r
1758         break;\r
1759       }\r
1760       break;\r
1761     }\r
1762       }\r
1763     } else {\r
1764       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1765     *q++ = ch;\r
1766     ch = get(cl);\r
1767       }\r
1768     }\r
1769     *q = NULLCHAR;\r
1770 \r
1771     switch (ad->argType) {\r
1772     case ArgInt:\r
1773       *(int *) ad->argLoc = atoi(argValue);\r
1774       break;\r
1775 \r
1776     case ArgX:\r
1777       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute\r
1778       break;\r
1779 \r
1780     case ArgY:\r
1781       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1782       break;\r
1783 \r
1784     case ArgZ:\r
1785       *(int *) ad->argLoc = atoi(argValue);\r
1786       EnsureOnScreen(&boardX, &boardY, minX, minY);\r
1787       break;\r
1788 \r
1789     case ArgFloat:\r
1790       *(float *) ad->argLoc = (float) atof(argValue);\r
1791       break;\r
1792 \r
1793     case ArgString:\r
1794     case ArgFilename:\r
1795       *(char **) ad->argLoc = strdup(argValue);\r
1796       break;\r
1797 \r
1798     case ArgSettingsFilename:\r
1799       {\r
1800     char fullname[MSG_SIZ];\r
1801     if (ParseSettingsFile(argValue, fullname)) {\r
1802       if (ad->argLoc != NULL) {\r
1803         *(char **) ad->argLoc = strdup(fullname);\r
1804       }\r
1805     } else {\r
1806       if (ad->argLoc != NULL) {\r
1807       } else {\r
1808         ExitArgError("Failed to open indirection file", argValue);\r
1809       }\r
1810     }\r
1811       }\r
1812       break;\r
1813 \r
1814     case ArgBoolean:\r
1815       switch (argValue[0]) {\r
1816       case 't':\r
1817       case 'T':\r
1818     *(Boolean *) ad->argLoc = TRUE;\r
1819     break;\r
1820       case 'f':\r
1821       case 'F':\r
1822     *(Boolean *) ad->argLoc = FALSE;\r
1823     break;\r
1824       default:\r
1825     ExitArgError("Unrecognized boolean argument value", argValue);\r
1826     break;\r
1827       }\r
1828       break;\r
1829 \r
1830     case ArgColor:\r
1831       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1832       break;\r
1833 \r
1834     case ArgAttribs: {\r
1835       ColorClass cc = (ColorClass)ad->argLoc;\r
1836       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1837       }\r
1838       break;\r
1839 \r
1840     case ArgBoardSize:\r
1841       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1842       break;\r
1843 \r
1844     case ArgFont:\r
1845       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1846       break;\r
1847 \r
1848     case ArgCommSettings:\r
1849       ParseCommSettings(argValue, &dcb);\r
1850       break;\r
1851 \r
1852     case ArgNone:\r
1853       ExitArgError("Unrecognized argument", argValue);\r
1854       break;\r
1855     case ArgTrue:\r
1856     case ArgFalse: ;\r
1857     }\r
1858   }\r
1859 }\r
1860 \r
1861 VOID\r
1862 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1863 {\r
1864   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1865   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1866   DeleteDC(hdc);\r
1867   lf->lfWidth = 0;\r
1868   lf->lfEscapement = 0;\r
1869   lf->lfOrientation = 0;\r
1870   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1871   lf->lfItalic = mfp->italic;\r
1872   lf->lfUnderline = mfp->underline;\r
1873   lf->lfStrikeOut = mfp->strikeout;\r
1874   lf->lfCharSet = DEFAULT_CHARSET;\r
1875   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1876   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1877   lf->lfQuality = DEFAULT_QUALITY;\r
1878   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1879   strcpy(lf->lfFaceName, mfp->faceName);\r
1880 }\r
1881 \r
1882 VOID\r
1883 CreateFontInMF(MyFont *mf)\r
1884 {\r
1885   LFfromMFP(&mf->lf, &mf->mfp);\r
1886   if (mf->hf) DeleteObject(mf->hf);\r
1887   mf->hf = CreateFontIndirect(&mf->lf);\r
1888 }\r
1889 \r
1890 VOID\r
1891 SetDefaultTextAttribs()\r
1892 {\r
1893   ColorClass cc;\r
1894   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1895     ParseAttribs(&textAttribs[cc].color,\r
1896          &textAttribs[cc].effects,\r
1897              defaultTextAttribs[cc]);\r
1898   }\r
1899 }\r
1900 \r
1901 VOID\r
1902 SetDefaultSounds()\r
1903 {\r
1904   ColorClass cc;\r
1905   SoundClass sc;\r
1906   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1907     textAttribs[cc].sound.name = strdup("");\r
1908     textAttribs[cc].sound.data = NULL;\r
1909   }\r
1910   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1911     sounds[sc].name = strdup("");\r
1912     sounds[sc].data = NULL;\r
1913   }\r
1914   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1915 }\r
1916 \r
1917 VOID\r
1918 LoadAllSounds()\r
1919 {\r
1920   ColorClass cc;\r
1921   SoundClass sc;\r
1922   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1923     MyLoadSound(&textAttribs[cc].sound);\r
1924   }\r
1925   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1926     MyLoadSound(&sounds[sc]);\r
1927   }\r
1928 }\r
1929 \r
1930 VOID\r
1931 InitAppData(LPSTR lpCmdLine)\r
1932 {\r
1933   int i, j;\r
1934   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1935   char *dummy, *p;\r
1936 \r
1937   programName = szAppName;\r
1938 \r
1939   /* Initialize to defaults */\r
1940   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1941   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1942   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1943   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1944   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1945   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1946   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1947   SetDefaultTextAttribs();\r
1948   SetDefaultSounds();\r
1949   appData.movesPerSession = MOVES_PER_SESSION;\r
1950   appData.initString = INIT_STRING;\r
1951   appData.secondInitString = INIT_STRING;\r
1952   appData.firstComputerString = COMPUTER_STRING;\r
1953   appData.secondComputerString = COMPUTER_STRING;\r
1954   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1955   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1956   appData.firstPlaysBlack = FALSE;\r
1957   appData.noChessProgram = FALSE;\r
1958   chessProgram = FALSE;\r
1959   appData.firstHost = FIRST_HOST;\r
1960   appData.secondHost = SECOND_HOST;\r
1961   appData.firstDirectory = FIRST_DIRECTORY;\r
1962   appData.secondDirectory = SECOND_DIRECTORY;\r
1963   appData.bitmapDirectory = "";\r
1964   appData.remoteShell = REMOTE_SHELL;\r
1965   appData.remoteUser = "";\r
1966   appData.timeDelay = TIME_DELAY;\r
1967   appData.timeControl = TIME_CONTROL;\r
1968   appData.timeIncrement = TIME_INCREMENT;\r
1969   appData.icsActive = FALSE;\r
1970   appData.icsHost = "";\r
1971   appData.icsPort = ICS_PORT;\r
1972   appData.icsCommPort = ICS_COMM_PORT;\r
1973   appData.icsLogon = ICS_LOGON;\r
1974   appData.icsHelper = "";\r
1975   appData.useTelnet = FALSE;\r
1976   appData.telnetProgram = TELNET_PROGRAM;\r
1977   appData.gateway = "";\r
1978   appData.loadGameFile = "";\r
1979   appData.loadGameIndex = 0;\r
1980   appData.saveGameFile = "";\r
1981   appData.autoSaveGames = FALSE;\r
1982   appData.loadPositionFile = "";\r
1983   appData.loadPositionIndex = 1;\r
1984   appData.savePositionFile = "";\r
1985   appData.matchMode = FALSE;\r
1986   appData.matchGames = 0;\r
1987   appData.monoMode = FALSE;\r
1988   appData.debugMode = FALSE;\r
1989   appData.clockMode = TRUE;\r
1990   boardSize = (BoardSize) -1; /* determine by screen size */\r
1991   appData.Iconic = FALSE; /*unused*/\r
1992   appData.searchTime = "";\r
1993   appData.searchDepth = 0;\r
1994   appData.showCoords = FALSE;\r
1995   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1996   appData.autoCallFlag = FALSE;\r
1997   appData.flipView = FALSE;\r
1998   appData.autoFlipView = TRUE;\r
1999   appData.cmailGameName = "";\r
2000   appData.alwaysPromoteToQueen = FALSE;\r
2001   appData.oldSaveStyle = FALSE;\r
2002   appData.quietPlay = FALSE;\r
2003   appData.showThinking = FALSE;\r
2004   appData.ponderNextMove = TRUE;\r
2005   appData.periodicUpdates = TRUE;\r
2006   appData.popupExitMessage = TRUE;\r
2007   appData.popupMoveErrors = FALSE;\r
2008   appData.autoObserve = FALSE;\r
2009   appData.autoComment = FALSE;\r
2010   appData.animate = TRUE;\r
2011   appData.animSpeed = 10;\r
2012   appData.animateDragging = TRUE;\r
2013   appData.highlightLastMove = TRUE;\r
2014   appData.getMoveList = TRUE;\r
2015   appData.testLegality = TRUE;\r
2016   appData.premove = TRUE;\r
2017   appData.premoveWhite = FALSE;\r
2018   appData.premoveWhiteText = "";\r
2019   appData.premoveBlack = FALSE;\r
2020   appData.premoveBlackText = "";\r
2021   appData.icsAlarm = TRUE;\r
2022   appData.icsAlarmTime = 5000;\r
2023   appData.autoRaiseBoard = TRUE;\r
2024   appData.localLineEditing = TRUE;\r
2025   appData.colorize = TRUE;\r
2026   appData.reuseFirst = TRUE;\r
2027   appData.reuseSecond = TRUE;\r
2028   appData.blindfold = FALSE;\r
2029   appData.icsEngineAnalyze = FALSE;\r
2030   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2031   dcb.DCBlength = sizeof(DCB);\r
2032   dcb.BaudRate = 9600;\r
2033   dcb.fBinary = TRUE;\r
2034   dcb.fParity = FALSE;\r
2035   dcb.fOutxCtsFlow = FALSE;\r
2036   dcb.fOutxDsrFlow = FALSE;\r
2037   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2038   dcb.fDsrSensitivity = FALSE;\r
2039   dcb.fTXContinueOnXoff = TRUE;\r
2040   dcb.fOutX = FALSE;\r
2041   dcb.fInX = FALSE;\r
2042   dcb.fNull = FALSE;\r
2043   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2044   dcb.fAbortOnError = FALSE;\r
2045   dcb.ByteSize = 7;\r
2046   dcb.Parity = SPACEPARITY;\r
2047   dcb.StopBits = ONESTOPBIT;\r
2048   settingsFileName = SETTINGS_FILE;\r
2049   saveSettingsOnExit = TRUE;\r
2050   boardX = CW_USEDEFAULT;\r
2051   boardY = CW_USEDEFAULT;\r
2052   analysisX = CW_USEDEFAULT;\r
2053   analysisY = CW_USEDEFAULT;\r
2054   analysisW = CW_USEDEFAULT;\r
2055   analysisH = CW_USEDEFAULT;\r
2056   commentX = CW_USEDEFAULT;\r
2057   commentY = CW_USEDEFAULT;\r
2058   commentW = CW_USEDEFAULT;\r
2059   commentH = CW_USEDEFAULT;\r
2060   editTagsX = CW_USEDEFAULT;\r
2061   editTagsY = CW_USEDEFAULT;\r
2062   editTagsW = CW_USEDEFAULT;\r
2063   editTagsH = CW_USEDEFAULT;\r
2064   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2065   icsNames = ICS_NAMES;\r
2066   firstChessProgramNames = FCP_NAMES;\r
2067   secondChessProgramNames = SCP_NAMES;\r
2068   appData.initialMode = "";\r
2069   appData.variant = "normal";\r
2070   appData.firstProtocolVersion = PROTOVER;\r
2071   appData.secondProtocolVersion = PROTOVER;\r
2072   appData.showButtonBar = TRUE;\r
2073 \r
2074    /* [AS] New properties (see comments in header file) */\r
2075   appData.firstScoreIsAbsolute = FALSE;\r
2076   appData.secondScoreIsAbsolute = FALSE;\r
2077   appData.saveExtendedInfoInPGN = FALSE;\r
2078   appData.hideThinkingFromHuman = FALSE;\r
2079   appData.liteBackTextureFile = "";\r
2080   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2081   appData.darkBackTextureFile = "";\r
2082   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2083   appData.renderPiecesWithFont = "";\r
2084   appData.fontToPieceTable = "";\r
2085   appData.fontBackColorWhite = 0;\r
2086   appData.fontForeColorWhite = 0;\r
2087   appData.fontBackColorBlack = 0;\r
2088   appData.fontForeColorBlack = 0;\r
2089   appData.fontPieceSize = 80;\r
2090   appData.overrideLineGap = 1;\r
2091   appData.adjudicateLossThreshold = 0;\r
2092   appData.delayBeforeQuit = 0;\r
2093   appData.delayAfterQuit = 0;\r
2094   appData.nameOfDebugFile = "winboard.debug";\r
2095   appData.pgnEventHeader = "Computer Chess Game";\r
2096   appData.defaultFrcPosition = -1;\r
2097   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2098   appData.saveOutOfBookInfo = TRUE;\r
2099   appData.showEvalInMoveHistory = TRUE;\r
2100   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2101   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2102   appData.highlightMoveWithArrow = FALSE;\r
2103   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2104   appData.useStickyWindows = TRUE;\r
2105   appData.adjudicateDrawMoves = 0;\r
2106   appData.autoDisplayComment = TRUE;\r
2107   appData.autoDisplayTags = TRUE;\r
2108   appData.firstIsUCI = FALSE;\r
2109   appData.secondIsUCI = FALSE;\r
2110   appData.firstHasOwnBookUCI = TRUE;\r
2111   appData.secondHasOwnBookUCI = TRUE;\r
2112   appData.polyglotDir = "";\r
2113   appData.usePolyglotBook = FALSE;\r
2114   appData.polyglotBook = "";\r
2115   appData.defaultHashSize = 64;\r
2116   appData.defaultCacheSizeEGTB = 4;\r
2117   appData.defaultPathEGTB = "c:\\egtb";\r
2118   appData.firstOptions = "";\r
2119   appData.secondOptions = "";\r
2120 \r
2121   InitWindowPlacement( &wpGameList );\r
2122   InitWindowPlacement( &wpMoveHistory );\r
2123   InitWindowPlacement( &wpEvalGraph );\r
2124   InitWindowPlacement( &wpEngineOutput );\r
2125   InitWindowPlacement( &wpConsole );\r
2126 \r
2127   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2128   appData.NrFiles      = -1;\r
2129   appData.NrRanks      = -1;\r
2130   appData.holdingsSize = -1;\r
2131   appData.testClaims   = FALSE;\r
2132   appData.checkMates   = FALSE;\r
2133   appData.materialDraws= FALSE;\r
2134   appData.trivialDraws = FALSE;\r
2135   appData.ruleMoves    = 51;\r
2136   appData.drawRepeats  = 6;\r
2137   appData.matchPause   = 10000;\r
2138   appData.alphaRank    = FALSE;\r
2139   appData.allWhite     = FALSE;\r
2140   appData.upsideDown   = FALSE;\r
2141   appData.serverPause  = 15;\r
2142   appData.serverMovesName   = NULL;\r
2143   appData.suppressLoadMoves = FALSE;\r
2144   appData.firstTimeOdds  = 1;\r
2145   appData.secondTimeOdds = 1;\r
2146   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2147   appData.secondAccumulateTC = 1;\r
2148   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2149   appData.secondNPS = -1;\r
2150   appData.engineComments = 1;\r
2151   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2152   appData.egtFormats = "";\r
2153 \r
2154 #ifdef ZIPPY\r
2155   appData.zippyTalk = ZIPPY_TALK;\r
2156   appData.zippyPlay = ZIPPY_PLAY;\r
2157   appData.zippyLines = ZIPPY_LINES;\r
2158   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2159   appData.zippyPassword = ZIPPY_PASSWORD;\r
2160   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2161   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2162   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2163   appData.zippyUseI = ZIPPY_USE_I;\r
2164   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2165   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2166   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2167   appData.zippyGameStart = ZIPPY_GAME_START;\r
2168   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2169   appData.zippyAbort = ZIPPY_ABORT;\r
2170   appData.zippyVariants = ZIPPY_VARIANTS;\r
2171   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2172   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2173 #endif\r
2174 \r
2175   /* Point font array elements to structures and\r
2176      parse default font names */\r
2177   for (i=0; i<NUM_FONTS; i++) {\r
2178     for (j=0; j<NUM_SIZES; j++) {\r
2179       font[j][i] = &fontRec[j][i];\r
2180       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2181     }\r
2182   }\r
2183 \r
2184   /* Parse default settings file if any */\r
2185   if (ParseSettingsFile(settingsFileName, buf)) {\r
2186     settingsFileName = strdup(buf);\r
2187   }\r
2188 \r
2189   /* Parse command line */\r
2190   ParseArgs(StringGet, &lpCmdLine);\r
2191 \r
2192   /* [HGM] make sure board size is acceptable */\r
2193   if(appData.NrFiles > BOARD_SIZE ||\r
2194      appData.NrRanks > BOARD_SIZE   )\r
2195       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2196 \r
2197   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2198    * with options from the command line, we now make an even higher priority\r
2199    * overrule by WB options attached to the engine command line. This so that\r
2200    * tournament managers can use WB options (such as /timeOdds) that follow\r
2201    * the engines.\r
2202    */\r
2203   if(appData.firstChessProgram != NULL) {\r
2204       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2205       static char *f = "first";\r
2206       char buf[MSG_SIZ], *q = buf;\r
2207       if(p != NULL) { // engine command line contains WinBoard options\r
2208           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2209           ParseArgs(StringGet, &q);\r
2210           p[-1] = 0; // cut them offengine command line\r
2211       }\r
2212   }\r
2213   // now do same for second chess program\r
2214   if(appData.secondChessProgram != NULL) {\r
2215       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2216       static char *s = "second";\r
2217       char buf[MSG_SIZ], *q = buf;\r
2218       if(p != NULL) { // engine command line contains WinBoard options\r
2219           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2220           ParseArgs(StringGet, &q);\r
2221           p[-1] = 0; // cut them offengine command line\r
2222       }\r
2223   }\r
2224 \r
2225 \r
2226   /* Propagate options that affect others */\r
2227   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2228   if (appData.icsActive || appData.noChessProgram) {\r
2229      chessProgram = FALSE;  /* not local chess program mode */\r
2230   }\r
2231 \r
2232   /* Open startup dialog if needed */\r
2233   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2234       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2235       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2236                         *appData.secondChessProgram == NULLCHAR))) {\r
2237     FARPROC lpProc;\r
2238 \r
2239     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2240     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2241     FreeProcInstance(lpProc);\r
2242   }\r
2243 \r
2244   /* Make sure save files land in the right (?) directory */\r
2245   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2246     appData.saveGameFile = strdup(buf);\r
2247   }\r
2248   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2249     appData.savePositionFile = strdup(buf);\r
2250   }\r
2251 \r
2252   /* Finish initialization for fonts and sounds */\r
2253   for (i=0; i<NUM_FONTS; i++) {\r
2254     for (j=0; j<NUM_SIZES; j++) {\r
2255       CreateFontInMF(font[j][i]);\r
2256     }\r
2257   }\r
2258   /* xboard, and older WinBoards, controlled the move sound with the\r
2259      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2260      always turn the option on (so that the backend will call us),\r
2261      then let the user turn the sound off by setting it to silence if\r
2262      desired.  To accommodate old winboard.ini files saved by old\r
2263      versions of WinBoard, we also turn off the sound if the option\r
2264      was initially set to false. */\r
2265   if (!appData.ringBellAfterMoves) {\r
2266     sounds[(int)SoundMove].name = strdup("");\r
2267     appData.ringBellAfterMoves = TRUE;\r
2268   }\r
2269   GetCurrentDirectory(MSG_SIZ, currDir);\r
2270   SetCurrentDirectory(installDir);\r
2271   LoadAllSounds();\r
2272   SetCurrentDirectory(currDir);\r
2273 \r
2274   p = icsTextMenuString;\r
2275   if (p[0] == '@') {\r
2276     FILE* f = fopen(p + 1, "r");\r
2277     if (f == NULL) {\r
2278       DisplayFatalError(p + 1, errno, 2);\r
2279       return;\r
2280     }\r
2281     i = fread(buf, 1, sizeof(buf)-1, f);\r
2282     fclose(f);\r
2283     buf[i] = NULLCHAR;\r
2284     p = buf;\r
2285   }\r
2286   ParseIcsTextMenu(strdup(p));\r
2287 }\r
2288 \r
2289 \r
2290 VOID\r
2291 InitMenuChecks()\r
2292 {\r
2293   HMENU hmenu = GetMenu(hwndMain);\r
2294 \r
2295   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2296             MF_BYCOMMAND|((appData.icsActive &&\r
2297                        *appData.icsCommPort != NULLCHAR) ?\r
2298                       MF_ENABLED : MF_GRAYED));\r
2299   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2300                MF_BYCOMMAND|(saveSettingsOnExit ?\r
2301                      MF_CHECKED : MF_UNCHECKED));\r
2302 }\r
2303 \r
2304 \r
2305 VOID\r
2306 SaveSettings(char* name)\r
2307 {\r
2308   FILE *f;\r
2309   ArgDescriptor *ad;\r
2310   WINDOWPLACEMENT wp;\r
2311   char dir[MSG_SIZ];\r
2312 \r
2313   if (!hwndMain) return;\r
2314 \r
2315   GetCurrentDirectory(MSG_SIZ, dir);\r
2316   SetCurrentDirectory(installDir);\r
2317   f = fopen(name, "w");\r
2318   SetCurrentDirectory(dir);\r
2319   if (f == NULL) {\r
2320     DisplayError(name, errno);\r
2321     return;\r
2322   }\r
2323   fprintf(f, ";\n");\r
2324   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2325   fprintf(f, ";\n");\r
2326   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2327   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2328   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2329   fprintf(f, ";\n");\r
2330 \r
2331   wp.length = sizeof(WINDOWPLACEMENT);\r
2332   GetWindowPlacement(hwndMain, &wp);\r
2333   boardX = wp.rcNormalPosition.left;\r
2334   boardY = wp.rcNormalPosition.top;\r
2335 \r
2336   if (hwndConsole) {\r
2337     GetWindowPlacement(hwndConsole, &wp);\r
2338     wpConsole.x = wp.rcNormalPosition.left;\r
2339     wpConsole.y = wp.rcNormalPosition.top;\r
2340     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2341     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2342   }\r
2343 \r
2344   if (analysisDialog) {\r
2345     GetWindowPlacement(analysisDialog, &wp);\r
2346     analysisX = wp.rcNormalPosition.left;\r
2347     analysisY = wp.rcNormalPosition.top;\r
2348     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2349     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2350   }\r
2351 \r
2352   if (commentDialog) {\r
2353     GetWindowPlacement(commentDialog, &wp);\r
2354     commentX = wp.rcNormalPosition.left;\r
2355     commentY = wp.rcNormalPosition.top;\r
2356     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2357     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2358   }\r
2359 \r
2360   if (editTagsDialog) {\r
2361     GetWindowPlacement(editTagsDialog, &wp);\r
2362     editTagsX = wp.rcNormalPosition.left;\r
2363     editTagsY = wp.rcNormalPosition.top;\r
2364     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2365     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2366   }\r
2367 \r
2368   if (gameListDialog) {\r
2369     GetWindowPlacement(gameListDialog, &wp);\r
2370     wpGameList.x = wp.rcNormalPosition.left;\r
2371     wpGameList.y = wp.rcNormalPosition.top;\r
2372     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2373     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2374   }\r
2375 \r
2376   /* [AS] Move history */\r
2377   wpMoveHistory.visible = MoveHistoryIsUp();\r
2378 \r
2379   if( moveHistoryDialog ) {\r
2380     GetWindowPlacement(moveHistoryDialog, &wp);\r
2381     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2382     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2383     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2384     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2385   }\r
2386 \r
2387   /* [AS] Eval graph */\r
2388   wpEvalGraph.visible = EvalGraphIsUp();\r
2389 \r
2390   if( evalGraphDialog ) {\r
2391     GetWindowPlacement(evalGraphDialog, &wp);\r
2392     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2393     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2394     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2395     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2396   }\r
2397 \r
2398   /* [AS] Engine output */\r
2399   wpEngineOutput.visible = EngineOutputIsUp();\r
2400 \r
2401   if( engineOutputDialog ) {\r
2402     GetWindowPlacement(engineOutputDialog, &wp);\r
2403     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2404     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2405     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2406     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2407   }\r
2408 \r
2409   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2410     if (!ad->save) continue;\r
2411     switch (ad->argType) {\r
2412     case ArgString:\r
2413       {\r
2414     char *p = *(char **)ad->argLoc;\r
2415     if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2416       /* Quote multiline values or \-containing values\r
2417          with { } if possible */\r
2418       fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2419     } else {\r
2420       /* Else quote with " " */\r
2421       fprintf(f, "/%s=\"", ad->argName);\r
2422       while (*p) {\r
2423         if (*p == '\n') fprintf(f, "\n");\r
2424         else if (*p == '\r') fprintf(f, "\\r");\r
2425         else if (*p == '\t') fprintf(f, "\\t");\r
2426         else if (*p == '\b') fprintf(f, "\\b");\r
2427         else if (*p == '\f') fprintf(f, "\\f");\r
2428         else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2429         else if (*p == '\"') fprintf(f, "\\\"");\r
2430         else if (*p == '\\') fprintf(f, "\\\\");\r
2431         else putc(*p, f);\r
2432         p++;\r
2433       }\r
2434       fprintf(f, "\"\n");\r
2435     }\r
2436       }\r
2437       break;\r
2438     case ArgInt:\r
2439     case ArgZ:\r
2440       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2441       break;\r
2442     case ArgX:\r
2443       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2444       break;\r
2445     case ArgY:\r
2446       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2447       break;\r
2448     case ArgFloat:\r
2449       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2450       break;\r
2451     case ArgBoolean:\r
2452       fprintf(f, "/%s=%s\n", ad->argName,\r
2453     (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2454       break;\r
2455     case ArgTrue:\r
2456       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2457       break;\r
2458     case ArgFalse:\r
2459       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2460       break;\r
2461     case ArgColor:\r
2462       {\r
2463     COLORREF color = *(COLORREF *)ad->argLoc;\r
2464     fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,\r
2465       color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2466       }\r
2467       break;\r
2468     case ArgAttribs:\r
2469       {\r
2470     MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2471     fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2472           (ta->effects & CFE_BOLD) ? "b" : "",\r
2473           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2474           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2475           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2476           (ta->effects) ? " " : "",\r
2477       ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2478       }\r
2479       break;\r
2480     case ArgFilename:\r
2481       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2482     fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2483       } else {\r
2484     fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2485       }\r
2486       break;\r
2487     case ArgBoardSize:\r
2488       fprintf(f, "/%s=%s\n", ad->argName,\r
2489           sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2490       break;\r
2491     case ArgFont:\r
2492       {\r
2493         int bs;\r
2494     for (bs=0; bs<NUM_SIZES; bs++) {\r
2495       MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2496           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2497       fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2498         ad->argName, mfp->faceName, mfp->pointSize,\r
2499             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2500         mfp->bold ? "b" : "",\r
2501         mfp->italic ? "i" : "",\r
2502         mfp->underline ? "u" : "",\r
2503         mfp->strikeout ? "s" : "");\r
2504     }\r
2505       }\r
2506       break;\r
2507     case ArgCommSettings:\r
2508       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2509     case ArgNone:\r
2510     case ArgSettingsFilename: ;\r
2511     }\r
2512   }\r
2513   fclose(f);\r
2514 }\r
2515 \r
2516 \r
2517 \r
2518 /*---------------------------------------------------------------------------*\\r
2519  *\r
2520  * GDI board drawing routines\r
2521  *\r
2522 \*---------------------------------------------------------------------------*/\r
2523 \r
2524 /* [AS] Draw square using background texture */\r
2525 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2526 {\r
2527     XFORM   x;\r
2528 \r
2529     if( mode == 0 ) {\r
2530         return; /* Should never happen! */\r
2531     }\r
2532 \r
2533     SetGraphicsMode( dst, GM_ADVANCED );\r
2534 \r
2535     switch( mode ) {\r
2536     case 1:\r
2537         /* Identity */\r
2538         break;\r
2539     case 2:\r
2540         /* X reflection */\r
2541         x.eM11 = -1.0;\r
2542         x.eM12 = 0;\r
2543         x.eM21 = 0;\r
2544         x.eM22 = 1.0;\r
2545         x.eDx = (FLOAT) dw + dx - 1;\r
2546         x.eDy = 0;\r
2547         dx = 0;\r
2548         SetWorldTransform( dst, &x );\r
2549         break;\r
2550     case 3:\r
2551         /* Y reflection */\r
2552         x.eM11 = 1.0;\r
2553         x.eM12 = 0;\r
2554         x.eM21 = 0;\r
2555         x.eM22 = -1.0;\r
2556         x.eDx = 0;\r
2557         x.eDy = (FLOAT) dh + dy - 1;\r
2558         dy = 0;\r
2559         SetWorldTransform( dst, &x );\r
2560         break;\r
2561     case 4:\r
2562         /* X/Y flip */\r
2563         x.eM11 = 0;\r
2564         x.eM12 = 1.0;\r
2565         x.eM21 = 1.0;\r
2566         x.eM22 = 0;\r
2567         x.eDx = (FLOAT) dx;\r
2568         x.eDy = (FLOAT) dy;\r
2569         dx = 0;\r
2570         dy = 0;\r
2571         SetWorldTransform( dst, &x );\r
2572         break;\r
2573     }\r
2574 \r
2575     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2576 \r
2577     x.eM11 = 1.0;\r
2578     x.eM12 = 0;\r
2579     x.eM21 = 0;\r
2580     x.eM22 = 1.0;\r
2581     x.eDx = 0;\r
2582     x.eDy = 0;\r
2583     SetWorldTransform( dst, &x );\r
2584 \r
2585     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2586 }\r
2587 \r
2588 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2589 enum {\r
2590     PM_WP = (int) WhitePawn,\r
2591     PM_WN = (int) WhiteKnight,\r
2592     PM_WB = (int) WhiteBishop,\r
2593     PM_WR = (int) WhiteRook,\r
2594     PM_WQ = (int) WhiteQueen,\r
2595     PM_WF = (int) WhiteFerz,\r
2596     PM_WW = (int) WhiteWazir,\r
2597     PM_WE = (int) WhiteAlfil,\r
2598     PM_WM = (int) WhiteMan,\r
2599     PM_WO = (int) WhiteCannon,\r
2600     PM_WU = (int) WhiteUnicorn,\r
2601     PM_WH = (int) WhiteNightrider,\r
2602     PM_WA = (int) WhiteAngel,\r
2603     PM_WC = (int) WhiteMarshall,\r
2604     PM_WAB = (int) WhiteCardinal,\r
2605     PM_WD = (int) WhiteDragon,\r
2606     PM_WL = (int) WhiteLance,\r
2607     PM_WS = (int) WhiteCobra,\r
2608     PM_WV = (int) WhiteFalcon,\r
2609     PM_WSG = (int) WhiteSilver,\r
2610     PM_WG = (int) WhiteGrasshopper,\r
2611     PM_WK = (int) WhiteKing,\r
2612     PM_BP = (int) BlackPawn,\r
2613     PM_BN = (int) BlackKnight,\r
2614     PM_BB = (int) BlackBishop,\r
2615     PM_BR = (int) BlackRook,\r
2616     PM_BQ = (int) BlackQueen,\r
2617     PM_BF = (int) BlackFerz,\r
2618     PM_BW = (int) BlackWazir,\r
2619     PM_BE = (int) BlackAlfil,\r
2620     PM_BM = (int) BlackMan,\r
2621     PM_BO = (int) BlackCannon,\r
2622     PM_BU = (int) BlackUnicorn,\r
2623     PM_BH = (int) BlackNightrider,\r
2624     PM_BA = (int) BlackAngel,\r
2625     PM_BC = (int) BlackMarshall,\r
2626     PM_BG = (int) BlackGrasshopper,\r
2627     PM_BAB = (int) BlackCardinal,\r
2628     PM_BD = (int) BlackDragon,\r
2629     PM_BL = (int) BlackLance,\r
2630     PM_BS = (int) BlackCobra,\r
2631     PM_BV = (int) BlackFalcon,\r
2632     PM_BSG = (int) BlackSilver,\r
2633     PM_BK = (int) BlackKing\r
2634 };\r
2635 \r
2636 static HFONT hPieceFont = NULL;\r
2637 static HBITMAP hPieceMask[(int) EmptySquare];\r
2638 static HBITMAP hPieceFace[(int) EmptySquare];\r
2639 static int fontBitmapSquareSize = 0;\r
2640 static char pieceToFontChar[(int) EmptySquare] =\r
2641                               { 'p', 'n', 'b', 'r', 'q',\r
2642                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2643                       'k', 'o', 'm', 'v', 't', 'w',\r
2644                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2645                                                               'l' };\r
2646 \r
2647 extern BOOL SetCharTable( char *table, const char * map );\r
2648 /* [HGM] moved to backend.c */\r
2649 \r
2650 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2651 {\r
2652     HBRUSH hbrush;\r
2653     BYTE r1 = GetRValue( color );\r
2654     BYTE g1 = GetGValue( color );\r
2655     BYTE b1 = GetBValue( color );\r
2656     BYTE r2 = r1 / 2;\r
2657     BYTE g2 = g1 / 2;\r
2658     BYTE b2 = b1 / 2;\r
2659     RECT rc;\r
2660 \r
2661     /* Create a uniform background first */\r
2662     hbrush = CreateSolidBrush( color );\r
2663     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2664     FillRect( hdc, &rc, hbrush );\r
2665     DeleteObject( hbrush );\r
2666 \r
2667     if( mode == 1 ) {\r
2668         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2669         int steps = squareSize / 2;\r
2670         int i;\r
2671 \r
2672         for( i=0; i<steps; i++ ) {\r
2673             BYTE r = r1 - (r1-r2) * i / steps;\r
2674             BYTE g = g1 - (g1-g2) * i / steps;\r
2675             BYTE b = b1 - (b1-b2) * i / steps;\r
2676 \r
2677             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2678             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2679             FillRect( hdc, &rc, hbrush );\r
2680             DeleteObject(hbrush);\r
2681         }\r
2682     }\r
2683     else if( mode == 2 ) {\r
2684         /* Diagonal gradient, good more or less for every piece */\r
2685         POINT triangle[3];\r
2686         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2687         HBRUSH hbrush_old;\r
2688         int steps = squareSize;\r
2689         int i;\r
2690 \r
2691         triangle[0].x = squareSize - steps;\r
2692         triangle[0].y = squareSize;\r
2693         triangle[1].x = squareSize;\r
2694         triangle[1].y = squareSize;\r
2695         triangle[2].x = squareSize;\r
2696         triangle[2].y = squareSize - steps;\r
2697 \r
2698         for( i=0; i<steps; i++ ) {\r
2699             BYTE r = r1 - (r1-r2) * i / steps;\r
2700             BYTE g = g1 - (g1-g2) * i / steps;\r
2701             BYTE b = b1 - (b1-b2) * i / steps;\r
2702 \r
2703             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2704             hbrush_old = SelectObject( hdc, hbrush );\r
2705             Polygon( hdc, triangle, 3 );\r
2706             SelectObject( hdc, hbrush_old );\r
2707             DeleteObject(hbrush);\r
2708             triangle[0].x++;\r
2709             triangle[2].y++;\r
2710         }\r
2711 \r
2712         SelectObject( hdc, hpen );\r
2713     }\r
2714 }\r
2715 \r
2716 /*\r
2717     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2718     seems to work ok. The main problem here is to find the "inside" of a chess\r
2719     piece: follow the steps as explained below.\r
2720 */\r
2721 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2722 {\r
2723     HBITMAP hbm;\r
2724     HBITMAP hbm_old;\r
2725     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2726     RECT rc;\r
2727     SIZE sz;\r
2728     POINT pt;\r
2729     int backColor = whitePieceColor;\r
2730     int foreColor = blackPieceColor;\r
2731 \r
2732     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2733         backColor = appData.fontBackColorWhite;\r
2734         foreColor = appData.fontForeColorWhite;\r
2735     }\r
2736     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2737         backColor = appData.fontBackColorBlack;\r
2738         foreColor = appData.fontForeColorBlack;\r
2739     }\r
2740 \r
2741     /* Mask */\r
2742     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2743 \r
2744     hbm_old = SelectObject( hdc, hbm );\r
2745 \r
2746     rc.left = 0;\r
2747     rc.top = 0;\r
2748     rc.right = squareSize;\r
2749     rc.bottom = squareSize;\r
2750 \r
2751     /* Step 1: background is now black */\r
2752     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2753 \r
2754     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2755 \r
2756     pt.x = (squareSize - sz.cx) / 2;\r
2757     pt.y = (squareSize - sz.cy) / 2;\r
2758 \r
2759     SetBkMode( hdc, TRANSPARENT );\r
2760     SetTextColor( hdc, chroma );\r
2761     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2762     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2763 \r
2764     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2765     /* Step 3: the area outside the piece is filled with white */\r
2766 //    FloodFill( hdc, 0, 0, chroma );\r
2767     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2768     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2769     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2770     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2771     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2772     /*\r
2773         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2774         but if the start point is not inside the piece we're lost!\r
2775         There should be a better way to do this... if we could create a region or path\r
2776         from the fill operation we would be fine for example.\r
2777     */\r
2778 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2779     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2780 \r
2781     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2782         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2783         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2784 \r
2785         SelectObject( dc2, bm2 );\r
2786         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2787         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2788         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2789         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2790         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2791 \r
2792         DeleteDC( dc2 );\r
2793         DeleteObject( bm2 );\r
2794     }\r
2795 \r
2796     SetTextColor( hdc, 0 );\r
2797     /*\r
2798         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2799         draw the piece again in black for safety.\r
2800     */\r
2801     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2802 \r
2803     SelectObject( hdc, hbm_old );\r
2804 \r
2805     if( hPieceMask[index] != NULL ) {\r
2806         DeleteObject( hPieceMask[index] );\r
2807     }\r
2808 \r
2809     hPieceMask[index] = hbm;\r
2810 \r
2811     /* Face */\r
2812     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2813 \r
2814     SelectObject( hdc, hbm );\r
2815 \r
2816     {\r
2817         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2818         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2819         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2820 \r
2821         SelectObject( dc1, hPieceMask[index] );\r
2822         SelectObject( dc2, bm2 );\r
2823         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2824         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2825 \r
2826         /*\r
2827             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2828             the piece background and deletes (makes transparent) the rest.\r
2829             Thanks to that mask, we are free to paint the background with the greates\r
2830             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2831             We use this, to make gradients and give the pieces a "roundish" look.\r
2832         */\r
2833         SetPieceBackground( hdc, backColor, 2 );\r
2834         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2835 \r
2836         DeleteDC( dc2 );\r
2837         DeleteDC( dc1 );\r
2838         DeleteObject( bm2 );\r
2839     }\r
2840 \r
2841     SetTextColor( hdc, foreColor );\r
2842     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2843 \r
2844     SelectObject( hdc, hbm_old );\r
2845 \r
2846     if( hPieceFace[index] != NULL ) {\r
2847         DeleteObject( hPieceFace[index] );\r
2848     }\r
2849 \r
2850     hPieceFace[index] = hbm;\r
2851 }\r
2852 \r
2853 static int TranslatePieceToFontPiece( int piece )\r
2854 {\r
2855     switch( piece ) {\r
2856     case BlackPawn:\r
2857         return PM_BP;\r
2858     case BlackKnight:\r
2859         return PM_BN;\r
2860     case BlackBishop:\r
2861         return PM_BB;\r
2862     case BlackRook:\r
2863         return PM_BR;\r
2864     case BlackQueen:\r
2865         return PM_BQ;\r
2866     case BlackKing:\r
2867         return PM_BK;\r
2868     case WhitePawn:\r
2869         return PM_WP;\r
2870     case WhiteKnight:\r
2871         return PM_WN;\r
2872     case WhiteBishop:\r
2873         return PM_WB;\r
2874     case WhiteRook:\r
2875         return PM_WR;\r
2876     case WhiteQueen:\r
2877         return PM_WQ;\r
2878     case WhiteKing:\r
2879         return PM_WK;\r
2880 \r
2881     case BlackAngel:\r
2882         return PM_BA;\r
2883     case BlackMarshall:\r
2884         return PM_BC;\r
2885     case BlackFerz:\r
2886         return PM_BF;\r
2887     case BlackNightrider:\r
2888         return PM_BH;\r
2889     case BlackAlfil:\r
2890         return PM_BE;\r
2891     case BlackWazir:\r
2892         return PM_BW;\r
2893     case BlackUnicorn:\r
2894         return PM_BU;\r
2895     case BlackCannon:\r
2896         return PM_BO;\r
2897     case BlackGrasshopper:\r
2898         return PM_BG;\r
2899     case BlackMan:\r
2900         return PM_BM;\r
2901     case BlackSilver:\r
2902         return PM_BSG;\r
2903     case BlackLance:\r
2904         return PM_BL;\r
2905     case BlackFalcon:\r
2906         return PM_BV;\r
2907     case BlackCobra:\r
2908         return PM_BS;\r
2909     case BlackCardinal:\r
2910         return PM_BAB;\r
2911     case BlackDragon:\r
2912         return PM_BD;\r
2913 \r
2914     case WhiteAngel:\r
2915         return PM_WA;\r
2916     case WhiteMarshall:\r
2917         return PM_WC;\r
2918     case WhiteFerz:\r
2919         return PM_WF;\r
2920     case WhiteNightrider:\r
2921         return PM_WH;\r
2922     case WhiteAlfil:\r
2923         return PM_WE;\r
2924     case WhiteWazir:\r
2925         return PM_WW;\r
2926     case WhiteUnicorn:\r
2927         return PM_WU;\r
2928     case WhiteCannon:\r
2929         return PM_WO;\r
2930     case WhiteGrasshopper:\r
2931         return PM_WG;\r
2932     case WhiteMan:\r
2933         return PM_WM;\r
2934     case WhiteSilver:\r
2935         return PM_WSG;\r
2936     case WhiteLance:\r
2937         return PM_WL;\r
2938     case WhiteFalcon:\r
2939         return PM_WV;\r
2940     case WhiteCobra:\r
2941         return PM_WS;\r
2942     case WhiteCardinal:\r
2943         return PM_WAB;\r
2944     case WhiteDragon:\r
2945         return PM_WD;\r
2946     }\r
2947 \r
2948     return 0;\r
2949 }\r
2950 \r
2951 void CreatePiecesFromFont()\r
2952 {\r
2953     LOGFONT lf;\r
2954     HDC hdc_window = NULL;\r
2955     HDC hdc = NULL;\r
2956     HFONT hfont_old;\r
2957     int fontHeight;\r
2958     int i;\r
2959 \r
2960     if( fontBitmapSquareSize < 0 ) {\r
2961         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2962         return;\r
2963     }\r
2964 \r
2965     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2966         fontBitmapSquareSize = -1;\r
2967         return;\r
2968     }\r
2969 \r
2970     if( fontBitmapSquareSize != squareSize ) {\r
2971         hdc_window = GetDC( hwndMain );\r
2972         hdc = CreateCompatibleDC( hdc_window );\r
2973 \r
2974         if( hPieceFont != NULL ) {\r
2975             DeleteObject( hPieceFont );\r
2976         }\r
2977         else {\r
2978             for( i=0; i<=(int)BlackKing; i++ ) {\r
2979                 hPieceMask[i] = NULL;\r
2980                 hPieceFace[i] = NULL;\r
2981             }\r
2982         }\r
2983 \r
2984         fontHeight = 75;\r
2985 \r
2986         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2987             fontHeight = appData.fontPieceSize;\r
2988         }\r
2989 \r
2990         fontHeight = (fontHeight * squareSize) / 100;\r
2991 \r
2992         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2993         lf.lfWidth = 0;\r
2994         lf.lfEscapement = 0;\r
2995         lf.lfOrientation = 0;\r
2996         lf.lfWeight = FW_NORMAL;\r
2997         lf.lfItalic = 0;\r
2998         lf.lfUnderline = 0;\r
2999         lf.lfStrikeOut = 0;\r
3000         lf.lfCharSet = DEFAULT_CHARSET;\r
3001         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
3002         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
3003         lf.lfQuality = PROOF_QUALITY;\r
3004         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3005         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3006         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3007 \r
3008         hPieceFont = CreateFontIndirect( &lf );\r
3009 \r
3010         if( hPieceFont == NULL ) {\r
3011             fontBitmapSquareSize = -2;\r
3012         }\r
3013         else {\r
3014             /* Setup font-to-piece character table */\r
3015             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3016                 /* No (or wrong) global settings, try to detect the font */\r
3017                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3018                     /* Alpha */\r
3019                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3020                 }\r
3021                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3022                     /* DiagramTT* family */\r
3023                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3024                 }\r
3025                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3026                     /* Fairy symbols */\r
3027                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3028                 }\r
3029                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3030                     /* Good Companion (Some characters get warped as literal :-( */\r
3031                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3032                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3033                     SetCharTable(pieceToFontChar, s);\r
3034                 }\r
3035                 else {\r
3036                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3037                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3038                 }\r
3039             }\r
3040 \r
3041             /* Create bitmaps */\r
3042             hfont_old = SelectObject( hdc, hPieceFont );\r
3043 #if 0\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3056 \r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3070             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3071             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3072             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3073             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3074             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3075             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3076             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3077             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3078             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3079             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3080             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3081             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3082             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3083             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3084             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3085             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3086             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3087             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3088             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3089 #else\r
3090         for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3091         if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3092             CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3093 #endif\r
3094             SelectObject( hdc, hfont_old );\r
3095 \r
3096             fontBitmapSquareSize = squareSize;\r
3097         }\r
3098     }\r
3099 \r
3100     if( hdc != NULL ) {\r
3101         DeleteDC( hdc );\r
3102     }\r
3103 \r
3104     if( hdc_window != NULL ) {\r
3105         ReleaseDC( hwndMain, hdc_window );\r
3106     }\r
3107 }\r
3108 \r
3109 HBITMAP\r
3110 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3111 {\r
3112   char name[128];\r
3113 \r
3114   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3115   if (gameInfo.event &&\r
3116       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3117       strcmp(name, "k80s") == 0) {\r
3118     strcpy(name, "tim");\r
3119   }\r
3120   return LoadBitmap(hinst, name);\r
3121 }\r
3122 \r
3123 \r
3124 /* Insert a color into the program's logical palette\r
3125    structure.  This code assumes the given color is\r
3126    the result of the RGB or PALETTERGB macro, and it\r
3127    knows how those macros work (which is documented).\r
3128 */\r
3129 VOID\r
3130 InsertInPalette(COLORREF color)\r
3131 {\r
3132   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3133 \r
3134   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3135     DisplayFatalError("Too many colors", 0, 1);\r
3136     pLogPal->palNumEntries--;\r
3137     return;\r
3138   }\r
3139 \r
3140   pe->peFlags = (char) 0;\r
3141   pe->peRed = (char) (0xFF & color);\r
3142   pe->peGreen = (char) (0xFF & (color >> 8));\r
3143   pe->peBlue = (char) (0xFF & (color >> 16));\r
3144   return;\r
3145 }\r
3146 \r
3147 \r
3148 VOID\r
3149 InitDrawingColors()\r
3150 {\r
3151   if (pLogPal == NULL) {\r
3152     /* Allocate enough memory for a logical palette with\r
3153      * PALETTESIZE entries and set the size and version fields\r
3154      * of the logical palette structure.\r
3155      */\r
3156     pLogPal = (NPLOGPALETTE)\r
3157       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3158                   (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3159     pLogPal->palVersion    = 0x300;\r
3160   }\r
3161   pLogPal->palNumEntries = 0;\r
3162 \r
3163   InsertInPalette(lightSquareColor);\r
3164   InsertInPalette(darkSquareColor);\r
3165   InsertInPalette(whitePieceColor);\r
3166   InsertInPalette(blackPieceColor);\r
3167   InsertInPalette(highlightSquareColor);\r
3168   InsertInPalette(premoveHighlightColor);\r
3169 \r
3170   /*  create a logical color palette according the information\r
3171    *  in the LOGPALETTE structure.\r
3172    */\r
3173   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3174 \r
3175   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3176   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3177   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3178   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3179   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3180   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3181   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3182   /* [AS] Force rendering of the font-based pieces */\r
3183   if( fontBitmapSquareSize > 0 ) {\r
3184     fontBitmapSquareSize = 0;\r
3185   }\r
3186 }\r
3187 \r
3188 \r
3189 int\r
3190 BoardWidth(int boardSize, int n)\r
3191 { /* [HGM] argument n added to allow different width and height */\r
3192   int lineGap = sizeInfo[boardSize].lineGap;\r
3193 \r
3194   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3195       lineGap = appData.overrideLineGap;\r
3196   }\r
3197 \r
3198   return (n + 1) * lineGap +\r
3199           n * sizeInfo[boardSize].squareSize;\r
3200 }\r
3201 \r
3202 /* Respond to board resize by dragging edge */\r
3203 VOID\r
3204 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3205 {\r
3206   BoardSize newSize = NUM_SIZES - 1;\r
3207   static int recurse = 0;\r
3208   if (IsIconic(hwndMain)) return;\r
3209   if (recurse > 0) return;\r
3210   recurse++;\r
3211   while (newSize > 0) {\r
3212     InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3213     if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3214        newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3215     newSize--;\r
3216   }\r
3217   boardSize = newSize;\r
3218   InitDrawingSizes(boardSize, flags);\r
3219   recurse--;\r
3220 }\r
3221 \r
3222 \r
3223 \r
3224 VOID\r
3225 InitDrawingSizes(BoardSize boardSize, int flags)\r
3226 {\r
3227   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3228   ChessSquare piece;\r
3229   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3230   HDC hdc;\r
3231   SIZE clockSize, messageSize;\r
3232   HFONT oldFont;\r
3233   char buf[MSG_SIZ];\r
3234   char *str;\r
3235   HMENU hmenu = GetMenu(hwndMain);\r
3236   RECT crect, wrect, oldRect;\r
3237   int offby;\r
3238   LOGBRUSH logbrush;\r
3239 \r
3240   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3241   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3242 \r
3243   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3244   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3245 \r
3246   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3247   oldRect.top = boardY;\r
3248   oldRect.right = boardX + winWidth;\r
3249   oldRect.bottom = boardY + winHeight;\r
3250 \r
3251   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3252   smallLayout = sizeInfo[boardSize].smallLayout;\r
3253   squareSize = sizeInfo[boardSize].squareSize;\r
3254   lineGap = sizeInfo[boardSize].lineGap;\r
3255   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3256 \r
3257   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3258       lineGap = appData.overrideLineGap;\r
3259   }\r
3260 \r
3261   if (tinyLayout != oldTinyLayout) {\r
3262     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3263     if (tinyLayout) {\r
3264       style &= ~WS_SYSMENU;\r
3265       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3266          "&Minimize\tCtrl+F4");\r
3267     } else {\r
3268       style |= WS_SYSMENU;\r
3269       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3270     }\r
3271     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3272 \r
3273     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3274       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,\r
3275     (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3276     }\r
3277     DrawMenuBar(hwndMain);\r
3278   }\r
3279 \r
3280   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3281   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3282 \r
3283   /* Get text area sizes */\r
3284   hdc = GetDC(hwndMain);\r
3285   if (appData.clockMode) {\r
3286     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3287   } else {\r
3288     sprintf(buf, "White");\r
3289   }\r
3290   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3291   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3292   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3293   str = "We only care about the height here";\r
3294   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3295   SelectObject(hdc, oldFont);\r
3296   ReleaseDC(hwndMain, hdc);\r
3297 \r
3298   /* Compute where everything goes */\r
3299   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3300         /* [HGM] logo: if either logo is on, reserve space for it */\r
3301     logoHeight =  2*clockSize.cy;\r
3302     leftLogoRect.left   = OUTER_MARGIN;\r
3303     leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3304     leftLogoRect.top    = OUTER_MARGIN;\r
3305     leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3306 \r
3307     rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3308     rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3309     rightLogoRect.top    = OUTER_MARGIN;\r
3310     rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3311 \r
3312 \r
3313     whiteRect.left = leftLogoRect.right;\r
3314     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3315     whiteRect.top = OUTER_MARGIN;\r
3316     whiteRect.bottom = whiteRect.top + logoHeight;\r
3317 \r
3318     blackRect.right = rightLogoRect.left;\r
3319     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3320     blackRect.top = whiteRect.top;\r
3321     blackRect.bottom = whiteRect.bottom;\r
3322   } else {\r
3323     whiteRect.left = OUTER_MARGIN;\r
3324     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3325     whiteRect.top = OUTER_MARGIN;\r
3326     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3327 \r
3328     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3329     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3330     blackRect.top = whiteRect.top;\r
3331     blackRect.bottom = whiteRect.bottom;\r
3332   }\r
3333 \r
3334   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3335   if (appData.showButtonBar) {\r
3336     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3337       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3338   } else {\r
3339     messageRect.right = OUTER_MARGIN + boardWidth;\r
3340   }\r
3341   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3342   messageRect.bottom = messageRect.top + messageSize.cy;\r
3343 \r
3344   boardRect.left = OUTER_MARGIN;\r
3345   boardRect.right = boardRect.left + boardWidth;\r
3346   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3347   boardRect.bottom = boardRect.top + boardHeight;\r
3348 \r
3349   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3350   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3351   oldBoardSize = boardSize;\r
3352   oldTinyLayout = tinyLayout;\r
3353   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3354   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3355     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3356   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3357   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3358   winHeight = winH; //       without disturbing window attachments\r
3359   GetWindowRect(hwndMain, &wrect);\r
3360   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3361            SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3362 \r
3363   // [HGM] placement: let attached windows follow size change.\r
3364   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3365   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3366   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3367   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3368   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3369 \r
3370   /* compensate if menu bar wrapped */\r
3371   GetClientRect(hwndMain, &crect);\r
3372   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3373   winHeight += offby;\r
3374   switch (flags) {\r
3375   case WMSZ_TOPLEFT:\r
3376     SetWindowPos(hwndMain, NULL,\r
3377                  wrect.right - winWidth, wrect.bottom - winHeight,\r
3378                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3379     break;\r
3380 \r
3381   case WMSZ_TOPRIGHT:\r
3382   case WMSZ_TOP:\r
3383     SetWindowPos(hwndMain, NULL,\r
3384                  wrect.left, wrect.bottom - winHeight,\r
3385                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3386     break;\r
3387 \r
3388   case WMSZ_BOTTOMLEFT:\r
3389   case WMSZ_LEFT:\r
3390     SetWindowPos(hwndMain, NULL,\r
3391                  wrect.right - winWidth, wrect.top,\r
3392                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3393     break;\r
3394 \r
3395   case WMSZ_BOTTOMRIGHT:\r
3396   case WMSZ_BOTTOM:\r
3397   case WMSZ_RIGHT:\r
3398   default:\r
3399     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3400                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3401     break;\r
3402   }\r
3403 \r
3404   hwndPause = NULL;\r
3405   for (i = 0; i < N_BUTTONS; i++) {\r
3406     if (buttonDesc[i].hwnd != NULL) {\r
3407       DestroyWindow(buttonDesc[i].hwnd);\r
3408       buttonDesc[i].hwnd = NULL;\r
3409     }\r
3410     if (appData.showButtonBar) {\r
3411       buttonDesc[i].hwnd =\r
3412     CreateWindow("BUTTON", buttonDesc[i].label,\r
3413              WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3414              boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3415              messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3416              (HMENU) buttonDesc[i].id,\r
3417              (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3418       if (tinyLayout) {\r
3419     SendMessage(buttonDesc[i].hwnd, WM_SETFONT,\r
3420             (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3421             MAKELPARAM(FALSE, 0));\r
3422       }\r
3423       if (buttonDesc[i].id == IDM_Pause)\r
3424     hwndPause = buttonDesc[i].hwnd;\r
3425       buttonDesc[i].wndproc = (WNDPROC)\r
3426     SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3427     }\r
3428   }\r
3429   if (gridPen != NULL) DeleteObject(gridPen);\r
3430   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3431   if (premovePen != NULL) DeleteObject(premovePen);\r
3432   if (lineGap != 0) {\r
3433     logbrush.lbStyle = BS_SOLID;\r
3434     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3435     gridPen =\r
3436       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3437                    lineGap, &logbrush, 0, NULL);\r
3438     logbrush.lbColor = highlightSquareColor;\r
3439     highlightPen =\r
3440       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3441                    lineGap, &logbrush, 0, NULL);\r
3442 \r
3443     logbrush.lbColor = premoveHighlightColor;\r
3444     premovePen =\r
3445       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3446                    lineGap, &logbrush, 0, NULL);\r
3447 \r
3448     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3449     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3450       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3451       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3452     boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3453       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3454         BOARD_WIDTH * (squareSize + lineGap);\r
3455       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3456     }\r
3457     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3458       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3459       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3460         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3461     lineGap / 2 + (i * (squareSize + lineGap));\r
3462       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3463         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3464       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3465     }\r
3466   }\r
3467 \r
3468   /* [HGM] Licensing requirement */\r
3469 #ifdef GOTHIC\r
3470   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3471 #endif\r
3472 #ifdef FALCON\r
3473   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3474 #endif\r
3475   GothicPopUp( "", VariantNormal);\r
3476 \r
3477 \r
3478 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3479 \r
3480   /* Load piece bitmaps for this board size */\r
3481   for (i=0; i<=2; i++) {\r
3482     for (piece = WhitePawn;\r
3483          (int) piece < (int) BlackPawn;\r
3484      piece = (ChessSquare) ((int) piece + 1)) {\r
3485       if (pieceBitmap[i][piece] != NULL)\r
3486     DeleteObject(pieceBitmap[i][piece]);\r
3487     }\r
3488   }\r
3489 \r
3490   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3491   // Orthodox Chess pieces\r
3492   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3493   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3494   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3495   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3496   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3497   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3498   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3499   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3500   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3501   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3502   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3503   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3504   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3505   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3506   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3507   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3508     // in Shogi, Hijack the unused Queen for Lance\r
3509     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3510     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3511     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3512   } else {\r
3513     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3514     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3515     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3516   }\r
3517 \r
3518   if(squareSize <= 72 && squareSize >= 33) {\r
3519     /* A & C are available in most sizes now */\r
3520     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3521       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3522       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3523       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3524       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3525       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3526       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3527       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3528       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3529       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3530       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3531       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3532       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3533     } else { // Smirf-like\r
3534       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3535       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3536       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3537     }\r
3538     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3539       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3540       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3541       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3542     } else { // WinBoard standard\r
3543       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3544       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3545       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3546     }\r
3547   }\r
3548 \r
3549 \r
3550   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3551     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3552     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3553     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3554     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3555     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3556     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3557     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3558     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3559     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3560     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3561     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3562     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3563     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3564     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3565     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3566     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3567     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3568     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3569     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3570     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3571     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3572     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3573     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3574     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3575     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3576     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3577     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3578     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3579     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3580     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3581 \r
3582     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3583       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3584       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3585       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3586       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3587       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3588       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3589       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3590       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3591       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3592       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3593       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3594       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3595     } else {\r
3596       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3597       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3598       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3599       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3600       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3601       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3602       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3603       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3604       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3605       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3606       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3607       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3608     }\r
3609 \r
3610   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3611     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3612     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3613     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3614     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3615     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3616     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3617     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3618     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3619     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3620     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3621     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3622     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3623     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3624     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3625   }\r
3626 \r
3627 \r
3628   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3629   /* special Shogi support in this size */\r
3630   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3631       for (piece = WhitePawn;\r
3632            (int) piece < (int) BlackPawn;\r
3633            piece = (ChessSquare) ((int) piece + 1)) {\r
3634         if (pieceBitmap[i][piece] != NULL)\r
3635           DeleteObject(pieceBitmap[i][piece]);\r
3636       }\r
3637     }\r
3638   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3639   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3640   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3641   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3642   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3643   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3644   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3645   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3646   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3647   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3648   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3649   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3650   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3651   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3652   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3653   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3654   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3655   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3656   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3657   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3658   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3659   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3660   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3661   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3662   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3663   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3664   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3665   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3666   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3667   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3668   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3669   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3670   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3671   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3672   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3673   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3674   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3675   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3676   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3677   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3678   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3679   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3680   minorSize = 0;\r
3681   }\r
3682 }\r
3683 \r
3684 HBITMAP\r
3685 PieceBitmap(ChessSquare p, int kind)\r
3686 {\r
3687   if ((int) p >= (int) BlackPawn)\r
3688     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3689 \r
3690   return pieceBitmap[kind][(int) p];\r
3691 }\r
3692 \r
3693 /***************************************************************/\r
3694 \r
3695 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3696 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3697 /*\r
3698 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3699 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3700 */\r
3701 \r
3702 VOID\r
3703 SquareToPos(int row, int column, int * x, int * y)\r
3704 {\r
3705   if (flipView) {\r
3706     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3707     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3708   } else {\r
3709     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3710     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3711   }\r
3712 }\r
3713 \r
3714 VOID\r
3715 DrawCoordsOnDC(HDC hdc)\r
3716 {\r
3717   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
3718   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
3719   char str[2] = { NULLCHAR, NULLCHAR };\r
3720   int oldMode, oldAlign, x, y, start, i;\r
3721   HFONT oldFont;\r
3722   HBRUSH oldBrush;\r
3723 \r
3724   if (!appData.showCoords)\r
3725     return;\r
3726 \r
3727   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3728 \r
3729   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3730   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3731   oldAlign = GetTextAlign(hdc);\r
3732   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3733 \r
3734   y = boardRect.top + lineGap;\r
3735   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3736 \r
3737   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3738   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3739     str[0] = files[start + i];\r
3740     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3741     y += squareSize + lineGap;\r
3742   }\r
3743 \r
3744   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3745 \r
3746   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3747   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3748     str[0] = ranks[start + i];\r
3749     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3750     x += squareSize + lineGap;\r
3751   }\r
3752 \r
3753   SelectObject(hdc, oldBrush);\r
3754   SetBkMode(hdc, oldMode);\r
3755   SetTextAlign(hdc, oldAlign);\r
3756   SelectObject(hdc, oldFont);\r
3757 }\r
3758 \r
3759 VOID\r
3760 DrawGridOnDC(HDC hdc)\r
3761 {\r
3762   HPEN oldPen;\r
3763 \r
3764   if (lineGap != 0) {\r
3765     oldPen = SelectObject(hdc, gridPen);\r
3766     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3767     SelectObject(hdc, oldPen);\r
3768   }\r
3769 }\r
3770 \r
3771 #define HIGHLIGHT_PEN 0\r
3772 #define PREMOVE_PEN   1\r
3773 \r
3774 VOID\r
3775 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3776 {\r
3777   int x1, y1;\r
3778   HPEN oldPen, hPen;\r
3779   if (lineGap == 0) return;\r
3780   if (flipView) {\r
3781     x1 = boardRect.left +\r
3782       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3783     y1 = boardRect.top +\r
3784       lineGap/2 + y * (squareSize + lineGap);\r
3785   } else {\r
3786     x1 = boardRect.left +\r
3787       lineGap/2 + x * (squareSize + lineGap);\r
3788     y1 = boardRect.top +\r
3789       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3790   }\r
3791   hPen = pen ? premovePen : highlightPen;\r
3792   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3793   MoveToEx(hdc, x1, y1, NULL);\r
3794   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3795   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3796   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3797   LineTo(hdc, x1, y1);\r
3798   SelectObject(hdc, oldPen);\r
3799 }\r
3800 \r
3801 VOID\r
3802 DrawHighlightsOnDC(HDC hdc)\r
3803 {\r
3804   int i;\r
3805   for (i=0; i<2; i++) {\r
3806     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)\r
3807       DrawHighlightOnDC(hdc, TRUE,\r
3808             highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3809             HIGHLIGHT_PEN);\r
3810   }\r
3811   for (i=0; i<2; i++) {\r
3812     if (premoveHighlightInfo.sq[i].x >= 0 &&\r
3813     premoveHighlightInfo.sq[i].y >= 0) {\r
3814     DrawHighlightOnDC(hdc, TRUE,\r
3815               premoveHighlightInfo.sq[i].x,\r
3816               premoveHighlightInfo.sq[i].y,\r
3817               PREMOVE_PEN);\r
3818     }\r
3819   }\r
3820 }\r
3821 \r
3822 /* Note: sqcolor is used only in monoMode */\r
3823 /* Note that this code is largely duplicated in woptions.c,\r
3824    function DrawSampleSquare, so that needs to be updated too */\r
3825 VOID\r
3826 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3827 {\r
3828   HBITMAP oldBitmap;\r
3829   HBRUSH oldBrush;\r
3830   int tmpSize;\r
3831 \r
3832   if (appData.blindfold) return;\r
3833 \r
3834   /* [AS] Use font-based pieces if needed */\r
3835   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3836     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3837     CreatePiecesFromFont();\r
3838 \r
3839     if( fontBitmapSquareSize == squareSize ) {\r
3840         int index = TranslatePieceToFontPiece(piece);\r
3841 \r
3842         SelectObject( tmphdc, hPieceMask[ index ] );\r
3843 \r
3844         BitBlt( hdc,\r
3845             x, y,\r
3846             squareSize, squareSize,\r
3847             tmphdc,\r
3848             0, 0,\r
3849             SRCAND );\r
3850 \r
3851         SelectObject( tmphdc, hPieceFace[ index ] );\r
3852 \r
3853         BitBlt( hdc,\r
3854             x, y,\r
3855             squareSize, squareSize,\r
3856             tmphdc,\r
3857             0, 0,\r
3858             SRCPAINT );\r
3859 \r
3860         return;\r
3861     }\r
3862   }\r
3863 \r
3864   if (appData.monoMode) {\r
3865     SelectObject(tmphdc, PieceBitmap(piece,\r
3866       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3867     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3868        sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3869   } else {\r
3870     tmpSize = squareSize;\r
3871     if(minorSize &&\r
3872         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3873          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3874       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3875       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3876       x += (squareSize - minorSize)>>1;\r
3877       y += squareSize - minorSize - 2;\r
3878       tmpSize = minorSize;\r
3879     }\r
3880     if (color || appData.allWhite ) {\r
3881       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3882       if( color )\r
3883               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3884       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3885       if(appData.upsideDown && color==flipView)\r
3886         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3887       else\r
3888         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3889 #if 0\r
3890       /* Use black piece color for outline of white pieces */\r
3891       /* Not sure this looks really good (though xboard does it).\r
3892      Maybe better to have another selectable color, default black */\r
3893       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3894       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3895       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3896 #else\r
3897       /* Use black for outline of white pieces */\r
3898       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3899       if(appData.upsideDown && color==flipView)\r
3900         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3901       else\r
3902         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3903 #endif\r
3904     } else {\r
3905 #if 0\r
3906       /* Use white piece color for details of black pieces */\r
3907       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3908      WHITE_PIECE ones aren't always the right shape. */\r
3909       /* Not sure this looks really good (though xboard does it).\r
3910      Maybe better to have another selectable color, default medium gray? */\r
3911       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3912       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3913       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3914       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3915       SelectObject(hdc, blackPieceBrush);\r
3916       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3917 #else\r
3918       /* Use square color for details of black pieces */\r
3919       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3920       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3921       if(appData.upsideDown && !flipView)\r
3922         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3923       else\r
3924         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3925 #endif\r
3926     }\r
3927     SelectObject(hdc, oldBrush);\r
3928     SelectObject(tmphdc, oldBitmap);\r
3929   }\r
3930 }\r
3931 \r
3932 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3933 int GetBackTextureMode( int algo )\r
3934 {\r
3935     int result = BACK_TEXTURE_MODE_DISABLED;\r
3936 \r
3937     switch( algo )\r
3938     {\r
3939         case BACK_TEXTURE_MODE_PLAIN:\r
3940             result = 1; /* Always use identity map */\r
3941             break;\r
3942         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3943             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3944             break;\r
3945     }\r
3946 \r
3947     return result;\r
3948 }\r
3949 \r
3950 /*\r
3951     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3952     to handle redraws cleanly (as random numbers would always be different).\r
3953 */\r
3954 VOID RebuildTextureSquareInfo()\r
3955 {\r
3956     BITMAP bi;\r
3957     int lite_w = 0;\r
3958     int lite_h = 0;\r
3959     int dark_w = 0;\r
3960     int dark_h = 0;\r
3961     int row;\r
3962     int col;\r
3963 \r
3964     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3965 \r
3966     if( liteBackTexture != NULL ) {\r
3967         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3968             lite_w = bi.bmWidth;\r
3969             lite_h = bi.bmHeight;\r
3970         }\r
3971     }\r
3972 \r
3973     if( darkBackTexture != NULL ) {\r
3974         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3975             dark_w = bi.bmWidth;\r
3976             dark_h = bi.bmHeight;\r
3977         }\r
3978     }\r
3979 \r
3980     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3981         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3982             if( (col + row) & 1 ) {\r
3983                 /* Lite square */\r
3984                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3985                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3986                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3987                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3988                 }\r
3989             }\r
3990             else {\r
3991                 /* Dark square */\r
3992                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3993                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3994                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3995                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3996                 }\r
3997             }\r
3998         }\r
3999     }\r
4000 }\r
4001 \r
4002 /* [AS] Arrow highlighting support */\r
4003 \r
4004 static int A_WIDTH = 5; /* Width of arrow body */\r
4005 \r
4006 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
4007 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
4008 \r
4009 static double Sqr( double x )\r
4010 {\r
4011     return x*x;\r
4012 }\r
4013 \r
4014 static int Round( double x )\r
4015 {\r
4016     return (int) (x + 0.5);\r
4017 }\r
4018 \r
4019 /* Draw an arrow between two points using current settings */\r
4020 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4021 {\r
4022     POINT arrow[7];\r
4023     double dx, dy, j, k, x, y;\r
4024 \r
4025     if( d_x == s_x ) {\r
4026         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4027 \r
4028         arrow[0].x = s_x + A_WIDTH;\r
4029         arrow[0].y = s_y;\r
4030 \r
4031         arrow[1].x = s_x + A_WIDTH;\r
4032         arrow[1].y = d_y - h;\r
4033 \r
4034         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4035         arrow[2].y = d_y - h;\r
4036 \r
4037         arrow[3].x = d_x;\r
4038         arrow[3].y = d_y;\r
4039 \r
4040         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4041         arrow[4].y = d_y - h;\r
4042 \r
4043         arrow[5].x = s_x - A_WIDTH;\r
4044         arrow[5].y = d_y - h;\r
4045 \r
4046         arrow[6].x = s_x - A_WIDTH;\r
4047         arrow[6].y = s_y;\r
4048     }\r
4049     else if( d_y == s_y ) {\r
4050         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4051 \r
4052         arrow[0].x = s_x;\r
4053         arrow[0].y = s_y + A_WIDTH;\r
4054 \r
4055         arrow[1].x = d_x - w;\r
4056         arrow[1].y = s_y + A_WIDTH;\r
4057 \r
4058         arrow[2].x = d_x - w;\r
4059         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4060 \r
4061         arrow[3].x = d_x;\r
4062         arrow[3].y = d_y;\r
4063 \r
4064         arrow[4].x = d_x - w;\r
4065         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4066 \r
4067         arrow[5].x = d_x - w;\r
4068         arrow[5].y = s_y - A_WIDTH;\r
4069 \r
4070         arrow[6].x = s_x;\r
4071         arrow[6].y = s_y - A_WIDTH;\r
4072     }\r
4073     else {\r
4074         /* [AS] Needed a lot of paper for this! :-) */\r
4075         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4076         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4077 \r
4078         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4079 \r
4080         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4081 \r
4082         x = s_x;\r
4083         y = s_y;\r
4084 \r
4085         arrow[0].x = Round(x - j);\r
4086         arrow[0].y = Round(y + j*dx);\r
4087 \r
4088         arrow[1].x = Round(x + j);\r
4089         arrow[1].y = Round(y - j*dx);\r
4090 \r
4091         if( d_x > s_x ) {\r
4092             x = (double) d_x - k;\r
4093             y = (double) d_y - k*dy;\r
4094         }\r
4095         else {\r
4096             x = (double) d_x + k;\r
4097             y = (double) d_y + k*dy;\r
4098         }\r
4099 \r
4100         arrow[2].x = Round(x + j);\r
4101         arrow[2].y = Round(y - j*dx);\r
4102 \r
4103         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4104         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4105 \r
4106         arrow[4].x = d_x;\r
4107         arrow[4].y = d_y;\r
4108 \r
4109         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4110         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4111 \r
4112         arrow[6].x = Round(x - j);\r
4113         arrow[6].y = Round(y + j*dx);\r
4114     }\r
4115 \r
4116     Polygon( hdc, arrow, 7 );\r
4117 }\r
4118 \r
4119 /* [AS] Draw an arrow between two squares */\r
4120 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4121 {\r
4122     int s_x, s_y, d_x, d_y;\r
4123     HPEN hpen;\r
4124     HPEN holdpen;\r
4125     HBRUSH hbrush;\r
4126     HBRUSH holdbrush;\r
4127     LOGBRUSH stLB;\r
4128 \r
4129     if( s_col == d_col && s_row == d_row ) {\r
4130         return;\r
4131     }\r
4132 \r
4133     /* Get source and destination points */\r
4134     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4135     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4136 \r
4137     if( d_y > s_y ) {\r
4138         d_y += squareSize / 4;\r
4139     }\r
4140     else if( d_y < s_y ) {\r
4141         d_y += 3 * squareSize / 4;\r
4142     }\r
4143     else {\r
4144         d_y += squareSize / 2;\r
4145     }\r
4146 \r
4147     if( d_x > s_x ) {\r
4148         d_x += squareSize / 4;\r
4149     }\r
4150     else if( d_x < s_x ) {\r
4151         d_x += 3 * squareSize / 4;\r
4152     }\r
4153     else {\r
4154         d_x += squareSize / 2;\r
4155     }\r
4156 \r
4157     s_x += squareSize / 2;\r
4158     s_y += squareSize / 2;\r
4159 \r
4160     /* Adjust width */\r
4161     A_WIDTH = squareSize / 14;\r
4162 \r
4163     /* Draw */\r
4164     stLB.lbStyle = BS_SOLID;\r
4165     stLB.lbColor = appData.highlightArrowColor;\r
4166     stLB.lbHatch = 0;\r
4167 \r
4168     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4169     holdpen = SelectObject( hdc, hpen );\r
4170     hbrush = CreateBrushIndirect( &stLB );\r
4171     holdbrush = SelectObject( hdc, hbrush );\r
4172 \r
4173     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4174 \r
4175     SelectObject( hdc, holdpen );\r
4176     SelectObject( hdc, holdbrush );\r
4177     DeleteObject( hpen );\r
4178     DeleteObject( hbrush );\r
4179 }\r
4180 \r
4181 BOOL HasHighlightInfo()\r
4182 {\r
4183     BOOL result = FALSE;\r
4184 \r
4185     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4186         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4187     {\r
4188         result = TRUE;\r
4189     }\r
4190 \r
4191     return result;\r
4192 }\r
4193 \r
4194 BOOL IsDrawArrowEnabled()\r
4195 {\r
4196     BOOL result = FALSE;\r
4197 \r
4198     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4199         result = TRUE;\r
4200     }\r
4201 \r
4202     return result;\r
4203 }\r
4204 \r
4205 VOID DrawArrowHighlight( HDC hdc )\r
4206 {\r
4207     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4208         DrawArrowBetweenSquares( hdc,\r
4209             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4210             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4211     }\r
4212 }\r
4213 \r
4214 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4215 {\r
4216     HRGN result = NULL;\r
4217 \r
4218     if( HasHighlightInfo() ) {\r
4219         int x1, y1, x2, y2;\r
4220         int sx, sy, dx, dy;\r
4221 \r
4222         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4223         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4224 \r
4225         sx = MIN( x1, x2 );\r
4226         sy = MIN( y1, y2 );\r
4227         dx = MAX( x1, x2 ) + squareSize;\r
4228         dy = MAX( y1, y2 ) + squareSize;\r
4229 \r
4230         result = CreateRectRgn( sx, sy, dx, dy );\r
4231     }\r
4232 \r
4233     return result;\r
4234 }\r
4235 \r
4236 /*\r
4237     Warning: this function modifies the behavior of several other functions.\r
4238 \r
4239     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4240     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4241     repaint is scattered all over the place, which is not good for features such as\r
4242     "arrow highlighting" that require a full repaint of the board.\r
4243 \r
4244     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4245     user interaction, when speed is not so important) but especially to avoid errors\r
4246     in the displayed graphics.\r
4247 \r
4248     In such patched places, I always try refer to this function so there is a single\r
4249     place to maintain knowledge.\r
4250 \r
4251     To restore the original behavior, just return FALSE unconditionally.\r
4252 */\r
4253 BOOL IsFullRepaintPreferrable()\r
4254 {\r
4255     BOOL result = FALSE;\r
4256 \r
4257     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4258         /* Arrow may appear on the board */\r
4259         result = TRUE;\r
4260     }\r
4261 \r
4262     return result;\r
4263 }\r
4264 \r
4265 /*\r
4266     This function is called by DrawPosition to know whether a full repaint must\r
4267     be forced or not.\r
4268 \r
4269     Only DrawPosition may directly call this function, which makes use of\r
4270     some state information. Other function should call DrawPosition specifying\r
4271     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4272 */\r
4273 BOOL DrawPositionNeedsFullRepaint()\r
4274 {\r
4275     BOOL result = FALSE;\r
4276 \r
4277     /*\r
4278         Probably a slightly better policy would be to trigger a full repaint\r
4279         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4280         but animation is fast enough that it's difficult to notice.\r
4281     */\r
4282     if( animInfo.piece == EmptySquare ) {\r
4283         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4284             result = TRUE;\r
4285         }\r
4286     }\r
4287 \r
4288     return result;\r
4289 }\r
4290 \r
4291 VOID\r
4292 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4293 {\r
4294   int row, column, x, y, square_color, piece_color;\r
4295   ChessSquare piece;\r
4296   HBRUSH oldBrush;\r
4297   HDC texture_hdc = NULL;\r
4298 \r
4299   /* [AS] Initialize background textures if needed */\r
4300   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4301       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4302       if( backTextureSquareSize != squareSize\r
4303        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4304       backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4305           backTextureSquareSize = squareSize;\r
4306           RebuildTextureSquareInfo();\r
4307       }\r
4308 \r
4309       texture_hdc = CreateCompatibleDC( hdc );\r
4310   }\r
4311 \r
4312   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4313     for (column = 0; column < BOARD_WIDTH; column++) {\r
4314 \r
4315       SquareToPos(row, column, &x, &y);\r
4316 \r
4317       piece = board[row][column];\r
4318 \r
4319       square_color = ((column + row) % 2) == 1;\r
4320       if( gameInfo.variant == VariantXiangqi ) {\r
4321           square_color = !InPalace(row, column);\r
4322           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4323           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4324       }\r
4325       piece_color = (int) piece < (int) BlackPawn;\r
4326 \r
4327 \r
4328       /* [HGM] holdings file: light square or black */\r
4329       if(column == BOARD_LEFT-2) {\r
4330             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4331                 square_color = 1;\r
4332             else {\r
4333                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4334                 continue;\r
4335             }\r
4336       } else\r
4337       if(column == BOARD_RGHT + 1 ) {\r
4338             if( row < gameInfo.holdingsSize )\r
4339                 square_color = 1;\r
4340             else {\r
4341                 DisplayHoldingsCount(hdc, x, y, 0, 0);\r
4342                 continue;\r
4343             }\r
4344       }\r
4345       if(column == BOARD_LEFT-1 ) /* left align */\r
4346             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4347       else if( column == BOARD_RGHT) /* right align */\r
4348             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4349       else\r
4350       if (appData.monoMode) {\r
4351         if (piece == EmptySquare) {\r
4352           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4353          square_color ? WHITENESS : BLACKNESS);\r
4354         } else {\r
4355           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4356         }\r
4357       }\r
4358       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4359           /* [AS] Draw the square using a texture bitmap */\r
4360           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4361       int r = row, c = column; // [HGM] do not flip board in flipView\r
4362       if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4363 \r
4364           DrawTile( x, y,\r
4365               squareSize, squareSize,\r
4366               hdc,\r
4367               texture_hdc,\r
4368               backTextureSquareInfo[r][c].mode,\r
4369               backTextureSquareInfo[r][c].x,\r
4370               backTextureSquareInfo[r][c].y );\r
4371 \r
4372           SelectObject( texture_hdc, hbm );\r
4373 \r
4374           if (piece != EmptySquare) {\r
4375               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4376           }\r
4377       }\r
4378       else {\r
4379         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4380 \r
4381         oldBrush = SelectObject(hdc, brush );\r
4382         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4383         SelectObject(hdc, oldBrush);\r
4384         if (piece != EmptySquare)\r
4385           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4386       }\r
4387     }\r
4388   }\r
4389 \r
4390   if( texture_hdc != NULL ) {\r
4391     DeleteDC( texture_hdc );\r
4392   }\r
4393 }\r
4394 \r
4395 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4396 void fputDW(FILE *f, int x)\r
4397 {\r
4398     fputc(x     & 255, f);\r
4399     fputc(x>>8  & 255, f);\r
4400     fputc(x>>16 & 255, f);\r
4401     fputc(x>>24 & 255, f);\r
4402 }\r
4403 \r
4404 #define MAX_CLIPS 200   /* more than enough */\r
4405 \r
4406 VOID\r
4407 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4408 {\r
4409 //  HBITMAP bufferBitmap;\r
4410   BITMAP bi;\r
4411 //  RECT Rect;\r
4412   HDC tmphdc;\r
4413   HBITMAP hbm;\r
4414   int w = 100, h = 50;\r
4415 \r
4416   if(logo == NULL) return;\r
4417 //  GetClientRect(hwndMain, &Rect);\r
4418 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4419 //                  Rect.bottom-Rect.top+1);\r
4420   tmphdc = CreateCompatibleDC(hdc);\r
4421   hbm = SelectObject(tmphdc, logo);\r
4422   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4423             w = bi.bmWidth;\r
4424             h = bi.bmHeight;\r
4425   }\r
4426   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,\r
4427                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4428   SelectObject(tmphdc, hbm);\r
4429   DeleteDC(tmphdc);\r
4430 }\r
4431 \r
4432 VOID\r
4433 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4434 {\r
4435   static Board lastReq, lastDrawn;\r
4436   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4437   static int lastDrawnFlipView = 0;\r
4438   static int lastReqValid = 0, lastDrawnValid = 0;\r
4439   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4440   HDC tmphdc;\r
4441   HDC hdcmem;\r
4442   HBITMAP bufferBitmap;\r
4443   HBITMAP oldBitmap;\r
4444   RECT Rect;\r
4445   HRGN clips[MAX_CLIPS];\r
4446   ChessSquare dragged_piece = EmptySquare;\r
4447 \r
4448   /* I'm undecided on this - this function figures out whether a full\r
4449    * repaint is necessary on its own, so there's no real reason to have the\r
4450    * caller tell it that.  I think this can safely be set to FALSE - but\r
4451    * if we trust the callers not to request full repaints unnessesarily, then\r
4452    * we could skip some clipping work.  In other words, only request a full\r
4453    * redraw when the majority of pieces have changed positions (ie. flip,\r
4454    * gamestart and similar)  --Hawk\r
4455    */\r
4456   Boolean fullrepaint = repaint;\r
4457 \r
4458   if( DrawPositionNeedsFullRepaint() ) {\r
4459       fullrepaint = TRUE;\r
4460   }\r
4461 \r
4462 #if 0\r
4463   if( fullrepaint ) {\r
4464       static int repaint_count = 0;\r
4465       char buf[128];\r
4466 \r
4467       repaint_count++;\r
4468       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4469       OutputDebugString( buf );\r
4470   }\r
4471 #endif\r
4472 \r
4473   if (board == NULL) {\r
4474     if (!lastReqValid) {\r
4475       return;\r
4476     }\r
4477     board = lastReq;\r
4478   } else {\r
4479     CopyBoard(lastReq, board);\r
4480     lastReqValid = 1;\r
4481   }\r
4482 \r
4483   if (doingSizing) {\r
4484     return;\r
4485   }\r
4486 \r
4487   if (IsIconic(hwndMain)) {\r
4488     return;\r
4489   }\r
4490 \r
4491   if (hdc == NULL) {\r
4492     hdc = GetDC(hwndMain);\r
4493     if (!appData.monoMode) {\r
4494       SelectPalette(hdc, hPal, FALSE);\r
4495       RealizePalette(hdc);\r
4496     }\r
4497     releaseDC = TRUE;\r
4498   } else {\r
4499     releaseDC = FALSE;\r
4500   }\r
4501 \r
4502 #if 0\r
4503   fprintf(debugFP, "*******************************\n"\r
4504                    "repaint = %s\n"\r
4505                    "dragInfo.from (%d,%d)\n"\r
4506                    "dragInfo.start (%d,%d)\n"\r
4507                    "dragInfo.pos (%d,%d)\n"\r
4508                    "dragInfo.lastpos (%d,%d)\n",\r
4509                     repaint ? "TRUE" : "FALSE",\r
4510                     dragInfo.from.x, dragInfo.from.y,\r
4511                     dragInfo.start.x, dragInfo.start.y,\r
4512                     dragInfo.pos.x, dragInfo.pos.y,\r
4513                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4514   fprintf(debugFP, "prev:  ");\r
4515   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4516     for (column = 0; column < BOARD_WIDTH; column++) {\r
4517       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4518     }\r
4519   }\r
4520   fprintf(debugFP, "\n");\r
4521   fprintf(debugFP, "board: ");\r
4522   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4523     for (column = 0; column < BOARD_WIDTH; column++) {\r
4524       fprintf(debugFP, "%d ", board[row][column]);\r
4525     }\r
4526   }\r
4527   fprintf(debugFP, "\n");\r
4528   fflush(debugFP);\r
4529 #endif\r
4530 \r
4531   /* Create some work-DCs */\r
4532   hdcmem = CreateCompatibleDC(hdc);\r
4533   tmphdc = CreateCompatibleDC(hdc);\r
4534 \r
4535   /* If dragging is in progress, we temporarely remove the piece */\r
4536   /* [HGM] or temporarily decrease count if stacked              */\r
4537   /*       !! Moved to before board compare !!                   */\r
4538   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4539     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4540     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4541             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4542         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4543     } else\r
4544     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4545             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4546         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4547     } else\r
4548         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4549   }\r
4550 \r
4551   /* Figure out which squares need updating by comparing the\r
4552    * newest board with the last drawn board and checking if\r
4553    * flipping has changed.\r
4554    */\r
4555   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4556     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4557       for (column = 0; column < BOARD_WIDTH; column++) {\r
4558     if (lastDrawn[row][column] != board[row][column]) {\r
4559       SquareToPos(row, column, &x, &y);\r
4560       clips[num_clips++] =\r
4561         CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4562     }\r
4563       }\r
4564     }\r
4565     for (i=0; i<2; i++) {\r
4566       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4567       lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4568     if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4569         lastDrawnHighlight.sq[i].y >= 0) {\r
4570       SquareToPos(lastDrawnHighlight.sq[i].y,\r
4571               lastDrawnHighlight.sq[i].x, &x, &y);\r
4572       clips[num_clips++] =\r
4573         CreateRectRgn(x - lineGap, y - lineGap,\r
4574                       x + squareSize + lineGap, y + squareSize + lineGap);\r
4575     }\r
4576     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4577       SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4578       clips[num_clips++] =\r
4579         CreateRectRgn(x - lineGap, y - lineGap,\r
4580                       x + squareSize + lineGap, y + squareSize + lineGap);\r
4581     }\r
4582       }\r
4583     }\r
4584     for (i=0; i<2; i++) {\r
4585       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4586       lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4587     if (lastDrawnPremove.sq[i].x >= 0 &&\r
4588         lastDrawnPremove.sq[i].y >= 0) {\r
4589       SquareToPos(lastDrawnPremove.sq[i].y,\r
4590               lastDrawnPremove.sq[i].x, &x, &y);\r
4591       clips[num_clips++] =\r
4592         CreateRectRgn(x - lineGap, y - lineGap,\r
4593                       x + squareSize + lineGap, y + squareSize + lineGap);\r
4594     }\r
4595     if (premoveHighlightInfo.sq[i].x >= 0 &&\r
4596         premoveHighlightInfo.sq[i].y >= 0) {\r
4597       SquareToPos(premoveHighlightInfo.sq[i].y,\r
4598               premoveHighlightInfo.sq[i].x, &x, &y);\r
4599       clips[num_clips++] =\r
4600         CreateRectRgn(x - lineGap, y - lineGap,\r
4601                       x + squareSize + lineGap, y + squareSize + lineGap);\r
4602     }\r
4603       }\r
4604     }\r
4605   } else {\r
4606     fullrepaint = TRUE;\r
4607   }\r
4608 \r
4609   /* Create a buffer bitmap - this is the actual bitmap\r
4610    * being written to.  When all the work is done, we can\r
4611    * copy it to the real DC (the screen).  This avoids\r
4612    * the problems with flickering.\r
4613    */\r
4614   GetClientRect(hwndMain, &Rect);\r
4615   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4616                     Rect.bottom-Rect.top+1);\r
4617   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4618   if (!appData.monoMode) {\r
4619     SelectPalette(hdcmem, hPal, FALSE);\r
4620   }\r
4621 \r
4622   /* Create clips for dragging */\r
4623   if (!fullrepaint) {\r
4624     if (dragInfo.from.x >= 0) {\r
4625       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4626       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4627     }\r
4628     if (dragInfo.start.x >= 0) {\r
4629       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4630       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4631     }\r
4632     if (dragInfo.pos.x >= 0) {\r
4633       x = dragInfo.pos.x - squareSize / 2;\r
4634       y = dragInfo.pos.y - squareSize / 2;\r
4635       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4636     }\r
4637     if (dragInfo.lastpos.x >= 0) {\r
4638       x = dragInfo.lastpos.x - squareSize / 2;\r
4639       y = dragInfo.lastpos.y - squareSize / 2;\r
4640       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4641     }\r
4642   }\r
4643 \r
4644   /* Are we animating a move?\r
4645    * If so,\r
4646    *   - remove the piece from the board (temporarely)\r
4647    *   - calculate the clipping region\r
4648    */\r
4649   if (!fullrepaint) {\r
4650     if (animInfo.piece != EmptySquare) {\r
4651       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4652       x = boardRect.left + animInfo.lastpos.x;\r
4653       y = boardRect.top + animInfo.lastpos.y;\r
4654       x2 = boardRect.left + animInfo.pos.x;\r
4655       y2 = boardRect.top + animInfo.pos.y;\r
4656       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4657       /* Slight kludge.  The real problem is that after AnimateMove is\r
4658      done, the position on the screen does not match lastDrawn.\r
4659      This currently causes trouble only on e.p. captures in\r
4660      atomic, where the piece moves to an empty square and then\r
4661      explodes.  The old and new positions both had an empty square\r
4662      at the destination, but animation has drawn a piece there and\r
4663      we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4664       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4665     }\r
4666   }\r
4667 \r
4668   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4669   if (num_clips == 0)\r
4670     fullrepaint = TRUE;\r
4671 \r
4672   /* Set clipping on the memory DC */\r
4673   if (!fullrepaint) {\r
4674     SelectClipRgn(hdcmem, clips[0]);\r
4675     for (x = 1; x < num_clips; x++) {\r
4676       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4677         abort();  // this should never ever happen!\r
4678     }\r
4679   }\r
4680 \r
4681   /* Do all the drawing to the memory DC */\r
4682   if(explodeInfo.radius) { // [HGM] atomic\r
4683     HBRUSH oldBrush;\r
4684     int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4685         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4686     SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4687     x += squareSize/2;\r
4688     y += squareSize/2;\r
4689         if(!fullrepaint) {\r
4690       clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4691       ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4692     }\r
4693     DrawGridOnDC(hdcmem);\r
4694     DrawHighlightsOnDC(hdcmem);\r
4695     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4696     oldBrush = SelectObject(hdcmem, explodeBrush);\r
4697     Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4698     SelectObject(hdcmem, oldBrush);\r
4699   } else {\r
4700     DrawGridOnDC(hdcmem);\r
4701     DrawHighlightsOnDC(hdcmem);\r
4702     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4703   }\r
4704   if(logoHeight) {\r
4705     HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4706     if(appData.autoLogo) {\r
4707 \r
4708       switch(gameMode) { // pick logos based on game mode\r
4709         case IcsObserving:\r
4710         whiteLogo = second.programLogo; // ICS logo\r
4711         blackLogo = second.programLogo;\r
4712         default:\r
4713         break;\r
4714         case IcsPlayingWhite:\r
4715         if(!appData.zippyPlay) whiteLogo = userLogo;\r
4716         blackLogo = second.programLogo; // ICS logo\r
4717         break;\r
4718         case IcsPlayingBlack:\r
4719         whiteLogo = second.programLogo; // ICS logo\r
4720         blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4721         break;\r
4722         case TwoMachinesPlay:\r
4723             if(first.twoMachinesColor[0] == 'b') {\r
4724             whiteLogo = second.programLogo;\r
4725             blackLogo = first.programLogo;\r
4726         }\r
4727         break;\r
4728         case MachinePlaysWhite:\r
4729         blackLogo = userLogo;\r
4730         break;\r
4731         case MachinePlaysBlack:\r
4732         whiteLogo = userLogo;\r
4733         blackLogo = first.programLogo;\r
4734       }\r
4735     }\r
4736     DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4737     DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4738   }\r
4739 \r
4740   if( appData.highlightMoveWithArrow ) {\r
4741     DrawArrowHighlight(hdcmem);\r
4742   }\r
4743 \r
4744   DrawCoordsOnDC(hdcmem);\r
4745 \r
4746   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4747                  /* to make sure lastDrawn contains what is actually drawn */\r
4748 \r
4749   /* Put the dragged piece back into place and draw it (out of place!) */\r
4750     if (dragged_piece != EmptySquare) {\r
4751     /* [HGM] or restack */\r
4752     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4753                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4754     else\r
4755     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4756                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4757     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4758     x = dragInfo.pos.x - squareSize / 2;\r
4759     y = dragInfo.pos.y - squareSize / 2;\r
4760     DrawPieceOnDC(hdcmem, dragged_piece,\r
4761           ((int) dragged_piece < (int) BlackPawn),\r
4762                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4763   }\r
4764 \r
4765   /* Put the animated piece back into place and draw it */\r
4766   if (animInfo.piece != EmptySquare) {\r
4767     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4768     x = boardRect.left + animInfo.pos.x;\r
4769     y = boardRect.top + animInfo.pos.y;\r
4770     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4771           ((int) animInfo.piece < (int) BlackPawn),\r
4772                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4773   }\r
4774 \r
4775   /* Release the bufferBitmap by selecting in the old bitmap\r
4776    * and delete the memory DC\r
4777    */\r
4778   SelectObject(hdcmem, oldBitmap);\r
4779   DeleteDC(hdcmem);\r
4780 \r
4781   /* Set clipping on the target DC */\r
4782   if (!fullrepaint) {\r
4783     SelectClipRgn(hdc, clips[0]);\r
4784     for (x = 1; x < num_clips; x++) {\r
4785       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4786         abort();   // this should never ever happen!\r
4787     }\r
4788   }\r
4789 \r
4790   /* Copy the new bitmap onto the screen in one go.\r
4791    * This way we avoid any flickering\r
4792    */\r
4793   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4794   BitBlt(hdc, boardRect.left, boardRect.top,\r
4795      boardRect.right - boardRect.left,\r
4796      boardRect.bottom - boardRect.top,\r
4797      tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4798   if(saveDiagFlag) {\r
4799     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];\r
4800     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4801 \r
4802     GetObject(bufferBitmap, sizeof(b), &b);\r
4803     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4804     bih.biSize = sizeof(BITMAPINFOHEADER);\r
4805     bih.biWidth = b.bmWidth;\r
4806     bih.biHeight = b.bmHeight;\r
4807     bih.biPlanes = 1;\r
4808     bih.biBitCount = b.bmBitsPixel;\r
4809     bih.biCompression = 0;\r
4810     bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4811     bih.biXPelsPerMeter = 0;\r
4812     bih.biYPelsPerMeter = 0;\r
4813     bih.biClrUsed = 0;\r
4814     bih.biClrImportant = 0;\r
4815 //  fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",\r
4816 //      b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4817     GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4818 //  fprintf(diagFile, "%8x\n", (int) pData);\r
4819 \r
4820 #if 1\r
4821     wb = b.bmWidthBytes;\r
4822     // count colors\r
4823     for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4824         int k = ((int*) pData)[i];\r
4825         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4826         if(j >= 16) break;\r
4827         color[j] = k;\r
4828         if(j >= nrColors) nrColors = j+1;\r
4829     }\r
4830     if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4831         INT p = 0;\r
4832         for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4833             for(w=0; w<(wb>>2); w+=2) {\r
4834             int k = ((int*) pData)[(wb*i>>2) + w];\r
4835             for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4836             k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4837             for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4838             pData[p++] = m | j<<4;\r
4839             }\r
4840             while(p&3) pData[p++] = 0;\r
4841         }\r
4842         fac = 3;\r
4843         wb = ((wb+31)>>5)<<2;\r
4844     }\r
4845     // write BITMAPFILEHEADER\r
4846     fprintf(diagFile, "BM");\r
4847         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4848         fputDW(diagFile, 0);\r
4849         fputDW(diagFile, 0x36 + (fac?64:0));\r
4850     // write BITMAPINFOHEADER\r
4851         fputDW(diagFile, 40);\r
4852         fputDW(diagFile, b.bmWidth);\r
4853         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4854     if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4855         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4856         fputDW(diagFile, 0);\r
4857         fputDW(diagFile, 0);\r
4858         fputDW(diagFile, 0);\r
4859         fputDW(diagFile, 0);\r
4860         fputDW(diagFile, 0);\r
4861         fputDW(diagFile, 0);\r
4862     // write color table\r
4863     if(fac)\r
4864     for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4865     // write bitmap data\r
4866     for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)\r
4867         fputc(pData[i], diagFile);\r
4868 #endif\r
4869      }\r
4870   }\r
4871 \r
4872   SelectObject(tmphdc, oldBitmap);\r
4873 \r
4874   /* Massive cleanup */\r
4875   for (x = 0; x < num_clips; x++)\r
4876     DeleteObject(clips[x]);\r
4877 \r
4878   DeleteDC(tmphdc);\r
4879   DeleteObject(bufferBitmap);\r
4880 \r
4881   if (releaseDC)\r
4882     ReleaseDC(hwndMain, hdc);\r
4883 \r
4884   if (lastDrawnFlipView != flipView) {\r
4885     if (flipView)\r
4886       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4887     else\r
4888       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4889   }\r
4890 \r
4891 /*  CopyBoard(lastDrawn, board);*/\r
4892   lastDrawnHighlight = highlightInfo;\r
4893   lastDrawnPremove   = premoveHighlightInfo;\r
4894   lastDrawnFlipView = flipView;\r
4895   lastDrawnValid = 1;\r
4896 }\r
4897 \r
4898 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4899 int\r
4900 SaveDiagram(f)\r
4901      FILE *f;\r
4902 {\r
4903     saveDiagFlag = 1; diagFile = f;\r
4904     HDCDrawPosition(NULL, TRUE, NULL);\r
4905 \r
4906     saveDiagFlag = 0;\r
4907 \r
4908 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4909 \r
4910     fclose(f);\r
4911     return TRUE;\r
4912 }\r
4913 \r
4914 \r
4915 /*---------------------------------------------------------------------------*\\r
4916 | CLIENT PAINT PROCEDURE\r
4917 |   This is the main event-handler for the WM_PAINT message.\r
4918 |\r
4919 \*---------------------------------------------------------------------------*/\r
4920 VOID\r
4921 PaintProc(HWND hwnd)\r
4922 {\r
4923   HDC         hdc;\r
4924   PAINTSTRUCT ps;\r
4925   HFONT       oldFont;\r
4926 \r
4927   if((hdc = BeginPaint(hwnd, &ps))) {\r
4928     if (IsIconic(hwnd)) {\r
4929       DrawIcon(hdc, 2, 2, iconCurrent);\r
4930     } else {\r
4931       if (!appData.monoMode) {\r
4932     SelectPalette(hdc, hPal, FALSE);\r
4933     RealizePalette(hdc);\r
4934       }\r
4935       HDCDrawPosition(hdc, 1, NULL);\r
4936       oldFont =\r
4937     SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4938       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4939          ETO_CLIPPED|ETO_OPAQUE,\r
4940          &messageRect, messageText, strlen(messageText), NULL);\r
4941       SelectObject(hdc, oldFont);\r
4942       DisplayBothClocks();\r
4943     }\r
4944     EndPaint(hwnd,&ps);\r
4945   }\r
4946 \r
4947   return;\r
4948 }\r
4949 \r
4950 \r
4951 /*\r
4952  * If the user selects on a border boundary, return -1; if off the board,\r
4953  *   return -2.  Otherwise map the event coordinate to the square.\r
4954  * The offset boardRect.left or boardRect.top must already have been\r
4955  *   subtracted from x.\r
4956  */\r
4957 int\r
4958 EventToSquare(int x)\r
4959 {\r
4960   if (x <= 0)\r
4961     return -2;\r
4962   if (x < lineGap)\r
4963     return -1;\r
4964   x -= lineGap;\r
4965   if ((x % (squareSize + lineGap)) >= squareSize)\r
4966     return -1;\r
4967   x /= (squareSize + lineGap);\r
4968   if (x >= BOARD_SIZE)\r
4969     return -2;\r
4970   return x;\r
4971 }\r
4972 \r
4973 typedef struct {\r
4974   char piece;\r
4975   int command;\r
4976   char* name;\r
4977 } DropEnable;\r
4978 \r
4979 DropEnable dropEnables[] = {\r
4980   { 'P', DP_Pawn, "Pawn" },\r
4981   { 'N', DP_Knight, "Knight" },\r
4982   { 'B', DP_Bishop, "Bishop" },\r
4983   { 'R', DP_Rook, "Rook" },\r
4984   { 'Q', DP_Queen, "Queen" },\r
4985 };\r
4986 \r
4987 VOID\r
4988 SetupDropMenu(HMENU hmenu)\r
4989 {\r
4990   int i, count, enable;\r
4991   char *p;\r
4992   extern char white_holding[], black_holding[];\r
4993   char item[MSG_SIZ];\r
4994 \r
4995   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4996     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4997            dropEnables[i].piece);\r
4998     count = 0;\r
4999     while (p && *p++ == dropEnables[i].piece) count++;\r
5000     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
5001     enable = count > 0 || !appData.testLegality\r
5002       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
5003               && !appData.icsActive);\r
5004     ModifyMenu(hmenu, dropEnables[i].command,\r
5005            MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
5006            dropEnables[i].command, item);\r
5007   }\r
5008 }\r
5009 \r
5010 /* Event handler for mouse messages */\r
5011 VOID\r
5012 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5013 {\r
5014   int x, y;\r
5015   POINT pt;\r
5016   static int recursive = 0;\r
5017   HMENU hmenu;\r
5018 //  BOOLEAN needsRedraw = FALSE;\r
5019   BOOLEAN saveAnimate;\r
5020   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5021   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5022   ChessMove moveType;\r
5023 \r
5024   if (recursive) {\r
5025     if (message == WM_MBUTTONUP) {\r
5026       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5027      to the middle button: we simulate pressing the left button too!\r
5028      */\r
5029       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5030       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5031     }\r
5032     return;\r
5033   }\r
5034   recursive++;\r
5035 \r
5036   pt.x = LOWORD(lParam);\r
5037   pt.y = HIWORD(lParam);\r
5038   x = EventToSquare(pt.x - boardRect.left);\r
5039   y = EventToSquare(pt.y - boardRect.top);\r
5040   if (!flipView && y >= 0) {\r
5041     y = BOARD_HEIGHT - 1 - y;\r
5042   }\r
5043   if (flipView && x >= 0) {\r
5044     x = BOARD_WIDTH - 1 - x;\r
5045   }\r
5046 \r
5047   switch (message) {\r
5048   case WM_LBUTTONDOWN:\r
5049     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5050     promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5051     if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5052     if(gameInfo.holdingsWidth &&\r
5053         (WhiteOnMove(currentMove)\r
5054             ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5055             : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5056         // click in right holdings, for determining promotion piece\r
5057         ChessSquare p = boards[currentMove][y][x];\r
5058         if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5059         if(p != EmptySquare) {\r
5060         FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5061         fromX = fromY = -1;\r
5062         break;\r
5063         }\r
5064     }\r
5065     DrawPosition(FALSE, boards[currentMove]);\r
5066     break;\r
5067     }\r
5068     ErrorPopDown();\r
5069     sameAgain = FALSE;\r
5070     if (y == -2) {\r
5071       /* Downclick vertically off board; check if on clock */\r
5072       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5073         if (gameMode == EditPosition) {\r
5074       SetWhiteToPlayEvent();\r
5075     } else if (gameMode == IcsPlayingBlack ||\r
5076            gameMode == MachinePlaysWhite) {\r
5077       CallFlagEvent();\r
5078         } else if (gameMode == EditGame) {\r
5079           AdjustClock(flipClock, -1);\r
5080         }\r
5081       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5082     if (gameMode == EditPosition) {\r
5083       SetBlackToPlayEvent();\r
5084     } else if (gameMode == IcsPlayingWhite ||\r
5085            gameMode == MachinePlaysBlack) {\r
5086       CallFlagEvent();\r
5087         } else if (gameMode == EditGame) {\r
5088           AdjustClock(!flipClock, -1);\r
5089     }\r
5090       }\r
5091       if (!appData.highlightLastMove) {\r
5092         ClearHighlights();\r
5093     DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5094       }\r
5095       fromX = fromY = -1;\r
5096       dragInfo.start.x = dragInfo.start.y = -1;\r
5097       dragInfo.from = dragInfo.start;\r
5098       break;\r
5099     } else if (x < 0 || y < 0\r
5100       /* [HGM] block clicks between board and holdings */\r
5101               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5102               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5103               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5104     /* EditPosition, empty square, or different color piece;\r
5105        click-click move is possible */\r
5106                                ) {\r
5107       break;\r
5108     } else if (fromX == x && fromY == y) {\r
5109       /* Downclick on same square again */\r
5110       ClearHighlights();\r
5111       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5112       sameAgain = TRUE;\r
5113     } else if (fromX != -1 &&\r
5114                x != BOARD_LEFT-2 && x != BOARD_RGHT+1\r
5115                                                                         ) {\r
5116       /* Downclick on different square. */\r
5117       /* [HGM] if on holdings file, should count as new first click ! */\r
5118       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5119     toX = x;\r
5120     toY = y;\r
5121         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5122            to make sure move is legal before showing promotion popup */\r
5123         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE);\r
5124     if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5125         fromX = fromY = -1;\r
5126         ClearHighlights();\r
5127         DrawPosition(FALSE, boards[currentMove]);\r
5128         break;\r
5129     } else\r
5130         if(moveType != ImpossibleMove && moveType != Comment) {\r
5131           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5132           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5133             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5134               appData.alwaysPromoteToQueen)) {\r
5135                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5136                   if (!appData.highlightLastMove) {\r
5137                       ClearHighlights();\r
5138                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5139                   }\r
5140           } else\r
5141           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5142                   SetHighlights(fromX, fromY, toX, toY);\r
5143                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5144                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5145                      If promotion to Q is legal, all are legal! */\r
5146           if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5147           { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5148             // kludge to temporarily execute move on display, without promoting yet\r
5149             promotionChoice = TRUE;\r
5150             boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5151             boards[currentMove][toY][toX] = p;\r
5152             DrawPosition(FALSE, boards[currentMove]);\r
5153             boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5154             boards[currentMove][toY][toX] = q;\r
5155             DisplayMessage("Select piece from holdings", "");\r
5156           } else\r
5157                   PromotionPopup(hwnd);\r
5158           goto noClear;\r
5159           } else { // not a promotion. Move can be illegal if testLegality off, and should be made then.\r
5160              if (appData.animate || appData.highlightLastMove) {\r
5161                  SetHighlights(fromX, fromY, toX, toY);\r
5162              } else {\r
5163                  ClearHighlights();\r
5164              }\r
5165              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5166              if (appData.animate && !appData.highlightLastMove) {\r
5167                   ClearHighlights();\r
5168                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5169              }\r
5170           }\r
5171           fromX = fromY = -1;\r
5172     noClear:\r
5173       break;\r
5174         }\r
5175         if (gotPremove && moveType != Comment) {\r
5176         SetPremoveHighlights(fromX, fromY, toX, toY);\r
5177 //            DrawPosition(forceFullRepaint || FALSE, NULL);\r
5178     } else ClearHighlights();\r
5179         fromX = fromY = -1;\r
5180         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5181     if(moveType != Comment) break;\r
5182     }\r
5183     /* First downclick, or restart on a square with same color piece */\r
5184     if (!frozen && OKToStartUserMove(x, y)) {\r
5185       fromX = x;\r
5186       fromY = y;\r
5187       dragInfo.lastpos = pt;\r
5188       dragInfo.from.x = fromX;\r
5189       dragInfo.from.y = fromY;\r
5190       dragInfo.start = dragInfo.from;\r
5191       SetCapture(hwndMain);\r
5192     } else {\r
5193       fromX = fromY = -1;\r
5194       dragInfo.start.x = dragInfo.start.y = -1;\r
5195       dragInfo.from = dragInfo.start;\r
5196       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5197     }\r
5198     break;\r
5199 \r
5200   case WM_LBUTTONUP:\r
5201     ReleaseCapture();\r
5202     if (fromX == -1) break;\r
5203     if (x == fromX && y == fromY) {\r
5204       dragInfo.from.x = dragInfo.from.y = -1;\r
5205       /* Upclick on same square */\r
5206       if (sameAgain) {\r
5207     /* Clicked same square twice: abort click-click move */\r
5208     fromX = fromY = -1;\r
5209     gotPremove = 0;\r
5210     ClearPremoveHighlights();\r
5211       } else {\r
5212     /* First square clicked: start click-click move */\r
5213     SetHighlights(fromX, fromY, -1, -1);\r
5214       }\r
5215       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5216     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5217       /* Errant click; ignore */\r
5218       break;\r
5219     } else {\r
5220       /* Finish drag move. */\r
5221     if (appData.debugMode) {\r
5222         fprintf(debugFP, "release\n");\r
5223     }\r
5224       dragInfo.from.x = dragInfo.from.y = -1;\r
5225       toX = x;\r
5226       toY = y;\r
5227       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5228       appData.animate = appData.animate && !appData.animateDragging;\r
5229       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, TRUE);\r
5230       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5231         fromX = fromY = -1;\r
5232         ClearHighlights();\r
5233         DrawPosition(FALSE, boards[currentMove]);\r
5234         appData.animate = saveAnimate;\r
5235         break;\r
5236       } else\r
5237       if(moveType != ImpossibleMove) {\r
5238           /* [HGM] use move type to determine if move is promotion.\r
5239              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5240           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5241             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5242               appData.alwaysPromoteToQueen))\r
5243                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5244           else\r
5245           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5246                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5247           if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5248           { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5249             // kludge to temporarily execute move on display, wthout promotng yet\r
5250             promotionChoice = TRUE;\r
5251             boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5252             boards[currentMove][toY][toX] = p;\r
5253             DrawPosition(FALSE, boards[currentMove]);\r
5254             boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5255             boards[currentMove][toY][toX] = q;\r
5256             appData.animate = saveAnimate;\r
5257             DisplayMessage("Select piece from holdings", "");\r
5258             break;\r
5259           } else\r
5260                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5261           } else {\r
5262         if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic\r
5263               && (boards[currentMove][toY][toX] != EmptySquare ||\r
5264                     moveType == WhiteCapturesEnPassant ||\r
5265                     moveType == BlackCapturesEnPassant   ) )\r
5266         AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5267         FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5268       }\r
5269       }\r
5270       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5271       appData.animate = saveAnimate;\r
5272       fromX = fromY = -1;\r
5273       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5274     ClearHighlights();\r
5275       }\r
5276       if (appData.animate || appData.animateDragging ||\r
5277       appData.highlightDragging || gotPremove) {\r
5278     DrawPosition(forceFullRepaint || FALSE, NULL);\r
5279       }\r
5280     }\r
5281     dragInfo.start.x = dragInfo.start.y = -1;\r
5282     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5283     break;\r
5284 \r
5285   case WM_MOUSEMOVE:\r
5286     if ((appData.animateDragging || appData.highlightDragging)\r
5287     && (wParam & MK_LBUTTON)\r
5288     && dragInfo.from.x >= 0)\r
5289     {\r
5290       BOOL full_repaint = FALSE;\r
5291 \r
5292       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5293       if (appData.animateDragging) {\r
5294     dragInfo.pos = pt;\r
5295       }\r
5296       if (appData.highlightDragging) {\r
5297     SetHighlights(fromX, fromY, x, y);\r
5298         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5299             full_repaint = TRUE;\r
5300         }\r
5301       }\r
5302 \r
5303       DrawPosition( full_repaint, NULL);\r
5304 \r
5305       dragInfo.lastpos = dragInfo.pos;\r
5306     }\r
5307     break;\r
5308 \r
5309   case WM_MOUSEWHEEL: // [DM]\r
5310     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5311        /* Mouse Wheel is being rolled forward\r
5312         * Play moves forward\r
5313         */\r
5314        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)\r
5315         { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5316        /* Mouse Wheel is being rolled backward\r
5317         * Play moves backward\r
5318         */\r
5319        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)\r
5320         { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5321     }\r
5322     break;\r
5323 \r
5324   case WM_MBUTTONDOWN:\r
5325   case WM_RBUTTONDOWN:\r
5326     ErrorPopDown();\r
5327     ReleaseCapture();\r
5328     fromX = fromY = -1;\r
5329     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5330     dragInfo.start.x = dragInfo.start.y = -1;\r
5331     dragInfo.from = dragInfo.start;\r
5332     dragInfo.lastpos = dragInfo.pos;\r
5333     if (appData.highlightDragging) {\r
5334       ClearHighlights();\r
5335     }\r
5336     if(y == -2) {\r
5337       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5338       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5339           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5340       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5341           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5342       }\r
5343     }\r
5344     DrawPosition(TRUE, NULL);\r
5345 \r
5346     switch (gameMode) {\r
5347     case EditPosition:\r
5348     case IcsExamining:\r
5349       if (x < 0 || y < 0) break;\r
5350       fromX = x;\r
5351       fromY = y;\r
5352       if (message == WM_MBUTTONDOWN) {\r
5353     buttonCount = 3;  /* even if system didn't think so */\r
5354     if (wParam & MK_SHIFT)\r
5355       MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5356     else\r
5357       MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5358       } else { /* message == WM_RBUTTONDOWN */\r
5359 #if 0\r
5360     if (buttonCount == 3) {\r
5361       if (wParam & MK_SHIFT)\r
5362         MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5363       else\r
5364         MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5365     } else {\r
5366       MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5367     }\r
5368 #else\r
5369     /* Just have one menu, on the right button.  Windows users don't\r
5370        think to try the middle one, and sometimes other software steals\r
5371        it, or it doesn't really exist. */\r
5372         if(gameInfo.variant != VariantShogi)\r
5373             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5374         else\r
5375             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5376 #endif\r
5377       }\r
5378       break;\r
5379     case IcsPlayingWhite:\r
5380     case IcsPlayingBlack:\r
5381     case EditGame:\r
5382     case MachinePlaysWhite:\r
5383     case MachinePlaysBlack:\r
5384       if (appData.testLegality &&\r
5385       gameInfo.variant != VariantBughouse &&\r
5386       gameInfo.variant != VariantCrazyhouse) break;\r
5387       if (x < 0 || y < 0) break;\r
5388       fromX = x;\r
5389       fromY = y;\r
5390       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5391       SetupDropMenu(hmenu);\r
5392       MenuPopup(hwnd, pt, hmenu, -1);\r
5393       break;\r
5394     default:\r
5395       break;\r
5396     }\r
5397     break;\r
5398   }\r
5399 \r
5400   recursive--;\r
5401 }\r
5402 \r
5403 /* Preprocess messages for buttons in main window */\r
5404 LRESULT CALLBACK\r
5405 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5406 {\r
5407   int id = GetWindowLong(hwnd, GWL_ID);\r
5408   int i, dir;\r
5409 \r
5410   for (i=0; i<N_BUTTONS; i++) {\r
5411     if (buttonDesc[i].id == id) break;\r
5412   }\r
5413   if (i == N_BUTTONS) return 0;\r
5414   switch (message) {\r
5415   case WM_KEYDOWN:\r
5416     switch (wParam) {\r
5417     case VK_LEFT:\r
5418     case VK_RIGHT:\r
5419       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5420       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5421       return TRUE;\r
5422     }\r
5423     break;\r
5424   case WM_CHAR:\r
5425     switch (wParam) {\r
5426     case '\r':\r
5427       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5428       return TRUE;\r
5429     default:\r
5430       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5431     // [HGM] movenum: only letters or leading zero should go to ICS input\r
5432         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5433     if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5434     SetFocus(h);\r
5435     SendMessage(h, WM_CHAR, wParam, lParam);\r
5436     return TRUE;\r
5437       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5438     PopUpMoveDialog((char)wParam);\r
5439       }\r
5440       break;\r
5441     }\r
5442     break;\r
5443   }\r
5444   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5445 }\r
5446 \r
5447 /* Process messages for Promotion dialog box */\r
5448 LRESULT CALLBACK\r
5449 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5450 {\r
5451   char promoChar;\r
5452 \r
5453   switch (message) {\r
5454   case WM_INITDIALOG: /* message: initialize dialog box */\r
5455     /* Center the dialog over the application window */\r
5456     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5457     ShowWindow(GetDlgItem(hDlg, PB_King),\r
5458       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5459        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5460            SW_SHOW : SW_HIDE);\r
5461     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5462     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5463        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5464          PieceToChar(WhiteAngel) != '~') ||\r
5465         (PieceToChar(BlackAngel) >= 'A' &&\r
5466          PieceToChar(BlackAngel) != '~')   ) ?\r
5467            SW_SHOW : SW_HIDE);\r
5468     ShowWindow(GetDlgItem(hDlg, PB_Chancellor),\r
5469        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5470          PieceToChar(WhiteMarshall) != '~') ||\r
5471         (PieceToChar(BlackMarshall) >= 'A' &&\r
5472          PieceToChar(BlackMarshall) != '~')   ) ?\r
5473            SW_SHOW : SW_HIDE);\r
5474     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5475     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5476        gameInfo.variant != VariantShogi ?\r
5477            SW_SHOW : SW_HIDE);\r
5478     ShowWindow(GetDlgItem(hDlg, PB_Bishop),\r
5479        gameInfo.variant != VariantShogi ?\r
5480            SW_SHOW : SW_HIDE);\r
5481     ShowWindow(GetDlgItem(hDlg, IDC_Yes),\r
5482        gameInfo.variant == VariantShogi ?\r
5483            SW_SHOW : SW_HIDE);\r
5484     ShowWindow(GetDlgItem(hDlg, IDC_No),\r
5485        gameInfo.variant == VariantShogi ?\r
5486            SW_SHOW : SW_HIDE);\r
5487     ShowWindow(GetDlgItem(hDlg, IDC_Centaur),\r
5488        gameInfo.variant == VariantSuper ?\r
5489            SW_SHOW : SW_HIDE);\r
5490     return TRUE;\r
5491 \r
5492   case WM_COMMAND: /* message: received a command */\r
5493     switch (LOWORD(wParam)) {\r
5494     case IDCANCEL:\r
5495       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5496       ClearHighlights();\r
5497       DrawPosition(FALSE, NULL);\r
5498       return TRUE;\r
5499     case PB_King:\r
5500       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5501       break;\r
5502     case PB_Queen:\r
5503       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5504       break;\r
5505     case PB_Rook:\r
5506       promoChar = PieceToChar(BlackRook);\r
5507       break;\r
5508     case PB_Bishop:\r
5509       promoChar = PieceToChar(BlackBishop);\r
5510       break;\r
5511     case PB_Chancellor:\r
5512       promoChar = PieceToChar(BlackMarshall);\r
5513       break;\r
5514     case PB_Archbishop:\r
5515       promoChar = PieceToChar(BlackAngel);\r
5516       break;\r
5517     case PB_Knight:\r
5518       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5519       break;\r
5520     default:\r
5521       return FALSE;\r
5522     }\r
5523     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5524     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5525        only show the popup when we are already sure the move is valid or\r
5526        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5527        will figure out it is a promotion from the promoChar. */\r
5528     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5529     if (!appData.highlightLastMove) {\r
5530       ClearHighlights();\r
5531       DrawPosition(FALSE, NULL);\r
5532     }\r
5533     return TRUE;\r
5534   }\r
5535   return FALSE;\r
5536 }\r
5537 \r
5538 /* Pop up promotion dialog */\r
5539 VOID\r
5540 PromotionPopup(HWND hwnd)\r
5541 {\r
5542   FARPROC lpProc;\r
5543 \r
5544   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5545   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5546     hwnd, (DLGPROC)lpProc);\r
5547   FreeProcInstance(lpProc);\r
5548 }\r
5549 \r
5550 /* Toggle ShowThinking */\r
5551 VOID\r
5552 ToggleShowThinking()\r
5553 {\r
5554   appData.showThinking = !appData.showThinking;\r
5555   ShowThinkingEvent();\r
5556 }\r
5557 \r
5558 VOID\r
5559 LoadGameDialog(HWND hwnd, char* title)\r
5560 {\r
5561   UINT number = 0;\r
5562   FILE *f;\r
5563   char fileTitle[MSG_SIZ];\r
5564   f = OpenFileDialog(hwnd, "rb", "",\r
5565                  appData.oldSaveStyle ? "gam" : "pgn",\r
5566              GAME_FILT,\r
5567              title, &number, fileTitle, NULL);\r
5568   if (f != NULL) {\r
5569     cmailMsgLoaded = FALSE;\r
5570     if (number == 0) {\r
5571       int error = GameListBuild(f);\r
5572       if (error) {\r
5573         DisplayError("Cannot build game list", error);\r
5574       } else if (!ListEmpty(&gameList) &&\r
5575                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5576     GameListPopUp(f, fileTitle);\r
5577         return;\r
5578       }\r
5579       GameListDestroy();\r
5580       number = 1;\r
5581     }\r
5582     LoadGame(f, number, fileTitle, FALSE);\r
5583   }\r
5584 }\r
5585 \r
5586 VOID\r
5587 ChangedConsoleFont()\r
5588 {\r
5589   CHARFORMAT cfmt;\r
5590   CHARRANGE tmpsel, sel;\r
5591   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5592   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5593   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5594   PARAFORMAT paraf;\r
5595 \r
5596   cfmt.cbSize = sizeof(CHARFORMAT);\r
5597   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5598   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5599   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5600    * size.  This was undocumented in the version of MSVC++ that I had\r
5601    * when I wrote the code, but is apparently documented now.\r
5602    */\r
5603   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5604   cfmt.bCharSet = f->lf.lfCharSet;\r
5605   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5606   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);\r
5607   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);\r
5608   /* Why are the following seemingly needed too? */\r
5609   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);\r
5610   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);\r
5611   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5612   tmpsel.cpMin = 0;\r
5613   tmpsel.cpMax = -1; /*999999?*/\r
5614   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5615   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);\r
5616   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5617    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5618    */\r
5619   paraf.cbSize = sizeof(paraf);\r
5620   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5621   paraf.dxStartIndent = 0;\r
5622   paraf.dxOffset = WRAP_INDENT;\r
5623   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5624   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5625 }\r
5626 \r
5627 /*---------------------------------------------------------------------------*\\r
5628  *\r
5629  * Window Proc for main window\r
5630  *\r
5631 \*---------------------------------------------------------------------------*/\r
5632 \r
5633 /* Process messages for main window, etc. */\r
5634 LRESULT CALLBACK\r
5635 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5636 {\r
5637   FARPROC lpProc;\r
5638   int wmId, wmEvent;\r
5639   char *defName;\r
5640   FILE *f;\r
5641   UINT number;\r
5642   char fileTitle[MSG_SIZ];\r
5643   char buf[MSG_SIZ];\r
5644   static SnapData sd;\r
5645 \r
5646   switch (message) {\r
5647 \r
5648   case WM_PAINT: /* message: repaint portion of window */\r
5649     PaintProc(hwnd);\r
5650     break;\r
5651 \r
5652   case WM_ERASEBKGND:\r
5653     if (IsIconic(hwnd)) {\r
5654       /* Cheat; change the message */\r
5655       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5656     } else {\r
5657       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5658     }\r
5659     break;\r
5660 \r
5661   case WM_LBUTTONDOWN:\r
5662   case WM_MBUTTONDOWN:\r
5663   case WM_RBUTTONDOWN:\r
5664   case WM_LBUTTONUP:\r
5665   case WM_MBUTTONUP:\r
5666   case WM_RBUTTONUP:\r
5667   case WM_MOUSEMOVE:\r
5668   case WM_MOUSEWHEEL:\r
5669     MouseEvent(hwnd, message, wParam, lParam);\r
5670     break;\r
5671 \r
5672   JAWS_KB_NAVIGATION\r
5673 \r
5674   case WM_CHAR:\r
5675 \r
5676     JAWS_ALT_INTERCEPT\r
5677 \r
5678     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) {\r
5679     // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5680     HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5681     if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5682     SetFocus(h);\r
5683     SendMessage(h, message, wParam, lParam);\r
5684     } else if(lParam != KF_REPEAT) {\r
5685     if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5686         PopUpMoveDialog((char)wParam);\r
5687     } else if((char)wParam == 003) CopyGameToClipboard();\r
5688      else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5689     }\r
5690 \r
5691     break;\r
5692 \r
5693   case WM_PALETTECHANGED:\r
5694     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5695       int nnew;\r
5696       HDC hdc = GetDC(hwndMain);\r
5697       SelectPalette(hdc, hPal, TRUE);\r
5698       nnew = RealizePalette(hdc);\r
5699       if (nnew > 0) {\r
5700     paletteChanged = TRUE;\r
5701 #if 0\r
5702         UpdateColors(hdc);\r
5703 #else\r
5704         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5705 #endif\r
5706       }\r
5707       ReleaseDC(hwnd, hdc);\r
5708     }\r
5709     break;\r
5710 \r
5711   case WM_QUERYNEWPALETTE:\r
5712     if (!appData.monoMode /*&& paletteChanged*/) {\r
5713       int nnew;\r
5714       HDC hdc = GetDC(hwndMain);\r
5715       paletteChanged = FALSE;\r
5716       SelectPalette(hdc, hPal, FALSE);\r
5717       nnew = RealizePalette(hdc);\r
5718       if (nnew > 0) {\r
5719     InvalidateRect(hwnd, &boardRect, FALSE);\r
5720       }\r
5721       ReleaseDC(hwnd, hdc);\r
5722       return TRUE;\r
5723     }\r
5724     return FALSE;\r
5725 \r
5726   case WM_COMMAND: /* message: command from application menu */\r
5727     wmId    = LOWORD(wParam);\r
5728     wmEvent = HIWORD(wParam);\r
5729 \r
5730     switch (wmId) {\r
5731     case IDM_NewGame:\r
5732       ResetGameEvent();\r
5733       AnalysisPopDown();\r
5734       SAY("new game enter a move to play against the computer with white");\r
5735       break;\r
5736 \r
5737     case IDM_NewGameFRC:\r
5738       if( NewGameFRC() == 0 ) {\r
5739         ResetGameEvent();\r
5740         AnalysisPopDown();\r
5741       }\r
5742       break;\r
5743 \r
5744     case IDM_NewVariant:\r
5745       NewVariantPopup(hwnd);\r
5746       break;\r
5747 \r
5748     case IDM_LoadGame:\r
5749       LoadGameDialog(hwnd, "Load Game from File");\r
5750       break;\r
5751 \r
5752     case IDM_LoadNextGame:\r
5753       ReloadGame(1);\r
5754       break;\r
5755 \r
5756     case IDM_LoadPrevGame:\r
5757       ReloadGame(-1);\r
5758       break;\r
5759 \r
5760     case IDM_ReloadGame:\r
5761       ReloadGame(0);\r
5762       break;\r
5763 \r
5764     case IDM_LoadPosition:\r
5765       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5766         Reset(FALSE, TRUE);\r
5767       }\r
5768       number = 1;\r
5769       f = OpenFileDialog(hwnd, "rb", "",\r
5770              appData.oldSaveStyle ? "pos" : "fen",\r
5771              POSITION_FILT,\r
5772              "Load Position from File", &number, fileTitle, NULL);\r
5773       if (f != NULL) {\r
5774     LoadPosition(f, number, fileTitle);\r
5775       }\r
5776       break;\r
5777 \r
5778     case IDM_LoadNextPosition:\r
5779       ReloadPosition(1);\r
5780       break;\r
5781 \r
5782     case IDM_LoadPrevPosition:\r
5783       ReloadPosition(-1);\r
5784       break;\r
5785 \r
5786     case IDM_ReloadPosition:\r
5787       ReloadPosition(0);\r
5788       break;\r
5789 \r
5790     case IDM_SaveGame:\r
5791       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5792       f = OpenFileDialog(hwnd, "a", defName,\r
5793              appData.oldSaveStyle ? "gam" : "pgn",\r
5794              GAME_FILT,\r
5795              "Save Game to File", NULL, fileTitle, NULL);\r
5796       if (f != NULL) {\r
5797     SaveGame(f, 0, "");\r
5798       }\r
5799       break;\r
5800 \r
5801     case IDM_SavePosition:\r
5802       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5803       f = OpenFileDialog(hwnd, "a", defName,\r
5804              appData.oldSaveStyle ? "pos" : "fen",\r
5805              POSITION_FILT,\r
5806              "Save Position to File", NULL, fileTitle, NULL);\r
5807       if (f != NULL) {\r
5808     SavePosition(f, 0, "");\r
5809       }\r
5810       break;\r
5811 \r
5812     case IDM_SaveDiagram:\r
5813       defName = "diagram";\r
5814       f = OpenFileDialog(hwnd, "wb", defName,\r
5815              "bmp",\r
5816              DIAGRAM_FILT,\r
5817              "Save Diagram to File", NULL, fileTitle, NULL);\r
5818       if (f != NULL) {\r
5819     SaveDiagram(f);\r
5820       }\r
5821       break;\r
5822 \r
5823     case IDM_CopyGame:\r
5824       CopyGameToClipboard();\r
5825       break;\r
5826 \r
5827     case IDM_PasteGame:\r
5828       PasteGameFromClipboard();\r
5829       break;\r
5830 \r
5831     case IDM_CopyGameListToClipboard:\r
5832       CopyGameListToClipboard();\r
5833       break;\r
5834 \r
5835     /* [AS] Autodetect FEN or PGN data */\r
5836     case IDM_PasteAny:\r
5837       PasteGameOrFENFromClipboard();\r
5838       break;\r
5839 \r
5840     /* [AS] Move history */\r
5841     case IDM_ShowMoveHistory:\r
5842         if( MoveHistoryIsUp() ) {\r
5843             MoveHistoryPopDown();\r
5844         }\r
5845         else {\r
5846             MoveHistoryPopUp();\r
5847         }\r
5848         break;\r
5849 \r
5850     /* [AS] Eval graph */\r
5851     case IDM_ShowEvalGraph:\r
5852         if( EvalGraphIsUp() ) {\r
5853             EvalGraphPopDown();\r
5854         }\r
5855         else {\r
5856             EvalGraphPopUp();\r
5857         SetFocus(hwndMain);\r
5858         }\r
5859         break;\r
5860 \r
5861     /* [AS] Engine output */\r
5862     case IDM_ShowEngineOutput:\r
5863         if( EngineOutputIsUp() ) {\r
5864             EngineOutputPopDown();\r
5865         }\r
5866         else {\r
5867             EngineOutputPopUp();\r
5868         }\r
5869         break;\r
5870 \r
5871     /* [AS] User adjudication */\r
5872     case IDM_UserAdjudication_White:\r
5873         UserAdjudicationEvent( +1 );\r
5874         break;\r
5875 \r
5876     case IDM_UserAdjudication_Black:\r
5877         UserAdjudicationEvent( -1 );\r
5878         break;\r
5879 \r
5880     case IDM_UserAdjudication_Draw:\r
5881         UserAdjudicationEvent( 0 );\r
5882         break;\r
5883 \r
5884     /* [AS] Game list options dialog */\r
5885     case IDM_GameListOptions:\r
5886       GameListOptions();\r
5887       break;\r
5888 \r
5889     case IDM_NewChat:\r
5890       ChatPopUp();\r
5891       break;\r
5892 \r
5893     case IDM_CopyPosition:\r
5894       CopyFENToClipboard();\r
5895       break;\r
5896 \r
5897     case IDM_PastePosition:\r
5898       PasteFENFromClipboard();\r
5899       break;\r
5900 \r
5901     case IDM_MailMove:\r
5902       MailMoveEvent();\r
5903       break;\r
5904 \r
5905     case IDM_ReloadCMailMsg:\r
5906       Reset(TRUE, TRUE);\r
5907       ReloadCmailMsgEvent(FALSE);\r
5908       break;\r
5909 \r
5910     case IDM_Minimize:\r
5911       ShowWindow(hwnd, SW_MINIMIZE);\r
5912       break;\r
5913 \r
5914     case IDM_Exit:\r
5915       ExitEvent(0);\r
5916       break;\r
5917 \r
5918     case IDM_MachineWhite:\r
5919       MachineWhiteEvent();\r
5920       /*\r
5921        * refresh the tags dialog only if it's visible\r
5922        */\r
5923       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5924       char *tags;\r
5925       tags = PGNTags(&gameInfo);\r
5926       TagsPopUp(tags, CmailMsg());\r
5927       free(tags);\r
5928       }\r
5929       SAY("computer starts playing white");\r
5930       break;\r
5931 \r
5932     case IDM_MachineBlack:\r
5933       MachineBlackEvent();\r
5934       /*\r
5935        * refresh the tags dialog only if it's visible\r
5936        */\r
5937       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5938       char *tags;\r
5939       tags = PGNTags(&gameInfo);\r
5940       TagsPopUp(tags, CmailMsg());\r
5941       free(tags);\r
5942       }\r
5943       SAY("computer starts playing black");\r
5944       break;\r
5945 \r
5946     case IDM_TwoMachines:\r
5947       TwoMachinesEvent();\r
5948       /*\r
5949        * refresh the tags dialog only if it's visible\r
5950        */\r
5951       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5952       char *tags;\r
5953       tags = PGNTags(&gameInfo);\r
5954       TagsPopUp(tags, CmailMsg());\r
5955       free(tags);\r
5956       }\r
5957       SAY("programs start playing each other");\r
5958       break;\r
5959 \r
5960     case IDM_AnalysisMode:\r
5961       if (!first.analysisSupport) {\r
5962         sprintf(buf, "%s does not support analysis", first.tidy);\r
5963         DisplayError(buf, 0);\r
5964       } else {\r
5965     SAY("analyzing current position");\r
5966         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5967         if (appData.icsActive) {\r
5968                if (gameMode != IcsObserving) {\r
5969                        sprintf(buf, "You are not observing a game");\r
5970                        DisplayError(buf, 0);\r
5971                        /* secure check */\r
5972                        if (appData.icsEngineAnalyze) {\r
5973                                if (appData.debugMode)\r
5974                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5975                                ExitAnalyzeMode();\r
5976                                ModeHighlight();\r
5977                                break;\r
5978                        }\r
5979                        break;\r
5980                } else {\r
5981                        /* if enable, user want disable icsEngineAnalyze */\r
5982                        if (appData.icsEngineAnalyze) {\r
5983                                ExitAnalyzeMode();\r
5984                                ModeHighlight();\r
5985                                break;\r
5986                        }\r
5987                        appData.icsEngineAnalyze = TRUE;\r
5988                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5989                }\r
5990         }\r
5991     if (!appData.showThinking) ToggleShowThinking();\r
5992     AnalyzeModeEvent();\r
5993       }\r
5994       break;\r
5995 \r
5996     case IDM_AnalyzeFile:\r
5997       if (!first.analysisSupport) {\r
5998         char buf[MSG_SIZ];\r
5999         sprintf(buf, "%s does not support analysis", first.tidy);\r
6000         DisplayError(buf, 0);\r
6001       } else {\r
6002     if (!appData.showThinking) ToggleShowThinking();\r
6003     AnalyzeFileEvent();\r
6004     LoadGameDialog(hwnd, "Analyze Game from File");\r
6005     AnalysisPeriodicEvent(1);\r
6006       }\r
6007       break;\r
6008 \r
6009     case IDM_IcsClient:\r
6010       IcsClientEvent();\r
6011       break;\r
6012 \r
6013     case IDM_EditGame:\r
6014       EditGameEvent();\r
6015       SAY("edit game");\r
6016       break;\r
6017 \r
6018     case IDM_EditPosition:\r
6019       EditPositionEvent();\r
6020       SAY("to set up a position type a FEN");\r
6021       break;\r
6022 \r
6023     case IDM_Training:\r
6024       TrainingEvent();\r
6025       break;\r
6026 \r
6027     case IDM_ShowGameList:\r
6028       ShowGameListProc();\r
6029       break;\r
6030 \r
6031     case IDM_EditTags:\r
6032       EditTagsProc();\r
6033       break;\r
6034 \r
6035     case IDM_EditComment:\r
6036       if (commentDialogUp && editComment) {\r
6037     CommentPopDown();\r
6038       } else {\r
6039     EditCommentEvent();\r
6040       }\r
6041       break;\r
6042 \r
6043     case IDM_Pause:\r
6044       PauseEvent();\r
6045       break;\r
6046 \r
6047     case IDM_Accept:\r
6048       AcceptEvent();\r
6049       break;\r
6050 \r
6051     case IDM_Decline:\r
6052       DeclineEvent();\r
6053       break;\r
6054 \r
6055     case IDM_Rematch:\r
6056       RematchEvent();\r
6057       break;\r
6058 \r
6059     case IDM_CallFlag:\r
6060       CallFlagEvent();\r
6061       break;\r
6062 \r
6063     case IDM_Draw:\r
6064       DrawEvent();\r
6065       break;\r
6066 \r
6067     case IDM_Adjourn:\r
6068       AdjournEvent();\r
6069       break;\r
6070 \r
6071     case IDM_Abort:\r
6072       AbortEvent();\r
6073       break;\r
6074 \r
6075     case IDM_Resign:\r
6076       ResignEvent();\r
6077       break;\r
6078 \r
6079     case IDM_StopObserving:\r
6080       StopObservingEvent();\r
6081       break;\r
6082 \r
6083     case IDM_StopExamining:\r
6084       StopExaminingEvent();\r
6085       break;\r
6086 \r
6087     case IDM_TypeInMove:\r
6088       PopUpMoveDialog('\000');\r
6089       break;\r
6090 \r
6091     case IDM_TypeInName:\r
6092       PopUpNameDialog('\000');\r
6093       break;\r
6094 \r
6095     case IDM_Backward:\r
6096       BackwardEvent();\r
6097       SetFocus(hwndMain);\r
6098       break;\r
6099 \r
6100     JAWS_MENU_ITEMS\r
6101 \r
6102     case IDM_Forward:\r
6103       ForwardEvent();\r
6104       SetFocus(hwndMain);\r
6105       break;\r
6106 \r
6107     case IDM_ToStart:\r
6108       ToStartEvent();\r
6109       SetFocus(hwndMain);\r
6110       break;\r
6111 \r
6112     case IDM_ToEnd:\r
6113       ToEndEvent();\r
6114       SetFocus(hwndMain);\r
6115       break;\r
6116 \r
6117     case IDM_Revert:\r
6118       RevertEvent();\r
6119       break;\r
6120 \r
6121     case IDM_TruncateGame:\r
6122       TruncateGameEvent();\r
6123       break;\r
6124 \r
6125     case IDM_MoveNow:\r
6126       MoveNowEvent();\r
6127       break;\r
6128 \r
6129     case IDM_RetractMove:\r
6130       RetractMoveEvent();\r
6131       break;\r
6132 \r
6133     case IDM_FlipView:\r
6134       flipView = !flipView;\r
6135       DrawPosition(FALSE, NULL);\r
6136       break;\r
6137 \r
6138     case IDM_FlipClock:\r
6139       flipClock = !flipClock;\r
6140       DisplayBothClocks();\r
6141       DrawPosition(FALSE, NULL);\r
6142       break;\r
6143 \r
6144     case IDM_MuteSounds:\r
6145       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6146       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,\r
6147                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6148       break;\r
6149 \r
6150     case IDM_GeneralOptions:\r
6151       GeneralOptionsPopup(hwnd);\r
6152       DrawPosition(TRUE, NULL);\r
6153       break;\r
6154 \r
6155     case IDM_BoardOptions:\r
6156       BoardOptionsPopup(hwnd);\r
6157       break;\r
6158 \r
6159     case IDM_EnginePlayOptions:\r
6160       EnginePlayOptionsPopup(hwnd);\r
6161       break;\r
6162 \r
6163     case IDM_Engine1Options:\r
6164       EngineOptionsPopup(hwnd, &first);\r
6165       break;\r
6166 \r
6167     case IDM_Engine2Options:\r
6168       EngineOptionsPopup(hwnd, &second);\r
6169       break;\r
6170 \r
6171     case IDM_OptionsUCI:\r
6172       UciOptionsPopup(hwnd);\r
6173       break;\r
6174 \r
6175     case IDM_IcsOptions:\r
6176       IcsOptionsPopup(hwnd);\r
6177       break;\r
6178 \r
6179     case IDM_Fonts:\r
6180       FontsOptionsPopup(hwnd);\r
6181       break;\r
6182 \r
6183     case IDM_Sounds:\r
6184       SoundOptionsPopup(hwnd);\r
6185       break;\r
6186 \r
6187     case IDM_CommPort:\r
6188       CommPortOptionsPopup(hwnd);\r
6189       break;\r
6190 \r
6191     case IDM_LoadOptions:\r
6192       LoadOptionsPopup(hwnd);\r
6193       break;\r
6194 \r
6195     case IDM_SaveOptions:\r
6196       SaveOptionsPopup(hwnd);\r
6197       break;\r
6198 \r
6199     case IDM_TimeControl:\r
6200       TimeControlOptionsPopup(hwnd);\r
6201       break;\r
6202 \r
6203     case IDM_SaveSettings:\r
6204       SaveSettings(settingsFileName);\r
6205       break;\r
6206 \r
6207     case IDM_SaveSettingsOnExit:\r
6208       saveSettingsOnExit = !saveSettingsOnExit;\r
6209       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6210                MF_BYCOMMAND|(saveSettingsOnExit ?\r
6211                      MF_CHECKED : MF_UNCHECKED));\r
6212       break;\r
6213 \r
6214     case IDM_Hint:\r
6215       HintEvent();\r
6216       break;\r
6217 \r
6218     case IDM_Book:\r
6219       BookEvent();\r
6220       break;\r
6221 \r
6222     case IDM_AboutGame:\r
6223       AboutGameEvent();\r
6224       break;\r
6225 \r
6226     case IDM_Debug:\r
6227       appData.debugMode = !appData.debugMode;\r
6228       if (appData.debugMode) {\r
6229     char dir[MSG_SIZ];\r
6230     GetCurrentDirectory(MSG_SIZ, dir);\r
6231     SetCurrentDirectory(installDir);\r
6232     debugFP = fopen(appData.nameOfDebugFile, "w");\r
6233         SetCurrentDirectory(dir);\r
6234         setbuf(debugFP, NULL);\r
6235       } else {\r
6236     fclose(debugFP);\r
6237         debugFP = NULL;\r
6238       }\r
6239       break;\r
6240 \r
6241     case IDM_HELPCONTENTS:\r
6242       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6243       !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6244       MessageBox (GetFocus(),\r
6245             "Unable to activate help",\r
6246             szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6247       }\r
6248       break;\r
6249 \r
6250     case IDM_HELPSEARCH:\r
6251         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6252         !HtmlHelp(hwnd, "winboard.chm", 0, 0)   ) {\r
6253     MessageBox (GetFocus(),\r
6254             "Unable to activate help",\r
6255             szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6256       }\r
6257       break;\r
6258 \r
6259     case IDM_HELPHELP:\r
6260       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6261     MessageBox (GetFocus(),\r
6262             "Unable to activate help",\r
6263             szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6264       }\r
6265       break;\r
6266 \r
6267     case IDM_ABOUT:\r
6268       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6269       DialogBox(hInst,\r
6270     (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6271     "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6272       FreeProcInstance(lpProc);\r
6273       break;\r
6274 \r
6275     case IDM_DirectCommand1:\r
6276       AskQuestionEvent("Direct Command",\r
6277                "Send to chess program:", "", "1");\r
6278       break;\r
6279     case IDM_DirectCommand2:\r
6280       AskQuestionEvent("Direct Command",\r
6281                "Send to second chess program:", "", "2");\r
6282       break;\r
6283 \r
6284     case EP_WhitePawn:\r
6285       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6286       fromX = fromY = -1;\r
6287       break;\r
6288 \r
6289     case EP_WhiteKnight:\r
6290       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6291       fromX = fromY = -1;\r
6292       break;\r
6293 \r
6294     case EP_WhiteBishop:\r
6295       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6296       fromX = fromY = -1;\r
6297       break;\r
6298 \r
6299     case EP_WhiteRook:\r
6300       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6301       fromX = fromY = -1;\r
6302       break;\r
6303 \r
6304     case EP_WhiteQueen:\r
6305       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6306       fromX = fromY = -1;\r
6307       break;\r
6308 \r
6309     case EP_WhiteFerz:\r
6310       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6311       fromX = fromY = -1;\r
6312       break;\r
6313 \r
6314     case EP_WhiteWazir:\r
6315       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6316       fromX = fromY = -1;\r
6317       break;\r
6318 \r
6319     case EP_WhiteAlfil:\r
6320       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6321       fromX = fromY = -1;\r
6322       break;\r
6323 \r
6324     case EP_WhiteCannon:\r
6325       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6326       fromX = fromY = -1;\r
6327       break;\r
6328 \r
6329     case EP_WhiteCardinal:\r
6330       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6331       fromX = fromY = -1;\r
6332       break;\r
6333 \r
6334     case EP_WhiteMarshall:\r
6335       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6336       fromX = fromY = -1;\r
6337       break;\r
6338 \r
6339     case EP_WhiteKing:\r
6340       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6341       fromX = fromY = -1;\r
6342       break;\r
6343 \r
6344     case EP_BlackPawn:\r
6345       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6346       fromX = fromY = -1;\r
6347       break;\r
6348 \r
6349     case EP_BlackKnight:\r
6350       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6351       fromX = fromY = -1;\r
6352       break;\r
6353 \r
6354     case EP_BlackBishop:\r
6355       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6356       fromX = fromY = -1;\r
6357       break;\r
6358 \r
6359     case EP_BlackRook:\r
6360       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6361       fromX = fromY = -1;\r
6362       break;\r
6363 \r
6364     case EP_BlackQueen:\r
6365       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6366       fromX = fromY = -1;\r
6367       break;\r
6368 \r
6369     case EP_BlackFerz:\r
6370       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6371       fromX = fromY = -1;\r
6372       break;\r
6373 \r
6374     case EP_BlackWazir:\r
6375       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6376       fromX = fromY = -1;\r
6377       break;\r
6378 \r
6379     case EP_BlackAlfil:\r
6380       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6381       fromX = fromY = -1;\r
6382       break;\r
6383 \r
6384     case EP_BlackCannon:\r
6385       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6386       fromX = fromY = -1;\r
6387       break;\r
6388 \r
6389     case EP_BlackCardinal:\r
6390       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6391       fromX = fromY = -1;\r
6392       break;\r
6393 \r
6394     case EP_BlackMarshall:\r
6395       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6396       fromX = fromY = -1;\r
6397       break;\r
6398 \r
6399     case EP_BlackKing:\r
6400       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6401       fromX = fromY = -1;\r
6402       break;\r
6403 \r
6404     case EP_EmptySquare:\r
6405       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6406       fromX = fromY = -1;\r
6407       break;\r
6408 \r
6409     case EP_ClearBoard:\r
6410       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6411       fromX = fromY = -1;\r
6412       break;\r
6413 \r
6414     case EP_White:\r
6415       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6416       fromX = fromY = -1;\r
6417       break;\r
6418 \r
6419     case EP_Black:\r
6420       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6421       fromX = fromY = -1;\r
6422       break;\r
6423 \r
6424     case EP_Promote:\r
6425       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6426       fromX = fromY = -1;\r
6427       break;\r
6428 \r
6429     case EP_Demote:\r
6430       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6431       fromX = fromY = -1;\r
6432       break;\r
6433 \r
6434     case DP_Pawn:\r
6435       DropMenuEvent(WhitePawn, fromX, fromY);\r
6436       fromX = fromY = -1;\r
6437       break;\r
6438 \r
6439     case DP_Knight:\r
6440       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6441       fromX = fromY = -1;\r
6442       break;\r
6443 \r
6444     case DP_Bishop:\r
6445       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6446       fromX = fromY = -1;\r
6447       break;\r
6448 \r
6449     case DP_Rook:\r
6450       DropMenuEvent(WhiteRook, fromX, fromY);\r
6451       fromX = fromY = -1;\r
6452       break;\r
6453 \r
6454     case DP_Queen:\r
6455       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6456       fromX = fromY = -1;\r
6457       break;\r
6458 \r
6459     default:\r
6460       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6461     }\r
6462     break;\r
6463 \r
6464   case WM_TIMER:\r
6465     switch (wParam) {\r
6466     case CLOCK_TIMER_ID:\r
6467       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6468       clockTimerEvent = 0;\r
6469       DecrementClocks(); /* call into back end */\r
6470       break;\r
6471     case LOAD_GAME_TIMER_ID:\r
6472       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6473       loadGameTimerEvent = 0;\r
6474       AutoPlayGameLoop(); /* call into back end */\r
6475       break;\r
6476     case ANALYSIS_TIMER_ID:\r
6477       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6478                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6479     AnalysisPeriodicEvent(0);\r
6480       } else {\r
6481     KillTimer(hwnd, analysisTimerEvent);\r
6482     analysisTimerEvent = 0;\r
6483       }\r
6484       break;\r
6485     case DELAYED_TIMER_ID:\r
6486       KillTimer(hwnd, delayedTimerEvent);\r
6487       delayedTimerEvent = 0;\r
6488       delayedTimerCallback();\r
6489       break;\r
6490     }\r
6491     break;\r
6492 \r
6493   case WM_USER_Input:\r
6494     InputEvent(hwnd, message, wParam, lParam);\r
6495     break;\r
6496 \r
6497   /* [AS] Also move "attached" child windows */\r
6498   case WM_WINDOWPOSCHANGING:\r
6499 \r
6500     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6501         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6502 \r
6503         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6504             /* Window is moving */\r
6505             RECT rcMain;\r
6506 \r
6507 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6508         rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6509         rcMain.right  = boardX + winWidth;\r
6510         rcMain.top    = boardY;\r
6511         rcMain.bottom = boardY + winHeight;\r
6512 \r
6513             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6514             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6515             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6516             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6517             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6518         boardX = lpwp->x;\r
6519             boardY = lpwp->y;\r
6520         }\r
6521     }\r
6522     break;\r
6523 \r
6524   /* [AS] Snapping */\r
6525   case WM_ENTERSIZEMOVE:\r
6526     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6527     if (hwnd == hwndMain) {\r
6528       doingSizing = TRUE;\r
6529       lastSizing = 0;\r
6530     }\r
6531     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6532     break;\r
6533 \r
6534   case WM_SIZING:\r
6535     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6536     if (hwnd == hwndMain) {\r
6537       lastSizing = wParam;\r
6538     }\r
6539     break;\r
6540 \r
6541   case WM_MOVING:\r
6542     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6543       return OnMoving( &sd, hwnd, wParam, lParam );\r
6544 \r
6545   case WM_EXITSIZEMOVE:\r
6546     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6547     if (hwnd == hwndMain) {\r
6548       RECT client;\r
6549       doingSizing = FALSE;\r
6550       InvalidateRect(hwnd, &boardRect, FALSE);\r
6551       GetClientRect(hwnd, &client);\r
6552       ResizeBoard(client.right, client.bottom, lastSizing);\r
6553       lastSizing = 0;\r
6554       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6555     }\r
6556     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6557     break;\r
6558 \r
6559   case WM_DESTROY: /* message: window being destroyed */\r
6560     PostQuitMessage(0);\r
6561     break;\r
6562 \r
6563   case WM_CLOSE:\r
6564     if (hwnd == hwndMain) {\r
6565       ExitEvent(0);\r
6566     }\r
6567     break;\r
6568 \r
6569   default:  /* Passes it on if unprocessed */\r
6570     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6571   }\r
6572   return 0;\r
6573 }\r
6574 \r
6575 /*---------------------------------------------------------------------------*\\r
6576  *\r
6577  * Misc utility routines\r
6578  *\r
6579 \*---------------------------------------------------------------------------*/\r
6580 \r
6581 /*\r
6582  * Decent random number generator, at least not as bad as Windows\r
6583  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6584  */\r
6585 unsigned int randstate;\r
6586 \r
6587 int\r
6588 myrandom(void)\r
6589 {\r
6590   randstate = randstate * 1664525 + 1013904223;\r
6591   return (int) randstate & 0x7fffffff;\r
6592 }\r
6593 \r
6594 void\r
6595 mysrandom(unsigned int seed)\r
6596 {\r
6597   randstate = seed;\r
6598 }\r
6599 \r
6600 \r
6601 /*\r
6602  * returns TRUE if user selects a different color, FALSE otherwise\r
6603  */\r
6604 \r
6605 BOOL\r
6606 ChangeColor(HWND hwnd, COLORREF *which)\r
6607 {\r
6608   static BOOL firstTime = TRUE;\r
6609   static DWORD customColors[16];\r
6610   CHOOSECOLOR cc;\r
6611   COLORREF newcolor;\r
6612   int i;\r
6613   ColorClass ccl;\r
6614 \r
6615   if (firstTime) {\r
6616     /* Make initial colors in use available as custom colors */\r
6617     /* Should we put the compiled-in defaults here instead? */\r
6618     i = 0;\r
6619     customColors[i++] = lightSquareColor & 0xffffff;\r
6620     customColors[i++] = darkSquareColor & 0xffffff;\r
6621     customColors[i++] = whitePieceColor & 0xffffff;\r
6622     customColors[i++] = blackPieceColor & 0xffffff;\r
6623     customColors[i++] = highlightSquareColor & 0xffffff;\r
6624     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6625 \r
6626     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6627       customColors[i++] = textAttribs[ccl].color;\r
6628     }\r
6629     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6630     firstTime = FALSE;\r
6631   }\r
6632 \r
6633   cc.lStructSize = sizeof(cc);\r
6634   cc.hwndOwner = hwnd;\r
6635   cc.hInstance = NULL;\r
6636   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6637   cc.lpCustColors = (LPDWORD) customColors;\r
6638   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6639 \r
6640   if (!ChooseColor(&cc)) return FALSE;\r
6641 \r
6642   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6643   if (newcolor == *which) return FALSE;\r
6644   *which = newcolor;\r
6645   return TRUE;\r
6646 \r
6647   /*\r
6648   InitDrawingColors();\r
6649   InvalidateRect(hwnd, &boardRect, FALSE);\r
6650   */\r
6651 }\r
6652 \r
6653 BOOLEAN\r
6654 MyLoadSound(MySound *ms)\r
6655 {\r
6656   BOOL ok = FALSE;\r
6657   struct stat st;\r
6658   FILE *f;\r
6659 \r
6660   if (ms->data) free(ms->data);\r
6661   ms->data = NULL;\r
6662 \r
6663   switch (ms->name[0]) {\r
6664   case NULLCHAR:\r
6665     /* Silence */\r
6666     ok = TRUE;\r
6667     break;\r
6668   case '$':\r
6669     /* System sound from Control Panel.  Don't preload here. */\r
6670     ok = TRUE;\r
6671     break;\r
6672   case '!':\r
6673     if (ms->name[1] == NULLCHAR) {\r
6674       /* "!" alone = silence */\r
6675       ok = TRUE;\r
6676     } else {\r
6677       /* Builtin wave resource.  Error if not found. */\r
6678       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6679       if (h == NULL) break;\r
6680       ms->data = (void *)LoadResource(hInst, h);\r
6681       if (h == NULL) break;\r
6682       ok = TRUE;\r
6683     }\r
6684     break;\r
6685   default:\r
6686     /* .wav file.  Error if not found. */\r
6687     f = fopen(ms->name, "rb");\r
6688     if (f == NULL) break;\r
6689     if (fstat(fileno(f), &st) < 0) break;\r
6690     ms->data = malloc(st.st_size);\r
6691     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6692     fclose(f);\r
6693     ok = TRUE;\r
6694     break;\r
6695   }\r
6696   if (!ok) {\r
6697     char buf[MSG_SIZ];\r
6698     sprintf(buf, "Error loading sound %s", ms->name);\r
6699     DisplayError(buf, GetLastError());\r
6700   }\r
6701   return ok;\r
6702 }\r
6703 \r
6704 BOOLEAN\r
6705 MyPlaySound(MySound *ms)\r
6706 {\r
6707   BOOLEAN ok = FALSE;\r
6708 \r
6709   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6710   switch (ms->name[0]) {\r
6711   case NULLCHAR:\r
6712     if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6713     /* Silence */\r
6714     ok = TRUE;\r
6715     break;\r
6716   case '$':\r
6717     /* System sound from Control Panel (deprecated feature).\r
6718        "$" alone or an unset sound name gets default beep (still in use). */\r
6719     if (ms->name[1]) {\r
6720       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6721     }\r
6722     if (!ok) ok = MessageBeep(MB_OK);\r
6723     break;\r
6724   case '!':\r
6725     /* Builtin wave resource, or "!" alone for silence */\r
6726     if (ms->name[1]) {\r
6727       if (ms->data == NULL) return FALSE;\r
6728       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6729     } else {\r
6730       ok = TRUE;\r
6731     }\r
6732     break;\r
6733   default:\r
6734     /* .wav file.  Error if not found. */\r
6735     if (ms->data == NULL) return FALSE;\r
6736     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6737     break;\r
6738   }\r
6739   /* Don't print an error: this can happen innocently if the sound driver\r
6740      is busy; for instance, if another instance of WinBoard is playing\r
6741      a sound at about the same time. */\r
6742 #if 0\r
6743   if (!ok) {\r
6744     char buf[MSG_SIZ];\r
6745     sprintf(buf, "Error playing sound %s", ms->name);\r
6746     DisplayError(buf, GetLastError());\r
6747   }\r
6748 #endif\r
6749   return ok;\r
6750 }\r
6751 \r
6752 \r
6753 LRESULT CALLBACK\r
6754 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6755 {\r
6756   BOOL ok;\r
6757   OPENFILENAME *ofn;\r
6758   static UINT *number; /* gross that this is static */\r
6759 \r
6760   switch (message) {\r
6761   case WM_INITDIALOG: /* message: initialize dialog box */\r
6762     /* Center the dialog over the application window */\r
6763     ofn = (OPENFILENAME *) lParam;\r
6764     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6765       number = (UINT *) ofn->lCustData;\r
6766       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6767     } else {\r
6768       number = NULL;\r
6769     }\r
6770     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6771     return FALSE;  /* Allow for further processing */\r
6772 \r
6773   case WM_COMMAND:\r
6774     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6775       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6776     }\r
6777     return FALSE;  /* Allow for further processing */\r
6778   }\r
6779   return FALSE;\r
6780 }\r
6781 \r
6782 UINT APIENTRY\r
6783 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6784 {\r
6785   static UINT *number;\r
6786   OPENFILENAME *ofname;\r
6787   OFNOTIFY *ofnot;\r
6788   switch (uiMsg) {\r
6789   case WM_INITDIALOG:\r
6790     ofname = (OPENFILENAME *)lParam;\r
6791     number = (UINT *)(ofname->lCustData);\r
6792     break;\r
6793   case WM_NOTIFY:\r
6794     ofnot = (OFNOTIFY *)lParam;\r
6795     if (ofnot->hdr.code == CDN_FILEOK) {\r
6796       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6797     }\r
6798     break;\r
6799   }\r
6800   return 0;\r
6801 }\r
6802 \r
6803 \r
6804 FILE *\r
6805 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6806            char *nameFilt, char *dlgTitle, UINT *number,\r
6807            char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6808 {\r
6809   OPENFILENAME openFileName;\r
6810   char buf1[MSG_SIZ];\r
6811   FILE *f;\r
6812 \r
6813   if (fileName == NULL) fileName = buf1;\r
6814   if (defName == NULL) {\r
6815     strcpy(fileName, "*.");\r
6816     strcat(fileName, defExt);\r
6817   } else {\r
6818     strcpy(fileName, defName);\r
6819   }\r
6820   if (fileTitle) strcpy(fileTitle, "");\r
6821   if (number) *number = 0;\r
6822 \r
6823   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6824   openFileName.hwndOwner         = hwnd;\r
6825   openFileName.hInstance         = (HANDLE) hInst;\r
6826   openFileName.lpstrFilter       = nameFilt;\r
6827   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6828   openFileName.nMaxCustFilter    = 0L;\r
6829   openFileName.nFilterIndex      = 1L;\r
6830   openFileName.lpstrFile         = fileName;\r
6831   openFileName.nMaxFile          = MSG_SIZ;\r
6832   openFileName.lpstrFileTitle    = fileTitle;\r
6833   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6834   openFileName.lpstrInitialDir   = NULL;\r
6835   openFileName.lpstrTitle        = dlgTitle;\r
6836   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY\r
6837     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)\r
6838     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6839     | (oldDialog ? 0 : OFN_EXPLORER);\r
6840   openFileName.nFileOffset       = 0;\r
6841   openFileName.nFileExtension    = 0;\r
6842   openFileName.lpstrDefExt       = defExt;\r
6843   openFileName.lCustData         = (LONG) number;\r
6844   openFileName.lpfnHook          = oldDialog ?\r
6845     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6846   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6847 \r
6848   if (write[0] != 'r' ? GetSaveFileName(&openFileName) :\r
6849                         GetOpenFileName(&openFileName)) {\r
6850     /* open the file */\r
6851     f = fopen(openFileName.lpstrFile, write);\r
6852     if (f == NULL) {\r
6853       MessageBox(hwnd, "File open failed", NULL,\r
6854          MB_OK|MB_ICONEXCLAMATION);\r
6855       return NULL;\r
6856     }\r
6857   } else {\r
6858     int err = CommDlgExtendedError();\r
6859     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6860     return FALSE;\r
6861   }\r
6862   return f;\r
6863 }\r
6864 \r
6865 \r
6866 \r
6867 VOID APIENTRY\r
6868 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6869 {\r
6870   HMENU hmenuTrackPopup;    /* floating pop-up menu  */\r
6871 \r
6872   /*\r
6873    * Get the first pop-up menu in the menu template. This is the\r
6874    * menu that TrackPopupMenu displays.\r
6875    */\r
6876   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6877 \r
6878   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6879 \r
6880   /*\r
6881    * TrackPopup uses screen coordinates, so convert the\r
6882    * coordinates of the mouse click to screen coordinates.\r
6883    */\r
6884   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6885 \r
6886   /* Draw and track the floating pop-up menu. */\r
6887   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6888          pt.x, pt.y, 0, hwnd, NULL);\r
6889 \r
6890   /* Destroy the menu.*/\r
6891   DestroyMenu(hmenu);\r
6892 }\r
6893 \r
6894 typedef struct {\r
6895   HWND hDlg, hText;\r
6896   int sizeX, sizeY, newSizeX, newSizeY;\r
6897   HDWP hdwp;\r
6898 } ResizeEditPlusButtonsClosure;\r
6899 \r
6900 BOOL CALLBACK\r
6901 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6902 {\r
6903   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6904   RECT rect;\r
6905   POINT pt;\r
6906 \r
6907   if (hChild == cl->hText) return TRUE;\r
6908   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6909   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6910   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6911   ScreenToClient(cl->hDlg, &pt);\r
6912   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,\r
6913     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6914   return TRUE;\r
6915 }\r
6916 \r
6917 /* Resize a dialog that has a (rich) edit field filling most of\r
6918    the top, with a row of buttons below */\r
6919 VOID\r
6920 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6921 {\r
6922   RECT rectText;\r
6923   int newTextHeight, newTextWidth;\r
6924   ResizeEditPlusButtonsClosure cl;\r
6925 \r
6926   /*if (IsIconic(hDlg)) return;*/\r
6927   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6928 \r
6929   cl.hdwp = BeginDeferWindowPos(8);\r
6930 \r
6931   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6932   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6933   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6934   if (newTextHeight < 0) {\r
6935     newSizeY += -newTextHeight;\r
6936     newTextHeight = 0;\r
6937   }\r
6938   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,\r
6939     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6940 \r
6941   cl.hDlg = hDlg;\r
6942   cl.hText = hText;\r
6943   cl.sizeX = sizeX;\r
6944   cl.sizeY = sizeY;\r
6945   cl.newSizeX = newSizeX;\r
6946   cl.newSizeY = newSizeY;\r
6947   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6948 \r
6949   EndDeferWindowPos(cl.hdwp);\r
6950 }\r
6951 \r
6952 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6953 {\r
6954     RECT    rChild, rParent;\r
6955     int     wChild, hChild, wParent, hParent;\r
6956     int     wScreen, hScreen, xNew, yNew;\r
6957     HDC     hdc;\r
6958 \r
6959     /* Get the Height and Width of the child window */\r
6960     GetWindowRect (hwndChild, &rChild);\r
6961     wChild = rChild.right - rChild.left;\r
6962     hChild = rChild.bottom - rChild.top;\r
6963 \r
6964     /* Get the Height and Width of the parent window */\r
6965     GetWindowRect (hwndParent, &rParent);\r
6966     wParent = rParent.right - rParent.left;\r
6967     hParent = rParent.bottom - rParent.top;\r
6968 \r
6969     /* Get the display limits */\r
6970     hdc = GetDC (hwndChild);\r
6971     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6972     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6973     ReleaseDC(hwndChild, hdc);\r
6974 \r
6975     /* Calculate new X position, then adjust for screen */\r
6976     xNew = rParent.left + ((wParent - wChild) /2);\r
6977     if (xNew < 0) {\r
6978     xNew = 0;\r
6979     } else if ((xNew+wChild) > wScreen) {\r
6980     xNew = wScreen - wChild;\r
6981     }\r
6982 \r
6983     /* Calculate new Y position, then adjust for screen */\r
6984     if( mode == 0 ) {\r
6985         yNew = rParent.top  + ((hParent - hChild) /2);\r
6986     }\r
6987     else {\r
6988         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6989     }\r
6990 \r
6991     if (yNew < 0) {\r
6992     yNew = 0;\r
6993     } else if ((yNew+hChild) > hScreen) {\r
6994     yNew = hScreen - hChild;\r
6995     }\r
6996 \r
6997     /* Set it, and return */\r
6998     return SetWindowPos (hwndChild, NULL,\r
6999              xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
7000 }\r
7001 \r
7002 /* Center one window over another */\r
7003 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
7004 {\r
7005     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
7006 }\r
7007 \r
7008 /*---------------------------------------------------------------------------*\\r
7009  *\r
7010  * Startup Dialog functions\r
7011  *\r
7012 \*---------------------------------------------------------------------------*/\r
7013 void\r
7014 InitComboStrings(HANDLE hwndCombo, char **cd)\r
7015 {\r
7016   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7017 \r
7018   while (*cd != NULL) {\r
7019     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
7020     cd++;\r
7021   }\r
7022 }\r
7023 \r
7024 void\r
7025 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
7026 {\r
7027   char buf1[ARG_MAX];\r
7028   int len;\r
7029 \r
7030   if (str[0] == '@') {\r
7031     FILE* f = fopen(str + 1, "r");\r
7032     if (f == NULL) {\r
7033       DisplayFatalError(str + 1, errno, 2);\r
7034       return;\r
7035     }\r
7036     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7037     fclose(f);\r
7038     buf1[len] = NULLCHAR;\r
7039     str = buf1;\r
7040   }\r
7041 \r
7042   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7043 \r
7044   for (;;) {\r
7045     char buf[MSG_SIZ];\r
7046     char *end = strchr(str, '\n');\r
7047     if (end == NULL) return;\r
7048     memcpy(buf, str, end - str);\r
7049     buf[end - str] = NULLCHAR;\r
7050     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7051     str = end + 1;\r
7052   }\r
7053 }\r
7054 \r
7055 void\r
7056 SetStartupDialogEnables(HWND hDlg)\r
7057 {\r
7058   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7059     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7060     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7061   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7062     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7063   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7064     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7065   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7066     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7067   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7068     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7069     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7070     IsDlgButtonChecked(hDlg, OPT_View));\r
7071 }\r
7072 \r
7073 char *\r
7074 QuoteForFilename(char *filename)\r
7075 {\r
7076   int dquote, space;\r
7077   dquote = strchr(filename, '"') != NULL;\r
7078   space = strchr(filename, ' ') != NULL;\r
7079   if (dquote || space) {\r
7080     if (dquote) {\r
7081       return "'";\r
7082     } else {\r
7083       return "\"";\r
7084     }\r
7085   } else {\r
7086     return "";\r
7087   }\r
7088 }\r
7089 \r
7090 VOID\r
7091 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7092 {\r
7093   char buf[MSG_SIZ];\r
7094   char *q;\r
7095 \r
7096   InitComboStringsFromOption(hwndCombo, nthnames);\r
7097   q = QuoteForFilename(nthcp);\r
7098   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7099   if (*nthdir != NULLCHAR) {\r
7100     q = QuoteForFilename(nthdir);\r
7101     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7102   }\r
7103   if (*nthcp == NULLCHAR) {\r
7104     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7105   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7106     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7107     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7108   }\r
7109 }\r
7110 \r
7111 LRESULT CALLBACK\r
7112 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7113 {\r
7114   char buf[MSG_SIZ];\r
7115   HANDLE hwndCombo;\r
7116   char *p;\r
7117 \r
7118   switch (message) {\r
7119   case WM_INITDIALOG:\r
7120     /* Center the dialog */\r
7121     CenterWindow (hDlg, GetDesktopWindow());\r
7122     /* Initialize the dialog items */\r
7123     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7124               appData.firstChessProgram, "fd", appData.firstDirectory,\r
7125           firstChessProgramNames);\r
7126     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7127               appData.secondChessProgram, "sd", appData.secondDirectory,\r
7128           secondChessProgramNames);\r
7129     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7130     InitComboStringsFromOption(hwndCombo, icsNames);\r
7131     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7132     if (*appData.icsHelper != NULLCHAR) {\r
7133       char *q = QuoteForFilename(appData.icsHelper);\r
7134       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7135     }\r
7136     if (*appData.icsHost == NULLCHAR) {\r
7137       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7138       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7139     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7140       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7141       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7142     }\r
7143 \r
7144     if (appData.icsActive) {\r
7145       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7146     }\r
7147     else if (appData.noChessProgram) {\r
7148       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7149     }\r
7150     else {\r
7151       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7152     }\r
7153 \r
7154     SetStartupDialogEnables(hDlg);\r
7155     return TRUE;\r
7156 \r
7157   case WM_COMMAND:\r
7158     switch (LOWORD(wParam)) {\r
7159     case IDOK:\r
7160       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7161         strcpy(buf, "/fcp=");\r
7162     GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7163         p = buf;\r
7164     ParseArgs(StringGet, &p);\r
7165         strcpy(buf, "/scp=");\r
7166     GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7167         p = buf;\r
7168     ParseArgs(StringGet, &p);\r
7169     appData.noChessProgram = FALSE;\r
7170     appData.icsActive = FALSE;\r
7171       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7172         strcpy(buf, "/ics /icshost=");\r
7173     GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7174         p = buf;\r
7175     ParseArgs(StringGet, &p);\r
7176     if (appData.zippyPlay) {\r
7177       strcpy(buf, "/fcp=");\r
7178       GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7179       p = buf;\r
7180       ParseArgs(StringGet, &p);\r
7181     }\r
7182       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7183     appData.noChessProgram = TRUE;\r
7184     appData.icsActive = FALSE;\r
7185       } else {\r
7186     MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7187            "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7188     return TRUE;\r
7189       }\r
7190       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7191     GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7192     p = buf;\r
7193     ParseArgs(StringGet, &p);\r
7194       }\r
7195       EndDialog(hDlg, TRUE);\r
7196       return TRUE;\r
7197 \r
7198     case IDCANCEL:\r
7199       ExitEvent(0);\r
7200       return TRUE;\r
7201 \r
7202     case IDM_HELPCONTENTS:\r
7203       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7204     MessageBox (GetFocus(),\r
7205             "Unable to activate help",\r
7206             szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7207       }\r
7208       break;\r
7209 \r
7210     default:\r
7211       SetStartupDialogEnables(hDlg);\r
7212       break;\r
7213     }\r
7214     break;\r
7215   }\r
7216   return FALSE;\r
7217 }\r
7218 \r
7219 /*---------------------------------------------------------------------------*\\r
7220  *\r
7221  * About box dialog functions\r
7222  *\r
7223 \*---------------------------------------------------------------------------*/\r
7224 \r
7225 /* Process messages for "About" dialog box */\r
7226 LRESULT CALLBACK\r
7227 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7228 {\r
7229   switch (message) {\r
7230   case WM_INITDIALOG: /* message: initialize dialog box */\r
7231     /* Center the dialog over the application window */\r
7232     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7233     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7234     JAWS_COPYRIGHT\r
7235     return (TRUE);\r
7236 \r
7237   case WM_COMMAND: /* message: received a command */\r
7238     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7239     || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7240       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7241       return (TRUE);\r
7242     }\r
7243     break;\r
7244   }\r
7245   return (FALSE);\r
7246 }\r
7247 \r
7248 /*---------------------------------------------------------------------------*\\r
7249  *\r
7250  * Comment Dialog functions\r
7251  *\r
7252 \*---------------------------------------------------------------------------*/\r
7253 \r
7254 LRESULT CALLBACK\r
7255 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7256 {\r
7257   static HANDLE hwndText = NULL;\r
7258   int len, newSizeX, newSizeY, flags;\r
7259   static int sizeX, sizeY;\r
7260   char *str;\r
7261   RECT rect;\r
7262   MINMAXINFO *mmi;\r
7263 \r
7264   switch (message) {\r
7265   case WM_INITDIALOG: /* message: initialize dialog box */\r
7266     /* Initialize the dialog items */\r
7267     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7268     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7269     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7270     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7271     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7272     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7273     SetWindowText(hDlg, commentTitle);\r
7274     if (editComment) {\r
7275       SetFocus(hwndText);\r
7276     } else {\r
7277       SetFocus(GetDlgItem(hDlg, IDOK));\r
7278     }\r
7279     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7280         WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7281         MAKELPARAM(FALSE, 0));\r
7282     /* Size and position the dialog */\r
7283     if (!commentDialog) {\r
7284       commentDialog = hDlg;\r
7285       flags = SWP_NOZORDER;\r
7286       GetClientRect(hDlg, &rect);\r
7287       sizeX = rect.right;\r
7288       sizeY = rect.bottom;\r
7289       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7290       commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7291     WINDOWPLACEMENT wp;\r
7292     EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7293     wp.length = sizeof(WINDOWPLACEMENT);\r
7294     wp.flags = 0;\r
7295     wp.showCmd = SW_SHOW;\r
7296     wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7297     wp.rcNormalPosition.left = commentX;\r
7298     wp.rcNormalPosition.right = commentX + commentW;\r
7299     wp.rcNormalPosition.top = commentY;\r
7300     wp.rcNormalPosition.bottom = commentY + commentH;\r
7301     SetWindowPlacement(hDlg, &wp);\r
7302 \r
7303     GetClientRect(hDlg, &rect);\r
7304     newSizeX = rect.right;\r
7305     newSizeY = rect.bottom;\r
7306         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7307                   newSizeX, newSizeY);\r
7308     sizeX = newSizeX;\r
7309     sizeY = newSizeY;\r
7310       }\r
7311     }\r
7312     return FALSE;\r
7313 \r
7314   case WM_COMMAND: /* message: received a command */\r
7315     switch (LOWORD(wParam)) {\r
7316     case IDOK:\r
7317       if (editComment) {\r
7318     char *p, *q;\r
7319     /* Read changed options from the dialog box */\r
7320     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7321     len = GetWindowTextLength(hwndText);\r
7322     str = (char *) malloc(len + 1);\r
7323     GetWindowText(hwndText, str, len + 1);\r
7324     p = q = str;\r
7325     while (*q) {\r
7326       if (*q == '\r')\r
7327         q++;\r
7328       else\r
7329         *p++ = *q++;\r
7330     }\r
7331     *p = NULLCHAR;\r
7332     ReplaceComment(commentIndex, str);\r
7333     free(str);\r
7334       }\r
7335       CommentPopDown();\r
7336       return TRUE;\r
7337 \r
7338     case IDCANCEL:\r
7339     case OPT_CancelComment:\r
7340       CommentPopDown();\r
7341       return TRUE;\r
7342 \r
7343     case OPT_ClearComment:\r
7344       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7345       break;\r
7346 \r
7347     case OPT_EditComment:\r
7348       EditCommentEvent();\r
7349       return TRUE;\r
7350 \r
7351     default:\r
7352       break;\r
7353     }\r
7354     break;\r
7355 \r
7356   case WM_SIZE:\r
7357     newSizeX = LOWORD(lParam);\r
7358     newSizeY = HIWORD(lParam);\r
7359     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7360     sizeX = newSizeX;\r
7361     sizeY = newSizeY;\r
7362     break;\r
7363 \r
7364   case WM_GETMINMAXINFO:\r
7365     /* Prevent resizing window too small */\r
7366     mmi = (MINMAXINFO *) lParam;\r
7367     mmi->ptMinTrackSize.x = 100;\r
7368     mmi->ptMinTrackSize.y = 100;\r
7369     break;\r
7370   }\r
7371   return FALSE;\r
7372 }\r
7373 \r
7374 VOID\r
7375 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7376 {\r
7377   FARPROC lpProc;\r
7378   char *p, *q;\r
7379 \r
7380   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7381 \r
7382   if (str == NULL) str = "";\r
7383   p = (char *) malloc(2 * strlen(str) + 2);\r
7384   q = p;\r
7385   while (*str) {\r
7386     if (*str == '\n') *q++ = '\r';\r
7387     *q++ = *str++;\r
7388   }\r
7389   *q = NULLCHAR;\r
7390   if (commentText != NULL) free(commentText);\r
7391 \r
7392   commentIndex = index;\r
7393   commentTitle = title;\r
7394   commentText = p;\r
7395   editComment = edit;\r
7396 \r
7397   if (commentDialog) {\r
7398     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7399     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7400   } else {\r
7401     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7402     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7403          hwndMain, (DLGPROC)lpProc);\r
7404     FreeProcInstance(lpProc);\r
7405   }\r
7406   commentDialogUp = TRUE;\r
7407 }\r
7408 \r
7409 \r
7410 /*---------------------------------------------------------------------------*\\r
7411  *\r
7412  * Type-in move dialog functions\r
7413  *\r
7414 \*---------------------------------------------------------------------------*/\r
7415 \r
7416 LRESULT CALLBACK\r
7417 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7418 {\r
7419   char move[MSG_SIZ];\r
7420   HWND hInput;\r
7421   ChessMove moveType;\r
7422   int fromX, fromY, toX, toY;\r
7423   char promoChar;\r
7424 \r
7425   switch (message) {\r
7426   case WM_INITDIALOG:\r
7427     move[0] = (char) lParam;\r
7428     move[1] = NULLCHAR;\r
7429     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7430     hInput = GetDlgItem(hDlg, OPT_Move);\r
7431     SetWindowText(hInput, move);\r
7432     SetFocus(hInput);\r
7433     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7434     return FALSE;\r
7435 \r
7436   case WM_COMMAND:\r
7437     switch (LOWORD(wParam)) {\r
7438     case IDOK:\r
7439       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7440       { int n; Board board;\r
7441     // [HGM] FENedit\r
7442     if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7443         EditPositionPasteFEN(move);\r
7444         EndDialog(hDlg, TRUE);\r
7445         return TRUE;\r
7446     }\r
7447     // [HGM] movenum: allow move number to be typed in any mode\r
7448     if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7449       currentMove = 2*n-1;\r
7450       if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7451       if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7452       EndDialog(hDlg, TRUE);\r
7453       DrawPosition(TRUE, boards[currentMove]);\r
7454       if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7455       else DisplayMessage("", "");\r
7456       return TRUE;\r
7457     }\r
7458       }\r
7459       if (gameMode != EditGame && currentMove != forwardMostMove &&\r
7460     gameMode != Training) {\r
7461     DisplayMoveError("Displayed move is not current");\r
7462       } else {\r
7463 //  GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7464     int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,\r
7465       &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7466     if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7467     if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,\r
7468       &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7469       if (gameMode != Training)\r
7470           forwardMostMove = currentMove;\r
7471       UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
7472     } else {\r
7473       DisplayMoveError("Could not parse move");\r
7474     }\r
7475       }\r
7476       EndDialog(hDlg, TRUE);\r
7477       return TRUE;\r
7478     case IDCANCEL:\r
7479       EndDialog(hDlg, FALSE);\r
7480       return TRUE;\r
7481     default:\r
7482       break;\r
7483     }\r
7484     break;\r
7485   }\r
7486   return FALSE;\r
7487 }\r
7488 \r
7489 VOID\r
7490 PopUpMoveDialog(char firstchar)\r
7491 {\r
7492     FARPROC lpProc;\r
7493 \r
7494     if ((gameMode == BeginningOfGame && !appData.icsActive) ||\r
7495         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7496     gameMode == AnalyzeMode || gameMode == EditGame ||\r
7497     gameMode == EditPosition || gameMode == IcsExamining ||\r
7498     gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7499     isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7500         ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7501           gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7502     gameMode == Training) {\r
7503       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7504       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7505     hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7506       FreeProcInstance(lpProc);\r
7507     }\r
7508 }\r
7509 \r
7510 /*---------------------------------------------------------------------------*\\r
7511  *\r
7512  * Type-in name dialog functions\r
7513  *\r
7514 \*---------------------------------------------------------------------------*/\r
7515 \r
7516 LRESULT CALLBACK\r
7517 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7518 {\r
7519   char move[MSG_SIZ];\r
7520   HWND hInput;\r
7521 \r
7522   switch (message) {\r
7523   case WM_INITDIALOG:\r
7524     move[0] = (char) lParam;\r
7525     move[1] = NULLCHAR;\r
7526     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7527     hInput = GetDlgItem(hDlg, OPT_Name);\r
7528     SetWindowText(hInput, move);\r
7529     SetFocus(hInput);\r
7530     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7531     return FALSE;\r
7532 \r
7533   case WM_COMMAND:\r
7534     switch (LOWORD(wParam)) {\r
7535     case IDOK:\r
7536       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7537       appData.userName = strdup(move);\r
7538       SetUserLogo();\r
7539 \r
7540       EndDialog(hDlg, TRUE);\r
7541       return TRUE;\r
7542     case IDCANCEL:\r
7543       EndDialog(hDlg, FALSE);\r
7544       return TRUE;\r
7545     default:\r
7546       break;\r
7547     }\r
7548     break;\r
7549   }\r
7550   return FALSE;\r
7551 }\r
7552 \r
7553 VOID\r
7554 PopUpNameDialog(char firstchar)\r
7555 {\r
7556     FARPROC lpProc;\r
7557 \r
7558       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7559       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7560     hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7561       FreeProcInstance(lpProc);\r
7562 }\r
7563 \r
7564 /*---------------------------------------------------------------------------*\\r
7565  *\r
7566  *  Error dialogs\r
7567  *\r
7568 \*---------------------------------------------------------------------------*/\r
7569 \r
7570 /* Nonmodal error box */\r
7571 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7572                  WPARAM wParam, LPARAM lParam);\r
7573 \r
7574 VOID\r
7575 ErrorPopUp(char *title, char *content)\r
7576 {\r
7577   FARPROC lpProc;\r
7578   char *p, *q;\r
7579   BOOLEAN modal = hwndMain == NULL;\r
7580 \r
7581   p = content;\r
7582   q = errorMessage;\r
7583   while (*p) {\r
7584     if (*p == '\n') {\r
7585       if (modal) {\r
7586     *q++ = ' ';\r
7587     p++;\r
7588       } else {\r
7589     *q++ = '\r';\r
7590     *q++ = *p++;\r
7591       }\r
7592     } else {\r
7593       *q++ = *p++;\r
7594     }\r
7595   }\r
7596   *q = NULLCHAR;\r
7597   strncpy(errorTitle, title, sizeof(errorTitle));\r
7598   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7599 \r
7600   if (modal) {\r
7601     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7602   } else {\r
7603     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7604     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7605          hwndMain, (DLGPROC)lpProc);\r
7606     FreeProcInstance(lpProc);\r
7607   }\r
7608 }\r
7609 \r
7610 VOID\r
7611 ErrorPopDown()\r
7612 {\r
7613   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7614   if (errorDialog == NULL) return;\r
7615   DestroyWindow(errorDialog);\r
7616   errorDialog = NULL;\r
7617 }\r
7618 \r
7619 LRESULT CALLBACK\r
7620 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7621 {\r
7622   HANDLE hwndText;\r
7623   RECT rChild;\r
7624 \r
7625   switch (message) {\r
7626   case WM_INITDIALOG:\r
7627     GetWindowRect(hDlg, &rChild);\r
7628 \r
7629     /*\r
7630     SetWindowPos(hDlg, NULL, rChild.left,\r
7631       rChild.top + boardRect.top - (rChild.bottom - rChild.top),\r
7632       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7633     */\r
7634 \r
7635     /*\r
7636         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7637         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7638         and it doesn't work when you resize the dialog.\r
7639         For now, just give it a default position.\r
7640     */\r
7641     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7642 \r
7643     errorDialog = hDlg;\r
7644     SetWindowText(hDlg, errorTitle);\r
7645     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7646     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7647     return FALSE;\r
7648 \r
7649   case WM_COMMAND:\r
7650     switch (LOWORD(wParam)) {\r
7651     case IDOK:\r
7652     case IDCANCEL:\r
7653       if (errorDialog == hDlg) errorDialog = NULL;\r
7654       DestroyWindow(hDlg);\r
7655       return TRUE;\r
7656 \r
7657     default:\r
7658       break;\r
7659     }\r
7660     break;\r
7661   }\r
7662   return FALSE;\r
7663 }\r
7664 \r
7665 #ifdef GOTHIC\r
7666 HWND gothicDialog = NULL;\r
7667 \r
7668 LRESULT CALLBACK\r
7669 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7670 {\r
7671   HANDLE hwndText;\r
7672   RECT rChild;\r
7673   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7674 \r
7675   switch (message) {\r
7676   case WM_INITDIALOG:\r
7677     GetWindowRect(hDlg, &rChild);\r
7678 \r
7679     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7680                                                              SWP_NOZORDER);\r
7681 \r
7682     /*\r
7683         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7684         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7685         and it doesn't work when you resize the dialog.\r
7686         For now, just give it a default position.\r
7687     */\r
7688     gothicDialog = hDlg;\r
7689     SetWindowText(hDlg, errorTitle);\r
7690     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7691     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7692     return FALSE;\r
7693 \r
7694   case WM_COMMAND:\r
7695     switch (LOWORD(wParam)) {\r
7696     case IDOK:\r
7697     case IDCANCEL:\r
7698       if (errorDialog == hDlg) errorDialog = NULL;\r
7699       DestroyWindow(hDlg);\r
7700       return TRUE;\r
7701 \r
7702     default:\r
7703       break;\r
7704     }\r
7705     break;\r
7706   }\r
7707   return FALSE;\r
7708 }\r
7709 \r
7710 VOID\r
7711 GothicPopUp(char *title, VariantClass variant)\r
7712 {\r
7713   FARPROC lpProc;\r
7714   static char *lastTitle;\r
7715 \r
7716   strncpy(errorTitle, title, sizeof(errorTitle));\r
7717   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7718 \r
7719   if(lastTitle != title && gothicDialog != NULL) {\r
7720     DestroyWindow(gothicDialog);\r
7721     gothicDialog = NULL;\r
7722   }\r
7723   if(variant != VariantNormal && gothicDialog == NULL) {\r
7724     title = lastTitle;\r
7725     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7726     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7727          hwndMain, (DLGPROC)lpProc);\r
7728     FreeProcInstance(lpProc);\r
7729   }\r
7730 }\r
7731 #endif\r
7732 \r
7733 /*---------------------------------------------------------------------------*\\r
7734  *\r
7735  *  Ics Interaction console functions\r
7736  *\r
7737 \*---------------------------------------------------------------------------*/\r
7738 \r
7739 #define HISTORY_SIZE 64\r
7740 static char *history[HISTORY_SIZE];\r
7741 int histIn = 0, histP = 0;\r
7742 \r
7743 VOID\r
7744 SaveInHistory(char *cmd)\r
7745 {\r
7746   if (history[histIn] != NULL) {\r
7747     free(history[histIn]);\r
7748     history[histIn] = NULL;\r
7749   }\r
7750   if (*cmd == NULLCHAR) return;\r
7751   history[histIn] = StrSave(cmd);\r
7752   histIn = (histIn + 1) % HISTORY_SIZE;\r
7753   if (history[histIn] != NULL) {\r
7754     free(history[histIn]);\r
7755     history[histIn] = NULL;\r
7756   }\r
7757   histP = histIn;\r
7758 }\r
7759 \r
7760 char *\r
7761 PrevInHistory(char *cmd)\r
7762 {\r
7763   int newhp;\r
7764   if (histP == histIn) {\r
7765     if (history[histIn] != NULL) free(history[histIn]);\r
7766     history[histIn] = StrSave(cmd);\r
7767   }\r
7768   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7769   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7770   histP = newhp;\r
7771   return history[histP];\r
7772 }\r
7773 \r
7774 char *\r
7775 NextInHistory()\r
7776 {\r
7777   if (histP == histIn) return NULL;\r
7778   histP = (histP + 1) % HISTORY_SIZE;\r
7779   return history[histP];\r
7780 }\r
7781 \r
7782 typedef struct {\r
7783   char *item;\r
7784   char *command;\r
7785   BOOLEAN getname;\r
7786   BOOLEAN immediate;\r
7787 } IcsTextMenuEntry;\r
7788 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7789 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7790 \r
7791 void\r
7792 ParseIcsTextMenu(char *icsTextMenuString)\r
7793 {\r
7794 //  int flags = 0;\r
7795   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7796   char *p = icsTextMenuString;\r
7797   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7798     free(e->item);\r
7799     e->item = NULL;\r
7800     if (e->command != NULL) {\r
7801       free(e->command);\r
7802       e->command = NULL;\r
7803     }\r
7804     e++;\r
7805   }\r
7806   e = icsTextMenuEntry;\r
7807   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7808     if (*p == ';' || *p == '\n') {\r
7809       e->item = strdup("-");\r
7810       e->command = NULL;\r
7811       p++;\r
7812     } else if (*p == '-') {\r
7813       e->item = strdup("-");\r
7814       e->command = NULL;\r
7815       p++;\r
7816       if (*p) p++;\r
7817     } else {\r
7818       char *q, *r, *s, *t;\r
7819       char c;\r
7820       q = strchr(p, ',');\r
7821       if (q == NULL) break;\r
7822       *q = NULLCHAR;\r
7823       r = strchr(q + 1, ',');\r
7824       if (r == NULL) break;\r
7825       *r = NULLCHAR;\r
7826       s = strchr(r + 1, ',');\r
7827       if (s == NULL) break;\r
7828       *s = NULLCHAR;\r
7829       c = ';';\r
7830       t = strchr(s + 1, c);\r
7831       if (t == NULL) {\r
7832     c = '\n';\r
7833     t = strchr(s + 1, c);\r
7834       }\r
7835       if (t != NULL) *t = NULLCHAR;\r
7836       e->item = strdup(p);\r
7837       e->command = strdup(q + 1);\r
7838       e->getname = *(r + 1) != '0';\r
7839       e->immediate = *(s + 1) != '0';\r
7840       *q = ',';\r
7841       *r = ',';\r
7842       *s = ',';\r
7843       if (t == NULL) break;\r
7844       *t = c;\r
7845       p = t + 1;\r
7846     }\r
7847     e++;\r
7848   }\r
7849 }\r
7850 \r
7851 HMENU\r
7852 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7853 {\r
7854   HMENU hmenu, h;\r
7855   int i = 0;\r
7856   hmenu = LoadMenu(hInst, "TextMenu");\r
7857   h = GetSubMenu(hmenu, 0);\r
7858   while (e->item) {\r
7859     if (strcmp(e->item, "-") == 0) {\r
7860       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7861     } else {\r
7862       if (e->item[0] == '|') {\r
7863     AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7864            IDM_CommandX + i, &e->item[1]);\r
7865       } else {\r
7866     AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7867       }\r
7868     }\r
7869     e++;\r
7870     i++;\r
7871   }\r
7872   return hmenu;\r
7873 }\r
7874 \r
7875 WNDPROC consoleTextWindowProc;\r
7876 \r
7877 void\r
7878 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7879 {\r
7880   char buf[MSG_SIZ], name[MSG_SIZ];\r
7881   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7882   CHARRANGE sel;\r
7883 \r
7884   if (!getname) {\r
7885     SetWindowText(hInput, command);\r
7886     if (immediate) {\r
7887       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7888     } else {\r
7889       sel.cpMin = 999999;\r
7890       sel.cpMax = 999999;\r
7891       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7892       SetFocus(hInput);\r
7893     }\r
7894     return;\r
7895   }\r
7896   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7897   if (sel.cpMin == sel.cpMax) {\r
7898     /* Expand to surrounding word */\r
7899     TEXTRANGE tr;\r
7900     do {\r
7901       tr.chrg.cpMax = sel.cpMin;\r
7902       tr.chrg.cpMin = --sel.cpMin;\r
7903       if (sel.cpMin < 0) break;\r
7904       tr.lpstrText = name;\r
7905       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7906     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7907     sel.cpMin++;\r
7908 \r
7909     do {\r
7910       tr.chrg.cpMin = sel.cpMax;\r
7911       tr.chrg.cpMax = ++sel.cpMax;\r
7912       tr.lpstrText = name;\r
7913       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7914     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7915     sel.cpMax--;\r
7916 \r
7917     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7918       MessageBeep(MB_ICONEXCLAMATION);\r
7919       return;\r
7920     }\r
7921     tr.chrg = sel;\r
7922     tr.lpstrText = name;\r
7923     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7924   } else {\r
7925     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7926       MessageBeep(MB_ICONEXCLAMATION);\r
7927       return;\r
7928     }\r
7929     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7930   }\r
7931   if (immediate) {\r
7932     sprintf(buf, "%s %s", command, name);\r
7933     SetWindowText(hInput, buf);\r
7934     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7935   } else {\r
7936     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7937     SetWindowText(hInput, buf);\r
7938     sel.cpMin = 999999;\r
7939     sel.cpMax = 999999;\r
7940     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7941     SetFocus(hInput);\r
7942   }\r
7943 }\r
7944 \r
7945 LRESULT CALLBACK\r
7946 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7947 {\r
7948   HWND hInput;\r
7949   CHARRANGE sel;\r
7950 \r
7951   switch (message) {\r
7952   case WM_KEYDOWN:\r
7953     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7954     switch (wParam) {\r
7955     case VK_PRIOR:\r
7956       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7957       return 0;\r
7958     case VK_NEXT:\r
7959       sel.cpMin = 999999;\r
7960       sel.cpMax = 999999;\r
7961       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7962       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7963       return 0;\r
7964     }\r
7965     break;\r
7966   case WM_CHAR:\r
7967    if(wParam != '\022') {\r
7968     if (wParam == '\t') {\r
7969       if (GetKeyState(VK_SHIFT) < 0) {\r
7970     /* shifted */\r
7971     if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7972     if (buttonDesc[0].hwnd) {\r
7973       SetFocus(buttonDesc[0].hwnd);\r
7974     } else {\r
7975       SetFocus(hwndMain);\r
7976     }\r
7977       } else {\r
7978     /* unshifted */\r
7979     SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7980       }\r
7981     } else {\r
7982       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7983       JAWS_DELETE( SetFocus(hInput); )\r
7984       SendMessage(hInput, message, wParam, lParam);\r
7985     }\r
7986     return 0;\r
7987    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7988   case WM_RBUTTONUP:\r
7989     if (GetKeyState(VK_SHIFT) & ~1) {\r
7990       SendDlgItemMessage(hwndConsole, OPT_ConsoleText,\r
7991         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7992     } else {\r
7993       POINT pt;\r
7994       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7995       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7996       if (sel.cpMin == sel.cpMax) {\r
7997         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7998         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7999       }\r
8000       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8001         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8002       }\r
8003       pt.x = LOWORD(lParam);\r
8004       pt.y = HIWORD(lParam);\r
8005       MenuPopup(hwnd, pt, hmenu, -1);\r
8006     }\r
8007     return 0;\r
8008   case WM_PASTE:\r
8009     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8010     SetFocus(hInput);\r
8011     return SendMessage(hInput, message, wParam, lParam);\r
8012   case WM_MBUTTONDOWN:\r
8013     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8014   case WM_RBUTTONDOWN:\r
8015     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
8016       /* Move selection here if it was empty */\r
8017       POINT pt;\r
8018       pt.x = LOWORD(lParam);\r
8019       pt.y = HIWORD(lParam);\r
8020       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8021       if (sel.cpMin == sel.cpMax) {\r
8022         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
8023     sel.cpMax = sel.cpMin;\r
8024     SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8025       }\r
8026       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
8027     }\r
8028     return 0;\r
8029   case WM_COMMAND:\r
8030     switch (LOWORD(wParam)) {\r
8031     case IDM_QuickPaste:\r
8032       {\r
8033         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8034     if (sel.cpMin == sel.cpMax) {\r
8035       MessageBeep(MB_ICONEXCLAMATION);\r
8036           return 0;\r
8037     }\r
8038     SendMessage(hwnd, WM_COPY, 0, 0);\r
8039     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8040         SendMessage(hInput, WM_PASTE, 0, 0);\r
8041         SetFocus(hInput);\r
8042         return 0;\r
8043       }\r
8044     case IDM_Cut:\r
8045       SendMessage(hwnd, WM_CUT, 0, 0);\r
8046       return 0;\r
8047     case IDM_Paste:\r
8048       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8049       return 0;\r
8050     case IDM_Copy:\r
8051       SendMessage(hwnd, WM_COPY, 0, 0);\r
8052       return 0;\r
8053     default:\r
8054       {\r
8055     int i = LOWORD(wParam) - IDM_CommandX;\r
8056     if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8057         icsTextMenuEntry[i].command != NULL) {\r
8058       CommandX(hwnd, icsTextMenuEntry[i].command,\r
8059            icsTextMenuEntry[i].getname,\r
8060            icsTextMenuEntry[i].immediate);\r
8061       return 0;\r
8062     }\r
8063       }\r
8064       break;\r
8065     }\r
8066     break;\r
8067   }\r
8068   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8069 }\r
8070 \r
8071 WNDPROC consoleInputWindowProc;\r
8072 \r
8073 LRESULT CALLBACK\r
8074 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8075 {\r
8076   char buf[MSG_SIZ];\r
8077   char *p;\r
8078   static BOOL sendNextChar = FALSE;\r
8079   static BOOL quoteNextChar = FALSE;\r
8080   InputSource *is = consoleInputSource;\r
8081   CHARFORMAT cf;\r
8082   CHARRANGE sel;\r
8083 \r
8084   switch (message) {\r
8085   case WM_CHAR:\r
8086     if (!appData.localLineEditing || sendNextChar) {\r
8087       is->buf[0] = (CHAR) wParam;\r
8088       is->count = 1;\r
8089       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8090       sendNextChar = FALSE;\r
8091       return 0;\r
8092     }\r
8093     if (quoteNextChar) {\r
8094       buf[0] = (char) wParam;\r
8095       buf[1] = NULLCHAR;\r
8096       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8097       quoteNextChar = FALSE;\r
8098       return 0;\r
8099     }\r
8100     switch (wParam) {\r
8101     case '\r':   /* Enter key */\r
8102       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);\r
8103       if (consoleEcho) SaveInHistory(is->buf);\r
8104       is->buf[is->count++] = '\n';\r
8105       is->buf[is->count] = NULLCHAR;\r
8106       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8107       if (consoleEcho) {\r
8108     ConsoleOutput(is->buf, is->count, TRUE);\r
8109       } else if (appData.localLineEditing) {\r
8110     ConsoleOutput("\n", 1, TRUE);\r
8111       }\r
8112       /* fall thru */\r
8113     case '\033': /* Escape key */\r
8114       SetWindowText(hwnd, "");\r
8115       cf.cbSize = sizeof(CHARFORMAT);\r
8116       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8117       if (consoleEcho) {\r
8118         cf.crTextColor = textAttribs[ColorNormal].color;\r
8119       } else {\r
8120     cf.crTextColor = COLOR_ECHOOFF;\r
8121       }\r
8122       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8123       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8124       return 0;\r
8125     case '\t':   /* Tab key */\r
8126       if (GetKeyState(VK_SHIFT) < 0) {\r
8127     /* shifted */\r
8128     SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8129       } else {\r
8130     /* unshifted */\r
8131     if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8132     if (buttonDesc[0].hwnd) {\r
8133       SetFocus(buttonDesc[0].hwnd);\r
8134     } else {\r
8135       SetFocus(hwndMain);\r
8136     }\r
8137       }\r
8138       return 0;\r
8139     case '\023': /* Ctrl+S */\r
8140       sendNextChar = TRUE;\r
8141       return 0;\r
8142     case '\021': /* Ctrl+Q */\r
8143       quoteNextChar = TRUE;\r
8144       return 0;\r
8145     JAWS_REPLAY\r
8146     default:\r
8147       break;\r
8148     }\r
8149     break;\r
8150   case WM_KEYDOWN:\r
8151     switch (wParam) {\r
8152     case VK_UP:\r
8153       GetWindowText(hwnd, buf, MSG_SIZ);\r
8154       p = PrevInHistory(buf);\r
8155       if (p != NULL) {\r
8156     SetWindowText(hwnd, p);\r
8157     sel.cpMin = 999999;\r
8158     sel.cpMax = 999999;\r
8159     SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8160         return 0;\r
8161       }\r
8162       break;\r
8163     case VK_DOWN:\r
8164       p = NextInHistory();\r
8165       if (p != NULL) {\r
8166     SetWindowText(hwnd, p);\r
8167     sel.cpMin = 999999;\r
8168     sel.cpMax = 999999;\r
8169     SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8170         return 0;\r
8171       }\r
8172       break;\r
8173     case VK_HOME:\r
8174     case VK_END:\r
8175       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8176       /* fall thru */\r
8177     case VK_PRIOR:\r
8178     case VK_NEXT:\r
8179       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8180       return 0;\r
8181     }\r
8182     break;\r
8183   case WM_MBUTTONDOWN:\r
8184     SendDlgItemMessage(hwndConsole, OPT_ConsoleText,\r
8185       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8186     break;\r
8187   case WM_RBUTTONUP:\r
8188     if (GetKeyState(VK_SHIFT) & ~1) {\r
8189       SendDlgItemMessage(hwndConsole, OPT_ConsoleText,\r
8190         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8191     } else {\r
8192       POINT pt;\r
8193       HMENU hmenu;\r
8194       hmenu = LoadMenu(hInst, "InputMenu");\r
8195       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8196       if (sel.cpMin == sel.cpMax) {\r
8197         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8198         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8199       }\r
8200       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8201         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8202       }\r
8203       pt.x = LOWORD(lParam);\r
8204       pt.y = HIWORD(lParam);\r
8205       MenuPopup(hwnd, pt, hmenu, -1);\r
8206     }\r
8207     return 0;\r
8208   case WM_COMMAND:\r
8209     switch (LOWORD(wParam)) {\r
8210     case IDM_Undo:\r
8211       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8212       return 0;\r
8213     case IDM_SelectAll:\r
8214       sel.cpMin = 0;\r
8215       sel.cpMax = -1; /*999999?*/\r
8216       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8217       return 0;\r
8218     case IDM_Cut:\r
8219       SendMessage(hwnd, WM_CUT, 0, 0);\r
8220       return 0;\r
8221     case IDM_Paste:\r
8222       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8223       return 0;\r
8224     case IDM_Copy:\r
8225       SendMessage(hwnd, WM_COPY, 0, 0);\r
8226       return 0;\r
8227     }\r
8228     break;\r
8229   }\r
8230   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8231 }\r
8232 \r
8233 #define CO_MAX  100000\r
8234 #define CO_TRIM   1000\r
8235 \r
8236 LRESULT CALLBACK\r
8237 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8238 {\r
8239   static SnapData sd;\r
8240   static HWND hText, hInput /*, hFocus*/;\r
8241 //  InputSource *is = consoleInputSource;\r
8242   RECT rect;\r
8243   static int sizeX, sizeY;\r
8244   int newSizeX, newSizeY;\r
8245   MINMAXINFO *mmi;\r
8246 \r
8247   switch (message) {\r
8248   case WM_NOTIFY:\r
8249     if (((NMHDR*)lParam)->code == EN_LINK)\r
8250     {\r
8251       ENLINK *pLink = (ENLINK*)lParam;\r
8252       if (pLink->msg == WM_LBUTTONUP)\r
8253       {\r
8254           TEXTRANGE tr;\r
8255 \r
8256           tr.chrg = pLink->chrg;\r
8257           tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8258           hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8259           SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8260           ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8261           free(tr.lpstrText);\r
8262       }\r
8263     }\r
8264     break;\r
8265   case WM_INITDIALOG: /* message: initialize dialog box */\r
8266     hwndConsole = hDlg;\r
8267     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8268     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8269     SetFocus(hInput);\r
8270     consoleTextWindowProc = (WNDPROC)\r
8271       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8272     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8273     consoleInputWindowProc = (WNDPROC)\r
8274       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8275     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8276     Colorize(ColorNormal, TRUE);\r
8277     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8278     ChangedConsoleFont();\r
8279     GetClientRect(hDlg, &rect);\r
8280     sizeX = rect.right;\r
8281     sizeY = rect.bottom;\r
8282     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8283     wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8284       WINDOWPLACEMENT wp;\r
8285       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8286       wp.length = sizeof(WINDOWPLACEMENT);\r
8287       wp.flags = 0;\r
8288       wp.showCmd = SW_SHOW;\r
8289       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8290       wp.rcNormalPosition.left = wpConsole.x;\r
8291       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8292       wp.rcNormalPosition.top = wpConsole.y;\r
8293       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8294       SetWindowPlacement(hDlg, &wp);\r
8295     }\r
8296 #if 1\r
8297    // [HGM] Chessknight's change 2004-07-13\r
8298    else { /* Determine Defaults */\r
8299        WINDOWPLACEMENT wp;\r
8300        wpConsole.x = winWidth + 1;\r
8301        wpConsole.y = boardY;\r
8302        wpConsole.width = screenWidth -  winWidth;\r
8303        wpConsole.height = winHeight;\r
8304        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8305        wp.length = sizeof(WINDOWPLACEMENT);\r
8306        wp.flags = 0;\r
8307        wp.showCmd = SW_SHOW;\r
8308        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8309        wp.rcNormalPosition.left = wpConsole.x;\r
8310        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8311        wp.rcNormalPosition.top = wpConsole.y;\r
8312        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8313        SetWindowPlacement(hDlg, &wp);\r
8314     }\r
8315 #endif\r
8316     return FALSE;\r
8317 \r
8318   case WM_SETFOCUS:\r
8319     SetFocus(hInput);\r
8320     return 0;\r
8321 \r
8322   case WM_CLOSE:\r
8323     ExitEvent(0);\r
8324     /* not reached */\r
8325     break;\r
8326 \r
8327   case WM_SIZE:\r
8328     if (IsIconic(hDlg)) break;\r
8329     newSizeX = LOWORD(lParam);\r
8330     newSizeY = HIWORD(lParam);\r
8331     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8332       RECT rectText, rectInput;\r
8333       POINT pt;\r
8334       int newTextHeight, newTextWidth;\r
8335       GetWindowRect(hText, &rectText);\r
8336       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8337       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8338       if (newTextHeight < 0) {\r
8339     newSizeY += -newTextHeight;\r
8340         newTextHeight = 0;\r
8341       }\r
8342       SetWindowPos(hText, NULL, 0, 0,\r
8343     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8344       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8345       pt.x = rectInput.left;\r
8346       pt.y = rectInput.top + newSizeY - sizeY;\r
8347       ScreenToClient(hDlg, &pt);\r
8348       SetWindowPos(hInput, NULL,\r
8349     pt.x, pt.y, /* needs client coords */\r
8350     rectInput.right - rectInput.left + newSizeX - sizeX,\r
8351     rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8352     }\r
8353     sizeX = newSizeX;\r
8354     sizeY = newSizeY;\r
8355     break;\r
8356 \r
8357   case WM_GETMINMAXINFO:\r
8358     /* Prevent resizing window too small */\r
8359     mmi = (MINMAXINFO *) lParam;\r
8360     mmi->ptMinTrackSize.x = 100;\r
8361     mmi->ptMinTrackSize.y = 100;\r
8362     break;\r
8363 \r
8364   /* [AS] Snapping */\r
8365   case WM_ENTERSIZEMOVE:\r
8366     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8367 \r
8368   case WM_SIZING:\r
8369     return OnSizing( &sd, hDlg, wParam, lParam );\r
8370 \r
8371   case WM_MOVING:\r
8372     return OnMoving( &sd, hDlg, wParam, lParam );\r
8373 \r
8374   case WM_EXITSIZEMOVE:\r
8375     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8376   }\r
8377 \r
8378   return DefWindowProc(hDlg, message, wParam, lParam);\r
8379 }\r
8380 \r
8381 \r
8382 VOID\r
8383 ConsoleCreate()\r
8384 {\r
8385   HWND hCons, hText;\r
8386   WORD wMask;\r
8387   if (hwndConsole) return;\r
8388   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8389   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8390 \r
8391   // make the text item in the console do URL links\r
8392   hText = GetDlgItem(hCons, OPT_ConsoleText);\r
8393   wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8394   SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8395   SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8396 }\r
8397 \r
8398 \r
8399 VOID\r
8400 ConsoleOutput(char* data, int length, int forceVisible)\r
8401 {\r
8402   HWND hText;\r
8403   int trim, exlen;\r
8404   char *p, *q;\r
8405   char buf[CO_MAX+1];\r
8406   POINT pEnd;\r
8407   RECT rect;\r
8408   static int delayLF = 0;\r
8409   CHARRANGE savesel, sel;\r
8410 \r
8411   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8412   p = data;\r
8413   q = buf;\r
8414   if (delayLF) {\r
8415     *q++ = '\r';\r
8416     *q++ = '\n';\r
8417     delayLF = 0;\r
8418   }\r
8419   while (length--) {\r
8420     if (*p == '\n') {\r
8421       if (*++p) {\r
8422     *q++ = '\r';\r
8423     *q++ = '\n';\r
8424       } else {\r
8425     delayLF = 1;\r
8426       }\r
8427     } else if (*p == '\007') {\r
8428        MyPlaySound(&sounds[(int)SoundBell]);\r
8429        p++;\r
8430     } else {\r
8431       *q++ = *p++;\r
8432     }\r
8433   }\r
8434   *q = NULLCHAR;\r
8435   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8436   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8437   /* Save current selection */\r
8438   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8439   exlen = GetWindowTextLength(hText);\r
8440   /* Find out whether current end of text is visible */\r
8441   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8442   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8443   /* Trim existing text if it's too long */\r
8444   if (exlen + (q - buf) > CO_MAX) {\r
8445     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8446     sel.cpMin = 0;\r
8447     sel.cpMax = trim;\r
8448     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8449     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8450     exlen -= trim;\r
8451     savesel.cpMin -= trim;\r
8452     savesel.cpMax -= trim;\r
8453     if (exlen < 0) exlen = 0;\r
8454     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8455     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8456   }\r
8457   /* Append the new text */\r
8458   sel.cpMin = exlen;\r
8459   sel.cpMax = exlen;\r
8460   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8461   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8462   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8463   if (forceVisible || exlen == 0 ||\r
8464       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8465        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8466     /* Scroll to make new end of text visible if old end of text\r
8467        was visible or new text is an echo of user typein */\r
8468     sel.cpMin = 9999999;\r
8469     sel.cpMax = 9999999;\r
8470     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8471     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8472     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8473     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8474   }\r
8475   if (savesel.cpMax == exlen || forceVisible) {\r
8476     /* Move insert point to new end of text if it was at the old\r
8477        end of text or if the new text is an echo of user typein */\r
8478     sel.cpMin = 9999999;\r
8479     sel.cpMax = 9999999;\r
8480     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8481   } else {\r
8482     /* Restore previous selection */\r
8483     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8484   }\r
8485   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8486 }\r
8487 \r
8488 /*---------*/\r
8489 \r
8490 \r
8491 void\r
8492 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8493 {\r
8494   char buf[100];\r
8495   char *str;\r
8496   COLORREF oldFg, oldBg;\r
8497   HFONT oldFont;\r
8498   RECT rect;\r
8499 \r
8500   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8501 \r
8502   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8503   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8504   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8505 \r
8506   rect.left = x;\r
8507   rect.right = x + squareSize;\r
8508   rect.top  = y;\r
8509   rect.bottom = y + squareSize;\r
8510   str = buf;\r
8511 \r
8512   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8513                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8514              y, ETO_CLIPPED|ETO_OPAQUE,\r
8515              &rect, str, strlen(str), NULL);\r
8516 \r
8517   (void) SetTextColor(hdc, oldFg);\r
8518   (void) SetBkColor(hdc, oldBg);\r
8519   (void) SelectObject(hdc, oldFont);\r
8520 }\r
8521 \r
8522 void\r
8523 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8524               RECT *rect, char *color, char *flagFell)\r
8525 {\r
8526   char buf[100];\r
8527   char *str;\r
8528   COLORREF oldFg, oldBg;\r
8529   HFONT oldFont;\r
8530 \r
8531   if (appData.clockMode) {\r
8532     if (tinyLayout)\r
8533       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8534     else\r
8535       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8536     str = buf;\r
8537   } else {\r
8538     str = color;\r
8539   }\r
8540 \r
8541   if (highlight) {\r
8542     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8543     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8544   } else {\r
8545     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8546     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8547   }\r
8548   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8549 \r
8550   JAWS_SILENCE\r
8551 \r
8552   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8553          rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8554          rect, str, strlen(str), NULL);\r
8555   if(logoHeight > 0 && appData.clockMode) {\r
8556       RECT r;\r
8557       sprintf(buf, "%s %s", buf+7, flagFell);\r
8558       r.top = rect->top + logoHeight/2;\r
8559       r.left = rect->left;\r
8560       r.right = rect->right;\r
8561       r.bottom = rect->bottom;\r
8562       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8563              r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8564              &r, str, strlen(str), NULL);\r
8565   }\r
8566   (void) SetTextColor(hdc, oldFg);\r
8567   (void) SetBkColor(hdc, oldBg);\r
8568   (void) SelectObject(hdc, oldFont);\r
8569 }\r
8570 \r
8571 \r
8572 int\r
8573 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8574        OVERLAPPED *ovl)\r
8575 {\r
8576   int ok, err;\r
8577 \r
8578   /* [AS]  */\r
8579   if( count <= 0 ) {\r
8580     if (appData.debugMode) {\r
8581       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8582     }\r
8583 \r
8584     return ERROR_INVALID_USER_BUFFER;\r
8585   }\r
8586 \r
8587   ResetEvent(ovl->hEvent);\r
8588   ovl->Offset = ovl->OffsetHigh = 0;\r
8589   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8590   if (ok) {\r
8591     err = NO_ERROR;\r
8592   } else {\r
8593     err = GetLastError();\r
8594     if (err == ERROR_IO_PENDING) {\r
8595       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8596       if (ok)\r
8597     err = NO_ERROR;\r
8598       else\r
8599     err = GetLastError();\r
8600     }\r
8601   }\r
8602   return err;\r
8603 }\r
8604 \r
8605 int\r
8606 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8607         OVERLAPPED *ovl)\r
8608 {\r
8609   int ok, err;\r
8610 \r
8611   ResetEvent(ovl->hEvent);\r
8612   ovl->Offset = ovl->OffsetHigh = 0;\r
8613   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8614   if (ok) {\r
8615     err = NO_ERROR;\r
8616   } else {\r
8617     err = GetLastError();\r
8618     if (err == ERROR_IO_PENDING) {\r
8619       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8620       if (ok)\r
8621     err = NO_ERROR;\r
8622       else\r
8623     err = GetLastError();\r
8624     }\r
8625   }\r
8626   return err;\r
8627 }\r
8628 \r
8629 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8630 void CheckForInputBufferFull( InputSource * is )\r
8631 {\r
8632     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8633         /* Look for end of line */\r
8634         char * p = is->buf;\r
8635 \r
8636         while( p < is->next && *p != '\n' ) {\r
8637             p++;\r
8638         }\r
8639 \r
8640         if( p >= is->next ) {\r
8641             if (appData.debugMode) {\r
8642                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8643             }\r
8644 \r
8645             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8646             is->count = (DWORD) -1;\r
8647             is->next = is->buf;\r
8648         }\r
8649     }\r
8650 }\r
8651 \r
8652 DWORD\r
8653 InputThread(LPVOID arg)\r
8654 {\r
8655   InputSource *is;\r
8656   OVERLAPPED ovl;\r
8657 \r
8658   is = (InputSource *) arg;\r
8659   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8660   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8661   while (is->hThread != NULL) {\r
8662     is->error = DoReadFile(is->hFile, is->next,\r
8663                INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8664                &is->count, &ovl);\r
8665     if (is->error == NO_ERROR) {\r
8666       is->next += is->count;\r
8667     } else {\r
8668       if (is->error == ERROR_BROKEN_PIPE) {\r
8669     /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8670     is->count = 0;\r
8671       } else {\r
8672     is->count = (DWORD) -1;\r
8673         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8674         break;\r
8675       }\r
8676     }\r
8677 \r
8678     CheckForInputBufferFull( is );\r
8679 \r
8680     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8681 \r
8682     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8683 \r
8684     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8685   }\r
8686 \r
8687   CloseHandle(ovl.hEvent);\r
8688   CloseHandle(is->hFile);\r
8689 \r
8690   if (appData.debugMode) {\r
8691     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8692   }\r
8693 \r
8694   return 0;\r
8695 }\r
8696 \r
8697 \r
8698 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8699 DWORD\r
8700 NonOvlInputThread(LPVOID arg)\r
8701 {\r
8702   InputSource *is;\r
8703   char *p, *q;\r
8704   int i;\r
8705   char prev;\r
8706 \r
8707   is = (InputSource *) arg;\r
8708   while (is->hThread != NULL) {\r
8709     is->error = ReadFile(is->hFile, is->next,\r
8710              INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8711              &is->count, NULL) ? NO_ERROR : GetLastError();\r
8712     if (is->error == NO_ERROR) {\r
8713       /* Change CRLF to LF */\r
8714       if (is->next > is->buf) {\r
8715     p = is->next - 1;\r
8716     i = is->count + 1;\r
8717       } else {\r
8718     p = is->next;\r
8719     i = is->count;\r
8720       }\r
8721       q = p;\r
8722       prev = NULLCHAR;\r
8723       while (i > 0) {\r
8724     if (prev == '\r' && *p == '\n') {\r
8725       *(q-1) = '\n';\r
8726       is->count--;\r
8727     } else {\r
8728       *q++ = *p;\r
8729     }\r
8730     prev = *p++;\r
8731     i--;\r
8732       }\r
8733       *q = NULLCHAR;\r
8734       is->next = q;\r
8735     } else {\r
8736       if (is->error == ERROR_BROKEN_PIPE) {\r
8737     /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8738     is->count = 0;\r
8739       } else {\r
8740     is->count = (DWORD) -1;\r
8741       }\r
8742     }\r
8743 \r
8744     CheckForInputBufferFull( is );\r
8745 \r
8746     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8747 \r
8748     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8749 \r
8750     if (is->count < 0) break;  /* Quit on error */\r
8751   }\r
8752   CloseHandle(is->hFile);\r
8753   return 0;\r
8754 }\r
8755 \r
8756 DWORD\r
8757 SocketInputThread(LPVOID arg)\r
8758 {\r
8759   InputSource *is;\r
8760 \r
8761   is = (InputSource *) arg;\r
8762   while (is->hThread != NULL) {\r
8763     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8764     if ((int)is->count == SOCKET_ERROR) {\r
8765       is->count = (DWORD) -1;\r
8766       is->error = WSAGetLastError();\r
8767     } else {\r
8768       is->error = NO_ERROR;\r
8769       is->next += is->count;\r
8770       if (is->count == 0 && is->second == is) {\r
8771     /* End of file on stderr; quit with no message */\r
8772     break;\r
8773       }\r
8774     }\r
8775     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8776 \r
8777     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8778 \r
8779     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8780   }\r
8781   return 0;\r
8782 }\r
8783 \r
8784 VOID\r
8785 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8786 {\r
8787   InputSource *is;\r
8788 \r
8789   is = (InputSource *) lParam;\r
8790   if (is->lineByLine) {\r
8791     /* Feed in lines one by one */\r
8792     char *p = is->buf;\r
8793     char *q = p;\r
8794     while (q < is->next) {\r
8795       if (*q++ == '\n') {\r
8796     (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8797     p = q;\r
8798       }\r
8799     }\r
8800 \r
8801     /* Move any partial line to the start of the buffer */\r
8802     q = is->buf;\r
8803     while (p < is->next) {\r
8804       *q++ = *p++;\r
8805     }\r
8806     is->next = q;\r
8807 \r
8808     if (is->error != NO_ERROR || is->count == 0) {\r
8809       /* Notify backend of the error.  Note: If there was a partial\r
8810      line at the end, it is not flushed through. */\r
8811       (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8812     }\r
8813   } else {\r
8814     /* Feed in the whole chunk of input at once */\r
8815     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8816     is->next = is->buf;\r
8817   }\r
8818 }\r
8819 \r
8820 /*---------------------------------------------------------------------------*\\r
8821  *\r
8822  *  Menu enables. Used when setting various modes.\r
8823  *\r
8824 \*---------------------------------------------------------------------------*/\r
8825 \r
8826 typedef struct {\r
8827   int item;\r
8828   int flags;\r
8829 } Enables;\r
8830 \r
8831 VOID\r
8832 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8833 {\r
8834   while (enab->item > 0) {\r
8835     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8836     enab++;\r
8837   }\r
8838 }\r
8839 \r
8840 Enables gnuEnables[] = {\r
8841   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8843   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8844   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8845   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8846   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8847   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8848   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8849   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8850   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8851   { -1, -1 }\r
8852 };\r
8853 \r
8854 Enables icsEnables[] = {\r
8855   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8856   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8857   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8858   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8859   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8860   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8861   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8862   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8863   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8866   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8867   { -1, -1 }\r
8868 };\r
8869 \r
8870 #ifdef ZIPPY\r
8871 Enables zippyEnables[] = {\r
8872   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8873   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8874   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8875   { -1, -1 }\r
8876 };\r
8877 #endif\r
8878 \r
8879 Enables ncpEnables[] = {\r
8880   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8881   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8882   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8883   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8884   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8885   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8886   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8887   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8888   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8889   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8890   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8891   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8892   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8893   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8894   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8895   { -1, -1 }\r
8896 };\r
8897 \r
8898 Enables trainingOnEnables[] = {\r
8899   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8900   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8901   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8902   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8903   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8904   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8905   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8906   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8907   { -1, -1 }\r
8908 };\r
8909 \r
8910 Enables trainingOffEnables[] = {\r
8911   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8912   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8913   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8914   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8915   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8916   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8917   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8918   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8919   { -1, -1 }\r
8920 };\r
8921 \r
8922 /* These modify either ncpEnables or gnuEnables */\r
8923 Enables cmailEnables[] = {\r
8924   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8925   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8926   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8927   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8928   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8929   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8930   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8931   { -1, -1 }\r
8932 };\r
8933 \r
8934 Enables machineThinkingEnables[] = {\r
8935   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8936   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8937   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8938   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8939   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8940   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8941   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8942   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8943   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8944   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8945   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8946   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8947   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8948   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8949   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8950   { -1, -1 }\r
8951 };\r
8952 \r
8953 Enables userThinkingEnables[] = {\r
8954   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8955   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8956   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8957   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8958   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8959   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8960   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8961   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8962   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8963   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8964   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8965   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8966   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8967   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8968   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8969   { -1, -1 }\r
8970 };\r
8971 \r
8972 /*---------------------------------------------------------------------------*\\r
8973  *\r
8974  *  Front-end interface functions exported by XBoard.\r
8975  *  Functions appear in same order as prototypes in frontend.h.\r
8976  *\r
8977 \*---------------------------------------------------------------------------*/\r
8978 VOID\r
8979 ModeHighlight()\r
8980 {\r
8981   static UINT prevChecked = 0;\r
8982   static int prevPausing = 0;\r
8983   UINT nowChecked;\r
8984 \r
8985   if (pausing != prevPausing) {\r
8986     prevPausing = pausing;\r
8987     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8988              MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8989     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8990   }\r
8991 \r
8992   switch (gameMode) {\r
8993   case BeginningOfGame:\r
8994     if (appData.icsActive)\r
8995       nowChecked = IDM_IcsClient;\r
8996     else if (appData.noChessProgram)\r
8997       nowChecked = IDM_EditGame;\r
8998     else\r
8999       nowChecked = IDM_MachineBlack;\r
9000     break;\r
9001   case MachinePlaysBlack:\r
9002     nowChecked = IDM_MachineBlack;\r
9003     break;\r
9004   case MachinePlaysWhite:\r
9005     nowChecked = IDM_MachineWhite;\r
9006     break;\r
9007   case TwoMachinesPlay:\r
9008     nowChecked = IDM_TwoMachines;\r
9009     break;\r
9010   case AnalyzeMode:\r
9011     nowChecked = IDM_AnalysisMode;\r
9012     break;\r
9013   case AnalyzeFile:\r
9014     nowChecked = IDM_AnalyzeFile;\r
9015     break;\r
9016   case EditGame:\r
9017     nowChecked = IDM_EditGame;\r
9018     break;\r
9019   case PlayFromGameFile:\r
9020     nowChecked = IDM_LoadGame;\r
9021     break;\r
9022   case EditPosition:\r
9023     nowChecked = IDM_EditPosition;\r
9024     break;\r
9025   case Training:\r
9026     nowChecked = IDM_Training;\r
9027     break;\r
9028   case IcsPlayingWhite:\r
9029   case IcsPlayingBlack:\r
9030   case IcsObserving:\r
9031   case IcsIdle:\r
9032     nowChecked = IDM_IcsClient;\r
9033     break;\r
9034   default:\r
9035   case EndOfGame:\r
9036     nowChecked = 0;\r
9037     break;\r
9038   }\r
9039   if (prevChecked != 0)\r
9040     (void) CheckMenuItem(GetMenu(hwndMain),\r
9041              prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
9042   if (nowChecked != 0)\r
9043     (void) CheckMenuItem(GetMenu(hwndMain),\r
9044              nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
9045 \r
9046   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
9047     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,\r
9048               MF_BYCOMMAND|MF_ENABLED);\r
9049   } else {\r
9050     (void) EnableMenuItem(GetMenu(hwndMain),\r
9051               IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
9052   }\r
9053 \r
9054   prevChecked = nowChecked;\r
9055 \r
9056   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
9057   if (appData.icsActive) {\r
9058        if (appData.icsEngineAnalyze) {\r
9059                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9060                        MF_BYCOMMAND|MF_CHECKED);\r
9061        } else {\r
9062                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9063                        MF_BYCOMMAND|MF_UNCHECKED);\r
9064        }\r
9065   }\r
9066 }\r
9067 \r
9068 VOID\r
9069 SetICSMode()\r
9070 {\r
9071   HMENU hmenu = GetMenu(hwndMain);\r
9072   SetMenuEnables(hmenu, icsEnables);\r
9073   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9074     MF_BYPOSITION|MF_ENABLED);\r
9075 #ifdef ZIPPY\r
9076   if (appData.zippyPlay) {\r
9077     SetMenuEnables(hmenu, zippyEnables);\r
9078     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9079          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9080           MF_BYCOMMAND|MF_ENABLED);\r
9081   }\r
9082 #endif\r
9083 }\r
9084 \r
9085 VOID\r
9086 SetGNUMode()\r
9087 {\r
9088   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9089 }\r
9090 \r
9091 VOID\r
9092 SetNCPMode()\r
9093 {\r
9094   HMENU hmenu = GetMenu(hwndMain);\r
9095   SetMenuEnables(hmenu, ncpEnables);\r
9096   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9097     MF_BYPOSITION|MF_GRAYED);\r
9098     DrawMenuBar(hwndMain);\r
9099 }\r
9100 \r
9101 VOID\r
9102 SetCmailMode()\r
9103 {\r
9104   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9105 }\r
9106 \r
9107 VOID\r
9108 SetTrainingModeOn()\r
9109 {\r
9110   int i;\r
9111   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9112   for (i = 0; i < N_BUTTONS; i++) {\r
9113     if (buttonDesc[i].hwnd != NULL)\r
9114       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9115   }\r
9116   CommentPopDown();\r
9117 }\r
9118 \r
9119 VOID SetTrainingModeOff()\r
9120 {\r
9121   int i;\r
9122   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9123   for (i = 0; i < N_BUTTONS; i++) {\r
9124     if (buttonDesc[i].hwnd != NULL)\r
9125       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9126   }\r
9127 }\r
9128 \r
9129 \r
9130 VOID\r
9131 SetUserThinkingEnables()\r
9132 {\r
9133   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9134 }\r
9135 \r
9136 VOID\r
9137 SetMachineThinkingEnables()\r
9138 {\r
9139   HMENU hMenu = GetMenu(hwndMain);\r
9140   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9141 \r
9142   SetMenuEnables(hMenu, machineThinkingEnables);\r
9143 \r
9144   if (gameMode == MachinePlaysBlack) {\r
9145     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9146   } else if (gameMode == MachinePlaysWhite) {\r
9147     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9148   } else if (gameMode == TwoMachinesPlay) {\r
9149     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9150   }\r
9151 }\r
9152 \r
9153 \r
9154 VOID\r
9155 DisplayTitle(char *str)\r
9156 {\r
9157   char title[MSG_SIZ], *host;\r
9158   if (str[0] != NULLCHAR) {\r
9159     strcpy(title, str);\r
9160   } else if (appData.icsActive) {\r
9161     if (appData.icsCommPort[0] != NULLCHAR)\r
9162       host = "ICS";\r
9163     else\r
9164       host = appData.icsHost;\r
9165     sprintf(title, "%s: %s", szTitle, host);\r
9166   } else if (appData.noChessProgram) {\r
9167     strcpy(title, szTitle);\r
9168   } else {\r
9169     strcpy(title, szTitle);\r
9170     strcat(title, ": ");\r
9171     strcat(title, first.tidy);\r
9172   }\r
9173   SetWindowText(hwndMain, title);\r
9174 }\r
9175 \r
9176 \r
9177 VOID\r
9178 DisplayMessage(char *str1, char *str2)\r
9179 {\r
9180   HDC hdc;\r
9181   HFONT oldFont;\r
9182   int remain = MESSAGE_TEXT_MAX - 1;\r
9183   int len;\r
9184 \r
9185   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9186   messageText[0] = NULLCHAR;\r
9187   if (*str1) {\r
9188     len = strlen(str1);\r
9189     if (len > remain) len = remain;\r
9190     strncpy(messageText, str1, len);\r
9191     messageText[len] = NULLCHAR;\r
9192     remain -= len;\r
9193   }\r
9194   if (*str2 && remain >= 2) {\r
9195     if (*str1) {\r
9196       strcat(messageText, "  ");\r
9197       remain -= 2;\r
9198     }\r
9199     len = strlen(str2);\r
9200     if (len > remain) len = remain;\r
9201     strncat(messageText, str2, len);\r
9202   }\r
9203   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9204 \r
9205   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9206 \r
9207   SAYMACHINEMOVE();\r
9208 \r
9209   hdc = GetDC(hwndMain);\r
9210   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9211   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9212          &messageRect, messageText, strlen(messageText), NULL);\r
9213   (void) SelectObject(hdc, oldFont);\r
9214   (void) ReleaseDC(hwndMain, hdc);\r
9215 }\r
9216 \r
9217 VOID\r
9218 DisplayError(char *str, int error)\r
9219 {\r
9220   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9221   int len;\r
9222 \r
9223   if (error == 0) {\r
9224     strcpy(buf, str);\r
9225   } else {\r
9226     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9227             NULL, error, LANG_NEUTRAL,\r
9228             (LPSTR) buf2, MSG_SIZ, NULL);\r
9229     if (len > 0) {\r
9230       sprintf(buf, "%s:\n%s", str, buf2);\r
9231     } else {\r
9232       ErrorMap *em = errmap;\r
9233       while (em->err != 0 && em->err != error) em++;\r
9234       if (em->err != 0) {\r
9235     sprintf(buf, "%s:\n%s", str, em->msg);\r
9236       } else {\r
9237     sprintf(buf, "%s:\nError code %d", str, error);\r
9238       }\r
9239     }\r
9240   }\r
9241 \r
9242   ErrorPopUp("Error", buf);\r
9243 }\r
9244 \r
9245 \r
9246 VOID\r
9247 DisplayMoveError(char *str)\r
9248 {\r
9249   fromX = fromY = -1;\r
9250   ClearHighlights();\r
9251   DrawPosition(FALSE, NULL);\r
9252   if (appData.popupMoveErrors) {\r
9253     ErrorPopUp("Error", str);\r
9254   } else {\r
9255     DisplayMessage(str, "");\r
9256     moveErrorMessageUp = TRUE;\r
9257   }\r
9258 }\r
9259 \r
9260 VOID\r
9261 DisplayFatalError(char *str, int error, int exitStatus)\r
9262 {\r
9263   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9264   int len;\r
9265   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9266 \r
9267   if (error != 0) {\r
9268     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9269             NULL, error, LANG_NEUTRAL,\r
9270             (LPSTR) buf2, MSG_SIZ, NULL);\r
9271     if (len > 0) {\r
9272       sprintf(buf, "%s:\n%s", str, buf2);\r
9273     } else {\r
9274       ErrorMap *em = errmap;\r
9275       while (em->err != 0 && em->err != error) em++;\r
9276       if (em->err != 0) {\r
9277     sprintf(buf, "%s:\n%s", str, em->msg);\r
9278       } else {\r
9279     sprintf(buf, "%s:\nError code %d", str, error);\r
9280       }\r
9281     }\r
9282     str = buf;\r
9283   }\r
9284   if (appData.debugMode) {\r
9285     fprintf(debugFP, "%s: %s\n", label, str);\r
9286   }\r
9287   if (appData.popupExitMessage) {\r
9288     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9289               (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9290   }\r
9291   ExitEvent(exitStatus);\r
9292 }\r
9293 \r
9294 \r
9295 VOID\r
9296 DisplayInformation(char *str)\r
9297 {\r
9298   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9299 }\r
9300 \r
9301 \r
9302 VOID\r
9303 DisplayNote(char *str)\r
9304 {\r
9305   ErrorPopUp("Note", str);\r
9306 }\r
9307 \r
9308 \r
9309 typedef struct {\r
9310   char *title, *question, *replyPrefix;\r
9311   ProcRef pr;\r
9312 } QuestionParams;\r
9313 \r
9314 LRESULT CALLBACK\r
9315 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9316 {\r
9317   static QuestionParams *qp;\r
9318   char reply[MSG_SIZ];\r
9319   int len, err;\r
9320 \r
9321   switch (message) {\r
9322   case WM_INITDIALOG:\r
9323     qp = (QuestionParams *) lParam;\r
9324     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9325     SetWindowText(hDlg, qp->title);\r
9326     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9327     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9328     return FALSE;\r
9329 \r
9330   case WM_COMMAND:\r
9331     switch (LOWORD(wParam)) {\r
9332     case IDOK:\r
9333       strcpy(reply, qp->replyPrefix);\r
9334       if (*reply) strcat(reply, " ");\r
9335       len = strlen(reply);\r
9336       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9337       strcat(reply, "\n");\r
9338       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9339       EndDialog(hDlg, TRUE);\r
9340       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9341       return TRUE;\r
9342     case IDCANCEL:\r
9343       EndDialog(hDlg, FALSE);\r
9344       return TRUE;\r
9345     default:\r
9346       break;\r
9347     }\r
9348     break;\r
9349   }\r
9350   return FALSE;\r
9351 }\r
9352 \r
9353 VOID\r
9354 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9355 {\r
9356     QuestionParams qp;\r
9357     FARPROC lpProc;\r
9358 \r
9359     qp.title = title;\r
9360     qp.question = question;\r
9361     qp.replyPrefix = replyPrefix;\r
9362     qp.pr = pr;\r
9363     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9364     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9365       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9366     FreeProcInstance(lpProc);\r
9367 }\r
9368 \r
9369 /* [AS] Pick FRC position */\r
9370 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9371 {\r
9372     static int * lpIndexFRC;\r
9373     BOOL index_is_ok;\r
9374     char buf[16];\r
9375 \r
9376     switch( message )\r
9377     {\r
9378     case WM_INITDIALOG:\r
9379         lpIndexFRC = (int *) lParam;\r
9380 \r
9381         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9382 \r
9383         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9384         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9385         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9386         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9387 \r
9388         break;\r
9389 \r
9390     case WM_COMMAND:\r
9391         switch( LOWORD(wParam) ) {\r
9392         case IDOK:\r
9393             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9394             EndDialog( hDlg, 0 );\r
9395         shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9396             return TRUE;\r
9397         case IDCANCEL:\r
9398             EndDialog( hDlg, 1 );\r
9399             return TRUE;\r
9400         case IDC_NFG_Edit:\r
9401             if( HIWORD(wParam) == EN_CHANGE ) {\r
9402                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9403 \r
9404                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9405             }\r
9406             return TRUE;\r
9407         case IDC_NFG_Random:\r
9408             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9409             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9410             return TRUE;\r
9411         }\r
9412 \r
9413         break;\r
9414     }\r
9415 \r
9416     return FALSE;\r
9417 }\r
9418 \r
9419 int NewGameFRC()\r
9420 {\r
9421     int result;\r
9422     int index = appData.defaultFrcPosition;\r
9423     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9424 \r
9425     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9426 \r
9427     if( result == 0 ) {\r
9428         appData.defaultFrcPosition = index;\r
9429     }\r
9430 \r
9431     return result;\r
9432 }\r
9433 \r
9434 /* [AS] Game list options */\r
9435 typedef struct {\r
9436     char id;\r
9437     char * name;\r
9438 } GLT_Item;\r
9439 \r
9440 static GLT_Item GLT_ItemInfo[] = {\r
9441     { GLT_EVENT,      "Event" },\r
9442     { GLT_SITE,       "Site" },\r
9443     { GLT_DATE,       "Date" },\r
9444     { GLT_ROUND,      "Round" },\r
9445     { GLT_PLAYERS,    "Players" },\r
9446     { GLT_RESULT,     "Result" },\r
9447     { GLT_WHITE_ELO,  "White Rating" },\r
9448     { GLT_BLACK_ELO,  "Black Rating" },\r
9449     { GLT_TIME_CONTROL,"Time Control" },\r
9450     { GLT_VARIANT,    "Variant" },\r
9451     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9452     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9453     { 0, 0 }\r
9454 };\r
9455 \r
9456 const char * GLT_FindItem( char id )\r
9457 {\r
9458     const char * result = 0;\r
9459 \r
9460     GLT_Item * list = GLT_ItemInfo;\r
9461 \r
9462     while( list->id != 0 ) {\r
9463         if( list->id == id ) {\r
9464             result = list->name;\r
9465             break;\r
9466         }\r
9467 \r
9468         list++;\r
9469     }\r
9470 \r
9471     return result;\r
9472 }\r
9473 \r
9474 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9475 {\r
9476     const char * name = GLT_FindItem( id );\r
9477 \r
9478     if( name != 0 ) {\r
9479         if( index >= 0 ) {\r
9480             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9481         }\r
9482         else {\r
9483             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9484         }\r
9485     }\r
9486 }\r
9487 \r
9488 void GLT_TagsToList( HWND hDlg, char * tags )\r
9489 {\r
9490     char * pc = tags;\r
9491 \r
9492     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9493 \r
9494     while( *pc ) {\r
9495         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9496         pc++;\r
9497     }\r
9498 \r
9499     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9500 \r
9501     pc = GLT_ALL_TAGS;\r
9502 \r
9503     while( *pc ) {\r
9504         if( strchr( tags, *pc ) == 0 ) {\r
9505             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9506         }\r
9507         pc++;\r
9508     }\r
9509 \r
9510     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9511 }\r
9512 \r
9513 char GLT_ListItemToTag( HWND hDlg, int index )\r
9514 {\r
9515     char result = '\0';\r
9516     char name[128];\r
9517 \r
9518     GLT_Item * list = GLT_ItemInfo;\r
9519 \r
9520     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9521         while( list->id != 0 ) {\r
9522             if( strcmp( list->name, name ) == 0 ) {\r
9523                 result = list->id;\r
9524                 break;\r
9525             }\r
9526 \r
9527             list++;\r
9528         }\r
9529     }\r
9530 \r
9531     return result;\r
9532 }\r
9533 \r
9534 void GLT_MoveSelection( HWND hDlg, int delta )\r
9535 {\r
9536     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9537     int idx2 = idx1 + delta;\r
9538     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9539 \r
9540     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9541         char buf[128];\r
9542 \r
9543         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9544         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9545         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9546         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9547     }\r
9548 }\r
9549 \r
9550 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9551 {\r
9552     static char glt[64];\r
9553     static char * lpUserGLT;\r
9554 \r
9555     switch( message )\r
9556     {\r
9557     case WM_INITDIALOG:\r
9558         lpUserGLT = (char *) lParam;\r
9559 \r
9560         strcpy( glt, lpUserGLT );\r
9561 \r
9562         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9563 \r
9564         /* Initialize list */\r
9565         GLT_TagsToList( hDlg, glt );\r
9566 \r
9567         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9568 \r
9569         break;\r
9570 \r
9571     case WM_COMMAND:\r
9572         switch( LOWORD(wParam) ) {\r
9573         case IDOK:\r
9574             {\r
9575                 char * pc = lpUserGLT;\r
9576                 int idx = 0;\r
9577 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9578                 char id;\r
9579 \r
9580                 do {\r
9581                     id = GLT_ListItemToTag( hDlg, idx );\r
9582 \r
9583                     *pc++ = id;\r
9584                     idx++;\r
9585                 } while( id != '\0' );\r
9586             }\r
9587             EndDialog( hDlg, 0 );\r
9588             return TRUE;\r
9589         case IDCANCEL:\r
9590             EndDialog( hDlg, 1 );\r
9591             return TRUE;\r
9592 \r
9593         case IDC_GLT_Default:\r
9594             strcpy( glt, GLT_DEFAULT_TAGS );\r
9595             GLT_TagsToList( hDlg, glt );\r
9596             return TRUE;\r
9597 \r
9598         case IDC_GLT_Restore:\r
9599             strcpy( glt, lpUserGLT );\r
9600             GLT_TagsToList( hDlg, glt );\r
9601             return TRUE;\r
9602 \r
9603         case IDC_GLT_Up:\r
9604             GLT_MoveSelection( hDlg, -1 );\r
9605             return TRUE;\r
9606 \r
9607         case IDC_GLT_Down:\r
9608             GLT_MoveSelection( hDlg, +1 );\r
9609             return TRUE;\r
9610         }\r
9611 \r
9612         break;\r
9613     }\r
9614 \r
9615     return FALSE;\r
9616 }\r
9617 \r
9618 int GameListOptions()\r
9619 {\r
9620     char glt[64];\r
9621     int result;\r
9622     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9623 \r
9624     strcpy( glt, appData.gameListTags );\r
9625 \r
9626     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9627 \r
9628     if( result == 0 ) {\r
9629         /* [AS] Memory leak here! */\r
9630         appData.gameListTags = strdup( glt );\r
9631     }\r
9632 \r
9633     return result;\r
9634 }\r
9635 \r
9636 \r
9637 VOID\r
9638 DisplayIcsInteractionTitle(char *str)\r
9639 {\r
9640   char consoleTitle[MSG_SIZ];\r
9641 \r
9642   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9643   SetWindowText(hwndConsole, consoleTitle);\r
9644 }\r
9645 \r
9646 void\r
9647 DrawPosition(int fullRedraw, Board board)\r
9648 {\r
9649   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);\r
9650 }\r
9651 \r
9652 \r
9653 VOID\r
9654 ResetFrontEnd()\r
9655 {\r
9656   fromX = fromY = -1;\r
9657   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9658     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9659     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9660     dragInfo.lastpos = dragInfo.pos;\r
9661     dragInfo.start.x = dragInfo.start.y = -1;\r
9662     dragInfo.from = dragInfo.start;\r
9663     ReleaseCapture();\r
9664     DrawPosition(TRUE, NULL);\r
9665   }\r
9666 }\r
9667 \r
9668 \r
9669 VOID\r
9670 CommentPopUp(char *title, char *str)\r
9671 {\r
9672   HWND hwnd = GetActiveWindow();\r
9673   EitherCommentPopUp(0, title, str, FALSE);\r
9674   SAY(str);\r
9675   SetActiveWindow(hwnd);\r
9676 }\r
9677 \r
9678 VOID\r
9679 CommentPopDown(void)\r
9680 {\r
9681   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9682   if (commentDialog) {\r
9683     ShowWindow(commentDialog, SW_HIDE);\r
9684   }\r
9685   commentDialogUp = FALSE;\r
9686 }\r
9687 \r
9688 VOID\r
9689 EditCommentPopUp(int index, char *title, char *str)\r
9690 {\r
9691   EitherCommentPopUp(index, title, str, TRUE);\r
9692 }\r
9693 \r
9694 \r
9695 VOID\r
9696 RingBell()\r
9697 {\r
9698   MyPlaySound(&sounds[(int)SoundMove]);\r
9699 }\r
9700 \r
9701 VOID PlayIcsWinSound()\r
9702 {\r
9703   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9704 }\r
9705 \r
9706 VOID PlayIcsLossSound()\r
9707 {\r
9708   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9709 }\r
9710 \r
9711 VOID PlayIcsDrawSound()\r
9712 {\r
9713   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9714 }\r
9715 \r
9716 VOID PlayIcsUnfinishedSound()\r
9717 {\r
9718   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9719 }\r
9720 \r
9721 VOID\r
9722 PlayAlarmSound()\r
9723 {\r
9724   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9725 }\r
9726 \r
9727 \r
9728 VOID\r
9729 EchoOn()\r
9730 {\r
9731   HWND hInput;\r
9732   consoleEcho = TRUE;\r
9733   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9734   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9735   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9736 }\r
9737 \r
9738 \r
9739 VOID\r
9740 EchoOff()\r
9741 {\r
9742   CHARFORMAT cf;\r
9743   HWND hInput;\r
9744   consoleEcho = FALSE;\r
9745   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9746   /* This works OK: set text and background both to the same color */\r
9747   cf = consoleCF;\r
9748   cf.crTextColor = COLOR_ECHOOFF;\r
9749   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9750   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9751 }\r
9752 \r
9753 /* No Raw()...? */\r
9754 \r
9755 void Colorize(ColorClass cc, int continuation)\r
9756 {\r
9757   currentColorClass = cc;\r
9758   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9759   consoleCF.crTextColor = textAttribs[cc].color;\r
9760   consoleCF.dwEffects = textAttribs[cc].effects;\r
9761   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9762 }\r
9763 \r
9764 char *\r
9765 UserName()\r
9766 {\r
9767   static char buf[MSG_SIZ];\r
9768   DWORD bufsiz = MSG_SIZ;\r
9769 \r
9770   if(appData.userName != NULL && appData.userName[0] != 0) {\r
9771     return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9772   }\r
9773   if (!GetUserName(buf, &bufsiz)) {\r
9774     /*DisplayError("Error getting user name", GetLastError());*/\r
9775     strcpy(buf, "User");\r
9776   }\r
9777   return buf;\r
9778 }\r
9779 \r
9780 char *\r
9781 HostName()\r
9782 {\r
9783   static char buf[MSG_SIZ];\r
9784   DWORD bufsiz = MSG_SIZ;\r
9785 \r
9786   if (!GetComputerName(buf, &bufsiz)) {\r
9787     /*DisplayError("Error getting host name", GetLastError());*/\r
9788     strcpy(buf, "Unknown");\r
9789   }\r
9790   return buf;\r
9791 }\r
9792 \r
9793 \r
9794 int\r
9795 ClockTimerRunning()\r
9796 {\r
9797   return clockTimerEvent != 0;\r
9798 }\r
9799 \r
9800 int\r
9801 StopClockTimer()\r
9802 {\r
9803   if (clockTimerEvent == 0) return FALSE;\r
9804   KillTimer(hwndMain, clockTimerEvent);\r
9805   clockTimerEvent = 0;\r
9806   return TRUE;\r
9807 }\r
9808 \r
9809 void\r
9810 StartClockTimer(long millisec)\r
9811 {\r
9812   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9813                  (UINT) millisec, NULL);\r
9814 }\r
9815 \r
9816 void\r
9817 DisplayWhiteClock(long timeRemaining, int highlight)\r
9818 {\r
9819   HDC hdc;\r
9820   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9821 \r
9822   if(appData.noGUI) return;\r
9823   hdc = GetDC(hwndMain);\r
9824   if (!IsIconic(hwndMain)) {\r
9825     DisplayAClock(hdc, timeRemaining, highlight,\r
9826             flipClock ? &blackRect : &whiteRect, "White", flag);\r
9827   }\r
9828   if (highlight && iconCurrent == iconBlack) {\r
9829     iconCurrent = iconWhite;\r
9830     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9831     if (IsIconic(hwndMain)) {\r
9832       DrawIcon(hdc, 2, 2, iconCurrent);\r
9833     }\r
9834   }\r
9835   (void) ReleaseDC(hwndMain, hdc);\r
9836   if (hwndConsole)\r
9837     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9838 }\r
9839 \r
9840 void\r
9841 DisplayBlackClock(long timeRemaining, int highlight)\r
9842 {\r
9843   HDC hdc;\r
9844   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9845 \r
9846   if(appData.noGUI) return;\r
9847   hdc = GetDC(hwndMain);\r
9848   if (!IsIconic(hwndMain)) {\r
9849     DisplayAClock(hdc, timeRemaining, highlight,\r
9850             flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9851   }\r
9852   if (highlight && iconCurrent == iconWhite) {\r
9853     iconCurrent = iconBlack;\r
9854     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9855     if (IsIconic(hwndMain)) {\r
9856       DrawIcon(hdc, 2, 2, iconCurrent);\r
9857     }\r
9858   }\r
9859   (void) ReleaseDC(hwndMain, hdc);\r
9860   if (hwndConsole)\r
9861     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9862 }\r
9863 \r
9864 \r
9865 int\r
9866 LoadGameTimerRunning()\r
9867 {\r
9868   return loadGameTimerEvent != 0;\r
9869 }\r
9870 \r
9871 int\r
9872 StopLoadGameTimer()\r
9873 {\r
9874   if (loadGameTimerEvent == 0) return FALSE;\r
9875   KillTimer(hwndMain, loadGameTimerEvent);\r
9876   loadGameTimerEvent = 0;\r
9877   return TRUE;\r
9878 }\r
9879 \r
9880 void\r
9881 StartLoadGameTimer(long millisec)\r
9882 {\r
9883   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9884                 (UINT) millisec, NULL);\r
9885 }\r
9886 \r
9887 void\r
9888 AutoSaveGame()\r
9889 {\r
9890   char *defName;\r
9891   FILE *f;\r
9892   char fileTitle[MSG_SIZ];\r
9893 \r
9894   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9895   f = OpenFileDialog(hwndMain, "a", defName,\r
9896              appData.oldSaveStyle ? "gam" : "pgn",\r
9897              GAME_FILT,\r
9898              "Save Game to File", NULL, fileTitle, NULL);\r
9899   if (f != NULL) {\r
9900     SaveGame(f, 0, "");\r
9901     fclose(f);\r
9902   }\r
9903 }\r
9904 \r
9905 \r
9906 void\r
9907 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9908 {\r
9909   if (delayedTimerEvent != 0) {\r
9910     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9911       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9912     }\r
9913     KillTimer(hwndMain, delayedTimerEvent);\r
9914     delayedTimerEvent = 0;\r
9915     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9916     delayedTimerCallback();\r
9917   }\r
9918   delayedTimerCallback = cb;\r
9919   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9920                 (UINT) millisec, NULL);\r
9921 }\r
9922 \r
9923 DelayedEventCallback\r
9924 GetDelayedEvent()\r
9925 {\r
9926   if (delayedTimerEvent) {\r
9927     return delayedTimerCallback;\r
9928   } else {\r
9929     return NULL;\r
9930   }\r
9931 }\r
9932 \r
9933 void\r
9934 CancelDelayedEvent()\r
9935 {\r
9936   if (delayedTimerEvent) {\r
9937     KillTimer(hwndMain, delayedTimerEvent);\r
9938     delayedTimerEvent = 0;\r
9939   }\r
9940 }\r
9941 \r
9942 DWORD GetWin32Priority(int nice)\r
9943 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9944 /*\r
9945 REALTIME_PRIORITY_CLASS     0x00000100\r
9946 HIGH_PRIORITY_CLASS         0x00000080\r
9947 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9948 NORMAL_PRIORITY_CLASS       0x00000020\r
9949 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9950 IDLE_PRIORITY_CLASS         0x00000040\r
9951 */\r
9952         if (nice < -15) return 0x00000080;\r
9953         if (nice < 0)   return 0x00008000;\r
9954         if (nice == 0)  return 0x00000020;\r
9955         if (nice < 15)  return 0x00004000;\r
9956         return 0x00000040;\r
9957 }\r
9958 \r
9959 /* Start a child process running the given program.\r
9960    The process's standard output can be read from "from", and its\r
9961    standard input can be written to "to".\r
9962    Exit with fatal error if anything goes wrong.\r
9963    Returns an opaque pointer that can be used to destroy the process\r
9964    later.\r
9965 */\r
9966 int\r
9967 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9968 {\r
9969 #define BUFSIZE 4096\r
9970 \r
9971   HANDLE hChildStdinRd, hChildStdinWr,\r
9972     hChildStdoutRd, hChildStdoutWr;\r
9973   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9974   SECURITY_ATTRIBUTES saAttr;\r
9975   BOOL fSuccess;\r
9976   PROCESS_INFORMATION piProcInfo;\r
9977   STARTUPINFO siStartInfo;\r
9978   ChildProc *cp;\r
9979   char buf[MSG_SIZ];\r
9980   DWORD err;\r
9981 \r
9982   if (appData.debugMode) {\r
9983     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9984   }\r
9985 \r
9986   *pr = NoProc;\r
9987 \r
9988   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9989   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9990   saAttr.bInheritHandle = TRUE;\r
9991   saAttr.lpSecurityDescriptor = NULL;\r
9992 \r
9993   /*\r
9994    * The steps for redirecting child's STDOUT:\r
9995    *     1. Create anonymous pipe to be STDOUT for child.\r
9996    *     2. Create a noninheritable duplicate of read handle,\r
9997    *         and close the inheritable read handle.\r
9998    */\r
9999 \r
10000   /* Create a pipe for the child's STDOUT. */\r
10001   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
10002     return GetLastError();\r
10003   }\r
10004 \r
10005   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
10006   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
10007                  GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
10008                  FALSE, /* not inherited */\r
10009                  DUPLICATE_SAME_ACCESS);\r
10010   if (! fSuccess) {\r
10011     return GetLastError();\r
10012   }\r
10013   CloseHandle(hChildStdoutRd);\r
10014 \r
10015   /*\r
10016    * The steps for redirecting child's STDIN:\r
10017    *     1. Create anonymous pipe to be STDIN for child.\r
10018    *     2. Create a noninheritable duplicate of write handle,\r
10019    *         and close the inheritable write handle.\r
10020    */\r
10021 \r
10022   /* Create a pipe for the child's STDIN. */\r
10023   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
10024     return GetLastError();\r
10025   }\r
10026 \r
10027   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
10028   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
10029                  GetCurrentProcess(), &hChildStdinWrDup, 0,\r
10030                  FALSE, /* not inherited */\r
10031                  DUPLICATE_SAME_ACCESS);\r
10032   if (! fSuccess) {\r
10033     return GetLastError();\r
10034   }\r
10035   CloseHandle(hChildStdinWr);\r
10036 \r
10037   /* Arrange to (1) look in dir for the child .exe file, and\r
10038    * (2) have dir be the child's working directory.  Interpret\r
10039    * dir relative to the directory WinBoard loaded from. */\r
10040   GetCurrentDirectory(MSG_SIZ, buf);\r
10041   SetCurrentDirectory(installDir);\r
10042   SetCurrentDirectory(dir);\r
10043 \r
10044   /* Now create the child process. */\r
10045 \r
10046   siStartInfo.cb = sizeof(STARTUPINFO);\r
10047   siStartInfo.lpReserved = NULL;\r
10048   siStartInfo.lpDesktop = NULL;\r
10049   siStartInfo.lpTitle = NULL;\r
10050   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
10051   siStartInfo.cbReserved2 = 0;\r
10052   siStartInfo.lpReserved2 = NULL;\r
10053   siStartInfo.hStdInput = hChildStdinRd;\r
10054   siStartInfo.hStdOutput = hChildStdoutWr;\r
10055   siStartInfo.hStdError = hChildStdoutWr;\r
10056 \r
10057   fSuccess = CreateProcess(NULL,\r
10058                cmdLine,    /* command line */\r
10059                NULL,       /* process security attributes */\r
10060                NULL,       /* primary thread security attrs */\r
10061                TRUE,       /* handles are inherited */\r
10062                DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10063                NULL,       /* use parent's environment */\r
10064                NULL,\r
10065                &siStartInfo, /* STARTUPINFO pointer */\r
10066                &piProcInfo); /* receives PROCESS_INFORMATION */\r
10067 \r
10068   err = GetLastError();\r
10069   SetCurrentDirectory(buf); /* return to prev directory */\r
10070   if (! fSuccess) {\r
10071     return err;\r
10072   }\r
10073 \r
10074   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10075     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10076     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10077   }\r
10078 \r
10079   /* Close the handles we don't need in the parent */\r
10080   CloseHandle(piProcInfo.hThread);\r
10081   CloseHandle(hChildStdinRd);\r
10082   CloseHandle(hChildStdoutWr);\r
10083 \r
10084   /* Prepare return value */\r
10085   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10086   cp->kind = CPReal;\r
10087   cp->hProcess = piProcInfo.hProcess;\r
10088   cp->pid = piProcInfo.dwProcessId;\r
10089   cp->hFrom = hChildStdoutRdDup;\r
10090   cp->hTo = hChildStdinWrDup;\r
10091 \r
10092   *pr = (void *) cp;\r
10093 \r
10094   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10095      2000 where engines sometimes don't see the initial command(s)\r
10096      from WinBoard and hang.  I don't understand how that can happen,\r
10097      but the Sleep is harmless, so I've put it in.  Others have also\r
10098      reported what may be the same problem, so hopefully this will fix\r
10099      it for them too.  */\r
10100   Sleep(500);\r
10101 \r
10102   return NO_ERROR;\r
10103 }\r
10104 \r
10105 \r
10106 void\r
10107 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10108 {\r
10109   ChildProc *cp; int result;\r
10110 \r
10111   cp = (ChildProc *) pr;\r
10112   if (cp == NULL) return;\r
10113 \r
10114   switch (cp->kind) {\r
10115   case CPReal:\r
10116     /* TerminateProcess is considered harmful, so... */\r
10117     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10118     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10119     /* The following doesn't work because the chess program\r
10120        doesn't "have the same console" as WinBoard.  Maybe\r
10121        we could arrange for this even though neither WinBoard\r
10122        nor the chess program uses a console for stdio? */\r
10123     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10124 \r
10125     /* [AS] Special termination modes for misbehaving programs... */\r
10126     if( signal == 9 ) {\r
10127         result = TerminateProcess( cp->hProcess, 0 );\r
10128 \r
10129         if ( appData.debugMode) {\r
10130             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10131         }\r
10132     }\r
10133     else if( signal == 10 ) {\r
10134         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10135 \r
10136         if( dw != WAIT_OBJECT_0 ) {\r
10137             result = TerminateProcess( cp->hProcess, 0 );\r
10138 \r
10139             if ( appData.debugMode) {\r
10140                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10141             }\r
10142 \r
10143         }\r
10144     }\r
10145 \r
10146     CloseHandle(cp->hProcess);\r
10147     break;\r
10148 \r
10149   case CPComm:\r
10150     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10151     break;\r
10152 \r
10153   case CPSock:\r
10154     closesocket(cp->sock);\r
10155     WSACleanup();\r
10156     break;\r
10157 \r
10158   case CPRcmd:\r
10159     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10160     closesocket(cp->sock);\r
10161     closesocket(cp->sock2);\r
10162     WSACleanup();\r
10163     break;\r
10164   }\r
10165   free(cp);\r
10166 }\r
10167 \r
10168 void\r
10169 InterruptChildProcess(ProcRef pr)\r
10170 {\r
10171   ChildProc *cp;\r
10172 \r
10173   cp = (ChildProc *) pr;\r
10174   if (cp == NULL) return;\r
10175   switch (cp->kind) {\r
10176   case CPReal:\r
10177     /* The following doesn't work because the chess program\r
10178        doesn't "have the same console" as WinBoard.  Maybe\r
10179        we could arrange for this even though neither WinBoard\r
10180        nor the chess program uses a console for stdio */\r
10181     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10182     break;\r
10183 \r
10184   case CPComm:\r
10185   case CPSock:\r
10186     /* Can't interrupt */\r
10187     break;\r
10188 \r
10189   case CPRcmd:\r
10190     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10191     break;\r
10192   }\r
10193 }\r
10194 \r
10195 \r
10196 int\r
10197 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10198 {\r
10199   char cmdLine[MSG_SIZ];\r
10200 \r
10201   if (port[0] == NULLCHAR) {\r
10202     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10203   } else {\r
10204     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10205   }\r
10206   return StartChildProcess(cmdLine, "", pr);\r
10207 }\r
10208 \r
10209 \r
10210 /* Code to open TCP sockets */\r
10211 \r
10212 int\r
10213 OpenTCP(char *host, char *port, ProcRef *pr)\r
10214 {\r
10215   ChildProc *cp;\r
10216   int err;\r
10217   SOCKET s;\r
10218   struct sockaddr_in sa, mysa;\r
10219   struct hostent FAR *hp;\r
10220   unsigned short uport;\r
10221   WORD wVersionRequested;\r
10222   WSADATA wsaData;\r
10223 \r
10224   /* Initialize socket DLL */\r
10225   wVersionRequested = MAKEWORD(1, 1);\r
10226   err = WSAStartup(wVersionRequested, &wsaData);\r
10227   if (err != 0) return err;\r
10228 \r
10229   /* Make socket */\r
10230   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10231     err = WSAGetLastError();\r
10232     WSACleanup();\r
10233     return err;\r
10234   }\r
10235 \r
10236   /* Bind local address using (mostly) don't-care values.\r
10237    */\r
10238   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10239   mysa.sin_family = AF_INET;\r
10240   mysa.sin_addr.s_addr = INADDR_ANY;\r
10241   uport = (unsigned short) 0;\r
10242   mysa.sin_port = htons(uport);\r
10243   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10244       == SOCKET_ERROR) {\r
10245     err = WSAGetLastError();\r
10246     WSACleanup();\r
10247     return err;\r
10248   }\r
10249 \r
10250   /* Resolve remote host name */\r
10251   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10252   if (!(hp = gethostbyname(host))) {\r
10253     unsigned int b0, b1, b2, b3;\r
10254 \r
10255     err = WSAGetLastError();\r
10256 \r
10257     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10258       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10259       hp->h_addrtype = AF_INET;\r
10260       hp->h_length = 4;\r
10261       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10262       hp->h_addr_list[0] = (char *) malloc(4);\r
10263       hp->h_addr_list[0][0] = (char) b0;\r
10264       hp->h_addr_list[0][1] = (char) b1;\r
10265       hp->h_addr_list[0][2] = (char) b2;\r
10266       hp->h_addr_list[0][3] = (char) b3;\r
10267     } else {\r
10268       WSACleanup();\r
10269       return err;\r
10270     }\r
10271   }\r
10272   sa.sin_family = hp->h_addrtype;\r
10273   uport = (unsigned short) atoi(port);\r
10274   sa.sin_port = htons(uport);\r
10275   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10276 \r
10277   /* Make connection */\r
10278   if (connect(s, (struct sockaddr *) &sa,\r
10279           sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10280     err = WSAGetLastError();\r
10281     WSACleanup();\r
10282     return err;\r
10283   }\r
10284 \r
10285   /* Prepare return value */\r
10286   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10287   cp->kind = CPSock;\r
10288   cp->sock = s;\r
10289   *pr = (ProcRef *) cp;\r
10290 \r
10291   return NO_ERROR;\r
10292 }\r
10293 \r
10294 int\r
10295 OpenCommPort(char *name, ProcRef *pr)\r
10296 {\r
10297   HANDLE h;\r
10298   COMMTIMEOUTS ct;\r
10299   ChildProc *cp;\r
10300   char fullname[MSG_SIZ];\r
10301 \r
10302   if (*name != '\\')\r
10303     sprintf(fullname, "\\\\.\\%s", name);\r
10304   else\r
10305     strcpy(fullname, name);\r
10306 \r
10307   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10308          0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10309   if (h == (HANDLE) -1) {\r
10310     return GetLastError();\r
10311   }\r
10312   hCommPort = h;\r
10313 \r
10314   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10315 \r
10316   /* Accumulate characters until a 100ms pause, then parse */\r
10317   ct.ReadIntervalTimeout = 100;\r
10318   ct.ReadTotalTimeoutMultiplier = 0;\r
10319   ct.ReadTotalTimeoutConstant = 0;\r
10320   ct.WriteTotalTimeoutMultiplier = 0;\r
10321   ct.WriteTotalTimeoutConstant = 0;\r
10322   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10323 \r
10324   /* Prepare return value */\r
10325   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10326   cp->kind = CPComm;\r
10327   cp->hFrom = h;\r
10328   cp->hTo = h;\r
10329   *pr = (ProcRef *) cp;\r
10330 \r
10331   return NO_ERROR;\r
10332 }\r
10333 \r
10334 int\r
10335 OpenLoopback(ProcRef *pr)\r
10336 {\r
10337   DisplayFatalError("Not implemented", 0, 1);\r
10338   return NO_ERROR;\r
10339 }\r
10340 \r
10341 \r
10342 int\r
10343 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10344 {\r
10345   ChildProc *cp;\r
10346   int err;\r
10347   SOCKET s, s2, s3;\r
10348   struct sockaddr_in sa, mysa;\r
10349   struct hostent FAR *hp;\r
10350   unsigned short uport;\r
10351   WORD wVersionRequested;\r
10352   WSADATA wsaData;\r
10353   int fromPort;\r
10354   char stderrPortStr[MSG_SIZ];\r
10355 \r
10356   /* Initialize socket DLL */\r
10357   wVersionRequested = MAKEWORD(1, 1);\r
10358   err = WSAStartup(wVersionRequested, &wsaData);\r
10359   if (err != 0) return err;\r
10360 \r
10361   /* Resolve remote host name */\r
10362   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10363   if (!(hp = gethostbyname(host))) {\r
10364     unsigned int b0, b1, b2, b3;\r
10365 \r
10366     err = WSAGetLastError();\r
10367 \r
10368     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10369       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10370       hp->h_addrtype = AF_INET;\r
10371       hp->h_length = 4;\r
10372       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10373       hp->h_addr_list[0] = (char *) malloc(4);\r
10374       hp->h_addr_list[0][0] = (char) b0;\r
10375       hp->h_addr_list[0][1] = (char) b1;\r
10376       hp->h_addr_list[0][2] = (char) b2;\r
10377       hp->h_addr_list[0][3] = (char) b3;\r
10378     } else {\r
10379       WSACleanup();\r
10380       return err;\r
10381     }\r
10382   }\r
10383   sa.sin_family = hp->h_addrtype;\r
10384   uport = (unsigned short) 514;\r
10385   sa.sin_port = htons(uport);\r
10386   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10387 \r
10388   /* Bind local socket to unused "privileged" port address\r
10389    */\r
10390   s = INVALID_SOCKET;\r
10391   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10392   mysa.sin_family = AF_INET;\r
10393   mysa.sin_addr.s_addr = INADDR_ANY;\r
10394   for (fromPort = 1023;; fromPort--) {\r
10395     if (fromPort < 0) {\r
10396       WSACleanup();\r
10397       return WSAEADDRINUSE;\r
10398     }\r
10399     if (s == INVALID_SOCKET) {\r
10400       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10401     err = WSAGetLastError();\r
10402     WSACleanup();\r
10403     return err;\r
10404       }\r
10405     }\r
10406     uport = (unsigned short) fromPort;\r
10407     mysa.sin_port = htons(uport);\r
10408     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10409     == SOCKET_ERROR) {\r
10410       err = WSAGetLastError();\r
10411       if (err == WSAEADDRINUSE) continue;\r
10412       WSACleanup();\r
10413       return err;\r
10414     }\r
10415     if (connect(s, (struct sockaddr *) &sa,\r
10416       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10417       err = WSAGetLastError();\r
10418       if (err == WSAEADDRINUSE) {\r
10419     closesocket(s);\r
10420         s = -1;\r
10421     continue;\r
10422       }\r
10423       WSACleanup();\r
10424       return err;\r
10425     }\r
10426     break;\r
10427   }\r
10428 \r
10429   /* Bind stderr local socket to unused "privileged" port address\r
10430    */\r
10431   s2 = INVALID_SOCKET;\r
10432   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10433   mysa.sin_family = AF_INET;\r
10434   mysa.sin_addr.s_addr = INADDR_ANY;\r
10435   for (fromPort = 1023;; fromPort--) {\r
10436     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10437     if (fromPort < 0) {\r
10438       (void) closesocket(s);\r
10439       WSACleanup();\r
10440       return WSAEADDRINUSE;\r
10441     }\r
10442     if (s2 == INVALID_SOCKET) {\r
10443       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10444     err = WSAGetLastError();\r
10445     closesocket(s);\r
10446     WSACleanup();\r
10447     return err;\r
10448       }\r
10449     }\r
10450     uport = (unsigned short) fromPort;\r
10451     mysa.sin_port = htons(uport);\r
10452     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10453     == SOCKET_ERROR) {\r
10454       err = WSAGetLastError();\r
10455       if (err == WSAEADDRINUSE) continue;\r
10456       (void) closesocket(s);\r
10457       WSACleanup();\r
10458       return err;\r
10459     }\r
10460     if (listen(s2, 1) == SOCKET_ERROR) {\r
10461       err = WSAGetLastError();\r
10462       if (err == WSAEADDRINUSE) {\r
10463     closesocket(s2);\r
10464     s2 = INVALID_SOCKET;\r
10465     continue;\r
10466       }\r
10467       (void) closesocket(s);\r
10468       (void) closesocket(s2);\r
10469       WSACleanup();\r
10470       return err;\r
10471     }\r
10472     break;\r
10473   }\r
10474   prevStderrPort = fromPort; // remember port used\r
10475   sprintf(stderrPortStr, "%d", fromPort);\r
10476 \r
10477   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10478     err = WSAGetLastError();\r
10479     (void) closesocket(s);\r
10480     (void) closesocket(s2);\r
10481     WSACleanup();\r
10482     return err;\r
10483   }\r
10484 \r
10485   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10486     err = WSAGetLastError();\r
10487     (void) closesocket(s);\r
10488     (void) closesocket(s2);\r
10489     WSACleanup();\r
10490     return err;\r
10491   }\r
10492   if (*user == NULLCHAR) user = UserName();\r
10493   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10494     err = WSAGetLastError();\r
10495     (void) closesocket(s);\r
10496     (void) closesocket(s2);\r
10497     WSACleanup();\r
10498     return err;\r
10499   }\r
10500   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10501     err = WSAGetLastError();\r
10502     (void) closesocket(s);\r
10503     (void) closesocket(s2);\r
10504     WSACleanup();\r
10505     return err;\r
10506   }\r
10507 \r
10508   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10509     err = WSAGetLastError();\r
10510     (void) closesocket(s);\r
10511     (void) closesocket(s2);\r
10512     WSACleanup();\r
10513     return err;\r
10514   }\r
10515   (void) closesocket(s2);  /* Stop listening */\r
10516 \r
10517   /* Prepare return value */\r
10518   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10519   cp->kind = CPRcmd;\r
10520   cp->sock = s;\r
10521   cp->sock2 = s3;\r
10522   *pr = (ProcRef *) cp;\r
10523 \r
10524   return NO_ERROR;\r
10525 }\r
10526 \r
10527 \r
10528 InputSourceRef\r
10529 AddInputSource(ProcRef pr, int lineByLine,\r
10530            InputCallback func, VOIDSTAR closure)\r
10531 {\r
10532   InputSource *is, *is2 = NULL;\r
10533   ChildProc *cp = (ChildProc *) pr;\r
10534 \r
10535   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10536   is->lineByLine = lineByLine;\r
10537   is->func = func;\r
10538   is->closure = closure;\r
10539   is->second = NULL;\r
10540   is->next = is->buf;\r
10541   if (pr == NoProc) {\r
10542     is->kind = CPReal;\r
10543     consoleInputSource = is;\r
10544   } else {\r
10545     is->kind = cp->kind;\r
10546     /*\r
10547         [AS] Try to avoid a race condition if the thread is given control too early:\r
10548         we create all threads suspended so that the is->hThread variable can be\r
10549         safely assigned, then let the threads start with ResumeThread.\r
10550     */\r
10551     switch (cp->kind) {\r
10552     case CPReal:\r
10553       is->hFile = cp->hFrom;\r
10554       cp->hFrom = NULL; /* now owned by InputThread */\r
10555       is->hThread =\r
10556     CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10557              (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10558       break;\r
10559 \r
10560     case CPComm:\r
10561       is->hFile = cp->hFrom;\r
10562       cp->hFrom = NULL; /* now owned by InputThread */\r
10563       is->hThread =\r
10564     CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10565              (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10566       break;\r
10567 \r
10568     case CPSock:\r
10569       is->sock = cp->sock;\r
10570       is->hThread =\r
10571     CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10572              (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10573       break;\r
10574 \r
10575     case CPRcmd:\r
10576       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10577       *is2 = *is;\r
10578       is->sock = cp->sock;\r
10579       is->second = is2;\r
10580       is2->sock = cp->sock2;\r
10581       is2->second = is2;\r
10582       is->hThread =\r
10583     CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10584              (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10585       is2->hThread =\r
10586     CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10587              (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10588       break;\r
10589     }\r
10590 \r
10591     if( is->hThread != NULL ) {\r
10592         ResumeThread( is->hThread );\r
10593     }\r
10594 \r
10595     if( is2 != NULL && is2->hThread != NULL ) {\r
10596         ResumeThread( is2->hThread );\r
10597     }\r
10598   }\r
10599 \r
10600   return (InputSourceRef) is;\r
10601 }\r
10602 \r
10603 void\r
10604 RemoveInputSource(InputSourceRef isr)\r
10605 {\r
10606   InputSource *is;\r
10607 \r
10608   is = (InputSource *) isr;\r
10609   is->hThread = NULL;  /* tell thread to stop */\r
10610   CloseHandle(is->hThread);\r
10611   if (is->second != NULL) {\r
10612     is->second->hThread = NULL;\r
10613     CloseHandle(is->second->hThread);\r
10614   }\r
10615 }\r
10616 \r
10617 \r
10618 int\r
10619 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10620 {\r
10621   DWORD dOutCount;\r
10622   int outCount = SOCKET_ERROR;\r
10623   ChildProc *cp = (ChildProc *) pr;\r
10624   static OVERLAPPED ovl;\r
10625 \r
10626   if (pr == NoProc) {\r
10627     ConsoleOutput(message, count, FALSE);\r
10628     return count;\r
10629   }\r
10630 \r
10631   if (ovl.hEvent == NULL) {\r
10632     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10633   }\r
10634   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10635 \r
10636   switch (cp->kind) {\r
10637   case CPSock:\r
10638   case CPRcmd:\r
10639     outCount = send(cp->sock, message, count, 0);\r
10640     if (outCount == SOCKET_ERROR) {\r
10641       *outError = WSAGetLastError();\r
10642     } else {\r
10643       *outError = NO_ERROR;\r
10644     }\r
10645     break;\r
10646 \r
10647   case CPReal:\r
10648     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10649           &dOutCount, NULL)) {\r
10650       *outError = NO_ERROR;\r
10651       outCount = (int) dOutCount;\r
10652     } else {\r
10653       *outError = GetLastError();\r
10654     }\r
10655     break;\r
10656 \r
10657   case CPComm:\r
10658     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10659                 &dOutCount, &ovl);\r
10660     if (*outError == NO_ERROR) {\r
10661       outCount = (int) dOutCount;\r
10662     }\r
10663     break;\r
10664   }\r
10665   return outCount;\r
10666 }\r
10667 \r
10668 int\r
10669 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10670                long msdelay)\r
10671 {\r
10672   /* Ignore delay, not implemented for WinBoard */\r
10673   return OutputToProcess(pr, message, count, outError);\r
10674 }\r
10675 \r
10676 \r
10677 void\r
10678 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10679             char *buf, int count, int error)\r
10680 {\r
10681   DisplayFatalError("Not implemented", 0, 1);\r
10682 }\r
10683 \r
10684 /* see wgamelist.c for Game List functions */\r
10685 /* see wedittags.c for Edit Tags functions */\r
10686 \r
10687 \r
10688 VOID\r
10689 ICSInitScript()\r
10690 {\r
10691   FILE *f;\r
10692   char buf[MSG_SIZ];\r
10693   char *dummy;\r
10694 \r
10695   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10696     f = fopen(buf, "r");\r
10697     if (f != NULL) {\r
10698       ProcessICSInitScript(f);\r
10699       fclose(f);\r
10700     }\r
10701   }\r
10702 }\r
10703 \r
10704 \r
10705 VOID\r
10706 StartAnalysisClock()\r
10707 {\r
10708   if (analysisTimerEvent) return;\r
10709   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10710                                 (UINT) 2000, NULL);\r
10711 }\r
10712 \r
10713 LRESULT CALLBACK\r
10714 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10715 {\r
10716   static HANDLE hwndText;\r
10717   RECT rect;\r
10718   static int sizeX, sizeY;\r
10719   int newSizeX, newSizeY, flags;\r
10720   MINMAXINFO *mmi;\r
10721 \r
10722   switch (message) {\r
10723   case WM_INITDIALOG: /* message: initialize dialog box */\r
10724     /* Initialize the dialog items */\r
10725     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10726     SetWindowText(hDlg, analysisTitle);\r
10727     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10728     /* Size and position the dialog */\r
10729     if (!analysisDialog) {\r
10730       analysisDialog = hDlg;\r
10731       flags = SWP_NOZORDER;\r
10732       GetClientRect(hDlg, &rect);\r
10733       sizeX = rect.right;\r
10734       sizeY = rect.bottom;\r
10735       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10736       analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10737     WINDOWPLACEMENT wp;\r
10738     EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10739     wp.length = sizeof(WINDOWPLACEMENT);\r
10740     wp.flags = 0;\r
10741     wp.showCmd = SW_SHOW;\r
10742     wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10743     wp.rcNormalPosition.left = analysisX;\r
10744     wp.rcNormalPosition.right = analysisX + analysisW;\r
10745     wp.rcNormalPosition.top = analysisY;\r
10746     wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10747     SetWindowPlacement(hDlg, &wp);\r
10748 \r
10749     GetClientRect(hDlg, &rect);\r
10750     newSizeX = rect.right;\r
10751     newSizeY = rect.bottom;\r
10752         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10753                   newSizeX, newSizeY);\r
10754     sizeX = newSizeX;\r
10755     sizeY = newSizeY;\r
10756       }\r
10757     }\r
10758     return FALSE;\r
10759 \r
10760   case WM_COMMAND: /* message: received a command */\r
10761     switch (LOWORD(wParam)) {\r
10762     case IDCANCEL:\r
10763       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10764           ExitAnalyzeMode();\r
10765           ModeHighlight();\r
10766           return TRUE;\r
10767       }\r
10768       EditGameEvent();\r
10769       return TRUE;\r
10770     default:\r
10771       break;\r
10772     }\r
10773     break;\r
10774 \r
10775   case WM_SIZE:\r
10776     newSizeX = LOWORD(lParam);\r
10777     newSizeY = HIWORD(lParam);\r
10778     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10779     sizeX = newSizeX;\r
10780     sizeY = newSizeY;\r
10781     break;\r
10782 \r
10783   case WM_GETMINMAXINFO:\r
10784     /* Prevent resizing window too small */\r
10785     mmi = (MINMAXINFO *) lParam;\r
10786     mmi->ptMinTrackSize.x = 100;\r
10787     mmi->ptMinTrackSize.y = 100;\r
10788     break;\r
10789   }\r
10790   return FALSE;\r
10791 }\r
10792 \r
10793 VOID\r
10794 AnalysisPopUp(char* title, char* str)\r
10795 {\r
10796   FARPROC lpProc;\r
10797   char *p, *q;\r
10798 \r
10799   /* [AS] */\r
10800   EngineOutputPopUp();\r
10801   return;\r
10802 \r
10803   if (str == NULL) str = "";\r
10804   p = (char *) malloc(2 * strlen(str) + 2);\r
10805   q = p;\r
10806   while (*str) {\r
10807     if (*str == '\n') *q++ = '\r';\r
10808     *q++ = *str++;\r
10809   }\r
10810   *q = NULLCHAR;\r
10811   if (analysisText != NULL) free(analysisText);\r
10812   analysisText = p;\r
10813 \r
10814   if (analysisDialog) {\r
10815     SetWindowText(analysisDialog, title);\r
10816     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10817     ShowWindow(analysisDialog, SW_SHOW);\r
10818   } else {\r
10819     analysisTitle = title;\r
10820     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10821     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10822          hwndMain, (DLGPROC)lpProc);\r
10823     FreeProcInstance(lpProc);\r
10824   }\r
10825   analysisDialogUp = TRUE;\r
10826 }\r
10827 \r
10828 VOID\r
10829 AnalysisPopDown()\r
10830 {\r
10831   if (analysisDialog) {\r
10832     ShowWindow(analysisDialog, SW_HIDE);\r
10833   }\r
10834   analysisDialogUp = FALSE;\r
10835 }\r
10836 \r
10837 \r
10838 VOID\r
10839 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10840 {\r
10841   highlightInfo.sq[0].x = fromX;\r
10842   highlightInfo.sq[0].y = fromY;\r
10843   highlightInfo.sq[1].x = toX;\r
10844   highlightInfo.sq[1].y = toY;\r
10845 }\r
10846 \r
10847 VOID\r
10848 ClearHighlights()\r
10849 {\r
10850   highlightInfo.sq[0].x = highlightInfo.sq[0].y =\r
10851     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10852 }\r
10853 \r
10854 VOID\r
10855 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10856 {\r
10857   premoveHighlightInfo.sq[0].x = fromX;\r
10858   premoveHighlightInfo.sq[0].y = fromY;\r
10859   premoveHighlightInfo.sq[1].x = toX;\r
10860   premoveHighlightInfo.sq[1].y = toY;\r
10861 }\r
10862 \r
10863 VOID\r
10864 ClearPremoveHighlights()\r
10865 {\r
10866   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =\r
10867     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10868 }\r
10869 \r
10870 VOID\r
10871 ShutDownFrontEnd()\r
10872 {\r
10873   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10874   DeleteClipboardTempFiles();\r
10875 }\r
10876 \r
10877 void\r
10878 BoardToTop()\r
10879 {\r
10880     if (IsIconic(hwndMain))\r
10881       ShowWindow(hwndMain, SW_RESTORE);\r
10882 \r
10883     SetActiveWindow(hwndMain);\r
10884 }\r
10885 \r
10886 /*\r
10887  * Prototypes for animation support routines\r
10888  */\r
10889 static void ScreenSquare(int column, int row, POINT * pt);\r
10890 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10891      POINT frames[], int * nFrames);\r
10892 \r
10893 \r
10894 void\r
10895 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10896 {   // [HGM] atomic: animate blast wave\r
10897     int i;\r
10898 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10899     explodeInfo.fromX = fromX;\r
10900     explodeInfo.fromY = fromY;\r
10901     explodeInfo.toX = toX;\r
10902     explodeInfo.toY = toY;\r
10903     for(i=1; i<nFrames; i++) {\r
10904         explodeInfo.radius = (i*180)/(nFrames-1);\r
10905         DrawPosition(FALSE, NULL);\r
10906         Sleep(appData.animSpeed);\r
10907     }\r
10908     explodeInfo.radius = 0;\r
10909     DrawPosition(TRUE, NULL);\r
10910 }\r
10911 \r
10912 #define kFactor 4\r
10913 \r
10914 void\r
10915 AnimateMove(board, fromX, fromY, toX, toY)\r
10916      Board board;\r
10917      int fromX;\r
10918      int fromY;\r
10919      int toX;\r
10920      int toY;\r
10921 {\r
10922   ChessSquare piece;\r
10923   POINT start, finish, mid;\r
10924   POINT frames[kFactor * 2 + 1];\r
10925   int nFrames, n;\r
10926 \r
10927   if (!appData.animate) return;\r
10928   if (doingSizing) return;\r
10929   if (fromY < 0 || fromX < 0) return;\r
10930   piece = board[fromY][fromX];\r
10931   if (piece >= EmptySquare) return;\r
10932 \r
10933   ScreenSquare(fromX, fromY, &start);\r
10934   ScreenSquare(toX, toY, &finish);\r
10935 \r
10936   /* All pieces except knights move in straight line */\r
10937   if (piece != WhiteKnight && piece != BlackKnight) {\r
10938     mid.x = start.x + (finish.x - start.x) / 2;\r
10939     mid.y = start.y + (finish.y - start.y) / 2;\r
10940   } else {\r
10941     /* Knight: make diagonal movement then straight */\r
10942     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10943        mid.x = start.x + (finish.x - start.x) / 2;\r
10944        mid.y = finish.y;\r
10945      } else {\r
10946        mid.x = finish.x;\r
10947        mid.y = start.y + (finish.y - start.y) / 2;\r
10948      }\r
10949   }\r
10950 \r
10951   /* Don't use as many frames for very short moves */\r
10952   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10953     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10954   else\r
10955     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10956 \r
10957   animInfo.from.x = fromX;\r
10958   animInfo.from.y = fromY;\r
10959   animInfo.to.x = toX;\r
10960   animInfo.to.y = toY;\r
10961   animInfo.lastpos = start;\r
10962   animInfo.piece = piece;\r
10963   for (n = 0; n < nFrames; n++) {\r
10964     animInfo.pos = frames[n];\r
10965     DrawPosition(FALSE, NULL);\r
10966     animInfo.lastpos = animInfo.pos;\r
10967     Sleep(appData.animSpeed);\r
10968   }\r
10969   animInfo.pos = finish;\r
10970   DrawPosition(FALSE, NULL);\r
10971   animInfo.piece = EmptySquare;\r
10972   if(gameInfo.variant == VariantAtomic &&\r
10973      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10974     AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10975 }\r
10976 \r
10977 /*      Convert board position to corner of screen rect and color       */\r
10978 \r
10979 static void\r
10980 ScreenSquare(column, row, pt)\r
10981      int column; int row; POINT * pt;\r
10982 {\r
10983   if (flipView) {\r
10984     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10985     pt->y = lineGap + row * (squareSize + lineGap);\r
10986   } else {\r
10987     pt->x = lineGap + column * (squareSize + lineGap);\r
10988     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10989   }\r
10990 }\r
10991 \r
10992 /*      Generate a series of frame coords from start->mid->finish.\r
10993         The movement rate doubles until the half way point is\r
10994         reached, then halves back down to the final destination,\r
10995         which gives a nice slow in/out effect. The algorithmn\r
10996         may seem to generate too many intermediates for short\r
10997         moves, but remember that the purpose is to attract the\r
10998         viewers attention to the piece about to be moved and\r
10999         then to where it ends up. Too few frames would be less\r
11000         noticeable.                                             */\r
11001 \r
11002 static void\r
11003 Tween(start, mid, finish, factor, frames, nFrames)\r
11004      POINT * start; POINT * mid;\r
11005      POINT * finish; int factor;\r
11006      POINT frames[]; int * nFrames;\r
11007 {\r
11008   int n, fraction = 1, count = 0;\r
11009 \r
11010   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
11011   for (n = 0; n < factor; n++)\r
11012     fraction *= 2;\r
11013   for (n = 0; n < factor; n++) {\r
11014     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
11015     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
11016     count ++;\r
11017     fraction = fraction / 2;\r
11018   }\r
11019 \r
11020   /* Midpoint */\r
11021   frames[count] = *mid;\r
11022   count ++;\r
11023 \r
11024   /* Slow out, stepping 1/2, then 1/4, ... */\r
11025   fraction = 2;\r
11026   for (n = 0; n < factor; n++) {\r
11027     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
11028     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
11029     count ++;\r
11030     fraction = fraction * 2;\r
11031   }\r
11032   *nFrames = count;\r
11033 }\r
11034 \r
11035 void\r
11036 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
11037 {\r
11038 #if 0\r
11039     char buf[256];\r
11040 \r
11041     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
11042         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
11043 \r
11044     OutputDebugString( buf );\r
11045 #endif\r
11046 \r
11047     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
11048 \r
11049     EvalGraphSet( first, last, current, pvInfoList );\r
11050 }\r
11051 \r
11052 void SetProgramStats( FrontEndProgramStats * stats )\r
11053 {\r
11054 #if 0\r
11055     char buf[1024];\r
11056 \r
11057     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
11058         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
11059 \r
11060     OutputDebugString( buf );\r
11061 #endif\r
11062 \r
11063     EngineOutputUpdate( stats );\r
11064 }\r