1st step for moving option parsing from winboard to the backend
[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 "frontend.h"\r
84 #include "backend.h"\r
85 #include "winboard.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 extern HANDLE chatHandle[];\r
102 extern int ics_type;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 void ChatPopUp P(());\r
112 typedef struct {\r
113   ChessSquare piece;  \r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117   POINT to;       /* board coordinates of the piece's new pos */\r
118 } AnimInfo;\r
119 \r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT start;    /* window coordinates of start pos */\r
124   POINT pos;      /* window coordinates of current pos */\r
125   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
126   POINT from;     /* board coordinates of the piece's orig pos */\r
127 } DragInfo;\r
128 \r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
130 \r
131 typedef struct {\r
132   POINT sq[2];    /* board coordinates of from, to squares */\r
133 } HighlightInfo;\r
134 \r
135 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
137 \r
138 typedef struct { // [HGM] atomic\r
139   int fromX, fromY, toX, toY, radius;\r
140 } ExplodeInfo;\r
141 \r
142 static ExplodeInfo explodeInfo;\r
143 \r
144 /* Window class names */\r
145 char szAppName[] = "WinBoard";\r
146 char szConsoleName[] = "WBConsole";\r
147 \r
148 /* Title bar text */\r
149 char szTitle[] = "WinBoard";\r
150 char szConsoleTitle[] = "I C S Interaction";\r
151 \r
152 char *programName;\r
153 char *settingsFileName;\r
154 BOOLEAN saveSettingsOnExit;\r
155 char installDir[MSG_SIZ];\r
156 \r
157 BoardSize boardSize;\r
158 BOOLEAN chessProgram;\r
159 //static int boardX, boardY;\r
160 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
161 static int squareSize, lineGap, minorSize;\r
162 static int winW, winH;\r
163 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
164 static int logoHeight = 0;\r
165 static char messageText[MESSAGE_TEXT_MAX];\r
166 static int clockTimerEvent = 0;\r
167 static int loadGameTimerEvent = 0;\r
168 static int analysisTimerEvent = 0;\r
169 static DelayedEventCallback delayedTimerCallback;\r
170 static int delayedTimerEvent = 0;\r
171 static int buttonCount = 2;\r
172 char *icsTextMenuString;\r
173 char *icsNames;\r
174 char *firstChessProgramNames;\r
175 char *secondChessProgramNames;\r
176 \r
177 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
178 \r
179 #define PALETTESIZE 256\r
180 \r
181 HINSTANCE hInst;          /* current instance */\r
182 BOOLEAN alwaysOnTop = FALSE;\r
183 RECT boardRect;\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
185   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
186 HPALETTE hPal;\r
187 ColorClass currentColorClass;\r
188 \r
189 HWND hCommPort = NULL;    /* currently open comm port */\r
190 static HWND hwndPause;    /* pause button */\r
191 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
192 static HBRUSH lightSquareBrush, darkSquareBrush,\r
193   blackSquareBrush, /* [HGM] for band between board and holdings */\r
194   explodeBrush,     /* [HGM] atomic */\r
195   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];\r
198 static HPEN gridPen = NULL;\r
199 static HPEN highlightPen = NULL;\r
200 static HPEN premovePen = NULL;\r
201 static NPLOGPALETTE pLogPal;\r
202 static BOOL paletteChanged = FALSE;\r
203 static HICON iconWhite, iconBlack, iconCurrent;\r
204 static int doingSizing = FALSE;\r
205 static int lastSizing = 0;\r
206 static int prevStderrPort;\r
207 static HBITMAP userLogo;\r
208 \r
209 /* [AS] Support for background textures */\r
210 #define BACK_TEXTURE_MODE_DISABLED      0\r
211 #define BACK_TEXTURE_MODE_PLAIN         1\r
212 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
213 \r
214 static HBITMAP liteBackTexture = NULL;\r
215 static HBITMAP darkBackTexture = NULL;\r
216 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int backTextureSquareSize = 0;\r
219 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];\r
220 \r
221 #if __GNUC__ && !defined(_winmajor)\r
222 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
223 #else\r
224 #if defined(_winmajor)\r
225 #define oldDialog (_winmajor < 4)\r
226 #else\r
227 #define oldDialog 0\r
228 #endif\r
229 #endif\r
230 \r
231 char *defaultTextAttribs[] = \r
232 {\r
233   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
234   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
235   COLOR_NONE\r
236 };\r
237 \r
238 typedef struct {\r
239   char *name;\r
240   int squareSize;\r
241   int lineGap;\r
242   int smallLayout;\r
243   int tinyLayout;\r
244   int cliWidth, cliHeight;\r
245 } SizeInfo;\r
246 \r
247 SizeInfo sizeInfo[] = \r
248 {\r
249   { "tiny",     21, 0, 1, 1, 0, 0 },\r
250   { "teeny",    25, 1, 1, 1, 0, 0 },\r
251   { "dinky",    29, 1, 1, 1, 0, 0 },\r
252   { "petite",   33, 1, 1, 1, 0, 0 },\r
253   { "slim",     37, 2, 1, 0, 0, 0 },\r
254   { "small",    40, 2, 1, 0, 0, 0 },\r
255   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
256   { "middling", 49, 2, 0, 0, 0, 0 },\r
257   { "average",  54, 2, 0, 0, 0, 0 },\r
258   { "moderate", 58, 3, 0, 0, 0, 0 },\r
259   { "medium",   64, 3, 0, 0, 0, 0 },\r
260   { "bulky",    72, 3, 0, 0, 0, 0 },\r
261   { "large",    80, 3, 0, 0, 0, 0 },\r
262   { "big",      87, 3, 0, 0, 0, 0 },\r
263   { "huge",     95, 3, 0, 0, 0, 0 },\r
264   { "giant",    108, 3, 0, 0, 0, 0 },\r
265   { "colossal", 116, 4, 0, 0, 0, 0 },\r
266   { "titanic",  129, 4, 0, 0, 0, 0 },\r
267   { NULL, 0, 0, 0, 0, 0, 0 }\r
268 };\r
269 \r
270 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
271 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
272 {\r
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291 };\r
292 \r
293 MyFont *font[NUM_SIZES][NUM_FONTS];\r
294 \r
295 typedef struct {\r
296   char *label;\r
297   int id;\r
298   HWND hwnd;\r
299   WNDPROC wndproc;\r
300 } MyButtonDesc;\r
301 \r
302 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
303 #define N_BUTTONS 5\r
304 \r
305 MyButtonDesc buttonDesc[N_BUTTONS] =\r
306 {\r
307   {"<<", IDM_ToStart, NULL, NULL},\r
308   {"<", IDM_Backward, NULL, NULL},\r
309   {"P", IDM_Pause, NULL, NULL},\r
310   {">", IDM_Forward, NULL, NULL},\r
311   {">>", IDM_ToEnd, NULL, NULL},\r
312 };\r
313 \r
314 int tinyLayout = 0, smallLayout = 0;\r
315 #define MENU_BAR_ITEMS 7\r
316 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
317   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
318   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
319 };\r
320 \r
321 \r
322 MySound sounds[(int)NSoundClasses];\r
323 MyTextAttribs textAttribs[(int)NColorClasses];\r
324 \r
325 MyColorizeAttribs colorizeAttribs[] = {\r
326   { (COLORREF)0, 0, "Shout Text" },\r
327   { (COLORREF)0, 0, "SShout/CShout" },\r
328   { (COLORREF)0, 0, "Channel 1 Text" },\r
329   { (COLORREF)0, 0, "Channel Text" },\r
330   { (COLORREF)0, 0, "Kibitz Text" },\r
331   { (COLORREF)0, 0, "Tell Text" },\r
332   { (COLORREF)0, 0, "Challenge Text" },\r
333   { (COLORREF)0, 0, "Request Text" },\r
334   { (COLORREF)0, 0, "Seek Text" },\r
335   { (COLORREF)0, 0, "Normal Text" },\r
336   { (COLORREF)0, 0, "None" }\r
337 };\r
338 \r
339 \r
340 \r
341 static char *commentTitle;\r
342 static char *commentText;\r
343 static int commentIndex;\r
344 static Boolean editComment = FALSE;\r
345 \r
346 \r
347 char errorTitle[MSG_SIZ];\r
348 char errorMessage[2*MSG_SIZ];\r
349 HWND errorDialog = NULL;\r
350 BOOLEAN moveErrorMessageUp = FALSE;\r
351 BOOLEAN consoleEcho = TRUE;\r
352 CHARFORMAT consoleCF;\r
353 COLORREF consoleBackgroundColor;\r
354 \r
355 char *programVersion;\r
356 \r
357 #define CPReal 1\r
358 #define CPComm 2\r
359 #define CPSock 3\r
360 #define CPRcmd 4\r
361 typedef int CPKind;\r
362 \r
363 typedef struct {\r
364   CPKind kind;\r
365   HANDLE hProcess;\r
366   DWORD pid;\r
367   HANDLE hTo;\r
368   HANDLE hFrom;\r
369   SOCKET sock;\r
370   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
371 } ChildProc;\r
372 \r
373 #define INPUT_SOURCE_BUF_SIZE 4096\r
374 \r
375 typedef struct _InputSource {\r
376   CPKind kind;\r
377   HANDLE hFile;\r
378   SOCKET sock;\r
379   int lineByLine;\r
380   HANDLE hThread;\r
381   DWORD id;\r
382   char buf[INPUT_SOURCE_BUF_SIZE];\r
383   char *next;\r
384   DWORD count;\r
385   int error;\r
386   InputCallback func;\r
387   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
388   VOIDSTAR closure;\r
389 } InputSource;\r
390 \r
391 InputSource *consoleInputSource;\r
392 \r
393 DCB dcb;\r
394 \r
395 /* forward */\r
396 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
397 VOID ConsoleCreate();\r
398 LRESULT CALLBACK\r
399   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
400 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
401 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
402 VOID ParseCommSettings(char *arg, DCB *dcb);\r
403 LRESULT CALLBACK\r
404   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
406 void ParseIcsTextMenu(char *icsTextMenuString);\r
407 VOID PopUpMoveDialog(char firstchar);\r
408 VOID PopUpNameDialog(char firstchar);\r
409 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
410 \r
411 /* [AS] */\r
412 int NewGameFRC();\r
413 int GameListOptions();\r
414 \r
415 int dummy; // [HGM] for obsolete args\r
416 \r
417 HWND hwndMain = NULL;        /* root window*/\r
418 HWND hwndConsole = NULL;\r
419 HWND commentDialog = NULL;\r
420 HWND moveHistoryDialog = NULL;\r
421 HWND evalGraphDialog = NULL;\r
422 HWND engineOutputDialog = NULL;\r
423 HWND gameListDialog = NULL;\r
424 HWND editTagsDialog = NULL;\r
425 \r
426 int commentUp = FALSE;\r
427 \r
428 WindowPlacement wpMain;\r
429 WindowPlacement wpConsole;\r
430 WindowPlacement wpComment;\r
431 WindowPlacement wpMoveHistory;\r
432 WindowPlacement wpEvalGraph;\r
433 WindowPlacement wpEngineOutput;\r
434 WindowPlacement wpGameList;\r
435 WindowPlacement wpTags;\r
436 \r
437 VOID EngineOptionsPopup(); // [HGM] settings\r
438 \r
439 VOID GothicPopUp(char *title, VariantClass variant);\r
440 /*\r
441  * Setting "frozen" should disable all user input other than deleting\r
442  * the window.  We do this while engines are initializing themselves.\r
443  */\r
444 static int frozen = 0;\r
445 static int oldMenuItemState[MENU_BAR_ITEMS];\r
446 void FreezeUI()\r
447 {\r
448   HMENU hmenu;\r
449   int i;\r
450 \r
451   if (frozen) return;\r
452   frozen = 1;\r
453   hmenu = GetMenu(hwndMain);\r
454   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
455     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
456   }\r
457   DrawMenuBar(hwndMain);\r
458 }\r
459 \r
460 /* Undo a FreezeUI */\r
461 void ThawUI()\r
462 {\r
463   HMENU hmenu;\r
464   int i;\r
465 \r
466   if (!frozen) return;\r
467   frozen = 0;\r
468   hmenu = GetMenu(hwndMain);\r
469   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
470     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
471   }\r
472   DrawMenuBar(hwndMain);\r
473 }\r
474 \r
475 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
476 \r
477 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
478 #ifdef JAWS\r
479 #include "jaws.c"\r
480 #else\r
481 #define JAWS_INIT\r
482 #define JAWS_ARGS\r
483 #define JAWS_ALT_INTERCEPT\r
484 #define JAWS_KB_NAVIGATION\r
485 #define JAWS_MENU_ITEMS\r
486 #define JAWS_SILENCE\r
487 #define JAWS_REPLAY\r
488 #define JAWS_ACCEL\r
489 #define JAWS_COPYRIGHT\r
490 #define JAWS_DELETE(X) X\r
491 #define SAYMACHINEMOVE()\r
492 #define SAY(X)\r
493 #endif\r
494 \r
495 /*---------------------------------------------------------------------------*\\r
496  *\r
497  * WinMain\r
498  *\r
499 \*---------------------------------------------------------------------------*/\r
500 \r
501 int APIENTRY\r
502 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
503         LPSTR lpCmdLine, int nCmdShow)\r
504 {\r
505   MSG msg;\r
506   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
507 //  INITCOMMONCONTROLSEX ex;\r
508 \r
509   debugFP = stderr;\r
510 \r
511   LoadLibrary("RICHED32.DLL");\r
512   consoleCF.cbSize = sizeof(CHARFORMAT);\r
513 \r
514   if (!InitApplication(hInstance)) {\r
515     return (FALSE);\r
516   }\r
517   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
518     return (FALSE);\r
519   }\r
520 \r
521   JAWS_INIT\r
522 \r
523 //  InitCommonControlsEx(&ex);\r
524   InitCommonControls();\r
525 \r
526   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
527   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
528   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
529 \r
530   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
531 \r
532   while (GetMessage(&msg, /* message structure */\r
533                     NULL, /* handle of window receiving the message */\r
534                     0,    /* lowest message to examine */\r
535                     0))   /* highest message to examine */\r
536     {\r
537 \r
538       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
539         // [HGM] navigate: switch between all windows with tab\r
540         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
541         int i, currentElement = 0;\r
542 \r
543         // first determine what element of the chain we come from (if any)\r
544         if(appData.icsActive) {\r
545             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
546             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
547         }\r
548         if(engineOutputDialog && EngineOutputIsUp()) {\r
549             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
550             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
551         }\r
552         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
553             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
554         }\r
555         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
556         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
557         if(msg.hwnd == e1)                 currentElement = 2; else\r
558         if(msg.hwnd == e2)                 currentElement = 3; else\r
559         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
560         if(msg.hwnd == mh)                currentElement = 4; else\r
561         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
562         if(msg.hwnd == hText)  currentElement = 5; else\r
563         if(msg.hwnd == hInput) currentElement = 6; else\r
564         for (i = 0; i < N_BUTTONS; i++) {\r
565             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
566         }\r
567 \r
568         // determine where to go to\r
569         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
570           do {\r
571             currentElement = (currentElement + direction) % 7;\r
572             switch(currentElement) {\r
573                 case 0:\r
574                   h = hwndMain; break; // passing this case always makes the loop exit\r
575                 case 1:\r
576                   h = buttonDesc[0].hwnd; break; // could be NULL\r
577                 case 2:\r
578                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
579                   h = e1; break;\r
580                 case 3:\r
581                   if(!EngineOutputIsUp()) continue;\r
582                   h = e2; break;\r
583                 case 4:\r
584                   if(!MoveHistoryIsUp()) continue;\r
585                   h = mh; break;\r
586 //              case 6: // input to eval graph does not seem to get here!\r
587 //                if(!EvalGraphIsUp()) continue;\r
588 //                h = evalGraphDialog; break;\r
589                 case 5:\r
590                   if(!appData.icsActive) continue;\r
591                   SAY("display");\r
592                   h = hText; break;\r
593                 case 6:\r
594                   if(!appData.icsActive) continue;\r
595                   SAY("input");\r
596                   h = hInput; break;\r
597             }\r
598           } while(h == 0);\r
599 \r
600           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
601           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
602           SetFocus(h);\r
603 \r
604           continue; // this message now has been processed\r
605         }\r
606       }\r
607 \r
608       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
609           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
610           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
611           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
612           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
613           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
614           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
615           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
616           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
617           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
618         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
619         for(i=0; i<MAX_CHAT; i++) \r
620             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
621                 done = 1; break;\r
622         }\r
623         if(done) continue; // [HGM] chat: end patch\r
624         TranslateMessage(&msg); /* Translates virtual key codes */\r
625         DispatchMessage(&msg);  /* Dispatches message to window */\r
626       }\r
627     }\r
628 \r
629 \r
630   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
631 }\r
632 \r
633 /*---------------------------------------------------------------------------*\\r
634  *\r
635  * Initialization functions\r
636  *\r
637 \*---------------------------------------------------------------------------*/\r
638 \r
639 void\r
640 SetUserLogo()\r
641 {   // update user logo if necessary\r
642     static char oldUserName[MSG_SIZ], *curName;\r
643 \r
644     if(appData.autoLogo) {\r
645           curName = UserName();\r
646           if(strcmp(curName, oldUserName)) {\r
647                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
648                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
649                 strcpy(oldUserName, curName);\r
650           }\r
651     }\r
652 }\r
653 \r
654 BOOL\r
655 InitApplication(HINSTANCE hInstance)\r
656 {\r
657   WNDCLASS wc;\r
658 \r
659   /* Fill in window class structure with parameters that describe the */\r
660   /* main window. */\r
661 \r
662   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
663   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
664   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
665   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
666   wc.hInstance     = hInstance;         /* Owner of this class */\r
667   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
668   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
669   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
670   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
671   wc.lpszClassName = szAppName;                 /* Name to register as */\r
672 \r
673   /* Register the window class and return success/failure code. */\r
674   if (!RegisterClass(&wc)) return FALSE;\r
675 \r
676   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
677   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
678   wc.cbClsExtra    = 0;\r
679   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
680   wc.hInstance     = hInstance;\r
681   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
682   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
683   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
684   wc.lpszMenuName  = NULL;\r
685   wc.lpszClassName = szConsoleName;\r
686 \r
687   if (!RegisterClass(&wc)) return FALSE;\r
688   return TRUE;\r
689 }\r
690 \r
691 \r
692 /* Set by InitInstance, used by EnsureOnScreen */\r
693 int screenHeight, screenWidth;\r
694 \r
695 void\r
696 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
697 {\r
698 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
699   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
700   if (*x > screenWidth - 32) *x = 0;\r
701   if (*y > screenHeight - 32) *y = 0;\r
702   if (*x < minX) *x = minX;\r
703   if (*y < minY) *y = minY;\r
704 }\r
705 \r
706 BOOL\r
707 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
708 {\r
709   HWND hwnd; /* Main window handle. */\r
710   int ibs;\r
711   WINDOWPLACEMENT wp;\r
712   char *filepart;\r
713 \r
714   hInst = hInstance;    /* Store instance handle in our global variable */\r
715 \r
716   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
717     *filepart = NULLCHAR;\r
718   } else {\r
719     GetCurrentDirectory(MSG_SIZ, installDir);\r
720   }\r
721   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
722   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
723   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
724   if (appData.debugMode) {\r
725     debugFP = fopen(appData.nameOfDebugFile, "w");\r
726     setbuf(debugFP, NULL);\r
727   }\r
728 \r
729   InitBackEnd1();\r
730 \r
731 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
732 //  InitEngineUCI( installDir, &second );\r
733 \r
734   /* Create a main window for this application instance. */\r
735   hwnd = CreateWindow(szAppName, szTitle,\r
736                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
737                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
738                       NULL, NULL, hInstance, NULL);\r
739   hwndMain = hwnd;\r
740 \r
741   /* If window could not be created, return "failure" */\r
742   if (!hwnd) {\r
743     return (FALSE);\r
744   }\r
745 \r
746   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
747   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
748       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
749 \r
750       if (first.programLogo == NULL && appData.debugMode) {\r
751           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
752       }\r
753   } else if(appData.autoLogo) {\r
754       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
755         char buf[MSG_SIZ];\r
756         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
757         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
758       }\r
759   }\r
760 \r
761   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
762       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
763 \r
764       if (second.programLogo == NULL && appData.debugMode) {\r
765           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
766       }\r
767   } else if(appData.autoLogo) {\r
768       char buf[MSG_SIZ];\r
769       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
770         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
771         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
772       } else\r
773       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
774         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
775         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
776       }\r
777   }\r
778 \r
779   SetUserLogo();\r
780 \r
781   iconWhite = LoadIcon(hInstance, "icon_white");\r
782   iconBlack = LoadIcon(hInstance, "icon_black");\r
783   iconCurrent = iconWhite;\r
784   InitDrawingColors();\r
785   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
786   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
787   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
788     /* Compute window size for each board size, and use the largest\r
789        size that fits on this screen as the default. */\r
790     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
791     if (boardSize == (BoardSize)-1 &&\r
792         winH <= screenHeight\r
793            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
794         && winW <= screenWidth) {\r
795       boardSize = (BoardSize)ibs;\r
796     }\r
797   }\r
798 \r
799   InitDrawingSizes(boardSize, 0);\r
800   InitMenuChecks();\r
801   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
802 \r
803   /* [AS] Load textures if specified */\r
804   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
805   \r
806   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
807       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
808       liteBackTextureMode = appData.liteBackTextureMode;\r
809 \r
810       if (liteBackTexture == NULL && appData.debugMode) {\r
811           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
812       }\r
813   }\r
814   \r
815   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
816       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
817       darkBackTextureMode = appData.darkBackTextureMode;\r
818 \r
819       if (darkBackTexture == NULL && appData.debugMode) {\r
820           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
821       }\r
822   }\r
823 \r
824   mysrandom( (unsigned) time(NULL) );\r
825 \r
826   /* [AS] Restore layout */\r
827   if( wpMoveHistory.visible ) {\r
828       MoveHistoryPopUp();\r
829   }\r
830 \r
831   if( wpEvalGraph.visible ) {\r
832       EvalGraphPopUp();\r
833   }\r
834 \r
835   if( wpEngineOutput.visible ) {\r
836       EngineOutputPopUp();\r
837   }\r
838 \r
839   InitBackEnd2();\r
840 \r
841   /* Make the window visible; update its client area; and return "success" */\r
842   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
843   wp.length = sizeof(WINDOWPLACEMENT);\r
844   wp.flags = 0;\r
845   wp.showCmd = nCmdShow;\r
846   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
847   wp.rcNormalPosition.left = wpMain.x;\r
848   wp.rcNormalPosition.right = wpMain.x + wpMain.width;\r
849   wp.rcNormalPosition.top = wpMain.y;\r
850   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
851   SetWindowPlacement(hwndMain, &wp);\r
852 \r
853   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
854                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
855 \r
856   if (hwndConsole) {\r
857 #if AOT_CONSOLE\r
858     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
859                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
860 #endif\r
861     ShowWindow(hwndConsole, nCmdShow);\r
862   }\r
863   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
864   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
865 \r
866   return TRUE;\r
867 \r
868 }\r
869 \r
870 \r
871 typedef enum {\r
872   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
873   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
874   ArgSettingsFilename,\r
875   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
876 } ArgType;\r
877 \r
878 typedef void *ArgIniType;\r
879 #define INVALID (ArgIniType) 6915 /* Some number unlikely to be needed as default for anything */\r
880 \r
881 typedef struct {\r
882   char *argName;\r
883   ArgType argType;\r
884   /***\r
885   union {\r
886     String *pString;       // ArgString\r
887     int *pInt;             // ArgInt\r
888     float *pFloat;         // ArgFloat\r
889     Boolean *pBoolean;     // ArgBoolean\r
890     COLORREF *pColor;      // ArgColor\r
891     ColorClass cc;         // ArgAttribs\r
892     String *pFilename;     // ArgFilename\r
893     BoardSize *pBoardSize; // ArgBoardSize\r
894     int whichFont;         // ArgFont\r
895     DCB *pDCB;             // ArgCommSettings\r
896     String *pFilename;     // ArgSettingsFilename\r
897   } argLoc;\r
898   ***/\r
899   LPVOID argLoc;\r
900   BOOL save;\r
901   ArgIniType defaultValue;\r
902 } ArgDescriptor;\r
903 \r
904 int junk;\r
905 \r
906 ArgDescriptor argDescriptors[] = {\r
907   /* positional arguments */\r
908   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE, INVALID },\r
909   { "", ArgNone, NULL, FALSE, INVALID },\r
910   /* keyword arguments */\r
911   JAWS_ARGS\r
912   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE, INVALID },\r
913   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE, INVALID },\r
914   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE, INVALID },\r
915   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE, INVALID },\r
916   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE, INVALID },\r
917   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE, INVALID },\r
918   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE, INVALID },\r
919   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE, INVALID },\r
920   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE, INVALID },\r
921   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE, INVALID },\r
922   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE, INVALID },\r
923   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE, INVALID },\r
924   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE, (ArgIniType) MOVES_PER_SESSION },\r
925   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE, INVALID },\r
926   { "initString", ArgString, (LPVOID) &appData.initString, FALSE, INVALID },\r
927   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE, (ArgIniType) INIT_STRING },\r
928   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE, (ArgIniType) INIT_STRING },\r
929   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
930     FALSE, (ArgIniType) COMPUTER_STRING },\r
931   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
932     FALSE, (ArgIniType) COMPUTER_STRING },\r
933   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
934     FALSE, (ArgIniType) FIRST_CHESS_PROGRAM },\r
935   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE, INVALID },\r
936   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
937     FALSE, (ArgIniType) SECOND_CHESS_PROGRAM },\r
938   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE, INVALID },\r
939   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE, FALSE },\r
940   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE, FALSE },\r
941   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE, INVALID },\r
942   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE, INVALID },\r
943   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE, FALSE },\r
944   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE, INVALID },\r
945   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE, INVALID },\r
946   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE, INVALID },\r
947   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE, (ArgIniType) FIRST_HOST },\r
948   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE, INVALID },\r
949   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE, (ArgIniType) SECOND_HOST },\r
950   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE, INVALID },\r
951   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE, (ArgIniType) FIRST_DIRECTORY },\r
952   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE, INVALID },\r
953   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE, (ArgIniType) SECOND_DIRECTORY },\r
954   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE, INVALID },\r
955   /*!!bitmapDirectory?*/\r
956   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE, (ArgIniType) REMOTE_SHELL },\r
957   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE, INVALID },\r
958   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE, INVALID },\r
959   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE, INVALID },\r
960   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE, INVALID },\r
961   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE, INVALID },\r
962   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE, (ArgIniType) TIME_CONTROL },\r
963   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE, INVALID },\r
964   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE, (ArgIniType) TIME_INCREMENT },\r
965   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE, INVALID },\r
966   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE, INVALID },\r
967   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE, (ArgIniType) FALSE },\r
968   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE, INVALID },\r
969   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE, INVALID },\r
970   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE, (ArgIniType) "" },\r
971   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE, INVALID },\r
972   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE, (ArgIniType) ICS_PORT },\r
973   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE, INVALID },\r
974   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE, (ArgIniType) ICS_COMM_PORT },\r
975   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE, INVALID },\r
976   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE, INVALID },\r
977   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE, INVALID },\r
978   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE, (ArgIniType) ICS_LOGON },\r
979   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE, INVALID },\r
980   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
981   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
982   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
983   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
984   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE, (ArgIniType) TELNET_PROGRAM },\r
985   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE, (ArgIniType) "" },\r
986   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE, (ArgIniType) "" },\r
987   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE, (ArgIniType) "" },\r
988   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE, INVALID },\r
989   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE, (ArgIniType) 0 },\r
990   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE, INVALID },\r
991   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE, (ArgIniType) "" },\r
992   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE, INVALID },\r
993   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE, (ArgIniType) FALSE },\r
994   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE, INVALID },\r
995   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE, INVALID },\r
996   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE, INVALID },\r
997   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE, (ArgIniType) "" },\r
998   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE, INVALID },\r
999   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE, (ArgIniType) 1 },\r
1000   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE, INVALID },\r
1001   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE, (ArgIniType) "" },\r
1002   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE, INVALID },\r
1003   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE, (ArgIniType) FALSE },\r
1004   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE, INVALID },\r
1005   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE, INVALID },\r
1006   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE, INVALID },\r
1007   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE, (ArgIniType) 0 },\r
1008   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE, INVALID },\r
1009   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE, (ArgIniType) FALSE },\r
1010   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE, INVALID },\r
1011   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE, INVALID },\r
1012   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE, INVALID },\r
1013   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE, (ArgIniType) FALSE },\r
1014   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE, INVALID },\r
1015   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE, INVALID },\r
1016   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE, INVALID },\r
1017   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE, (ArgIniType) TRUE },\r
1018   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE, INVALID },\r
1019   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE, INVALID },\r
1020   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE, INVALID },\r
1021   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE, (ArgIniType) "" },\r
1022   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE, INVALID },\r
1023   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE, (ArgIniType) 0 },\r
1024   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE, INVALID },\r
1025   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE, (ArgIniType) FALSE },\r
1026   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE, INVALID },\r
1027   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE, INVALID },\r
1028   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE, INVALID },\r
1029   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE, (ArgIniType) FALSE },\r
1030   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE, INVALID },\r
1031   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE, INVALID },\r
1032   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE, INVALID },\r
1033   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE, (ArgIniType) TRUE },\r
1034   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE, INVALID },\r
1035   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE, INVALID },\r
1036   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE, INVALID },\r
1037   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE, (ArgIniType) TRUE },\r
1038   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE, INVALID },\r
1039   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE, INVALID },\r
1040   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE, INVALID },\r
1041   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE, (ArgIniType) TRUE },\r
1042   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE, INVALID },\r
1043   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE, INVALID },\r
1044   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE, INVALID },\r
1045   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE, (ArgIniType) FALSE },\r
1046   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE, INVALID },\r
1047   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE, INVALID },\r
1048   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE, INVALID },\r
1049   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1050     FALSE, INVALID }, /* only so that old WinBoard.ini files from betas can be read */\r
1051   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE, INVALID },\r
1052   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE, INVALID },\r
1053   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE, INVALID },\r
1054   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE, INVALID },\r
1055   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE, INVALID },\r
1056   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE, INVALID },\r
1057   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE, INVALID }, /* [AS] */\r
1058   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1059     TRUE, (ArgIniType) -1 }, /* must come after all fonts */\r
1060   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE, INVALID },\r
1061   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1062     FALSE, (ArgIniType) TRUE }, /* historical; kept only so old winboard.ini files will parse */\r
1063   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE, INVALID },\r
1064   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE, INVALID },\r
1065   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE, INVALID },\r
1066   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE, INVALID },\r
1067   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE, (ArgIniType) FALSE },\r
1068   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE, INVALID },\r
1069   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE, INVALID },\r
1070   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE, INVALID },\r
1071   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE, (ArgIniType) FALSE },\r
1072   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE, INVALID },\r
1073   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE, INVALID },\r
1074   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE, INVALID },\r
1075   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE, (ArgIniType) FALSE },\r
1076   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE, INVALID },\r
1077   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE, INVALID },\r
1078   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE, INVALID },\r
1079   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE, (ArgIniType) FALSE },\r
1080   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE, INVALID },\r
1081   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE, INVALID },\r
1082   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE, INVALID },\r
1083   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE, (ArgIniType) TRUE },\r
1084   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE, INVALID },\r
1085   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE, INVALID },\r
1086   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE, INVALID },\r
1087   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE, (ArgIniType) TRUE },\r
1088   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE, INVALID },\r
1089   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE, INVALID },\r
1090   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE, INVALID },\r
1091   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE, (ArgIniType) FALSE },\r
1092   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE, INVALID },\r
1093   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE, INVALID },\r
1094   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE, INVALID },\r
1095   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE, (ArgIniType) FALSE },\r
1096   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE, INVALID },\r
1097   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE, INVALID },\r
1098   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE, INVALID },\r
1099   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE, (ArgIniType) FALSE },\r
1100   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE, INVALID },\r
1101   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE, INVALID },\r
1102   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE, INVALID },\r
1103   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE, (ArgIniType) TRUE },\r
1104   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE, INVALID },\r
1105   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE, INVALID },\r
1106   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE, INVALID },\r
1107   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE, (ArgIniType) TRUE },\r
1108   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE, INVALID },\r
1109   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE, INVALID },\r
1110   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE, INVALID },\r
1111   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE, (ArgIniType) TRUE },\r
1112   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE, INVALID },\r
1113   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE, INVALID },\r
1114   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE, INVALID },\r
1115   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE, (ArgIniType) FALSE },\r
1116   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE, INVALID },\r
1117   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE, INVALID },\r
1118   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE, INVALID },\r
1119   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE, (ArgIniType) "" },\r
1120   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE, (ArgIniType) FALSE },\r
1121   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE, INVALID },\r
1122   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE, INVALID },\r
1123   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE, INVALID },\r
1124   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE, (ArgIniType) "" },\r
1125   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE, (ArgIniType) TRUE},\r
1126   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1127   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1128   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1129   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE, (ArgIniType) 5000},\r
1130   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE, (ArgIniType) TRUE},\r
1131   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE, INVALID },\r
1132   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE, INVALID },\r
1133   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE, INVALID },\r
1134   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE, (ArgIniType) TRUE },\r
1135   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE, INVALID },\r
1136   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE, INVALID },\r
1137   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE, INVALID },\r
1138   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE, (ArgIniType) 10 },\r
1139   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE, (ArgIniType) TRUE },\r
1140   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE, INVALID },\r
1141   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE, INVALID },\r
1142   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE, INVALID },\r
1143   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE, (ArgIniType) FALSE },\r
1144   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE, INVALID },\r
1145   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE, INVALID },\r
1146   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE, INVALID },\r
1147   { "highlightLastMove", ArgBoolean,\r
1148     (LPVOID) &appData.highlightLastMove, TRUE, (ArgIniType) TRUE },\r
1149   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE, INVALID },\r
1150   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE, INVALID },\r
1151   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE, INVALID },\r
1152   { "highlightDragging", ArgBoolean,\r
1153     (LPVOID) &appData.highlightDragging, TRUE, INVALID },\r
1154   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE, INVALID },\r
1155   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE, INVALID },\r
1156   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE, INVALID },\r
1157   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE, (ArgIniType) TRUE },\r
1158   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE, INVALID },\r
1159   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE, INVALID },\r
1160   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE, INVALID },\r
1161   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE, INVALID },\r
1162   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE, INVALID },\r
1163   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE, INVALID },\r
1164   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE, INVALID },\r
1165   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE, INVALID },\r
1166   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE, INVALID },\r
1167   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE, INVALID },\r
1168   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE, INVALID },\r
1169   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE, INVALID },\r
1170   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE, INVALID },\r
1171   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE, INVALID },\r
1172   { "soundShout", ArgFilename,\r
1173     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE, INVALID },\r
1174   { "soundSShout", ArgFilename,\r
1175     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE, INVALID },\r
1176   { "soundChannel1", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE, INVALID },\r
1178   { "soundChannel", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE, INVALID },\r
1180   { "soundKibitz", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE, INVALID },\r
1182   { "soundTell", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE, INVALID },\r
1184   { "soundChallenge", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE, INVALID },\r
1186   { "soundRequest", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE, INVALID },\r
1188   { "soundSeek", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE, INVALID },\r
1190   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE, INVALID },\r
1191   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE, INVALID },\r
1192   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name, TRUE, INVALID },\r
1193   { "soundIcsLoss", ArgFilename, \r
1194     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE, INVALID },\r
1195   { "soundIcsDraw", ArgFilename, \r
1196     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE, INVALID },\r
1197   { "soundIcsUnfinished", ArgFilename, \r
1198     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE, INVALID },\r
1199   { "soundIcsAlarm", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE, INVALID },\r
1201   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE, (ArgIniType) TRUE },\r
1202   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE, INVALID },\r
1203   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE, INVALID },\r
1204   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE, INVALID },\r
1205   { "reuseChessPrograms", ArgBoolean,\r
1206     (LPVOID) &appData.reuseFirst, FALSE, INVALID }, /* backward compat only */\r
1207   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE, (ArgIniType) TRUE },\r
1208   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE, INVALID },\r
1209   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE, INVALID },\r
1210   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE, INVALID },\r
1211   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE, INVALID },\r
1212   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE, (ArgIniType) SETTINGS_FILE },\r
1213   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE, INVALID },\r
1214   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE, (ArgIniType) TRUE },\r
1215   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE, (ArgIniType) FALSE },\r
1216   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE, INVALID },\r
1217   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE, INVALID },\r
1218   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE, INVALID },\r
1219   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE, (ArgIniType) ICS_TEXT_MENU_DEFAULT },\r
1220   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE, (ArgIniType) ICS_NAMES },\r
1221   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1222     TRUE, (ArgIniType) FCP_NAMES },\r
1223   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1224     TRUE, (ArgIniType) SCP_NAMES },\r
1225   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE, (ArgIniType) "" },\r
1226   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE, INVALID },\r
1227   { "variant", ArgString, (LPVOID) &appData.variant, FALSE, (ArgIniType) "normal" },\r
1228   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE, (ArgIniType) PROTOVER },\r
1229   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE, (ArgIniType) PROTOVER },\r
1230   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE, (ArgIniType) TRUE },\r
1231   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE, INVALID },\r
1232   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE, INVALID },\r
1233   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE, INVALID },\r
1234 \r
1235   /* [AS] New features */\r
1236   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE, (ArgIniType) FALSE },\r
1237   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE, (ArgIniType) FALSE },\r
1238   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE, (ArgIniType) FALSE },\r
1239   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE, (ArgIniType) FALSE },\r
1240   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE, (ArgIniType) "" },\r
1241   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE, (ArgIniType) "" },\r
1242   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE, (ArgIniType) BACK_TEXTURE_MODE_PLAIN },\r
1243   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE, (ArgIniType) BACK_TEXTURE_MODE_PLAIN },\r
1244   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE, (ArgIniType) "" },\r
1245   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE, (ArgIniType) "" },\r
1246   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE, (ArgIniType) 0 },\r
1247   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE, (ArgIniType) 0 },\r
1248   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE, (ArgIniType) 0 },\r
1249   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE, (ArgIniType) 0 },\r
1250   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE, (ArgIniType) 80 },\r
1251   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE, (ArgIniType) 1 },\r
1252   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE, (ArgIniType) 0 },\r
1253   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE, (ArgIniType) 0 },\r
1254   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE, (ArgIniType) 0 },\r
1255   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE, (ArgIniType) "winboard.debug" },\r
1256   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE, INVALID },\r
1257   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE, (ArgIniType) "Computer Chess Game" },\r
1258   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE, (ArgIniType) -1 },\r
1259   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE, (ArgIniType) GLT_DEFAULT_TAGS },\r
1260   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE, (ArgIniType) TRUE },\r
1261   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE, (ArgIniType) TRUE },\r
1262   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE, INVALID },\r
1263   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE, INVALID },\r
1264   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE, (ArgIniType) FALSE },\r
1265   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE, INVALID },\r
1266   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE, (ArgIniType) TRUE },\r
1267   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE, (ArgIniType) TRUE },\r
1268   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE, (ArgIniType) TRUE },\r
1269   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE, (ArgIniType) TRUE },\r
1270   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE, (ArgIniType) FALSE },\r
1271   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE, INVALID },\r
1272   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE, (ArgIniType) FALSE },\r
1273   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE, INVALID },\r
1274   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE, (ArgIniType) TRUE },\r
1275   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE, INVALID },\r
1276   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE, INVALID },\r
1277   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE, (ArgIniType) TRUE },\r
1278   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE, INVALID },\r
1279   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE, INVALID },\r
1280   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE, (ArgIniType) "" },\r
1281   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE, (ArgIniType) FALSE },\r
1282   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE, (ArgIniType) "" },\r
1283   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE, (ArgIniType) 64 }, \r
1284   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE, (ArgIniType) 4 },\r
1285   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE, (ArgIniType) "c:\\egtb" },\r
1286 \r
1287   /* [HGM] board-size, adjudication and misc. options */\r
1288   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE, (ArgIniType) -1 },\r
1289   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE, (ArgIniType) -1 },\r
1290   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE, (ArgIniType) -1 },\r
1291   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE, (ArgIniType) 10000 },\r
1292   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE, INVALID },\r
1293   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE, (ArgIniType) FALSE },\r
1294   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE, (ArgIniType) FALSE },\r
1295   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE, (ArgIniType) FALSE },\r
1296   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE, (ArgIniType) FALSE },\r
1297   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE, (ArgIniType) FALSE },\r
1298   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE, (ArgIniType) FALSE },\r
1299   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE, (ArgIniType) FALSE },\r
1300   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE, (ArgIniType) FALSE },\r
1301   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE, (ArgIniType) FALSE },\r
1302   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE, (ArgIniType) 51 },\r
1303   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE, (ArgIniType) 6 },\r
1304   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE, INVALID },\r
1305   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE, (ArgIniType) 1 },\r
1306   { "userName", ArgString, (LPVOID) &appData.userName, FALSE, INVALID },\r
1307   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE, INVALID },\r
1308   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE, INVALID },\r
1309   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE, (ArgIniType) 1 },\r
1310   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE, (ArgIniType) "" },\r
1311   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE, INVALID },\r
1312   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE, INVALID },\r
1313   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE, INVALID },\r
1314   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE, INVALID },\r
1315   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE, (ArgIniType) "" },\r
1316   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE, (ArgIniType) "" },\r
1317   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE, (ArgIniType) "" },\r
1318   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE, (ArgIniType) "" },\r
1319   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE, INVALID },\r
1320   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE, INVALID },\r
1321   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE, INVALID },\r
1322 \r
1323 #ifdef ZIPPY\r
1324   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE, (ArgIniType) ZIPPY_TALK },\r
1325   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE, INVALID },\r
1326   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE, INVALID },\r
1327   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE, INVALID },\r
1328   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE, (ArgIniType) ZIPPY_PLAY },\r
1329   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE, INVALID },\r
1330   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE, INVALID },\r
1331   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE, INVALID },\r
1332   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE, (ArgIniType) ZIPPY_LINES },\r
1333   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE, (ArgIniType) ZIPPY_PINHEAD },\r
1334   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE, (ArgIniType) ZIPPY_PASSWORD },\r
1335   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE, (ArgIniType) ZIPPY_PASSWORD2 },\r
1336   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1337     FALSE, (ArgIniType) ZIPPY_WRONG_PASSWORD },\r
1338   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE, (ArgIniType) ZIPPY_ACCEPT_ONLY },\r
1339   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE, (ArgIniType) ZIPPY_USE_I },\r
1340   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE, INVALID },\r
1341   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE, INVALID },\r
1342   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE, INVALID },\r
1343   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE, (ArgIniType) ZIPPY_BUGHOUSE },\r
1344   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1345     FALSE, (ArgIniType) ZIPPY_NOPLAY_CRAFTY },\r
1346   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE, INVALID },\r
1347   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE, INVALID },\r
1348   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE, INVALID },\r
1349   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE, (ArgIniType) ZIPPY_GAME_END },\r
1350   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE, (ArgIniType) ZIPPY_GAME_START },\r
1351   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE, (ArgIniType) ZIPPY_ADJOURN },\r
1352   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE, INVALID },\r
1353   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE, INVALID },\r
1354   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE, INVALID },\r
1355   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE, (ArgIniType) ZIPPY_ABORT },\r
1356   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE, INVALID },\r
1357   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE, INVALID },\r
1358   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE, INVALID },\r
1359   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE, (ArgIniType) ZIPPY_VARIANTS },\r
1360   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE, (ArgIniType) ZIPPY_MAX_GAMES},\r
1361   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE, (ArgIniType) ZIPPY_REPLAY_TIMEOUT },\r
1362   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE, INVALID },\r
1363   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1364   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE, INVALID },\r
1365 #endif\r
1366   /* [HGM] options for broadcasting and time odds */\r
1367   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE, (ArgIniType) NULL },\r
1368   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE, (ArgIniType) FALSE },\r
1369   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE, (ArgIniType) 15 },\r
1370   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE, (ArgIniType) 1 },\r
1371   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE, (ArgIniType) 1 },\r
1372   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE, INVALID },\r
1373   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE, (ArgIniType) 1 },\r
1374   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE, (ArgIniType) 1 },\r
1375   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE, (ArgIniType) -1 },\r
1376   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE, (ArgIniType) -1 },\r
1377   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE, INVALID },\r
1378   { "keepLineBreaksICS", ArgBoolean, (LPVOID) &appData.noJoin, TRUE, INVALID },\r
1379   { "wrapContinuationSequence", ArgString, (LPVOID) &appData.wrapContSeq, FALSE, INVALID },\r
1380   { "useInternalWrap", ArgTrue, (LPVOID) &appData.useInternalWrap, FALSE, INVALID }, /* noJoin usurps this if set */\r
1381   \r
1382   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1383   { "minX", ArgZ, (LPVOID) &minX, FALSE, INVALID }, // [HGM] placement: to make suer auxialary windows can be placed\r
1384   { "minY", ArgZ, (LPVOID) &minY, FALSE, INVALID },\r
1385   { "winWidth",  ArgInt, (LPVOID) &wpMain.width,  TRUE, INVALID }, // [HGM] placement: dummies to remember right & bottom\r
1386   { "winHeight", ArgInt, (LPVOID) &wpMain.height, TRUE, INVALID }, //       for attaching auxiliary windows to them\r
1387   { "x", ArgInt, (LPVOID) &wpMain.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1388   { "y", ArgInt, (LPVOID) &wpMain.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1389   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1390   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1391   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1392   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1393   { "analysisX", ArgX,   (LPVOID) &dummy, FALSE, INVALID }, // [HGM] placement: analysis window no longer exists\r
1394   { "analysisY", ArgY,   (LPVOID) &dummy, FALSE, INVALID }, //       provided for compatibility with old ini files\r
1395   { "analysisW", ArgInt, (LPVOID) &dummy, FALSE, INVALID },\r
1396   { "analysisH", ArgInt, (LPVOID) &dummy, FALSE, INVALID },\r
1397   { "commentX", ArgX,   (LPVOID) &wpComment.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1398   { "commentY", ArgY,   (LPVOID) &wpComment.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1399   { "commentW", ArgInt, (LPVOID) &wpComment.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1400   { "commentH", ArgInt, (LPVOID) &wpComment.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1401   { "tagsX", ArgX,   (LPVOID) &wpTags.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1402   { "tagsY", ArgY,   (LPVOID) &wpTags.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1403   { "tagsW", ArgInt, (LPVOID) &wpTags.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1404   { "tagsH", ArgInt, (LPVOID) &wpTags.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1405   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1406   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1407   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1408   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1409   /* [AS] Layout stuff */\r
1410   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE, (ArgIniType) TRUE },\r
1411   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1412   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1413   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1414   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1415 \r
1416   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE, (ArgIniType) TRUE },\r
1417   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1418   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1419   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1420   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1421 \r
1422   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE, (ArgIniType) TRUE },\r
1423   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1424   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1425   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1426   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1427 \r
1428   { NULL, ArgNone, NULL, FALSE, INVALID }\r
1429 };\r
1430 \r
1431 \r
1432 /* Kludge for indirection files on command line */\r
1433 char* lastIndirectionFilename;\r
1434 ArgDescriptor argDescriptorIndirection =\r
1435 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1436 \r
1437 \r
1438 VOID\r
1439 ExitArgError(char *msg, char *badArg)\r
1440 {\r
1441   char buf[MSG_SIZ];\r
1442 \r
1443   sprintf(buf, "%s %s", msg, badArg);\r
1444   DisplayFatalError(buf, 0, 2);\r
1445   exit(2);\r
1446 }\r
1447 \r
1448 /* Command line font name parser.  NULL name means do nothing.\r
1449    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1450    For backward compatibility, syntax without the colon is also\r
1451    accepted, but font names with digits in them won't work in that case.\r
1452 */\r
1453 VOID\r
1454 ParseFontName(char *name, MyFontParams *mfp)\r
1455 {\r
1456   char *p, *q;\r
1457   if (name == NULL) return;\r
1458   p = name;\r
1459   q = strchr(p, ':');\r
1460   if (q) {\r
1461     if (q - p >= sizeof(mfp->faceName))\r
1462       ExitArgError("Font name too long:", name);\r
1463     memcpy(mfp->faceName, p, q - p);\r
1464     mfp->faceName[q - p] = NULLCHAR;\r
1465     p = q + 1;\r
1466   } else {\r
1467     q = mfp->faceName;\r
1468     while (*p && !isdigit(*p)) {\r
1469       *q++ = *p++;\r
1470       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1471         ExitArgError("Font name too long:", name);\r
1472     }\r
1473     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1474     *q = NULLCHAR;\r
1475   }\r
1476   if (!*p) ExitArgError("Font point size missing:", name);\r
1477   mfp->pointSize = (float) atof(p);\r
1478   mfp->bold = (strchr(p, 'b') != NULL);\r
1479   mfp->italic = (strchr(p, 'i') != NULL);\r
1480   mfp->underline = (strchr(p, 'u') != NULL);\r
1481   mfp->strikeout = (strchr(p, 's') != NULL);\r
1482   mfp->charset = DEFAULT_CHARSET;\r
1483   q = strchr(p, 'c');\r
1484   if (q)\r
1485     mfp->charset = (BYTE) atoi(q+1);\r
1486 }\r
1487 \r
1488 /* Color name parser.\r
1489    X version accepts X color names, but this one\r
1490    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1491 COLORREF\r
1492 ParseColorName(char *name)\r
1493 {\r
1494   int red, green, blue, count;\r
1495   char buf[MSG_SIZ];\r
1496 \r
1497   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1498   if (count != 3) {\r
1499     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1500       &red, &green, &blue);\r
1501   }\r
1502   if (count != 3) {\r
1503     sprintf(buf, "Can't parse color name %s", name);\r
1504     DisplayError(buf, 0);\r
1505     return RGB(0, 0, 0);\r
1506   }\r
1507   return PALETTERGB(red, green, blue);\r
1508 }\r
1509 \r
1510 \r
1511 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1512 {\r
1513   char *e = argValue;\r
1514   int eff = 0;\r
1515 \r
1516   while (*e) {\r
1517     if (*e == 'b')      eff |= CFE_BOLD;\r
1518     else if (*e == 'i') eff |= CFE_ITALIC;\r
1519     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1520     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1521     else if (*e == '#' || isdigit(*e)) break;\r
1522     e++;\r
1523   }\r
1524   *effects = eff;\r
1525   *color   = ParseColorName(e);\r
1526 }\r
1527 \r
1528 \r
1529 BoardSize\r
1530 ParseBoardSize(char *name)\r
1531 {\r
1532   BoardSize bs = SizeTiny;\r
1533   while (sizeInfo[bs].name != NULL) {\r
1534     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1535     bs++;\r
1536   }\r
1537   ExitArgError("Unrecognized board size value", name);\r
1538   return bs; /* not reached */\r
1539 }\r
1540 \r
1541 \r
1542 char\r
1543 StringGet(void *getClosure)\r
1544 {\r
1545   char **p = (char **) getClosure;\r
1546   return *((*p)++);\r
1547 }\r
1548 \r
1549 char\r
1550 FileGet(void *getClosure)\r
1551 {\r
1552   int c;\r
1553   FILE* f = (FILE*) getClosure;\r
1554 \r
1555   c = getc(f);\r
1556   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1557   if (c == EOF)\r
1558     return NULLCHAR;\r
1559   else\r
1560     return (char) c;\r
1561 }\r
1562 \r
1563 /* Parse settings file named "name". If file found, return the\r
1564    full name in fullname and return TRUE; else return FALSE */\r
1565 BOOLEAN\r
1566 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1567 {\r
1568   char *dummy;\r
1569   FILE *f;\r
1570   int ok; char buf[MSG_SIZ];\r
1571 \r
1572   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1573   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1574     sprintf(buf, "%s.ini", name);\r
1575     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1576   }\r
1577   if (ok) {\r
1578     f = fopen(fullname, "r");\r
1579     if (f != NULL) {\r
1580       ParseArgs(FileGet, f);\r
1581       fclose(f);\r
1582       return TRUE;\r
1583     }\r
1584   }\r
1585   return FALSE;\r
1586 }\r
1587 \r
1588 VOID\r
1589 ParseArgs(GetFunc get, void *cl)\r
1590 {\r
1591   char argName[ARG_MAX];\r
1592   char argValue[ARG_MAX];\r
1593   ArgDescriptor *ad;\r
1594   char start;\r
1595   char *q;\r
1596   int i, octval;\r
1597   char ch;\r
1598   int posarg = 0;\r
1599 \r
1600   ch = get(cl);\r
1601   for (;;) {\r
1602     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1603     if (ch == NULLCHAR) break;\r
1604     if (ch == ';') {\r
1605       /* Comment to end of line */\r
1606       ch = get(cl);\r
1607       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1608       continue;\r
1609     } else if (ch == '/' || ch == '-') {\r
1610       /* Switch */\r
1611       q = argName;\r
1612       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1613              ch != '\n' && ch != '\t') {\r
1614         *q++ = ch;\r
1615         ch = get(cl);\r
1616       }\r
1617       *q = NULLCHAR;\r
1618 \r
1619       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1620         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1621 \r
1622       if (ad->argName == NULL)\r
1623         ExitArgError("Unrecognized argument", argName);\r
1624 \r
1625     } else if (ch == '@') {\r
1626       /* Indirection file */\r
1627       ad = &argDescriptorIndirection;\r
1628       ch = get(cl);\r
1629     } else {\r
1630       /* Positional argument */\r
1631       ad = &argDescriptors[posarg++];\r
1632       strcpy(argName, ad->argName);\r
1633     }\r
1634 \r
1635     if (ad->argType == ArgTrue) {\r
1636       *(Boolean *) ad->argLoc = TRUE;\r
1637       continue;\r
1638     }\r
1639     if (ad->argType == ArgFalse) {\r
1640       *(Boolean *) ad->argLoc = FALSE;\r
1641       continue;\r
1642     }\r
1643 \r
1644     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1645     if (ch == NULLCHAR || ch == '\n') {\r
1646       ExitArgError("No value provided for argument", argName);\r
1647     }\r
1648     q = argValue;\r
1649     if (ch == '{') {\r
1650       // Quoting with { }.  No characters have to (or can) be escaped.\r
1651       // Thus the string cannot contain a '}' character.\r
1652       start = ch;\r
1653       ch = get(cl);\r
1654       while (start) {\r
1655         switch (ch) {\r
1656         case NULLCHAR:\r
1657           start = NULLCHAR;\r
1658           break;\r
1659           \r
1660         case '}':\r
1661           ch = get(cl);\r
1662           start = NULLCHAR;\r
1663           break;\r
1664 \r
1665         default:\r
1666           *q++ = ch;\r
1667           ch = get(cl);\r
1668           break;\r
1669         }\r
1670       }   \r
1671     } else if (ch == '\'' || ch == '"') {\r
1672       // Quoting with ' ' or " ", with \ as escape character.\r
1673       // Inconvenient for long strings that may contain Windows filenames.\r
1674       start = ch;\r
1675       ch = get(cl);\r
1676       while (start) {\r
1677         switch (ch) {\r
1678         case NULLCHAR:\r
1679           start = NULLCHAR;\r
1680           break;\r
1681 \r
1682         default:\r
1683         not_special:\r
1684           *q++ = ch;\r
1685           ch = get(cl);\r
1686           break;\r
1687 \r
1688         case '\'':\r
1689         case '\"':\r
1690           if (ch == start) {\r
1691             ch = get(cl);\r
1692             start = NULLCHAR;\r
1693             break;\r
1694           } else {\r
1695             goto not_special;\r
1696           }\r
1697 \r
1698         case '\\':\r
1699           if (ad->argType == ArgFilename\r
1700               || ad->argType == ArgSettingsFilename) {\r
1701               goto not_special;\r
1702           }\r
1703           ch = get(cl);\r
1704           switch (ch) {\r
1705           case NULLCHAR:\r
1706             ExitArgError("Incomplete \\ escape in value for", argName);\r
1707             break;\r
1708           case 'n':\r
1709             *q++ = '\n';\r
1710             ch = get(cl);\r
1711             break;\r
1712           case 'r':\r
1713             *q++ = '\r';\r
1714             ch = get(cl);\r
1715             break;\r
1716           case 't':\r
1717             *q++ = '\t';\r
1718             ch = get(cl);\r
1719             break;\r
1720           case 'b':\r
1721             *q++ = '\b';\r
1722             ch = get(cl);\r
1723             break;\r
1724           case 'f':\r
1725             *q++ = '\f';\r
1726             ch = get(cl);\r
1727             break;\r
1728           default:\r
1729             octval = 0;\r
1730             for (i = 0; i < 3; i++) {\r
1731               if (ch >= '0' && ch <= '7') {\r
1732                 octval = octval*8 + (ch - '0');\r
1733                 ch = get(cl);\r
1734               } else {\r
1735                 break;\r
1736               }\r
1737             }\r
1738             if (i > 0) {\r
1739               *q++ = (char) octval;\r
1740             } else {\r
1741               *q++ = ch;\r
1742               ch = get(cl);\r
1743             }\r
1744             break;\r
1745           }\r
1746           break;\r
1747         }\r
1748       }\r
1749     } else {\r
1750       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1751         *q++ = ch;\r
1752         ch = get(cl);\r
1753       }\r
1754     }\r
1755     *q = NULLCHAR;\r
1756 \r
1757     switch (ad->argType) {\r
1758     case ArgInt:\r
1759       *(int *) ad->argLoc = atoi(argValue);\r
1760       break;\r
1761 \r
1762     case ArgX:\r
1763       *(int *) ad->argLoc = atoi(argValue) + wpMain.x; // [HGM] placement: translate stored relative to absolute \r
1764       break;\r
1765 \r
1766     case ArgY:\r
1767       *(int *) ad->argLoc = atoi(argValue) + wpMain.y; // (this is really kludgey, it should be done where used...)\r
1768       break;\r
1769 \r
1770     case ArgZ:\r
1771       *(int *) ad->argLoc = atoi(argValue);\r
1772       EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY); \r
1773       break;\r
1774 \r
1775     case ArgFloat:\r
1776       *(float *) ad->argLoc = (float) atof(argValue);\r
1777       break;\r
1778 \r
1779     case ArgString:\r
1780     case ArgFilename:\r
1781       *(char **) ad->argLoc = strdup(argValue);\r
1782       break;\r
1783 \r
1784     case ArgSettingsFilename:\r
1785       {\r
1786         char fullname[MSG_SIZ];\r
1787         if (ParseSettingsFile(argValue, fullname)) {\r
1788           if (ad->argLoc != NULL) {\r
1789             *(char **) ad->argLoc = strdup(fullname);\r
1790           }\r
1791         } else {\r
1792           if (ad->argLoc != NULL) {\r
1793           } else {\r
1794             ExitArgError("Failed to open indirection file", argValue);\r
1795           }\r
1796         }\r
1797       }\r
1798       break;\r
1799 \r
1800     case ArgBoolean:\r
1801       switch (argValue[0]) {\r
1802       case 't':\r
1803       case 'T':\r
1804         *(Boolean *) ad->argLoc = TRUE;\r
1805         break;\r
1806       case 'f':\r
1807       case 'F':\r
1808         *(Boolean *) ad->argLoc = FALSE;\r
1809         break;\r
1810       default:\r
1811         ExitArgError("Unrecognized boolean argument value", argValue);\r
1812         break;\r
1813       }\r
1814       break;\r
1815 \r
1816     case ArgColor:\r
1817       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1818       break;\r
1819 \r
1820     case ArgAttribs: {\r
1821       ColorClass cc = (ColorClass)ad->argLoc;\r
1822       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1823       }\r
1824       break;\r
1825       \r
1826     case ArgBoardSize:\r
1827       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1828       break;\r
1829 \r
1830     case ArgFont:\r
1831       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1832       break;\r
1833 \r
1834     case ArgCommSettings:\r
1835       ParseCommSettings(argValue, &dcb);\r
1836       break;\r
1837 \r
1838     case ArgNone:\r
1839       ExitArgError("Unrecognized argument", argValue);\r
1840       break;\r
1841     case ArgTrue:\r
1842     case ArgFalse: ;\r
1843     }\r
1844   }\r
1845 }\r
1846 \r
1847 VOID\r
1848 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1849 {\r
1850   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1851   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1852   DeleteDC(hdc);\r
1853   lf->lfWidth = 0;\r
1854   lf->lfEscapement = 0;\r
1855   lf->lfOrientation = 0;\r
1856   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1857   lf->lfItalic = mfp->italic;\r
1858   lf->lfUnderline = mfp->underline;\r
1859   lf->lfStrikeOut = mfp->strikeout;\r
1860   lf->lfCharSet = mfp->charset;\r
1861   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1862   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1863   lf->lfQuality = DEFAULT_QUALITY;\r
1864   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1865   strcpy(lf->lfFaceName, mfp->faceName);\r
1866 }\r
1867 \r
1868 VOID\r
1869 CreateFontInMF(MyFont *mf)\r
1870 {\r
1871   LFfromMFP(&mf->lf, &mf->mfp);\r
1872   if (mf->hf) DeleteObject(mf->hf);\r
1873   mf->hf = CreateFontIndirect(&mf->lf);\r
1874 }\r
1875 \r
1876 VOID\r
1877 SetDefaultTextAttribs()\r
1878 {\r
1879   ColorClass cc;\r
1880   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1881     ParseAttribs(&textAttribs[cc].color, \r
1882                  &textAttribs[cc].effects, \r
1883                  defaultTextAttribs[cc]);\r
1884   }\r
1885 }\r
1886 \r
1887 VOID\r
1888 SetDefaultSounds()\r
1889 {\r
1890   ColorClass cc;\r
1891   SoundClass sc;\r
1892   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1893     textAttribs[cc].sound.name = strdup("");\r
1894     textAttribs[cc].sound.data = NULL;\r
1895   }\r
1896   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1897     sounds[sc].name = strdup("");\r
1898     sounds[sc].data = NULL;\r
1899   }\r
1900   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1901 }\r
1902 \r
1903 VOID\r
1904 LoadAllSounds()\r
1905 {\r
1906   ColorClass cc;\r
1907   SoundClass sc;\r
1908   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1909     MyLoadSound(&textAttribs[cc].sound);\r
1910   }\r
1911   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1912     MyLoadSound(&sounds[sc]);\r
1913   }\r
1914 }\r
1915 \r
1916 void\r
1917 SetDefaultsFromList()\r
1918 { // [HGM] ini: take defaults from argDescriptor list\r
1919   int i;\r
1920 \r
1921   for(i=0; argDescriptors[i].argName != NULL; i++) {\r
1922     if(argDescriptors[i].defaultValue != INVALID)\r
1923       switch(argDescriptors[i].argType) {\r
1924         case ArgBoolean:\r
1925         case ArgTrue:\r
1926         case ArgFalse:\r
1927           *(Boolean *) argDescriptors[i].argLoc = (int)argDescriptors[i].defaultValue;\r
1928           break;\r
1929         case ArgInt:\r
1930         case ArgX:\r
1931         case ArgY:\r
1932         case ArgZ:\r
1933           *(int *) argDescriptors[i].argLoc = (int)argDescriptors[i].defaultValue;\r
1934           break;\r
1935         case ArgString:\r
1936         case ArgFilename:\r
1937         case ArgSettingsFilename:\r
1938           *(char **) argDescriptors[i].argLoc = (char *)argDescriptors[i].defaultValue;\r
1939           break;\r
1940         case ArgBoardSize:\r
1941           *(BoardSize *) argDescriptors[i].argLoc = (BoardSize)argDescriptors[i].defaultValue;\r
1942           break;\r
1943         case ArgFloat: // floats cannot be casted to int without precision loss\r
1944         default: ; // some arg types cannot be initialized through table\r
1945     }\r
1946   }\r
1947 }\r
1948 \r
1949 VOID\r
1950 InitAppData(LPSTR lpCmdLine)\r
1951 {\r
1952   int i, j;\r
1953   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1954   char *dummy, *p;\r
1955 \r
1956   programName = szAppName;\r
1957 \r
1958   /* Initialize to defaults */\r
1959   SetDefaultsFromList(); // this sets most defaults\r
1960 \r
1961   // some parameters for which there are no options!\r
1962   appData.Iconic = FALSE; /*unused*/\r
1963   appData.cmailGameName = "";\r
1964   appData.icsEngineAnalyze = FALSE;\r
1965 \r
1966   // float: casting to int is not harmless, so default cannot be contained in table\r
1967   appData.timeDelay = TIME_DELAY;\r
1968 \r
1969   // colors have platform-dependent option format and internal representation\r
1970   // their setting and parsing must remain in front-end\r
1971   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1972   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1973   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1974   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1975   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1976   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1977   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1978   // the following must be moved out of appData to front-end variables\r
1979   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1980   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1981   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1982 \r
1983   // some complex, platform-dependent stuff\r
1984   SetDefaultTextAttribs();\r
1985   SetDefaultSounds();\r
1986 \r
1987   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1988   dcb.DCBlength = sizeof(DCB);\r
1989   dcb.BaudRate = 9600;\r
1990   dcb.fBinary = TRUE;\r
1991   dcb.fParity = FALSE;\r
1992   dcb.fOutxCtsFlow = FALSE;\r
1993   dcb.fOutxDsrFlow = FALSE;\r
1994   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1995   dcb.fDsrSensitivity = FALSE;\r
1996   dcb.fTXContinueOnXoff = TRUE;\r
1997   dcb.fOutX = FALSE;\r
1998   dcb.fInX = FALSE;\r
1999   dcb.fNull = FALSE;\r
2000   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2001   dcb.fAbortOnError = FALSE;\r
2002   dcb.ByteSize = 7;\r
2003   dcb.Parity = SPACEPARITY;\r
2004   dcb.StopBits = ONESTOPBIT;\r
2005 \r
2006   /* Point font array elements to structures and\r
2007      parse default font names */\r
2008   for (i=0; i<NUM_FONTS; i++) {\r
2009     for (j=0; j<NUM_SIZES; j++) {\r
2010       font[j][i] = &fontRec[j][i];\r
2011       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2012     }\r
2013   }\r
2014   \r
2015   /* Parse default settings file if any */\r
2016   if (ParseSettingsFile(settingsFileName, buf)) {\r
2017     settingsFileName = strdup(buf);\r
2018   }\r
2019 \r
2020   /* Parse command line */\r
2021   ParseArgs(StringGet, &lpCmdLine);\r
2022 \r
2023   /* [HGM] make sure board size is acceptable */\r
2024   if(appData.NrFiles > BOARD_FILES ||\r
2025      appData.NrRanks > BOARD_RANKS   )\r
2026       DisplayFatalError("Recompile with BOARD_RANKS or BOARD_FILES, to support this size", 0, 2);\r
2027 \r
2028   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2029    * with options from the command line, we now make an even higher priority\r
2030    * overrule by WB options attached to the engine command line. This so that\r
2031    * tournament managers can use WB options (such as /timeOdds) that follow\r
2032    * the engines.\r
2033    */\r
2034   if(appData.firstChessProgram != NULL) {\r
2035       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2036       static char *f = "first";\r
2037       char buf[MSG_SIZ], *q = buf;\r
2038       if(p != NULL) { // engine command line contains WinBoard options\r
2039           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2040           ParseArgs(StringGet, &q);\r
2041           p[-1] = 0; // cut them offengine command line\r
2042       }\r
2043   }\r
2044   // now do same for second chess program\r
2045   if(appData.secondChessProgram != NULL) {\r
2046       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2047       static char *s = "second";\r
2048       char buf[MSG_SIZ], *q = buf;\r
2049       if(p != NULL) { // engine command line contains WinBoard options\r
2050           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2051           ParseArgs(StringGet, &q);\r
2052           p[-1] = 0; // cut them offengine command line\r
2053       }\r
2054   }\r
2055 \r
2056 \r
2057   /* Propagate options that affect others */\r
2058   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2059   if (appData.icsActive || appData.noChessProgram) {\r
2060      chessProgram = FALSE;  /* not local chess program mode */\r
2061   }\r
2062 \r
2063   /* Open startup dialog if needed */\r
2064   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2065       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2066       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2067                         *appData.secondChessProgram == NULLCHAR))) {\r
2068     FARPROC lpProc;\r
2069     \r
2070     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2071     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2072     FreeProcInstance(lpProc);\r
2073   }\r
2074 \r
2075   /* Make sure save files land in the right (?) directory */\r
2076   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2077     appData.saveGameFile = strdup(buf);\r
2078   }\r
2079   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2080     appData.savePositionFile = strdup(buf);\r
2081   }\r
2082 \r
2083   /* Finish initialization for fonts and sounds */\r
2084   for (i=0; i<NUM_FONTS; i++) {\r
2085     for (j=0; j<NUM_SIZES; j++) {\r
2086       CreateFontInMF(font[j][i]);\r
2087     }\r
2088   }\r
2089   /* xboard, and older WinBoards, controlled the move sound with the\r
2090      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2091      always turn the option on (so that the backend will call us),\r
2092      then let the user turn the sound off by setting it to silence if\r
2093      desired.  To accommodate old winboard.ini files saved by old\r
2094      versions of WinBoard, we also turn off the sound if the option\r
2095      was initially set to false. */\r
2096   if (!appData.ringBellAfterMoves) {\r
2097     sounds[(int)SoundMove].name = strdup("");\r
2098     appData.ringBellAfterMoves = TRUE;\r
2099   }\r
2100   GetCurrentDirectory(MSG_SIZ, currDir);\r
2101   SetCurrentDirectory(installDir);\r
2102   LoadAllSounds();\r
2103   SetCurrentDirectory(currDir);\r
2104 \r
2105   p = icsTextMenuString;\r
2106   if (p[0] == '@') {\r
2107     FILE* f = fopen(p + 1, "r");\r
2108     if (f == NULL) {\r
2109       DisplayFatalError(p + 1, errno, 2);\r
2110       return;\r
2111     }\r
2112     i = fread(buf, 1, sizeof(buf)-1, f);\r
2113     fclose(f);\r
2114     buf[i] = NULLCHAR;\r
2115     p = buf;\r
2116   }\r
2117   ParseIcsTextMenu(strdup(p));\r
2118 }\r
2119 \r
2120 \r
2121 VOID\r
2122 InitMenuChecks()\r
2123 {\r
2124   HMENU hmenu = GetMenu(hwndMain);\r
2125 \r
2126   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2127                         MF_BYCOMMAND|((appData.icsActive &&\r
2128                                        *appData.icsCommPort != NULLCHAR) ?\r
2129                                       MF_ENABLED : MF_GRAYED));\r
2130   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2131                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2132                                      MF_CHECKED : MF_UNCHECKED));\r
2133 }\r
2134 \r
2135 // [HGM] args: these three cases taken out to stay in front-end\r
2136 void\r
2137 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
2138 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
2139         // while the curent board size determines the element. This system should be ported to XBoard.\r
2140         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
2141         int bs;\r
2142         for (bs=0; bs<NUM_SIZES; bs++) {\r
2143           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2144           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2145           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
2146             ad->argName, mfp->faceName, mfp->pointSize,\r
2147             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2148             mfp->bold ? "b" : "",\r
2149             mfp->italic ? "i" : "",\r
2150             mfp->underline ? "u" : "",\r
2151             mfp->strikeout ? "s" : "",\r
2152             (int)mfp->charset);\r
2153         }\r
2154       }\r
2155 \r
2156 void\r
2157 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
2158 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
2159         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2160         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2161           (ta->effects & CFE_BOLD) ? "b" : "",\r
2162           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2163           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2164           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2165           (ta->effects) ? " " : "",\r
2166           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2167       }\r
2168 \r
2169 void\r
2170 SaveColor(FILE *f, ArgDescriptor *ad)\r
2171 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
2172         COLORREF color = *(COLORREF *)ad->argLoc;\r
2173         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2174           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2175 }\r
2176 \r
2177 int\r
2178 MainWindowUp()\r
2179 { // [HGM] args: allows testing if main window is realized from back-end\r
2180   return hwndMain != NULL;\r
2181 }\r
2182 \r
2183 VOID\r
2184 SaveSettings(char* name)\r
2185 {\r
2186   FILE *f;\r
2187   ArgDescriptor *ad;\r
2188   char dir[MSG_SIZ];\r
2189 \r
2190   if (!MainWindowUp()) return;\r
2191 \r
2192   GetCurrentDirectory(MSG_SIZ, dir);\r
2193   SetCurrentDirectory(installDir);\r
2194   f = fopen(name, "w");\r
2195   SetCurrentDirectory(dir);\r
2196   if (f == NULL) {\r
2197     DisplayError(name, errno);\r
2198     return;\r
2199   }\r
2200   fprintf(f, ";\n");\r
2201   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2202   fprintf(f, ";\n");\r
2203   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2204   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2205   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2206   fprintf(f, ";\n");\r
2207 \r
2208   GetActualPlacement(hwndMain, &wpMain);\r
2209   GetActualPlacement(hwndConsole, &wpConsole);\r
2210   GetActualPlacement(commentDialog, &wpComment);\r
2211   GetActualPlacement(editTagsDialog, &wpTags);\r
2212   GetActualPlacement(gameListDialog, &wpGameList);\r
2213 \r
2214   /* [AS] Move history */\r
2215   wpMoveHistory.visible = MoveHistoryIsUp();\r
2216   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
2217 \r
2218   /* [AS] Eval graph */\r
2219   wpEvalGraph.visible = EvalGraphIsUp();\r
2220   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
2221 \r
2222   /* [AS] Engine output */\r
2223   wpEngineOutput.visible = EngineOutputIsUp();\r
2224   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
2225 \r
2226   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2227     if (!ad->save) continue;\r
2228     switch (ad->argType) {\r
2229     case ArgString:\r
2230       {\r
2231         char *p = *(char **)ad->argLoc;\r
2232         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2233           /* Quote multiline values or \-containing values\r
2234              with { } if possible */\r
2235           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2236         } else {\r
2237           /* Else quote with " " */\r
2238           fprintf(f, "/%s=\"", ad->argName);\r
2239           while (*p) {\r
2240             if (*p == '\n') fprintf(f, "\n");\r
2241             else if (*p == '\r') fprintf(f, "\\r");\r
2242             else if (*p == '\t') fprintf(f, "\\t");\r
2243             else if (*p == '\b') fprintf(f, "\\b");\r
2244             else if (*p == '\f') fprintf(f, "\\f");\r
2245             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2246             else if (*p == '\"') fprintf(f, "\\\"");\r
2247             else if (*p == '\\') fprintf(f, "\\\\");\r
2248             else putc(*p, f);\r
2249             p++;\r
2250           }\r
2251           fprintf(f, "\"\n");\r
2252         }\r
2253       }\r
2254       break;\r
2255     case ArgInt:\r
2256     case ArgZ:\r
2257       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2258       break;\r
2259     case ArgX:\r
2260       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - wpMain.x); // [HGM] placement: stor relative value\r
2261       break;\r
2262     case ArgY:\r
2263       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - wpMain.y);\r
2264       break;\r
2265     case ArgFloat:\r
2266       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2267       break;\r
2268     case ArgBoolean:\r
2269       fprintf(f, "/%s=%s\n", ad->argName, \r
2270         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2271       break;\r
2272     case ArgTrue:\r
2273       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2274       break;\r
2275     case ArgFalse:\r
2276       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2277       break;\r
2278     case ArgColor:\r
2279       SaveColor(f, ad);\r
2280       break;\r
2281     case ArgAttribs:\r
2282       break;\r
2283     case ArgFilename:\r
2284       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2285         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2286       } else {\r
2287         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2288       }\r
2289       break;\r
2290     case ArgBoardSize:\r
2291       fprintf(f, "/%s=%s\n", ad->argName,\r
2292               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2293       break;\r
2294     case ArgFont:\r
2295       SaveFontArg(f, ad);\r
2296       break;\r
2297     case ArgCommSettings:\r
2298       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2299     case ArgNone:\r
2300     case ArgSettingsFilename: ;\r
2301     }\r
2302   }\r
2303   fclose(f);\r
2304 }\r
2305 \r
2306 \r
2307 \r
2308 /*---------------------------------------------------------------------------*\\r
2309  *\r
2310  * GDI board drawing routines\r
2311  *\r
2312 \*---------------------------------------------------------------------------*/\r
2313 \r
2314 /* [AS] Draw square using background texture */\r
2315 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2316 {\r
2317     XFORM   x;\r
2318 \r
2319     if( mode == 0 ) {\r
2320         return; /* Should never happen! */\r
2321     }\r
2322 \r
2323     SetGraphicsMode( dst, GM_ADVANCED );\r
2324 \r
2325     switch( mode ) {\r
2326     case 1:\r
2327         /* Identity */\r
2328         break;\r
2329     case 2:\r
2330         /* X reflection */\r
2331         x.eM11 = -1.0;\r
2332         x.eM12 = 0;\r
2333         x.eM21 = 0;\r
2334         x.eM22 = 1.0;\r
2335         x.eDx = (FLOAT) dw + dx - 1;\r
2336         x.eDy = 0;\r
2337         dx = 0;\r
2338         SetWorldTransform( dst, &x );\r
2339         break;\r
2340     case 3:\r
2341         /* Y reflection */\r
2342         x.eM11 = 1.0;\r
2343         x.eM12 = 0;\r
2344         x.eM21 = 0;\r
2345         x.eM22 = -1.0;\r
2346         x.eDx = 0;\r
2347         x.eDy = (FLOAT) dh + dy - 1;\r
2348         dy = 0;\r
2349         SetWorldTransform( dst, &x );\r
2350         break;\r
2351     case 4:\r
2352         /* X/Y flip */\r
2353         x.eM11 = 0;\r
2354         x.eM12 = 1.0;\r
2355         x.eM21 = 1.0;\r
2356         x.eM22 = 0;\r
2357         x.eDx = (FLOAT) dx;\r
2358         x.eDy = (FLOAT) dy;\r
2359         dx = 0;\r
2360         dy = 0;\r
2361         SetWorldTransform( dst, &x );\r
2362         break;\r
2363     }\r
2364 \r
2365     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2366 \r
2367     x.eM11 = 1.0;\r
2368     x.eM12 = 0;\r
2369     x.eM21 = 0;\r
2370     x.eM22 = 1.0;\r
2371     x.eDx = 0;\r
2372     x.eDy = 0;\r
2373     SetWorldTransform( dst, &x );\r
2374 \r
2375     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2376 }\r
2377 \r
2378 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2379 enum {\r
2380     PM_WP = (int) WhitePawn, \r
2381     PM_WN = (int) WhiteKnight, \r
2382     PM_WB = (int) WhiteBishop, \r
2383     PM_WR = (int) WhiteRook, \r
2384     PM_WQ = (int) WhiteQueen, \r
2385     PM_WF = (int) WhiteFerz, \r
2386     PM_WW = (int) WhiteWazir, \r
2387     PM_WE = (int) WhiteAlfil, \r
2388     PM_WM = (int) WhiteMan, \r
2389     PM_WO = (int) WhiteCannon, \r
2390     PM_WU = (int) WhiteUnicorn, \r
2391     PM_WH = (int) WhiteNightrider, \r
2392     PM_WA = (int) WhiteAngel, \r
2393     PM_WC = (int) WhiteMarshall, \r
2394     PM_WAB = (int) WhiteCardinal, \r
2395     PM_WD = (int) WhiteDragon, \r
2396     PM_WL = (int) WhiteLance, \r
2397     PM_WS = (int) WhiteCobra, \r
2398     PM_WV = (int) WhiteFalcon, \r
2399     PM_WSG = (int) WhiteSilver, \r
2400     PM_WG = (int) WhiteGrasshopper, \r
2401     PM_WK = (int) WhiteKing,\r
2402     PM_BP = (int) BlackPawn, \r
2403     PM_BN = (int) BlackKnight, \r
2404     PM_BB = (int) BlackBishop, \r
2405     PM_BR = (int) BlackRook, \r
2406     PM_BQ = (int) BlackQueen, \r
2407     PM_BF = (int) BlackFerz, \r
2408     PM_BW = (int) BlackWazir, \r
2409     PM_BE = (int) BlackAlfil, \r
2410     PM_BM = (int) BlackMan,\r
2411     PM_BO = (int) BlackCannon, \r
2412     PM_BU = (int) BlackUnicorn, \r
2413     PM_BH = (int) BlackNightrider, \r
2414     PM_BA = (int) BlackAngel, \r
2415     PM_BC = (int) BlackMarshall, \r
2416     PM_BG = (int) BlackGrasshopper, \r
2417     PM_BAB = (int) BlackCardinal,\r
2418     PM_BD = (int) BlackDragon,\r
2419     PM_BL = (int) BlackLance,\r
2420     PM_BS = (int) BlackCobra,\r
2421     PM_BV = (int) BlackFalcon,\r
2422     PM_BSG = (int) BlackSilver,\r
2423     PM_BK = (int) BlackKing\r
2424 };\r
2425 \r
2426 static HFONT hPieceFont = NULL;\r
2427 static HBITMAP hPieceMask[(int) EmptySquare];\r
2428 static HBITMAP hPieceFace[(int) EmptySquare];\r
2429 static int fontBitmapSquareSize = 0;\r
2430 static char pieceToFontChar[(int) EmptySquare] =\r
2431                               { 'p', 'n', 'b', 'r', 'q', \r
2432                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2433                       'k', 'o', 'm', 'v', 't', 'w', \r
2434                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2435                                                               'l' };\r
2436 \r
2437 extern BOOL SetCharTable( char *table, const char * map );\r
2438 /* [HGM] moved to backend.c */\r
2439 \r
2440 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2441 {\r
2442     HBRUSH hbrush;\r
2443     BYTE r1 = GetRValue( color );\r
2444     BYTE g1 = GetGValue( color );\r
2445     BYTE b1 = GetBValue( color );\r
2446     BYTE r2 = r1 / 2;\r
2447     BYTE g2 = g1 / 2;\r
2448     BYTE b2 = b1 / 2;\r
2449     RECT rc;\r
2450 \r
2451     /* Create a uniform background first */\r
2452     hbrush = CreateSolidBrush( color );\r
2453     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2454     FillRect( hdc, &rc, hbrush );\r
2455     DeleteObject( hbrush );\r
2456     \r
2457     if( mode == 1 ) {\r
2458         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2459         int steps = squareSize / 2;\r
2460         int i;\r
2461 \r
2462         for( i=0; i<steps; i++ ) {\r
2463             BYTE r = r1 - (r1-r2) * i / steps;\r
2464             BYTE g = g1 - (g1-g2) * i / steps;\r
2465             BYTE b = b1 - (b1-b2) * i / steps;\r
2466 \r
2467             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2468             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2469             FillRect( hdc, &rc, hbrush );\r
2470             DeleteObject(hbrush);\r
2471         }\r
2472     }\r
2473     else if( mode == 2 ) {\r
2474         /* Diagonal gradient, good more or less for every piece */\r
2475         POINT triangle[3];\r
2476         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2477         HBRUSH hbrush_old;\r
2478         int steps = squareSize;\r
2479         int i;\r
2480 \r
2481         triangle[0].x = squareSize - steps;\r
2482         triangle[0].y = squareSize;\r
2483         triangle[1].x = squareSize;\r
2484         triangle[1].y = squareSize;\r
2485         triangle[2].x = squareSize;\r
2486         triangle[2].y = squareSize - steps;\r
2487 \r
2488         for( i=0; i<steps; i++ ) {\r
2489             BYTE r = r1 - (r1-r2) * i / steps;\r
2490             BYTE g = g1 - (g1-g2) * i / steps;\r
2491             BYTE b = b1 - (b1-b2) * i / steps;\r
2492 \r
2493             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2494             hbrush_old = SelectObject( hdc, hbrush );\r
2495             Polygon( hdc, triangle, 3 );\r
2496             SelectObject( hdc, hbrush_old );\r
2497             DeleteObject(hbrush);\r
2498             triangle[0].x++;\r
2499             triangle[2].y++;\r
2500         }\r
2501 \r
2502         SelectObject( hdc, hpen );\r
2503     }\r
2504 }\r
2505 \r
2506 /*\r
2507     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2508     seems to work ok. The main problem here is to find the "inside" of a chess\r
2509     piece: follow the steps as explained below.\r
2510 */\r
2511 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2512 {\r
2513     HBITMAP hbm;\r
2514     HBITMAP hbm_old;\r
2515     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2516     RECT rc;\r
2517     SIZE sz;\r
2518     POINT pt;\r
2519     int backColor = whitePieceColor; \r
2520     int foreColor = blackPieceColor;\r
2521     \r
2522     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2523         backColor = appData.fontBackColorWhite;\r
2524         foreColor = appData.fontForeColorWhite;\r
2525     }\r
2526     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2527         backColor = appData.fontBackColorBlack;\r
2528         foreColor = appData.fontForeColorBlack;\r
2529     }\r
2530 \r
2531     /* Mask */\r
2532     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2533 \r
2534     hbm_old = SelectObject( hdc, hbm );\r
2535 \r
2536     rc.left = 0;\r
2537     rc.top = 0;\r
2538     rc.right = squareSize;\r
2539     rc.bottom = squareSize;\r
2540 \r
2541     /* Step 1: background is now black */\r
2542     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2543 \r
2544     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2545 \r
2546     pt.x = (squareSize - sz.cx) / 2;\r
2547     pt.y = (squareSize - sz.cy) / 2;\r
2548 \r
2549     SetBkMode( hdc, TRANSPARENT );\r
2550     SetTextColor( hdc, chroma );\r
2551     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2552     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2553 \r
2554     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2555     /* Step 3: the area outside the piece is filled with white */\r
2556 //    FloodFill( hdc, 0, 0, chroma );\r
2557     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2558     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2559     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2560     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2561     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2562     /* \r
2563         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2564         but if the start point is not inside the piece we're lost!\r
2565         There should be a better way to do this... if we could create a region or path\r
2566         from the fill operation we would be fine for example.\r
2567     */\r
2568 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2569     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2570 \r
2571     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2572         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2573         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2574 \r
2575         SelectObject( dc2, bm2 );\r
2576         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2577         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2578         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2579         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2580         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2581 \r
2582         DeleteDC( dc2 );\r
2583         DeleteObject( bm2 );\r
2584     }\r
2585 \r
2586     SetTextColor( hdc, 0 );\r
2587     /* \r
2588         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2589         draw the piece again in black for safety.\r
2590     */\r
2591     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2592 \r
2593     SelectObject( hdc, hbm_old );\r
2594 \r
2595     if( hPieceMask[index] != NULL ) {\r
2596         DeleteObject( hPieceMask[index] );\r
2597     }\r
2598 \r
2599     hPieceMask[index] = hbm;\r
2600 \r
2601     /* Face */\r
2602     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2603 \r
2604     SelectObject( hdc, hbm );\r
2605 \r
2606     {\r
2607         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2608         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2609         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2610 \r
2611         SelectObject( dc1, hPieceMask[index] );\r
2612         SelectObject( dc2, bm2 );\r
2613         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2614         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2615         \r
2616         /* \r
2617             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2618             the piece background and deletes (makes transparent) the rest.\r
2619             Thanks to that mask, we are free to paint the background with the greates\r
2620             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2621             We use this, to make gradients and give the pieces a "roundish" look.\r
2622         */\r
2623         SetPieceBackground( hdc, backColor, 2 );\r
2624         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2625 \r
2626         DeleteDC( dc2 );\r
2627         DeleteDC( dc1 );\r
2628         DeleteObject( bm2 );\r
2629     }\r
2630 \r
2631     SetTextColor( hdc, foreColor );\r
2632     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2633 \r
2634     SelectObject( hdc, hbm_old );\r
2635 \r
2636     if( hPieceFace[index] != NULL ) {\r
2637         DeleteObject( hPieceFace[index] );\r
2638     }\r
2639 \r
2640     hPieceFace[index] = hbm;\r
2641 }\r
2642 \r
2643 static int TranslatePieceToFontPiece( int piece )\r
2644 {\r
2645     switch( piece ) {\r
2646     case BlackPawn:\r
2647         return PM_BP;\r
2648     case BlackKnight:\r
2649         return PM_BN;\r
2650     case BlackBishop:\r
2651         return PM_BB;\r
2652     case BlackRook:\r
2653         return PM_BR;\r
2654     case BlackQueen:\r
2655         return PM_BQ;\r
2656     case BlackKing:\r
2657         return PM_BK;\r
2658     case WhitePawn:\r
2659         return PM_WP;\r
2660     case WhiteKnight:\r
2661         return PM_WN;\r
2662     case WhiteBishop:\r
2663         return PM_WB;\r
2664     case WhiteRook:\r
2665         return PM_WR;\r
2666     case WhiteQueen:\r
2667         return PM_WQ;\r
2668     case WhiteKing:\r
2669         return PM_WK;\r
2670 \r
2671     case BlackAngel:\r
2672         return PM_BA;\r
2673     case BlackMarshall:\r
2674         return PM_BC;\r
2675     case BlackFerz:\r
2676         return PM_BF;\r
2677     case BlackNightrider:\r
2678         return PM_BH;\r
2679     case BlackAlfil:\r
2680         return PM_BE;\r
2681     case BlackWazir:\r
2682         return PM_BW;\r
2683     case BlackUnicorn:\r
2684         return PM_BU;\r
2685     case BlackCannon:\r
2686         return PM_BO;\r
2687     case BlackGrasshopper:\r
2688         return PM_BG;\r
2689     case BlackMan:\r
2690         return PM_BM;\r
2691     case BlackSilver:\r
2692         return PM_BSG;\r
2693     case BlackLance:\r
2694         return PM_BL;\r
2695     case BlackFalcon:\r
2696         return PM_BV;\r
2697     case BlackCobra:\r
2698         return PM_BS;\r
2699     case BlackCardinal:\r
2700         return PM_BAB;\r
2701     case BlackDragon:\r
2702         return PM_BD;\r
2703 \r
2704     case WhiteAngel:\r
2705         return PM_WA;\r
2706     case WhiteMarshall:\r
2707         return PM_WC;\r
2708     case WhiteFerz:\r
2709         return PM_WF;\r
2710     case WhiteNightrider:\r
2711         return PM_WH;\r
2712     case WhiteAlfil:\r
2713         return PM_WE;\r
2714     case WhiteWazir:\r
2715         return PM_WW;\r
2716     case WhiteUnicorn:\r
2717         return PM_WU;\r
2718     case WhiteCannon:\r
2719         return PM_WO;\r
2720     case WhiteGrasshopper:\r
2721         return PM_WG;\r
2722     case WhiteMan:\r
2723         return PM_WM;\r
2724     case WhiteSilver:\r
2725         return PM_WSG;\r
2726     case WhiteLance:\r
2727         return PM_WL;\r
2728     case WhiteFalcon:\r
2729         return PM_WV;\r
2730     case WhiteCobra:\r
2731         return PM_WS;\r
2732     case WhiteCardinal:\r
2733         return PM_WAB;\r
2734     case WhiteDragon:\r
2735         return PM_WD;\r
2736     }\r
2737 \r
2738     return 0;\r
2739 }\r
2740 \r
2741 void CreatePiecesFromFont()\r
2742 {\r
2743     LOGFONT lf;\r
2744     HDC hdc_window = NULL;\r
2745     HDC hdc = NULL;\r
2746     HFONT hfont_old;\r
2747     int fontHeight;\r
2748     int i;\r
2749 \r
2750     if( fontBitmapSquareSize < 0 ) {\r
2751         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2752         return;\r
2753     }\r
2754 \r
2755     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2756         fontBitmapSquareSize = -1;\r
2757         return;\r
2758     }\r
2759 \r
2760     if( fontBitmapSquareSize != squareSize ) {\r
2761         hdc_window = GetDC( hwndMain );\r
2762         hdc = CreateCompatibleDC( hdc_window );\r
2763 \r
2764         if( hPieceFont != NULL ) {\r
2765             DeleteObject( hPieceFont );\r
2766         }\r
2767         else {\r
2768             for( i=0; i<=(int)BlackKing; i++ ) {\r
2769                 hPieceMask[i] = NULL;\r
2770                 hPieceFace[i] = NULL;\r
2771             }\r
2772         }\r
2773 \r
2774         fontHeight = 75;\r
2775 \r
2776         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2777             fontHeight = appData.fontPieceSize;\r
2778         }\r
2779 \r
2780         fontHeight = (fontHeight * squareSize) / 100;\r
2781 \r
2782         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2783         lf.lfWidth = 0;\r
2784         lf.lfEscapement = 0;\r
2785         lf.lfOrientation = 0;\r
2786         lf.lfWeight = FW_NORMAL;\r
2787         lf.lfItalic = 0;\r
2788         lf.lfUnderline = 0;\r
2789         lf.lfStrikeOut = 0;\r
2790         lf.lfCharSet = DEFAULT_CHARSET;\r
2791         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2792         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2793         lf.lfQuality = PROOF_QUALITY;\r
2794         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2795         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2796         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2797 \r
2798         hPieceFont = CreateFontIndirect( &lf );\r
2799 \r
2800         if( hPieceFont == NULL ) {\r
2801             fontBitmapSquareSize = -2;\r
2802         }\r
2803         else {\r
2804             /* Setup font-to-piece character table */\r
2805             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2806                 /* No (or wrong) global settings, try to detect the font */\r
2807                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2808                     /* Alpha */\r
2809                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2810                 }\r
2811                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2812                     /* DiagramTT* family */\r
2813                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2814                 }\r
2815                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2816                     /* Fairy symbols */\r
2817                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2818                 }\r
2819                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2820                     /* Good Companion (Some characters get warped as literal :-( */\r
2821                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2822                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2823                     SetCharTable(pieceToFontChar, s);\r
2824                 }\r
2825                 else {\r
2826                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2827                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2828                 }\r
2829             }\r
2830 \r
2831             /* Create bitmaps */\r
2832             hfont_old = SelectObject( hdc, hPieceFont );\r
2833             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2834                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2835                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2836 \r
2837             SelectObject( hdc, hfont_old );\r
2838 \r
2839             fontBitmapSquareSize = squareSize;\r
2840         }\r
2841     }\r
2842 \r
2843     if( hdc != NULL ) {\r
2844         DeleteDC( hdc );\r
2845     }\r
2846 \r
2847     if( hdc_window != NULL ) {\r
2848         ReleaseDC( hwndMain, hdc_window );\r
2849     }\r
2850 }\r
2851 \r
2852 HBITMAP\r
2853 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2854 {\r
2855   char name[128];\r
2856 \r
2857   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2858   if (gameInfo.event &&\r
2859       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2860       strcmp(name, "k80s") == 0) {\r
2861     strcpy(name, "tim");\r
2862   }\r
2863   return LoadBitmap(hinst, name);\r
2864 }\r
2865 \r
2866 \r
2867 /* Insert a color into the program's logical palette\r
2868    structure.  This code assumes the given color is\r
2869    the result of the RGB or PALETTERGB macro, and it\r
2870    knows how those macros work (which is documented).\r
2871 */\r
2872 VOID\r
2873 InsertInPalette(COLORREF color)\r
2874 {\r
2875   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2876 \r
2877   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2878     DisplayFatalError("Too many colors", 0, 1);\r
2879     pLogPal->palNumEntries--;\r
2880     return;\r
2881   }\r
2882 \r
2883   pe->peFlags = (char) 0;\r
2884   pe->peRed = (char) (0xFF & color);\r
2885   pe->peGreen = (char) (0xFF & (color >> 8));\r
2886   pe->peBlue = (char) (0xFF & (color >> 16));\r
2887   return;\r
2888 }\r
2889 \r
2890 \r
2891 VOID\r
2892 InitDrawingColors()\r
2893 {\r
2894   if (pLogPal == NULL) {\r
2895     /* Allocate enough memory for a logical palette with\r
2896      * PALETTESIZE entries and set the size and version fields\r
2897      * of the logical palette structure.\r
2898      */\r
2899     pLogPal = (NPLOGPALETTE)\r
2900       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2901                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2902     pLogPal->palVersion    = 0x300;\r
2903   }\r
2904   pLogPal->palNumEntries = 0;\r
2905 \r
2906   InsertInPalette(lightSquareColor);\r
2907   InsertInPalette(darkSquareColor);\r
2908   InsertInPalette(whitePieceColor);\r
2909   InsertInPalette(blackPieceColor);\r
2910   InsertInPalette(highlightSquareColor);\r
2911   InsertInPalette(premoveHighlightColor);\r
2912 \r
2913   /*  create a logical color palette according the information\r
2914    *  in the LOGPALETTE structure.\r
2915    */\r
2916   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2917 \r
2918   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2919   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2920   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2921   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2922   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2923   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2924   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2925   /* [AS] Force rendering of the font-based pieces */\r
2926   if( fontBitmapSquareSize > 0 ) {\r
2927     fontBitmapSquareSize = 0;\r
2928   }\r
2929 }\r
2930 \r
2931 \r
2932 int\r
2933 BoardWidth(int boardSize, int n)\r
2934 { /* [HGM] argument n added to allow different width and height */\r
2935   int lineGap = sizeInfo[boardSize].lineGap;\r
2936 \r
2937   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2938       lineGap = appData.overrideLineGap;\r
2939   }\r
2940 \r
2941   return (n + 1) * lineGap +\r
2942           n * sizeInfo[boardSize].squareSize;\r
2943 }\r
2944 \r
2945 /* Respond to board resize by dragging edge */\r
2946 VOID\r
2947 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2948 {\r
2949   BoardSize newSize = NUM_SIZES - 1;\r
2950   static int recurse = 0;\r
2951   if (IsIconic(hwndMain)) return;\r
2952   if (recurse > 0) return;\r
2953   recurse++;\r
2954   while (newSize > 0) {\r
2955         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2956         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2957            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2958     newSize--;\r
2959   } \r
2960   boardSize = newSize;\r
2961   InitDrawingSizes(boardSize, flags);\r
2962   recurse--;\r
2963 }\r
2964 \r
2965 \r
2966 \r
2967 VOID\r
2968 InitDrawingSizes(BoardSize boardSize, int flags)\r
2969 {\r
2970   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2971   ChessSquare piece;\r
2972   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2973   HDC hdc;\r
2974   SIZE clockSize, messageSize;\r
2975   HFONT oldFont;\r
2976   char buf[MSG_SIZ];\r
2977   char *str;\r
2978   HMENU hmenu = GetMenu(hwndMain);\r
2979   RECT crect, wrect, oldRect;\r
2980   int offby;\r
2981   LOGBRUSH logbrush;\r
2982 \r
2983   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
2984   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
2985 \r
2986   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
2987   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
2988 \r
2989   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
2990   oldRect.top = wpMain.y;\r
2991   oldRect.right = wpMain.x + wpMain.width;\r
2992   oldRect.bottom = wpMain.y + wpMain.height;\r
2993 \r
2994   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2995   smallLayout = sizeInfo[boardSize].smallLayout;\r
2996   squareSize = sizeInfo[boardSize].squareSize;\r
2997   lineGap = sizeInfo[boardSize].lineGap;\r
2998   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
2999 \r
3000   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3001       lineGap = appData.overrideLineGap;\r
3002   }\r
3003 \r
3004   if (tinyLayout != oldTinyLayout) {\r
3005     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3006     if (tinyLayout) {\r
3007       style &= ~WS_SYSMENU;\r
3008       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3009                  "&Minimize\tCtrl+F4");\r
3010     } else {\r
3011       style |= WS_SYSMENU;\r
3012       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3013     }\r
3014     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3015 \r
3016     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3017       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3018         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3019     }\r
3020     DrawMenuBar(hwndMain);\r
3021   }\r
3022 \r
3023   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3024   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3025 \r
3026   /* Get text area sizes */\r
3027   hdc = GetDC(hwndMain);\r
3028   if (appData.clockMode) {\r
3029     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3030   } else {\r
3031     sprintf(buf, "White");\r
3032   }\r
3033   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3034   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3035   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3036   str = "We only care about the height here";\r
3037   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3038   SelectObject(hdc, oldFont);\r
3039   ReleaseDC(hwndMain, hdc);\r
3040 \r
3041   /* Compute where everything goes */\r
3042   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3043         /* [HGM] logo: if either logo is on, reserve space for it */\r
3044         logoHeight =  2*clockSize.cy;\r
3045         leftLogoRect.left   = OUTER_MARGIN;\r
3046         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3047         leftLogoRect.top    = OUTER_MARGIN;\r
3048         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3049 \r
3050         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3051         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3052         rightLogoRect.top    = OUTER_MARGIN;\r
3053         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3054 \r
3055 \r
3056     whiteRect.left = leftLogoRect.right;\r
3057     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3058     whiteRect.top = OUTER_MARGIN;\r
3059     whiteRect.bottom = whiteRect.top + logoHeight;\r
3060 \r
3061     blackRect.right = rightLogoRect.left;\r
3062     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3063     blackRect.top = whiteRect.top;\r
3064     blackRect.bottom = whiteRect.bottom;\r
3065   } else {\r
3066     whiteRect.left = OUTER_MARGIN;\r
3067     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3068     whiteRect.top = OUTER_MARGIN;\r
3069     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3070 \r
3071     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3072     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3073     blackRect.top = whiteRect.top;\r
3074     blackRect.bottom = whiteRect.bottom;\r
3075   }\r
3076 \r
3077   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3078   if (appData.showButtonBar) {\r
3079     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3080       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3081   } else {\r
3082     messageRect.right = OUTER_MARGIN + boardWidth;\r
3083   }\r
3084   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3085   messageRect.bottom = messageRect.top + messageSize.cy;\r
3086 \r
3087   boardRect.left = OUTER_MARGIN;\r
3088   boardRect.right = boardRect.left + boardWidth;\r
3089   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3090   boardRect.bottom = boardRect.top + boardHeight;\r
3091 \r
3092   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3093   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3094   oldBoardSize = boardSize;\r
3095   oldTinyLayout = tinyLayout;\r
3096   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3097   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3098     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3099   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3100   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3101   wpMain.height = winH; //       without disturbing window attachments\r
3102   GetWindowRect(hwndMain, &wrect);\r
3103   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
3104                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3105 \r
3106   // [HGM] placement: let attached windows follow size change.\r
3107   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
3108   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
3109   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
3110   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
3111   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
3112 \r
3113   /* compensate if menu bar wrapped */\r
3114   GetClientRect(hwndMain, &crect);\r
3115   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3116   wpMain.height += offby;\r
3117   switch (flags) {\r
3118   case WMSZ_TOPLEFT:\r
3119     SetWindowPos(hwndMain, NULL, \r
3120                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
3121                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3122     break;\r
3123 \r
3124   case WMSZ_TOPRIGHT:\r
3125   case WMSZ_TOP:\r
3126     SetWindowPos(hwndMain, NULL, \r
3127                  wrect.left, wrect.bottom - wpMain.height, \r
3128                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3129     break;\r
3130 \r
3131   case WMSZ_BOTTOMLEFT:\r
3132   case WMSZ_LEFT:\r
3133     SetWindowPos(hwndMain, NULL, \r
3134                  wrect.right - wpMain.width, wrect.top, \r
3135                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3136     break;\r
3137 \r
3138   case WMSZ_BOTTOMRIGHT:\r
3139   case WMSZ_BOTTOM:\r
3140   case WMSZ_RIGHT:\r
3141   default:\r
3142     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
3143                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3144     break;\r
3145   }\r
3146 \r
3147   hwndPause = NULL;\r
3148   for (i = 0; i < N_BUTTONS; i++) {\r
3149     if (buttonDesc[i].hwnd != NULL) {\r
3150       DestroyWindow(buttonDesc[i].hwnd);\r
3151       buttonDesc[i].hwnd = NULL;\r
3152     }\r
3153     if (appData.showButtonBar) {\r
3154       buttonDesc[i].hwnd =\r
3155         CreateWindow("BUTTON", buttonDesc[i].label,\r
3156                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3157                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3158                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3159                      (HMENU) buttonDesc[i].id,\r
3160                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3161       if (tinyLayout) {\r
3162         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3163                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3164                     MAKELPARAM(FALSE, 0));\r
3165       }\r
3166       if (buttonDesc[i].id == IDM_Pause)\r
3167         hwndPause = buttonDesc[i].hwnd;\r
3168       buttonDesc[i].wndproc = (WNDPROC)\r
3169         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3170     }\r
3171   }\r
3172   if (gridPen != NULL) DeleteObject(gridPen);\r
3173   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3174   if (premovePen != NULL) DeleteObject(premovePen);\r
3175   if (lineGap != 0) {\r
3176     logbrush.lbStyle = BS_SOLID;\r
3177     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3178     gridPen =\r
3179       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3180                    lineGap, &logbrush, 0, NULL);\r
3181     logbrush.lbColor = highlightSquareColor;\r
3182     highlightPen =\r
3183       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3184                    lineGap, &logbrush, 0, NULL);\r
3185 \r
3186     logbrush.lbColor = premoveHighlightColor; \r
3187     premovePen =\r
3188       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3189                    lineGap, &logbrush, 0, NULL);\r
3190 \r
3191     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3192     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3193       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3194       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3195         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3196       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3197         BOARD_WIDTH * (squareSize + lineGap);\r
3198       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3199     }\r
3200     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3201       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3202       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3203         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3204         lineGap / 2 + (i * (squareSize + lineGap));\r
3205       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3206         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3207       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3208     }\r
3209   }\r
3210 \r
3211   /* [HGM] Licensing requirement */\r
3212 #ifdef GOTHIC\r
3213   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3214 #endif\r
3215 #ifdef FALCON\r
3216   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3217 #endif\r
3218   GothicPopUp( "", VariantNormal);\r
3219 \r
3220 \r
3221 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3222 \r
3223   /* Load piece bitmaps for this board size */\r
3224   for (i=0; i<=2; i++) {\r
3225     for (piece = WhitePawn;\r
3226          (int) piece < (int) BlackPawn;\r
3227          piece = (ChessSquare) ((int) piece + 1)) {\r
3228       if (pieceBitmap[i][piece] != NULL)\r
3229         DeleteObject(pieceBitmap[i][piece]);\r
3230     }\r
3231   }\r
3232 \r
3233   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3234   // Orthodox Chess pieces\r
3235   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3236   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3237   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3238   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3239   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3240   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3241   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3242   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3243   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3244   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3245   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3246   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3247   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3248   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3249   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3250   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3251     // in Shogi, Hijack the unused Queen for Lance\r
3252     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3253     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3254     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3255   } else {\r
3256     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3257     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3258     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3259   }\r
3260 \r
3261   if(squareSize <= 72 && squareSize >= 33) { \r
3262     /* A & C are available in most sizes now */\r
3263     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3264       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3265       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3266       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3267       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3268       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3269       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3270       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3271       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3272       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3273       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3274       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3275       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3276     } else { // Smirf-like\r
3277       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3278       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3279       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3280     }\r
3281     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3282       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3283       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3284       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3285     } else { // WinBoard standard\r
3286       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3287       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3288       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3289     }\r
3290   }\r
3291 \r
3292 \r
3293   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3294     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3295     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3296     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3297     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3298     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3299     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3300     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3301     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3302     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3303     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3304     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3305     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3306     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3307     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3308     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3309     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3310     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3311     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3312     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3313     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3314     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3315     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3316     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3317     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3318     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3319     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3320     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3321     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3322     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3323     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3324 \r
3325     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3326       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3327       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3328       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3329       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3330       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3331       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3332       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3333       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3334       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3335       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3336       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3337       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3338     } else {\r
3339       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3340       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3341       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3342       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3343       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3344       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3345       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3346       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3347       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3348       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3349       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3350       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3351     }\r
3352 \r
3353   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3354     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3355     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3356     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3357     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3358     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3359     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3360     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3361     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3362     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3363     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3364     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3365     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3366     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3367     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3368   }\r
3369 \r
3370 \r
3371   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3372   /* special Shogi support in this size */\r
3373   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3374       for (piece = WhitePawn;\r
3375            (int) piece < (int) BlackPawn;\r
3376            piece = (ChessSquare) ((int) piece + 1)) {\r
3377         if (pieceBitmap[i][piece] != NULL)\r
3378           DeleteObject(pieceBitmap[i][piece]);\r
3379       }\r
3380     }\r
3381   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3382   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3383   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3384   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3385   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3386   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3387   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3388   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3389   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3390   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3391   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3392   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3393   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3394   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3395   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3396   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3397   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3398   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3399   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3400   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3401   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3402   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3403   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3404   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3405   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3406   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3407   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3408   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3409   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3410   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3411   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3412   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3413   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3414   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3415   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3416   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3417   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3418   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3419   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3420   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3421   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3422   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3423   minorSize = 0;\r
3424   }\r
3425 }\r
3426 \r
3427 HBITMAP\r
3428 PieceBitmap(ChessSquare p, int kind)\r
3429 {\r
3430   if ((int) p >= (int) BlackPawn)\r
3431     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3432 \r
3433   return pieceBitmap[kind][(int) p];\r
3434 }\r
3435 \r
3436 /***************************************************************/\r
3437 \r
3438 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3439 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3440 /*\r
3441 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3442 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3443 */\r
3444 \r
3445 VOID\r
3446 SquareToPos(int row, int column, int * x, int * y)\r
3447 {\r
3448   if (flipView) {\r
3449     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3450     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3451   } else {\r
3452     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3453     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3454   }\r
3455 }\r
3456 \r
3457 VOID\r
3458 DrawCoordsOnDC(HDC hdc)\r
3459 {\r
3460   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
3461   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
3462   char str[2] = { NULLCHAR, NULLCHAR };\r
3463   int oldMode, oldAlign, x, y, start, i;\r
3464   HFONT oldFont;\r
3465   HBRUSH oldBrush;\r
3466 \r
3467   if (!appData.showCoords)\r
3468     return;\r
3469 \r
3470   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3471 \r
3472   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3473   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3474   oldAlign = GetTextAlign(hdc);\r
3475   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3476 \r
3477   y = boardRect.top + lineGap;\r
3478   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3479 \r
3480   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3481   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3482     str[0] = files[start + i];\r
3483     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3484     y += squareSize + lineGap;\r
3485   }\r
3486 \r
3487   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3488 \r
3489   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3490   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3491     str[0] = ranks[start + i];\r
3492     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3493     x += squareSize + lineGap;\r
3494   }    \r
3495 \r
3496   SelectObject(hdc, oldBrush);\r
3497   SetBkMode(hdc, oldMode);\r
3498   SetTextAlign(hdc, oldAlign);\r
3499   SelectObject(hdc, oldFont);\r
3500 }\r
3501 \r
3502 VOID\r
3503 DrawGridOnDC(HDC hdc)\r
3504 {\r
3505   HPEN oldPen;\r
3506  \r
3507   if (lineGap != 0) {\r
3508     oldPen = SelectObject(hdc, gridPen);\r
3509     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3510     SelectObject(hdc, oldPen);\r
3511   }\r
3512 }\r
3513 \r
3514 #define HIGHLIGHT_PEN 0\r
3515 #define PREMOVE_PEN   1\r
3516 \r
3517 VOID\r
3518 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3519 {\r
3520   int x1, y1;\r
3521   HPEN oldPen, hPen;\r
3522   if (lineGap == 0) return;\r
3523   if (flipView) {\r
3524     x1 = boardRect.left +\r
3525       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3526     y1 = boardRect.top +\r
3527       lineGap/2 + y * (squareSize + lineGap);\r
3528   } else {\r
3529     x1 = boardRect.left +\r
3530       lineGap/2 + x * (squareSize + lineGap);\r
3531     y1 = boardRect.top +\r
3532       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3533   }\r
3534   hPen = pen ? premovePen : highlightPen;\r
3535   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3536   MoveToEx(hdc, x1, y1, NULL);\r
3537   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3538   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3539   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3540   LineTo(hdc, x1, y1);\r
3541   SelectObject(hdc, oldPen);\r
3542 }\r
3543 \r
3544 VOID\r
3545 DrawHighlightsOnDC(HDC hdc)\r
3546 {\r
3547   int i;\r
3548   for (i=0; i<2; i++) {\r
3549     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3550       DrawHighlightOnDC(hdc, TRUE,\r
3551                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3552                         HIGHLIGHT_PEN);\r
3553   }\r
3554   for (i=0; i<2; i++) {\r
3555     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3556         premoveHighlightInfo.sq[i].y >= 0) {\r
3557         DrawHighlightOnDC(hdc, TRUE,\r
3558                           premoveHighlightInfo.sq[i].x, \r
3559                           premoveHighlightInfo.sq[i].y,\r
3560                           PREMOVE_PEN);\r
3561     }\r
3562   }\r
3563 }\r
3564 \r
3565 /* Note: sqcolor is used only in monoMode */\r
3566 /* Note that this code is largely duplicated in woptions.c,\r
3567    function DrawSampleSquare, so that needs to be updated too */\r
3568 VOID\r
3569 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3570 {\r
3571   HBITMAP oldBitmap;\r
3572   HBRUSH oldBrush;\r
3573   int tmpSize;\r
3574 \r
3575   if (appData.blindfold) return;\r
3576 \r
3577   /* [AS] Use font-based pieces if needed */\r
3578   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3579     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3580     CreatePiecesFromFont();\r
3581 \r
3582     if( fontBitmapSquareSize == squareSize ) {\r
3583         int index = TranslatePieceToFontPiece(piece);\r
3584 \r
3585         SelectObject( tmphdc, hPieceMask[ index ] );\r
3586 \r
3587         BitBlt( hdc,\r
3588             x, y,\r
3589             squareSize, squareSize,\r
3590             tmphdc,\r
3591             0, 0,\r
3592             SRCAND );\r
3593 \r
3594         SelectObject( tmphdc, hPieceFace[ index ] );\r
3595 \r
3596         BitBlt( hdc,\r
3597             x, y,\r
3598             squareSize, squareSize,\r
3599             tmphdc,\r
3600             0, 0,\r
3601             SRCPAINT );\r
3602 \r
3603         return;\r
3604     }\r
3605   }\r
3606 \r
3607   if (appData.monoMode) {\r
3608     SelectObject(tmphdc, PieceBitmap(piece, \r
3609       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3610     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3611            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3612   } else {\r
3613     tmpSize = squareSize;\r
3614     if(minorSize &&\r
3615         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3616          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3617       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3618       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3619       x += (squareSize - minorSize)>>1;\r
3620       y += squareSize - minorSize - 2;\r
3621       tmpSize = minorSize;\r
3622     }\r
3623     if (color || appData.allWhite ) {\r
3624       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3625       if( color )\r
3626               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3627       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3628       if(appData.upsideDown && color==flipView)\r
3629         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3630       else\r
3631         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3632       /* Use black for outline of white pieces */\r
3633       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3634       if(appData.upsideDown && color==flipView)\r
3635         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3636       else\r
3637         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3638     } else {\r
3639       /* Use square color for details of black pieces */\r
3640       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3641       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3642       if(appData.upsideDown && !flipView)\r
3643         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3644       else\r
3645         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3646     }\r
3647     SelectObject(hdc, oldBrush);\r
3648     SelectObject(tmphdc, oldBitmap);\r
3649   }\r
3650 }\r
3651 \r
3652 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3653 int GetBackTextureMode( int algo )\r
3654 {\r
3655     int result = BACK_TEXTURE_MODE_DISABLED;\r
3656 \r
3657     switch( algo ) \r
3658     {\r
3659         case BACK_TEXTURE_MODE_PLAIN:\r
3660             result = 1; /* Always use identity map */\r
3661             break;\r
3662         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3663             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3664             break;\r
3665     }\r
3666 \r
3667     return result;\r
3668 }\r
3669 \r
3670 /* \r
3671     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3672     to handle redraws cleanly (as random numbers would always be different).\r
3673 */\r
3674 VOID RebuildTextureSquareInfo()\r
3675 {\r
3676     BITMAP bi;\r
3677     int lite_w = 0;\r
3678     int lite_h = 0;\r
3679     int dark_w = 0;\r
3680     int dark_h = 0;\r
3681     int row;\r
3682     int col;\r
3683 \r
3684     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3685 \r
3686     if( liteBackTexture != NULL ) {\r
3687         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3688             lite_w = bi.bmWidth;\r
3689             lite_h = bi.bmHeight;\r
3690         }\r
3691     }\r
3692 \r
3693     if( darkBackTexture != NULL ) {\r
3694         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3695             dark_w = bi.bmWidth;\r
3696             dark_h = bi.bmHeight;\r
3697         }\r
3698     }\r
3699 \r
3700     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3701         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3702             if( (col + row) & 1 ) {\r
3703                 /* Lite square */\r
3704                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3705                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3706                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3707                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3708                 }\r
3709             }\r
3710             else {\r
3711                 /* Dark square */\r
3712                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3713                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3714                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3715                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3716                 }\r
3717             }\r
3718         }\r
3719     }\r
3720 }\r
3721 \r
3722 /* [AS] Arrow highlighting support */\r
3723 \r
3724 static int A_WIDTH = 5; /* Width of arrow body */\r
3725 \r
3726 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3727 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3728 \r
3729 static double Sqr( double x )\r
3730 {\r
3731     return x*x;\r
3732 }\r
3733 \r
3734 static int Round( double x )\r
3735 {\r
3736     return (int) (x + 0.5);\r
3737 }\r
3738 \r
3739 /* Draw an arrow between two points using current settings */\r
3740 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3741 {\r
3742     POINT arrow[7];\r
3743     double dx, dy, j, k, x, y;\r
3744 \r
3745     if( d_x == s_x ) {\r
3746         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3747 \r
3748         arrow[0].x = s_x + A_WIDTH;\r
3749         arrow[0].y = s_y;\r
3750 \r
3751         arrow[1].x = s_x + A_WIDTH;\r
3752         arrow[1].y = d_y - h;\r
3753 \r
3754         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3755         arrow[2].y = d_y - h;\r
3756 \r
3757         arrow[3].x = d_x;\r
3758         arrow[3].y = d_y;\r
3759 \r
3760         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3761         arrow[4].y = d_y - h;\r
3762 \r
3763         arrow[5].x = s_x - A_WIDTH;\r
3764         arrow[5].y = d_y - h;\r
3765 \r
3766         arrow[6].x = s_x - A_WIDTH;\r
3767         arrow[6].y = s_y;\r
3768     }\r
3769     else if( d_y == s_y ) {\r
3770         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3771 \r
3772         arrow[0].x = s_x;\r
3773         arrow[0].y = s_y + A_WIDTH;\r
3774 \r
3775         arrow[1].x = d_x - w;\r
3776         arrow[1].y = s_y + A_WIDTH;\r
3777 \r
3778         arrow[2].x = d_x - w;\r
3779         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3780 \r
3781         arrow[3].x = d_x;\r
3782         arrow[3].y = d_y;\r
3783 \r
3784         arrow[4].x = d_x - w;\r
3785         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3786 \r
3787         arrow[5].x = d_x - w;\r
3788         arrow[5].y = s_y - A_WIDTH;\r
3789 \r
3790         arrow[6].x = s_x;\r
3791         arrow[6].y = s_y - A_WIDTH;\r
3792     }\r
3793     else {\r
3794         /* [AS] Needed a lot of paper for this! :-) */\r
3795         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3796         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3797   \r
3798         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3799 \r
3800         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3801 \r
3802         x = s_x;\r
3803         y = s_y;\r
3804 \r
3805         arrow[0].x = Round(x - j);\r
3806         arrow[0].y = Round(y + j*dx);\r
3807 \r
3808         arrow[1].x = Round(x + j);\r
3809         arrow[1].y = Round(y - j*dx);\r
3810 \r
3811         if( d_x > s_x ) {\r
3812             x = (double) d_x - k;\r
3813             y = (double) d_y - k*dy;\r
3814         }\r
3815         else {\r
3816             x = (double) d_x + k;\r
3817             y = (double) d_y + k*dy;\r
3818         }\r
3819 \r
3820         arrow[2].x = Round(x + j);\r
3821         arrow[2].y = Round(y - j*dx);\r
3822 \r
3823         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3824         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3825 \r
3826         arrow[4].x = d_x;\r
3827         arrow[4].y = d_y;\r
3828 \r
3829         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3830         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3831 \r
3832         arrow[6].x = Round(x - j);\r
3833         arrow[6].y = Round(y + j*dx);\r
3834     }\r
3835 \r
3836     Polygon( hdc, arrow, 7 );\r
3837 }\r
3838 \r
3839 /* [AS] Draw an arrow between two squares */\r
3840 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3841 {\r
3842     int s_x, s_y, d_x, d_y;\r
3843     HPEN hpen;\r
3844     HPEN holdpen;\r
3845     HBRUSH hbrush;\r
3846     HBRUSH holdbrush;\r
3847     LOGBRUSH stLB;\r
3848 \r
3849     if( s_col == d_col && s_row == d_row ) {\r
3850         return;\r
3851     }\r
3852 \r
3853     /* Get source and destination points */\r
3854     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3855     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3856 \r
3857     if( d_y > s_y ) {\r
3858         d_y += squareSize / 4;\r
3859     }\r
3860     else if( d_y < s_y ) {\r
3861         d_y += 3 * squareSize / 4;\r
3862     }\r
3863     else {\r
3864         d_y += squareSize / 2;\r
3865     }\r
3866 \r
3867     if( d_x > s_x ) {\r
3868         d_x += squareSize / 4;\r
3869     }\r
3870     else if( d_x < s_x ) {\r
3871         d_x += 3 * squareSize / 4;\r
3872     }\r
3873     else {\r
3874         d_x += squareSize / 2;\r
3875     }\r
3876 \r
3877     s_x += squareSize / 2;\r
3878     s_y += squareSize / 2;\r
3879 \r
3880     /* Adjust width */\r
3881     A_WIDTH = squareSize / 14;\r
3882 \r
3883     /* Draw */\r
3884     stLB.lbStyle = BS_SOLID;\r
3885     stLB.lbColor = appData.highlightArrowColor;\r
3886     stLB.lbHatch = 0;\r
3887 \r
3888     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3889     holdpen = SelectObject( hdc, hpen );\r
3890     hbrush = CreateBrushIndirect( &stLB );\r
3891     holdbrush = SelectObject( hdc, hbrush );\r
3892 \r
3893     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3894 \r
3895     SelectObject( hdc, holdpen );\r
3896     SelectObject( hdc, holdbrush );\r
3897     DeleteObject( hpen );\r
3898     DeleteObject( hbrush );\r
3899 }\r
3900 \r
3901 BOOL HasHighlightInfo()\r
3902 {\r
3903     BOOL result = FALSE;\r
3904 \r
3905     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3906         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3907     {\r
3908         result = TRUE;\r
3909     }\r
3910 \r
3911     return result;\r
3912 }\r
3913 \r
3914 BOOL IsDrawArrowEnabled()\r
3915 {\r
3916     BOOL result = FALSE;\r
3917 \r
3918     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3919         result = TRUE;\r
3920     }\r
3921 \r
3922     return result;\r
3923 }\r
3924 \r
3925 VOID DrawArrowHighlight( HDC hdc )\r
3926 {\r
3927     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3928         DrawArrowBetweenSquares( hdc,\r
3929             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3930             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3931     }\r
3932 }\r
3933 \r
3934 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3935 {\r
3936     HRGN result = NULL;\r
3937 \r
3938     if( HasHighlightInfo() ) {\r
3939         int x1, y1, x2, y2;\r
3940         int sx, sy, dx, dy;\r
3941 \r
3942         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3943         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3944 \r
3945         sx = MIN( x1, x2 );\r
3946         sy = MIN( y1, y2 );\r
3947         dx = MAX( x1, x2 ) + squareSize;\r
3948         dy = MAX( y1, y2 ) + squareSize;\r
3949 \r
3950         result = CreateRectRgn( sx, sy, dx, dy );\r
3951     }\r
3952 \r
3953     return result;\r
3954 }\r
3955 \r
3956 /*\r
3957     Warning: this function modifies the behavior of several other functions. \r
3958     \r
3959     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3960     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3961     repaint is scattered all over the place, which is not good for features such as\r
3962     "arrow highlighting" that require a full repaint of the board.\r
3963 \r
3964     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3965     user interaction, when speed is not so important) but especially to avoid errors\r
3966     in the displayed graphics.\r
3967 \r
3968     In such patched places, I always try refer to this function so there is a single\r
3969     place to maintain knowledge.\r
3970     \r
3971     To restore the original behavior, just return FALSE unconditionally.\r
3972 */\r
3973 BOOL IsFullRepaintPreferrable()\r
3974 {\r
3975     BOOL result = FALSE;\r
3976 \r
3977     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3978         /* Arrow may appear on the board */\r
3979         result = TRUE;\r
3980     }\r
3981 \r
3982     return result;\r
3983 }\r
3984 \r
3985 /* \r
3986     This function is called by DrawPosition to know whether a full repaint must\r
3987     be forced or not.\r
3988 \r
3989     Only DrawPosition may directly call this function, which makes use of \r
3990     some state information. Other function should call DrawPosition specifying \r
3991     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
3992 */\r
3993 BOOL DrawPositionNeedsFullRepaint()\r
3994 {\r
3995     BOOL result = FALSE;\r
3996 \r
3997     /* \r
3998         Probably a slightly better policy would be to trigger a full repaint\r
3999         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4000         but animation is fast enough that it's difficult to notice.\r
4001     */\r
4002     if( animInfo.piece == EmptySquare ) {\r
4003         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
4004             result = TRUE;\r
4005         }\r
4006     }\r
4007 \r
4008     return result;\r
4009 }\r
4010 \r
4011 VOID\r
4012 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4013 {\r
4014   int row, column, x, y, square_color, piece_color;\r
4015   ChessSquare piece;\r
4016   HBRUSH oldBrush;\r
4017   HDC texture_hdc = NULL;\r
4018 \r
4019   /* [AS] Initialize background textures if needed */\r
4020   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4021       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4022       if( backTextureSquareSize != squareSize \r
4023        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
4024           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
4025           backTextureSquareSize = squareSize;\r
4026           RebuildTextureSquareInfo();\r
4027       }\r
4028 \r
4029       texture_hdc = CreateCompatibleDC( hdc );\r
4030   }\r
4031 \r
4032   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4033     for (column = 0; column < BOARD_WIDTH; column++) {\r
4034   \r
4035       SquareToPos(row, column, &x, &y);\r
4036 \r
4037       piece = board[row][column];\r
4038 \r
4039       square_color = ((column + row) % 2) == 1;\r
4040       if( gameInfo.variant == VariantXiangqi ) {\r
4041           square_color = !InPalace(row, column);\r
4042           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4043           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4044       }\r
4045       piece_color = (int) piece < (int) BlackPawn;\r
4046 \r
4047 \r
4048       /* [HGM] holdings file: light square or black */\r
4049       if(column == BOARD_LEFT-2) {\r
4050             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4051                 square_color = 1;\r
4052             else {\r
4053                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4054                 continue;\r
4055             }\r
4056       } else\r
4057       if(column == BOARD_RGHT + 1 ) {\r
4058             if( row < gameInfo.holdingsSize )\r
4059                 square_color = 1;\r
4060             else {\r
4061                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4062                 continue;\r
4063             }\r
4064       }\r
4065       if(column == BOARD_LEFT-1 ) /* left align */\r
4066             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4067       else if( column == BOARD_RGHT) /* right align */\r
4068             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4069       else\r
4070       if (appData.monoMode) {\r
4071         if (piece == EmptySquare) {\r
4072           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4073                  square_color ? WHITENESS : BLACKNESS);\r
4074         } else {\r
4075           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4076         }\r
4077       } \r
4078       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4079           /* [AS] Draw the square using a texture bitmap */\r
4080           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4081           int r = row, c = column; // [HGM] do not flip board in flipView\r
4082           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4083 \r
4084           DrawTile( x, y, \r
4085               squareSize, squareSize, \r
4086               hdc, \r
4087               texture_hdc,\r
4088               backTextureSquareInfo[r][c].mode,\r
4089               backTextureSquareInfo[r][c].x,\r
4090               backTextureSquareInfo[r][c].y );\r
4091 \r
4092           SelectObject( texture_hdc, hbm );\r
4093 \r
4094           if (piece != EmptySquare) {\r
4095               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4096           }\r
4097       }\r
4098       else {\r
4099         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4100 \r
4101         oldBrush = SelectObject(hdc, brush );\r
4102         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4103         SelectObject(hdc, oldBrush);\r
4104         if (piece != EmptySquare)\r
4105           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4106       }\r
4107     }\r
4108   }\r
4109 \r
4110   if( texture_hdc != NULL ) {\r
4111     DeleteDC( texture_hdc );\r
4112   }\r
4113 }\r
4114 \r
4115 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4116 void fputDW(FILE *f, int x)\r
4117 {\r
4118         fputc(x     & 255, f);\r
4119         fputc(x>>8  & 255, f);\r
4120         fputc(x>>16 & 255, f);\r
4121         fputc(x>>24 & 255, f);\r
4122 }\r
4123 \r
4124 #define MAX_CLIPS 200   /* more than enough */\r
4125 \r
4126 VOID\r
4127 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4128 {\r
4129 //  HBITMAP bufferBitmap;\r
4130   BITMAP bi;\r
4131 //  RECT Rect;\r
4132   HDC tmphdc;\r
4133   HBITMAP hbm;\r
4134   int w = 100, h = 50;\r
4135 \r
4136   if(logo == NULL) return;\r
4137 //  GetClientRect(hwndMain, &Rect);\r
4138 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4139 //                                      Rect.bottom-Rect.top+1);\r
4140   tmphdc = CreateCompatibleDC(hdc);\r
4141   hbm = SelectObject(tmphdc, logo);\r
4142   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4143             w = bi.bmWidth;\r
4144             h = bi.bmHeight;\r
4145   }\r
4146   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4147                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4148   SelectObject(tmphdc, hbm);\r
4149   DeleteDC(tmphdc);\r
4150 }\r
4151 \r
4152 VOID\r
4153 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4154 {\r
4155   static Board lastReq, lastDrawn;\r
4156   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4157   static int lastDrawnFlipView = 0;\r
4158   static int lastReqValid = 0, lastDrawnValid = 0;\r
4159   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4160   HDC tmphdc;\r
4161   HDC hdcmem;\r
4162   HBITMAP bufferBitmap;\r
4163   HBITMAP oldBitmap;\r
4164   RECT Rect;\r
4165   HRGN clips[MAX_CLIPS];\r
4166   ChessSquare dragged_piece = EmptySquare;\r
4167 \r
4168   /* I'm undecided on this - this function figures out whether a full\r
4169    * repaint is necessary on its own, so there's no real reason to have the\r
4170    * caller tell it that.  I think this can safely be set to FALSE - but\r
4171    * if we trust the callers not to request full repaints unnessesarily, then\r
4172    * we could skip some clipping work.  In other words, only request a full\r
4173    * redraw when the majority of pieces have changed positions (ie. flip, \r
4174    * gamestart and similar)  --Hawk\r
4175    */\r
4176   Boolean fullrepaint = repaint;\r
4177 \r
4178   if( DrawPositionNeedsFullRepaint() ) {\r
4179       fullrepaint = TRUE;\r
4180   }\r
4181 \r
4182   if (board == NULL) {\r
4183     if (!lastReqValid) {\r
4184       return;\r
4185     }\r
4186     board = lastReq;\r
4187   } else {\r
4188     CopyBoard(lastReq, board);\r
4189     lastReqValid = 1;\r
4190   }\r
4191 \r
4192   if (doingSizing) {\r
4193     return;\r
4194   }\r
4195 \r
4196   if (IsIconic(hwndMain)) {\r
4197     return;\r
4198   }\r
4199 \r
4200   if (hdc == NULL) {\r
4201     hdc = GetDC(hwndMain);\r
4202     if (!appData.monoMode) {\r
4203       SelectPalette(hdc, hPal, FALSE);\r
4204       RealizePalette(hdc);\r
4205     }\r
4206     releaseDC = TRUE;\r
4207   } else {\r
4208     releaseDC = FALSE;\r
4209   }\r
4210 \r
4211   /* Create some work-DCs */\r
4212   hdcmem = CreateCompatibleDC(hdc);\r
4213   tmphdc = CreateCompatibleDC(hdc);\r
4214 \r
4215   /* If dragging is in progress, we temporarely remove the piece */\r
4216   /* [HGM] or temporarily decrease count if stacked              */\r
4217   /*       !! Moved to before board compare !!                   */\r
4218   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4219     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4220     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4221             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4222         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4223     } else \r
4224     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4225             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4226         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4227     } else \r
4228         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4229   }\r
4230 \r
4231   /* Figure out which squares need updating by comparing the \r
4232    * newest board with the last drawn board and checking if\r
4233    * flipping has changed.\r
4234    */\r
4235   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4236     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4237       for (column = 0; column < BOARD_WIDTH; column++) {\r
4238         if (lastDrawn[row][column] != board[row][column]) {\r
4239           SquareToPos(row, column, &x, &y);\r
4240           clips[num_clips++] =\r
4241             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4242         }\r
4243       }\r
4244     }\r
4245     for (i=0; i<2; i++) {\r
4246       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4247           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4248         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4249             lastDrawnHighlight.sq[i].y >= 0) {\r
4250           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4251                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4252           clips[num_clips++] =\r
4253             CreateRectRgn(x - lineGap, y - lineGap, \r
4254                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4255         }\r
4256         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4257           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4258           clips[num_clips++] =\r
4259             CreateRectRgn(x - lineGap, y - lineGap, \r
4260                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4261         }\r
4262       }\r
4263     }\r
4264     for (i=0; i<2; i++) {\r
4265       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4266           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4267         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4268             lastDrawnPremove.sq[i].y >= 0) {\r
4269           SquareToPos(lastDrawnPremove.sq[i].y,\r
4270                       lastDrawnPremove.sq[i].x, &x, &y);\r
4271           clips[num_clips++] =\r
4272             CreateRectRgn(x - lineGap, y - lineGap, \r
4273                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4274         }\r
4275         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4276             premoveHighlightInfo.sq[i].y >= 0) {\r
4277           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4278                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4279           clips[num_clips++] =\r
4280             CreateRectRgn(x - lineGap, y - lineGap, \r
4281                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4282         }\r
4283       }\r
4284     }\r
4285   } else {\r
4286     fullrepaint = TRUE;\r
4287   }\r
4288 \r
4289   /* Create a buffer bitmap - this is the actual bitmap\r
4290    * being written to.  When all the work is done, we can\r
4291    * copy it to the real DC (the screen).  This avoids\r
4292    * the problems with flickering.\r
4293    */\r
4294   GetClientRect(hwndMain, &Rect);\r
4295   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4296                                         Rect.bottom-Rect.top+1);\r
4297   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4298   if (!appData.monoMode) {\r
4299     SelectPalette(hdcmem, hPal, FALSE);\r
4300   }\r
4301 \r
4302   /* Create clips for dragging */\r
4303   if (!fullrepaint) {\r
4304     if (dragInfo.from.x >= 0) {\r
4305       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4306       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4307     }\r
4308     if (dragInfo.start.x >= 0) {\r
4309       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4310       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4311     }\r
4312     if (dragInfo.pos.x >= 0) {\r
4313       x = dragInfo.pos.x - squareSize / 2;\r
4314       y = dragInfo.pos.y - squareSize / 2;\r
4315       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4316     }\r
4317     if (dragInfo.lastpos.x >= 0) {\r
4318       x = dragInfo.lastpos.x - squareSize / 2;\r
4319       y = dragInfo.lastpos.y - squareSize / 2;\r
4320       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4321     }\r
4322   }\r
4323 \r
4324   /* Are we animating a move?  \r
4325    * If so, \r
4326    *   - remove the piece from the board (temporarely)\r
4327    *   - calculate the clipping region\r
4328    */\r
4329   if (!fullrepaint) {\r
4330     if (animInfo.piece != EmptySquare) {\r
4331       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4332       x = boardRect.left + animInfo.lastpos.x;\r
4333       y = boardRect.top + animInfo.lastpos.y;\r
4334       x2 = boardRect.left + animInfo.pos.x;\r
4335       y2 = boardRect.top + animInfo.pos.y;\r
4336       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4337       /* Slight kludge.  The real problem is that after AnimateMove is\r
4338          done, the position on the screen does not match lastDrawn.\r
4339          This currently causes trouble only on e.p. captures in\r
4340          atomic, where the piece moves to an empty square and then\r
4341          explodes.  The old and new positions both had an empty square\r
4342          at the destination, but animation has drawn a piece there and\r
4343          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4344       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4345     }\r
4346   }\r
4347 \r
4348   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4349   if (num_clips == 0)\r
4350     fullrepaint = TRUE;\r
4351 \r
4352   /* Set clipping on the memory DC */\r
4353   if (!fullrepaint) {\r
4354     SelectClipRgn(hdcmem, clips[0]);\r
4355     for (x = 1; x < num_clips; x++) {\r
4356       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4357         abort();  // this should never ever happen!\r
4358     }\r
4359   }\r
4360 \r
4361   /* Do all the drawing to the memory DC */\r
4362   if(explodeInfo.radius) { // [HGM] atomic\r
4363         HBRUSH oldBrush;\r
4364         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4365         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4366         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4367         x += squareSize/2;\r
4368         y += squareSize/2;\r
4369         if(!fullrepaint) {\r
4370           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4371           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4372         }\r
4373         DrawGridOnDC(hdcmem);\r
4374         DrawHighlightsOnDC(hdcmem);\r
4375         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4376         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4377         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4378         SelectObject(hdcmem, oldBrush);\r
4379   } else {\r
4380     DrawGridOnDC(hdcmem);\r
4381     DrawHighlightsOnDC(hdcmem);\r
4382     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4383   }\r
4384   if(logoHeight) {\r
4385         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4386         if(appData.autoLogo) {\r
4387           \r
4388           switch(gameMode) { // pick logos based on game mode\r
4389             case IcsObserving:\r
4390                 whiteLogo = second.programLogo; // ICS logo\r
4391                 blackLogo = second.programLogo;\r
4392             default:\r
4393                 break;\r
4394             case IcsPlayingWhite:\r
4395                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4396                 blackLogo = second.programLogo; // ICS logo\r
4397                 break;\r
4398             case IcsPlayingBlack:\r
4399                 whiteLogo = second.programLogo; // ICS logo\r
4400                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4401                 break;\r
4402             case TwoMachinesPlay:\r
4403                 if(first.twoMachinesColor[0] == 'b') {\r
4404                     whiteLogo = second.programLogo;\r
4405                     blackLogo = first.programLogo;\r
4406                 }\r
4407                 break;\r
4408             case MachinePlaysWhite:\r
4409                 blackLogo = userLogo;\r
4410                 break;\r
4411             case MachinePlaysBlack:\r
4412                 whiteLogo = userLogo;\r
4413                 blackLogo = first.programLogo;\r
4414           }\r
4415         }\r
4416         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4417         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4418   }\r
4419 \r
4420   if( appData.highlightMoveWithArrow ) {\r
4421     DrawArrowHighlight(hdcmem);\r
4422   }\r
4423 \r
4424   DrawCoordsOnDC(hdcmem);\r
4425 \r
4426   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4427                  /* to make sure lastDrawn contains what is actually drawn */\r
4428 \r
4429   /* Put the dragged piece back into place and draw it (out of place!) */\r
4430     if (dragged_piece != EmptySquare) {\r
4431     /* [HGM] or restack */\r
4432     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4433                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4434     else\r
4435     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4436                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4437     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4438     x = dragInfo.pos.x - squareSize / 2;\r
4439     y = dragInfo.pos.y - squareSize / 2;\r
4440     DrawPieceOnDC(hdcmem, dragged_piece,\r
4441                   ((int) dragged_piece < (int) BlackPawn), \r
4442                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4443   }   \r
4444   \r
4445   /* Put the animated piece back into place and draw it */\r
4446   if (animInfo.piece != EmptySquare) {\r
4447     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4448     x = boardRect.left + animInfo.pos.x;\r
4449     y = boardRect.top + animInfo.pos.y;\r
4450     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4451                   ((int) animInfo.piece < (int) BlackPawn),\r
4452                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4453   }\r
4454 \r
4455   /* Release the bufferBitmap by selecting in the old bitmap \r
4456    * and delete the memory DC\r
4457    */\r
4458   SelectObject(hdcmem, oldBitmap);\r
4459   DeleteDC(hdcmem);\r
4460 \r
4461   /* Set clipping on the target DC */\r
4462   if (!fullrepaint) {\r
4463     SelectClipRgn(hdc, clips[0]);\r
4464     for (x = 1; x < num_clips; x++) {\r
4465       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4466         abort();   // this should never ever happen!\r
4467     } \r
4468   }\r
4469 \r
4470   /* Copy the new bitmap onto the screen in one go.\r
4471    * This way we avoid any flickering\r
4472    */\r
4473   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4474   BitBlt(hdc, boardRect.left, boardRect.top,\r
4475          boardRect.right - boardRect.left,\r
4476          boardRect.bottom - boardRect.top,\r
4477          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4478   if(saveDiagFlag) { \r
4479     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4480     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4481 \r
4482     GetObject(bufferBitmap, sizeof(b), &b);\r
4483     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4484         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4485         bih.biWidth = b.bmWidth;\r
4486         bih.biHeight = b.bmHeight;\r
4487         bih.biPlanes = 1;\r
4488         bih.biBitCount = b.bmBitsPixel;\r
4489         bih.biCompression = 0;\r
4490         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4491         bih.biXPelsPerMeter = 0;\r
4492         bih.biYPelsPerMeter = 0;\r
4493         bih.biClrUsed = 0;\r
4494         bih.biClrImportant = 0;\r
4495 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4496 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4497         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4498 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4499 \r
4500         wb = b.bmWidthBytes;\r
4501         // count colors\r
4502         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4503                 int k = ((int*) pData)[i];\r
4504                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4505                 if(j >= 16) break;\r
4506                 color[j] = k;\r
4507                 if(j >= nrColors) nrColors = j+1;\r
4508         }\r
4509         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4510                 INT p = 0;\r
4511                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4512                     for(w=0; w<(wb>>2); w+=2) {\r
4513                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4514                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4515                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4516                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4517                         pData[p++] = m | j<<4;\r
4518                     }\r
4519                     while(p&3) pData[p++] = 0;\r
4520                 }\r
4521                 fac = 3;\r
4522                 wb = ((wb+31)>>5)<<2;\r
4523         }\r
4524         // write BITMAPFILEHEADER\r
4525         fprintf(diagFile, "BM");\r
4526         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4527         fputDW(diagFile, 0);\r
4528         fputDW(diagFile, 0x36 + (fac?64:0));\r
4529         // write BITMAPINFOHEADER\r
4530         fputDW(diagFile, 40);\r
4531         fputDW(diagFile, b.bmWidth);\r
4532         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4533         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4534         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4535         fputDW(diagFile, 0);\r
4536         fputDW(diagFile, 0);\r
4537         fputDW(diagFile, 0);\r
4538         fputDW(diagFile, 0);\r
4539         fputDW(diagFile, 0);\r
4540         fputDW(diagFile, 0);\r
4541         // write color table\r
4542         if(fac)\r
4543         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4544         // write bitmap data\r
4545         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4546                 fputc(pData[i], diagFile);\r
4547      }\r
4548   }\r
4549 \r
4550   SelectObject(tmphdc, oldBitmap);\r
4551 \r
4552   /* Massive cleanup */\r
4553   for (x = 0; x < num_clips; x++)\r
4554     DeleteObject(clips[x]);\r
4555 \r
4556   DeleteDC(tmphdc);\r
4557   DeleteObject(bufferBitmap);\r
4558 \r
4559   if (releaseDC) \r
4560     ReleaseDC(hwndMain, hdc);\r
4561   \r
4562   if (lastDrawnFlipView != flipView) {\r
4563     if (flipView)\r
4564       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4565     else\r
4566       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4567   }\r
4568 \r
4569 /*  CopyBoard(lastDrawn, board);*/\r
4570   lastDrawnHighlight = highlightInfo;\r
4571   lastDrawnPremove   = premoveHighlightInfo;\r
4572   lastDrawnFlipView = flipView;\r
4573   lastDrawnValid = 1;\r
4574 }\r
4575 \r
4576 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4577 int\r
4578 SaveDiagram(f)\r
4579      FILE *f;\r
4580 {\r
4581     saveDiagFlag = 1; diagFile = f;\r
4582     HDCDrawPosition(NULL, TRUE, NULL);\r
4583 \r
4584     saveDiagFlag = 0;\r
4585 \r
4586 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4587     \r
4588     fclose(f);\r
4589     return TRUE;\r
4590 }\r
4591 \r
4592 \r
4593 /*---------------------------------------------------------------------------*\\r
4594 | CLIENT PAINT PROCEDURE\r
4595 |   This is the main event-handler for the WM_PAINT message.\r
4596 |\r
4597 \*---------------------------------------------------------------------------*/\r
4598 VOID\r
4599 PaintProc(HWND hwnd)\r
4600 {\r
4601   HDC         hdc;\r
4602   PAINTSTRUCT ps;\r
4603   HFONT       oldFont;\r
4604 \r
4605   if((hdc = BeginPaint(hwnd, &ps))) {\r
4606     if (IsIconic(hwnd)) {\r
4607       DrawIcon(hdc, 2, 2, iconCurrent);\r
4608     } else {\r
4609       if (!appData.monoMode) {\r
4610         SelectPalette(hdc, hPal, FALSE);\r
4611         RealizePalette(hdc);\r
4612       }\r
4613       HDCDrawPosition(hdc, 1, NULL);\r
4614       oldFont =\r
4615         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4616       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4617                  ETO_CLIPPED|ETO_OPAQUE,\r
4618                  &messageRect, messageText, strlen(messageText), NULL);\r
4619       SelectObject(hdc, oldFont);\r
4620       DisplayBothClocks();\r
4621     }\r
4622     EndPaint(hwnd,&ps);\r
4623   }\r
4624 \r
4625   return;\r
4626 }\r
4627 \r
4628 \r
4629 /*\r
4630  * If the user selects on a border boundary, return -1; if off the board,\r
4631  *   return -2.  Otherwise map the event coordinate to the square.\r
4632  * The offset boardRect.left or boardRect.top must already have been\r
4633  *   subtracted from x.\r
4634  */\r
4635 int EventToSquare(x, limit)\r
4636      int x, limit;\r
4637 {\r
4638   if (x <= 0)\r
4639     return -2;\r
4640   if (x < lineGap)\r
4641     return -1;\r
4642   x -= lineGap;\r
4643   if ((x % (squareSize + lineGap)) >= squareSize)\r
4644     return -1;\r
4645   x /= (squareSize + lineGap);\r
4646     if (x >= limit)\r
4647     return -2;\r
4648   return x;\r
4649 }\r
4650 \r
4651 typedef struct {\r
4652   char piece;\r
4653   int command;\r
4654   char* name;\r
4655 } DropEnable;\r
4656 \r
4657 DropEnable dropEnables[] = {\r
4658   { 'P', DP_Pawn, "Pawn" },\r
4659   { 'N', DP_Knight, "Knight" },\r
4660   { 'B', DP_Bishop, "Bishop" },\r
4661   { 'R', DP_Rook, "Rook" },\r
4662   { 'Q', DP_Queen, "Queen" },\r
4663 };\r
4664 \r
4665 VOID\r
4666 SetupDropMenu(HMENU hmenu)\r
4667 {\r
4668   int i, count, enable;\r
4669   char *p;\r
4670   extern char white_holding[], black_holding[];\r
4671   char item[MSG_SIZ];\r
4672 \r
4673   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4674     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4675                dropEnables[i].piece);\r
4676     count = 0;\r
4677     while (p && *p++ == dropEnables[i].piece) count++;\r
4678     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4679     enable = count > 0 || !appData.testLegality\r
4680       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4681                       && !appData.icsActive);\r
4682     ModifyMenu(hmenu, dropEnables[i].command,\r
4683                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4684                dropEnables[i].command, item);\r
4685   }\r
4686 }\r
4687 \r
4688 void DragPieceBegin(int x, int y)\r
4689 {\r
4690       dragInfo.lastpos.x = boardRect.left + x;\r
4691       dragInfo.lastpos.y = boardRect.top + y;\r
4692       dragInfo.from.x = fromX;\r
4693       dragInfo.from.y = fromY;\r
4694       dragInfo.start = dragInfo.from;\r
4695       SetCapture(hwndMain);\r
4696 }\r
4697 \r
4698 void DragPieceEnd(int x, int y)\r
4699 {\r
4700     ReleaseCapture();\r
4701     dragInfo.start.x = dragInfo.start.y = -1;\r
4702     dragInfo.from = dragInfo.start;\r
4703     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4704 }\r
4705 \r
4706 /* Event handler for mouse messages */\r
4707 VOID\r
4708 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4709 {\r
4710   int x, y;\r
4711   POINT pt;\r
4712   static int recursive = 0;\r
4713   HMENU hmenu;\r
4714   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4715 \r
4716   if (recursive) {\r
4717     if (message == WM_MBUTTONUP) {\r
4718       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4719          to the middle button: we simulate pressing the left button too!\r
4720          */\r
4721       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4722       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4723     }\r
4724     return;\r
4725   }\r
4726   recursive++;\r
4727   \r
4728   pt.x = LOWORD(lParam);\r
4729   pt.y = HIWORD(lParam);\r
4730   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4731   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4732   if (!flipView && y >= 0) {\r
4733     y = BOARD_HEIGHT - 1 - y;\r
4734   }\r
4735   if (flipView && x >= 0) {\r
4736     x = BOARD_WIDTH - 1 - x;\r
4737   }\r
4738 \r
4739   switch (message) {\r
4740   case WM_LBUTTONDOWN:\r
4741       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4742         if (gameMode == EditPosition) {\r
4743           SetWhiteToPlayEvent();\r
4744         } else if (gameMode == IcsPlayingBlack ||\r
4745                    gameMode == MachinePlaysWhite) {\r
4746           CallFlagEvent();\r
4747         } else if (gameMode == EditGame) {\r
4748           AdjustClock(flipClock, -1);\r
4749         }\r
4750       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4751         if (gameMode == EditPosition) {\r
4752           SetBlackToPlayEvent();\r
4753         } else if (gameMode == IcsPlayingWhite ||\r
4754                    gameMode == MachinePlaysBlack) {\r
4755           CallFlagEvent();\r
4756         } else if (gameMode == EditGame) {\r
4757           AdjustClock(!flipClock, -1);\r
4758         }\r
4759       }\r
4760       dragInfo.start.x = dragInfo.start.y = -1;\r
4761       dragInfo.from = dragInfo.start;\r
4762     if(fromX == -1 && frozen) { // not sure where this is for\r
4763                 fromX = fromY = -1; \r
4764       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4765       break;\r
4766     }\r
4767       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4768       DrawPosition(TRUE, NULL);\r
4769     break;\r
4770 \r
4771   case WM_LBUTTONUP:\r
4772       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4773       DrawPosition(TRUE, NULL);\r
4774     break;\r
4775 \r
4776   case WM_MOUSEMOVE:\r
4777     if ((appData.animateDragging || appData.highlightDragging)\r
4778         && (wParam & MK_LBUTTON)\r
4779         && dragInfo.from.x >= 0) \r
4780     {\r
4781       BOOL full_repaint = FALSE;\r
4782 \r
4783       if (appData.animateDragging) {\r
4784         dragInfo.pos = pt;\r
4785       }\r
4786       if (appData.highlightDragging) {\r
4787         SetHighlights(fromX, fromY, x, y);\r
4788         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4789             full_repaint = TRUE;\r
4790         }\r
4791       }\r
4792       \r
4793       DrawPosition( full_repaint, NULL);\r
4794       \r
4795       dragInfo.lastpos = dragInfo.pos;\r
4796     }\r
4797     break;\r
4798 \r
4799   case WM_MOUSEWHEEL: // [DM]\r
4800     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4801        /* Mouse Wheel is being rolled forward\r
4802         * Play moves forward\r
4803         */\r
4804        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
4805                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4806        /* Mouse Wheel is being rolled backward\r
4807         * Play moves backward\r
4808         */\r
4809        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
4810                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4811     }\r
4812     break;\r
4813 \r
4814   case WM_MBUTTONDOWN:\r
4815   case WM_RBUTTONDOWN:\r
4816     ErrorPopDown();\r
4817     ReleaseCapture();\r
4818     fromX = fromY = -1;\r
4819     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4820     dragInfo.start.x = dragInfo.start.y = -1;\r
4821     dragInfo.from = dragInfo.start;\r
4822     dragInfo.lastpos = dragInfo.pos;\r
4823     if (appData.highlightDragging) {\r
4824       ClearHighlights();\r
4825     }\r
4826     if(y == -2) {\r
4827       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4828       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4829           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4830       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4831           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4832       }\r
4833     }\r
4834     DrawPosition(TRUE, NULL);\r
4835 \r
4836     switch (gameMode) {\r
4837     case EditPosition:\r
4838     case IcsExamining:\r
4839       if (x < 0 || y < 0) break;\r
4840       fromX = x;\r
4841       fromY = y;\r
4842       if (message == WM_MBUTTONDOWN) {\r
4843         buttonCount = 3;  /* even if system didn't think so */\r
4844         if (wParam & MK_SHIFT) \r
4845           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4846         else\r
4847           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4848       } else { /* message == WM_RBUTTONDOWN */\r
4849         /* Just have one menu, on the right button.  Windows users don't\r
4850            think to try the middle one, and sometimes other software steals\r
4851            it, or it doesn't really exist. */\r
4852         if(gameInfo.variant != VariantShogi)\r
4853             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4854         else\r
4855             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4856       }\r
4857       break;\r
4858     case IcsPlayingWhite:\r
4859     case IcsPlayingBlack:\r
4860     case EditGame:\r
4861     case MachinePlaysWhite:\r
4862     case MachinePlaysBlack:\r
4863       if (appData.testLegality &&\r
4864           gameInfo.variant != VariantBughouse &&\r
4865           gameInfo.variant != VariantCrazyhouse) break;\r
4866       if (x < 0 || y < 0) break;\r
4867       fromX = x;\r
4868       fromY = y;\r
4869       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4870       SetupDropMenu(hmenu);\r
4871       MenuPopup(hwnd, pt, hmenu, -1);\r
4872       break;\r
4873     default:\r
4874       break;\r
4875     }\r
4876     break;\r
4877   }\r
4878 \r
4879   recursive--;\r
4880 }\r
4881 \r
4882 /* Preprocess messages for buttons in main window */\r
4883 LRESULT CALLBACK\r
4884 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4885 {\r
4886   int id = GetWindowLong(hwnd, GWL_ID);\r
4887   int i, dir;\r
4888 \r
4889   for (i=0; i<N_BUTTONS; i++) {\r
4890     if (buttonDesc[i].id == id) break;\r
4891   }\r
4892   if (i == N_BUTTONS) return 0;\r
4893   switch (message) {\r
4894   case WM_KEYDOWN:\r
4895     switch (wParam) {\r
4896     case VK_LEFT:\r
4897     case VK_RIGHT:\r
4898       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4899       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4900       return TRUE;\r
4901     }\r
4902     break;\r
4903   case WM_CHAR:\r
4904     switch (wParam) {\r
4905     case '\r':\r
4906       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4907       return TRUE;\r
4908     default:\r
4909       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4910         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4911         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4912         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4913         SetFocus(h);\r
4914         SendMessage(h, WM_CHAR, wParam, lParam);\r
4915         return TRUE;\r
4916       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4917         PopUpMoveDialog((char)wParam);\r
4918       }\r
4919       break;\r
4920     }\r
4921     break;\r
4922   }\r
4923   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4924 }\r
4925 \r
4926 /* Process messages for Promotion dialog box */\r
4927 LRESULT CALLBACK\r
4928 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4929 {\r
4930   char promoChar;\r
4931 \r
4932   switch (message) {\r
4933   case WM_INITDIALOG: /* message: initialize dialog box */\r
4934     /* Center the dialog over the application window */\r
4935     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4936     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4937       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4938        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4939                SW_SHOW : SW_HIDE);\r
4940     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4941     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4942        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4943          PieceToChar(WhiteAngel) != '~') ||\r
4944         (PieceToChar(BlackAngel) >= 'A' &&\r
4945          PieceToChar(BlackAngel) != '~')   ) ?\r
4946                SW_SHOW : SW_HIDE);\r
4947     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4948        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4949          PieceToChar(WhiteMarshall) != '~') ||\r
4950         (PieceToChar(BlackMarshall) >= 'A' &&\r
4951          PieceToChar(BlackMarshall) != '~')   ) ?\r
4952                SW_SHOW : SW_HIDE);\r
4953     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4954     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4955        gameInfo.variant != VariantShogi ?\r
4956                SW_SHOW : SW_HIDE);\r
4957     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4958        gameInfo.variant != VariantShogi ?\r
4959                SW_SHOW : SW_HIDE);\r
4960     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4961        gameInfo.variant == VariantShogi ?\r
4962                SW_SHOW : SW_HIDE);\r
4963     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4964        gameInfo.variant == VariantShogi ?\r
4965                SW_SHOW : SW_HIDE);\r
4966     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4967        gameInfo.variant == VariantSuper ?\r
4968                SW_SHOW : SW_HIDE);\r
4969     return TRUE;\r
4970 \r
4971   case WM_COMMAND: /* message: received a command */\r
4972     switch (LOWORD(wParam)) {\r
4973     case IDCANCEL:\r
4974       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4975       ClearHighlights();\r
4976       DrawPosition(FALSE, NULL);\r
4977       return TRUE;\r
4978     case PB_King:\r
4979       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
4980       break;\r
4981     case PB_Queen:\r
4982       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
4983       break;\r
4984     case PB_Rook:\r
4985       promoChar = PieceToChar(BlackRook);\r
4986       break;\r
4987     case PB_Bishop:\r
4988       promoChar = PieceToChar(BlackBishop);\r
4989       break;\r
4990     case PB_Chancellor:\r
4991       promoChar = PieceToChar(BlackMarshall);\r
4992       break;\r
4993     case PB_Archbishop:\r
4994       promoChar = PieceToChar(BlackAngel);\r
4995       break;\r
4996     case PB_Knight:\r
4997       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
4998       break;\r
4999     default:\r
5000       return FALSE;\r
5001     }\r
5002     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5003     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5004        only show the popup when we are already sure the move is valid or\r
5005        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5006        will figure out it is a promotion from the promoChar. */\r
5007     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
5008     fromX = fromY = -1;\r
5009     if (!appData.highlightLastMove) {\r
5010       ClearHighlights();\r
5011       DrawPosition(FALSE, NULL);\r
5012     }\r
5013     return TRUE;\r
5014   }\r
5015   return FALSE;\r
5016 }\r
5017 \r
5018 /* Pop up promotion dialog */\r
5019 VOID\r
5020 PromotionPopup(HWND hwnd)\r
5021 {\r
5022   FARPROC lpProc;\r
5023 \r
5024   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5025   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5026     hwnd, (DLGPROC)lpProc);\r
5027   FreeProcInstance(lpProc);\r
5028 }\r
5029 \r
5030 void\r
5031 PromotionPopUp()\r
5032 {\r
5033   DrawPosition(TRUE, NULL);\r
5034   PromotionPopup(hwndMain);\r
5035 }\r
5036 \r
5037 /* Toggle ShowThinking */\r
5038 VOID\r
5039 ToggleShowThinking()\r
5040 {\r
5041   appData.showThinking = !appData.showThinking;\r
5042   ShowThinkingEvent();\r
5043 }\r
5044 \r
5045 VOID\r
5046 LoadGameDialog(HWND hwnd, char* title)\r
5047 {\r
5048   UINT number = 0;\r
5049   FILE *f;\r
5050   char fileTitle[MSG_SIZ];\r
5051   f = OpenFileDialog(hwnd, "rb", "",\r
5052                      appData.oldSaveStyle ? "gam" : "pgn",\r
5053                      GAME_FILT,\r
5054                      title, &number, fileTitle, NULL);\r
5055   if (f != NULL) {\r
5056     cmailMsgLoaded = FALSE;\r
5057     if (number == 0) {\r
5058       int error = GameListBuild(f);\r
5059       if (error) {\r
5060         DisplayError("Cannot build game list", error);\r
5061       } else if (!ListEmpty(&gameList) &&\r
5062                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5063         GameListPopUp(f, fileTitle);\r
5064         return;\r
5065       }\r
5066       GameListDestroy();\r
5067       number = 1;\r
5068     }\r
5069     LoadGame(f, number, fileTitle, FALSE);\r
5070   }\r
5071 }\r
5072 \r
5073 int get_term_width()\r
5074 {\r
5075     HDC hdc;\r
5076     TEXTMETRIC tm;\r
5077     RECT rc;\r
5078     HFONT hfont, hold_font;\r
5079     LOGFONT lf;\r
5080     HWND hText;\r
5081 \r
5082     if (hwndConsole)\r
5083         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5084     else\r
5085         return 79;\r
5086 \r
5087     // get the text metrics\r
5088     hdc = GetDC(hText);\r
5089     lf = font[boardSize][CONSOLE_FONT]->lf;\r
5090     if (consoleCF.dwEffects & CFE_BOLD)\r
5091         lf.lfWeight = FW_BOLD;\r
5092     if (consoleCF.dwEffects & CFE_ITALIC)\r
5093         lf.lfItalic = TRUE;\r
5094     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
5095         lf.lfStrikeOut = TRUE;\r
5096     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
5097         lf.lfUnderline = TRUE;\r
5098     hfont = CreateFontIndirect(&lf);\r
5099     hold_font = SelectObject(hdc, hfont);\r
5100     GetTextMetrics(hdc, &tm);\r
5101     SelectObject(hdc, hold_font);\r
5102     DeleteObject(hfont);\r
5103     ReleaseDC(hText, hdc);\r
5104 \r
5105     // get the rectangle\r
5106     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5107 \r
5108     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
5109 }\r
5110 \r
5111 void UpdateICSWidth(HWND hText)\r
5112 {\r
5113     LONG old_width, new_width;\r
5114 \r
5115     new_width = get_term_width(hText, FALSE);\r
5116     old_width = GetWindowLong(hText, GWL_USERDATA);\r
5117     if (new_width != old_width)\r
5118     {\r
5119         ics_update_width(new_width);\r
5120         SetWindowLong(hText, GWL_USERDATA, new_width);\r
5121     }\r
5122 }\r
5123 \r
5124 VOID\r
5125 ChangedConsoleFont()\r
5126 {\r
5127   CHARFORMAT cfmt;\r
5128   CHARRANGE tmpsel, sel;\r
5129   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5130   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5131   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5132   PARAFORMAT paraf;\r
5133 \r
5134   cfmt.cbSize = sizeof(CHARFORMAT);\r
5135   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5136   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5137   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5138    * size.  This was undocumented in the version of MSVC++ that I had\r
5139    * when I wrote the code, but is apparently documented now.\r
5140    */\r
5141   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5142   cfmt.bCharSet = f->lf.lfCharSet;\r
5143   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5144   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5145   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5146   /* Why are the following seemingly needed too? */\r
5147   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5148   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5149   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5150   tmpsel.cpMin = 0;\r
5151   tmpsel.cpMax = -1; /*999999?*/\r
5152   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5153   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5154   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5155    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5156    */\r
5157   paraf.cbSize = sizeof(paraf);\r
5158   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5159   paraf.dxStartIndent = 0;\r
5160   paraf.dxOffset = WRAP_INDENT;\r
5161   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5162   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5163   UpdateICSWidth(hText);\r
5164 }\r
5165 \r
5166 /*---------------------------------------------------------------------------*\\r
5167  *\r
5168  * Window Proc for main window\r
5169  *\r
5170 \*---------------------------------------------------------------------------*/\r
5171 \r
5172 /* Process messages for main window, etc. */\r
5173 LRESULT CALLBACK\r
5174 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5175 {\r
5176   FARPROC lpProc;\r
5177   int wmId, wmEvent;\r
5178   char *defName;\r
5179   FILE *f;\r
5180   UINT number;\r
5181   char fileTitle[MSG_SIZ];\r
5182   char buf[MSG_SIZ];\r
5183   static SnapData sd;\r
5184 \r
5185   switch (message) {\r
5186 \r
5187   case WM_PAINT: /* message: repaint portion of window */\r
5188     PaintProc(hwnd);\r
5189     break;\r
5190 \r
5191   case WM_ERASEBKGND:\r
5192     if (IsIconic(hwnd)) {\r
5193       /* Cheat; change the message */\r
5194       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5195     } else {\r
5196       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5197     }\r
5198     break;\r
5199 \r
5200   case WM_LBUTTONDOWN:\r
5201   case WM_MBUTTONDOWN:\r
5202   case WM_RBUTTONDOWN:\r
5203   case WM_LBUTTONUP:\r
5204   case WM_MBUTTONUP:\r
5205   case WM_RBUTTONUP:\r
5206   case WM_MOUSEMOVE:\r
5207   case WM_MOUSEWHEEL:\r
5208     MouseEvent(hwnd, message, wParam, lParam);\r
5209     break;\r
5210 \r
5211   JAWS_KB_NAVIGATION\r
5212 \r
5213   case WM_CHAR:\r
5214     \r
5215     JAWS_ALT_INTERCEPT\r
5216 \r
5217     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
5218         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5219         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5220         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5221         SetFocus(h);\r
5222         SendMessage(h, message, wParam, lParam);\r
5223     } else if(lParam != KF_REPEAT) {\r
5224         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5225                 PopUpMoveDialog((char)wParam);\r
5226         } else if((char)wParam == 003) CopyGameToClipboard();\r
5227          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5228     }\r
5229 \r
5230     break;\r
5231 \r
5232   case WM_PALETTECHANGED:\r
5233     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5234       int nnew;\r
5235       HDC hdc = GetDC(hwndMain);\r
5236       SelectPalette(hdc, hPal, TRUE);\r
5237       nnew = RealizePalette(hdc);\r
5238       if (nnew > 0) {\r
5239         paletteChanged = TRUE;\r
5240         InvalidateRect(hwnd, &boardRect, FALSE);\r
5241       }\r
5242       ReleaseDC(hwnd, hdc);\r
5243     }\r
5244     break;\r
5245 \r
5246   case WM_QUERYNEWPALETTE:\r
5247     if (!appData.monoMode /*&& paletteChanged*/) {\r
5248       int nnew;\r
5249       HDC hdc = GetDC(hwndMain);\r
5250       paletteChanged = FALSE;\r
5251       SelectPalette(hdc, hPal, FALSE);\r
5252       nnew = RealizePalette(hdc);\r
5253       if (nnew > 0) {\r
5254         InvalidateRect(hwnd, &boardRect, FALSE);\r
5255       }\r
5256       ReleaseDC(hwnd, hdc);\r
5257       return TRUE;\r
5258     }\r
5259     return FALSE;\r
5260 \r
5261   case WM_COMMAND: /* message: command from application menu */\r
5262     wmId    = LOWORD(wParam);\r
5263     wmEvent = HIWORD(wParam);\r
5264 \r
5265     switch (wmId) {\r
5266     case IDM_NewGame:\r
5267       ResetGameEvent();\r
5268       SAY("new game enter a move to play against the computer with white");\r
5269       break;\r
5270 \r
5271     case IDM_NewGameFRC:\r
5272       if( NewGameFRC() == 0 ) {\r
5273         ResetGameEvent();\r
5274       }\r
5275       break;\r
5276 \r
5277     case IDM_NewVariant:\r
5278       NewVariantPopup(hwnd);\r
5279       break;\r
5280 \r
5281     case IDM_LoadGame:\r
5282       LoadGameDialog(hwnd, "Load Game from File");\r
5283       break;\r
5284 \r
5285     case IDM_LoadNextGame:\r
5286       ReloadGame(1);\r
5287       break;\r
5288 \r
5289     case IDM_LoadPrevGame:\r
5290       ReloadGame(-1);\r
5291       break;\r
5292 \r
5293     case IDM_ReloadGame:\r
5294       ReloadGame(0);\r
5295       break;\r
5296 \r
5297     case IDM_LoadPosition:\r
5298       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5299         Reset(FALSE, TRUE);\r
5300       }\r
5301       number = 1;\r
5302       f = OpenFileDialog(hwnd, "rb", "",\r
5303                          appData.oldSaveStyle ? "pos" : "fen",\r
5304                          POSITION_FILT,\r
5305                          "Load Position from File", &number, fileTitle, NULL);\r
5306       if (f != NULL) {\r
5307         LoadPosition(f, number, fileTitle);\r
5308       }\r
5309       break;\r
5310 \r
5311     case IDM_LoadNextPosition:\r
5312       ReloadPosition(1);\r
5313       break;\r
5314 \r
5315     case IDM_LoadPrevPosition:\r
5316       ReloadPosition(-1);\r
5317       break;\r
5318 \r
5319     case IDM_ReloadPosition:\r
5320       ReloadPosition(0);\r
5321       break;\r
5322 \r
5323     case IDM_SaveGame:\r
5324       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5325       f = OpenFileDialog(hwnd, "a", defName,\r
5326                          appData.oldSaveStyle ? "gam" : "pgn",\r
5327                          GAME_FILT,\r
5328                          "Save Game to File", NULL, fileTitle, NULL);\r
5329       if (f != NULL) {\r
5330         SaveGame(f, 0, "");\r
5331       }\r
5332       break;\r
5333 \r
5334     case IDM_SavePosition:\r
5335       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5336       f = OpenFileDialog(hwnd, "a", defName,\r
5337                          appData.oldSaveStyle ? "pos" : "fen",\r
5338                          POSITION_FILT,\r
5339                          "Save Position to File", NULL, fileTitle, NULL);\r
5340       if (f != NULL) {\r
5341         SavePosition(f, 0, "");\r
5342       }\r
5343       break;\r
5344 \r
5345     case IDM_SaveDiagram:\r
5346       defName = "diagram";\r
5347       f = OpenFileDialog(hwnd, "wb", defName,\r
5348                          "bmp",\r
5349                          DIAGRAM_FILT,\r
5350                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5351       if (f != NULL) {\r
5352         SaveDiagram(f);\r
5353       }\r
5354       break;\r
5355 \r
5356     case IDM_CopyGame:\r
5357       CopyGameToClipboard();\r
5358       break;\r
5359 \r
5360     case IDM_PasteGame:\r
5361       PasteGameFromClipboard();\r
5362       break;\r
5363 \r
5364     case IDM_CopyGameListToClipboard:\r
5365       CopyGameListToClipboard();\r
5366       break;\r
5367 \r
5368     /* [AS] Autodetect FEN or PGN data */\r
5369     case IDM_PasteAny:\r
5370       PasteGameOrFENFromClipboard();\r
5371       break;\r
5372 \r
5373     /* [AS] Move history */\r
5374     case IDM_ShowMoveHistory:\r
5375         if( MoveHistoryIsUp() ) {\r
5376             MoveHistoryPopDown();\r
5377         }\r
5378         else {\r
5379             MoveHistoryPopUp();\r
5380         }\r
5381         break;\r
5382 \r
5383     /* [AS] Eval graph */\r
5384     case IDM_ShowEvalGraph:\r
5385         if( EvalGraphIsUp() ) {\r
5386             EvalGraphPopDown();\r
5387         }\r
5388         else {\r
5389             EvalGraphPopUp();\r
5390             SetFocus(hwndMain);\r
5391         }\r
5392         break;\r
5393 \r
5394     /* [AS] Engine output */\r
5395     case IDM_ShowEngineOutput:\r
5396         if( EngineOutputIsUp() ) {\r
5397             EngineOutputPopDown();\r
5398         }\r
5399         else {\r
5400             EngineOutputPopUp();\r
5401         }\r
5402         break;\r
5403 \r
5404     /* [AS] User adjudication */\r
5405     case IDM_UserAdjudication_White:\r
5406         UserAdjudicationEvent( +1 );\r
5407         break;\r
5408 \r
5409     case IDM_UserAdjudication_Black:\r
5410         UserAdjudicationEvent( -1 );\r
5411         break;\r
5412 \r
5413     case IDM_UserAdjudication_Draw:\r
5414         UserAdjudicationEvent( 0 );\r
5415         break;\r
5416 \r
5417     /* [AS] Game list options dialog */\r
5418     case IDM_GameListOptions:\r
5419       GameListOptions();\r
5420       break;\r
5421 \r
5422     case IDM_NewChat:\r
5423       ChatPopUp();\r
5424       break;\r
5425 \r
5426     case IDM_CopyPosition:\r
5427       CopyFENToClipboard();\r
5428       break;\r
5429 \r
5430     case IDM_PastePosition:\r
5431       PasteFENFromClipboard();\r
5432       break;\r
5433 \r
5434     case IDM_MailMove:\r
5435       MailMoveEvent();\r
5436       break;\r
5437 \r
5438     case IDM_ReloadCMailMsg:\r
5439       Reset(TRUE, TRUE);\r
5440       ReloadCmailMsgEvent(FALSE);\r
5441       break;\r
5442 \r
5443     case IDM_Minimize:\r
5444       ShowWindow(hwnd, SW_MINIMIZE);\r
5445       break;\r
5446 \r
5447     case IDM_Exit:\r
5448       ExitEvent(0);\r
5449       break;\r
5450 \r
5451     case IDM_MachineWhite:\r
5452       MachineWhiteEvent();\r
5453       /*\r
5454        * refresh the tags dialog only if it's visible\r
5455        */\r
5456       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5457           char *tags;\r
5458           tags = PGNTags(&gameInfo);\r
5459           TagsPopUp(tags, CmailMsg());\r
5460           free(tags);\r
5461       }\r
5462       SAY("computer starts playing white");\r
5463       break;\r
5464 \r
5465     case IDM_MachineBlack:\r
5466       MachineBlackEvent();\r
5467       /*\r
5468        * refresh the tags dialog only if it's visible\r
5469        */\r
5470       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5471           char *tags;\r
5472           tags = PGNTags(&gameInfo);\r
5473           TagsPopUp(tags, CmailMsg());\r
5474           free(tags);\r
5475       }\r
5476       SAY("computer starts playing black");\r
5477       break;\r
5478 \r
5479     case IDM_TwoMachines:\r
5480       TwoMachinesEvent();\r
5481       /*\r
5482        * refresh the tags dialog only if it's visible\r
5483        */\r
5484       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5485           char *tags;\r
5486           tags = PGNTags(&gameInfo);\r
5487           TagsPopUp(tags, CmailMsg());\r
5488           free(tags);\r
5489       }\r
5490       SAY("programs start playing each other");\r
5491       break;\r
5492 \r
5493     case IDM_AnalysisMode:\r
5494       if (!first.analysisSupport) {\r
5495         sprintf(buf, "%s does not support analysis", first.tidy);\r
5496         DisplayError(buf, 0);\r
5497       } else {\r
5498         SAY("analyzing current position");\r
5499         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5500         if (appData.icsActive) {\r
5501                if (gameMode != IcsObserving) {\r
5502                        sprintf(buf, "You are not observing a game");\r
5503                        DisplayError(buf, 0);\r
5504                        /* secure check */\r
5505                        if (appData.icsEngineAnalyze) {\r
5506                                if (appData.debugMode) \r
5507                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5508                                ExitAnalyzeMode();\r
5509                                ModeHighlight();\r
5510                                break;\r
5511                        }\r
5512                        break;\r
5513                } else {\r
5514                        /* if enable, user want disable icsEngineAnalyze */\r
5515                        if (appData.icsEngineAnalyze) {\r
5516                                ExitAnalyzeMode();\r
5517                                ModeHighlight();\r
5518                                break;\r
5519                        }\r
5520                        appData.icsEngineAnalyze = TRUE;\r
5521                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5522                }\r
5523         } \r
5524         if (!appData.showThinking) ToggleShowThinking();\r
5525         AnalyzeModeEvent();\r
5526       }\r
5527       break;\r
5528 \r
5529     case IDM_AnalyzeFile:\r
5530       if (!first.analysisSupport) {\r
5531         char buf[MSG_SIZ];\r
5532         sprintf(buf, "%s does not support analysis", first.tidy);\r
5533         DisplayError(buf, 0);\r
5534       } else {\r
5535         if (!appData.showThinking) ToggleShowThinking();\r
5536         AnalyzeFileEvent();\r
5537         LoadGameDialog(hwnd, "Analyze Game from File");\r
5538         AnalysisPeriodicEvent(1);\r
5539       }\r
5540       break;\r
5541 \r
5542     case IDM_IcsClient:\r
5543       IcsClientEvent();\r
5544       break;\r
5545 \r
5546     case IDM_EditGame:\r
5547       EditGameEvent();\r
5548       SAY("edit game");\r
5549       break;\r
5550 \r
5551     case IDM_EditPosition:\r
5552       EditPositionEvent();\r
5553       SAY("to set up a position type a FEN");\r
5554       break;\r
5555 \r
5556     case IDM_Training:\r
5557       TrainingEvent();\r
5558       break;\r
5559 \r
5560     case IDM_ShowGameList:\r
5561       ShowGameListProc();\r
5562       break;\r
5563 \r
5564     case IDM_EditTags:\r
5565       EditTagsProc();\r
5566       break;\r
5567 \r
5568     case IDM_EditComment:\r
5569       if (commentUp && editComment) {\r
5570         CommentPopDown();\r
5571       } else {\r
5572         EditCommentEvent();\r
5573       }\r
5574       break;\r
5575 \r
5576     case IDM_Pause:\r
5577       PauseEvent();\r
5578       break;\r
5579 \r
5580     case IDM_Accept:\r
5581       AcceptEvent();\r
5582       break;\r
5583 \r
5584     case IDM_Decline:\r
5585       DeclineEvent();\r
5586       break;\r
5587 \r
5588     case IDM_Rematch:\r
5589       RematchEvent();\r
5590       break;\r
5591 \r
5592     case IDM_CallFlag:\r
5593       CallFlagEvent();\r
5594       break;\r
5595 \r
5596     case IDM_Draw:\r
5597       DrawEvent();\r
5598       break;\r
5599 \r
5600     case IDM_Adjourn:\r
5601       AdjournEvent();\r
5602       break;\r
5603 \r
5604     case IDM_Abort:\r
5605       AbortEvent();\r
5606       break;\r
5607 \r
5608     case IDM_Resign:\r
5609       ResignEvent();\r
5610       break;\r
5611 \r
5612     case IDM_StopObserving:\r
5613       StopObservingEvent();\r
5614       break;\r
5615 \r
5616     case IDM_StopExamining:\r
5617       StopExaminingEvent();\r
5618       break;\r
5619 \r
5620     case IDM_TypeInMove:\r
5621       PopUpMoveDialog('\000');\r
5622       break;\r
5623 \r
5624     case IDM_TypeInName:\r
5625       PopUpNameDialog('\000');\r
5626       break;\r
5627 \r
5628     case IDM_Backward:\r
5629       BackwardEvent();\r
5630       SetFocus(hwndMain);\r
5631       break;\r
5632 \r
5633     JAWS_MENU_ITEMS\r
5634 \r
5635     case IDM_Forward:\r
5636       ForwardEvent();\r
5637       SetFocus(hwndMain);\r
5638       break;\r
5639 \r
5640     case IDM_ToStart:\r
5641       ToStartEvent();\r
5642       SetFocus(hwndMain);\r
5643       break;\r
5644 \r
5645     case IDM_ToEnd:\r
5646       ToEndEvent();\r
5647       SetFocus(hwndMain);\r
5648       break;\r
5649 \r
5650     case IDM_Revert:\r
5651       RevertEvent();\r
5652       break;\r
5653 \r
5654     case IDM_TruncateGame:\r
5655       TruncateGameEvent();\r
5656       break;\r
5657 \r
5658     case IDM_MoveNow:\r
5659       MoveNowEvent();\r
5660       break;\r
5661 \r
5662     case IDM_RetractMove:\r
5663       RetractMoveEvent();\r
5664       break;\r
5665 \r
5666     case IDM_FlipView:\r
5667       flipView = !flipView;\r
5668       DrawPosition(FALSE, NULL);\r
5669       break;\r
5670 \r
5671     case IDM_FlipClock:\r
5672       flipClock = !flipClock;\r
5673       DisplayBothClocks();\r
5674       DrawPosition(FALSE, NULL);\r
5675       break;\r
5676 \r
5677     case IDM_MuteSounds:\r
5678       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5679       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5680                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5681       break;\r
5682 \r
5683     case IDM_GeneralOptions:\r
5684       GeneralOptionsPopup(hwnd);\r
5685       DrawPosition(TRUE, NULL);\r
5686       break;\r
5687 \r
5688     case IDM_BoardOptions:\r
5689       BoardOptionsPopup(hwnd);\r
5690       break;\r
5691 \r
5692     case IDM_EnginePlayOptions:\r
5693       EnginePlayOptionsPopup(hwnd);\r
5694       break;\r
5695 \r
5696     case IDM_Engine1Options:\r
5697       EngineOptionsPopup(hwnd, &first);\r
5698       break;\r
5699 \r
5700     case IDM_Engine2Options:\r
5701       EngineOptionsPopup(hwnd, &second);\r
5702       break;\r
5703 \r
5704     case IDM_OptionsUCI:\r
5705       UciOptionsPopup(hwnd);\r
5706       break;\r
5707 \r
5708     case IDM_IcsOptions:\r
5709       IcsOptionsPopup(hwnd);\r
5710       break;\r
5711 \r
5712     case IDM_Fonts:\r
5713       FontsOptionsPopup(hwnd);\r
5714       break;\r
5715 \r
5716     case IDM_Sounds:\r
5717       SoundOptionsPopup(hwnd);\r
5718       break;\r
5719 \r
5720     case IDM_CommPort:\r
5721       CommPortOptionsPopup(hwnd);\r
5722       break;\r
5723 \r
5724     case IDM_LoadOptions:\r
5725       LoadOptionsPopup(hwnd);\r
5726       break;\r
5727 \r
5728     case IDM_SaveOptions:\r
5729       SaveOptionsPopup(hwnd);\r
5730       break;\r
5731 \r
5732     case IDM_TimeControl:\r
5733       TimeControlOptionsPopup(hwnd);\r
5734       break;\r
5735 \r
5736     case IDM_SaveSettings:\r
5737       SaveSettings(settingsFileName);\r
5738       break;\r
5739 \r
5740     case IDM_SaveSettingsOnExit:\r
5741       saveSettingsOnExit = !saveSettingsOnExit;\r
5742       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5743                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5744                                          MF_CHECKED : MF_UNCHECKED));\r
5745       break;\r
5746 \r
5747     case IDM_Hint:\r
5748       HintEvent();\r
5749       break;\r
5750 \r
5751     case IDM_Book:\r
5752       BookEvent();\r
5753       break;\r
5754 \r
5755     case IDM_AboutGame:\r
5756       AboutGameEvent();\r
5757       break;\r
5758 \r
5759     case IDM_Debug:\r
5760       appData.debugMode = !appData.debugMode;\r
5761       if (appData.debugMode) {\r
5762         char dir[MSG_SIZ];\r
5763         GetCurrentDirectory(MSG_SIZ, dir);\r
5764         SetCurrentDirectory(installDir);\r
5765         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5766         SetCurrentDirectory(dir);\r
5767         setbuf(debugFP, NULL);\r
5768       } else {\r
5769         fclose(debugFP);\r
5770         debugFP = NULL;\r
5771       }\r
5772       break;\r
5773 \r
5774     case IDM_HELPCONTENTS:\r
5775       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5776           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5777           MessageBox (GetFocus(),\r
5778                     "Unable to activate help",\r
5779                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5780       }\r
5781       break;\r
5782 \r
5783     case IDM_HELPSEARCH:\r
5784         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5785             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5786         MessageBox (GetFocus(),\r
5787                     "Unable to activate help",\r
5788                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5789       }\r
5790       break;\r
5791 \r
5792     case IDM_HELPHELP:\r
5793       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5794         MessageBox (GetFocus(),\r
5795                     "Unable to activate help",\r
5796                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5797       }\r
5798       break;\r
5799 \r
5800     case IDM_ABOUT:\r
5801       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5802       DialogBox(hInst, \r
5803         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5804         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5805       FreeProcInstance(lpProc);\r
5806       break;\r
5807 \r
5808     case IDM_DirectCommand1:\r
5809       AskQuestionEvent("Direct Command",\r
5810                        "Send to chess program:", "", "1");\r
5811       break;\r
5812     case IDM_DirectCommand2:\r
5813       AskQuestionEvent("Direct Command",\r
5814                        "Send to second chess program:", "", "2");\r
5815       break;\r
5816 \r
5817     case EP_WhitePawn:\r
5818       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5819       fromX = fromY = -1;\r
5820       break;\r
5821 \r
5822     case EP_WhiteKnight:\r
5823       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5824       fromX = fromY = -1;\r
5825       break;\r
5826 \r
5827     case EP_WhiteBishop:\r
5828       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5829       fromX = fromY = -1;\r
5830       break;\r
5831 \r
5832     case EP_WhiteRook:\r
5833       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5834       fromX = fromY = -1;\r
5835       break;\r
5836 \r
5837     case EP_WhiteQueen:\r
5838       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5839       fromX = fromY = -1;\r
5840       break;\r
5841 \r
5842     case EP_WhiteFerz:\r
5843       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5844       fromX = fromY = -1;\r
5845       break;\r
5846 \r
5847     case EP_WhiteWazir:\r
5848       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5849       fromX = fromY = -1;\r
5850       break;\r
5851 \r
5852     case EP_WhiteAlfil:\r
5853       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5854       fromX = fromY = -1;\r
5855       break;\r
5856 \r
5857     case EP_WhiteCannon:\r
5858       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5859       fromX = fromY = -1;\r
5860       break;\r
5861 \r
5862     case EP_WhiteCardinal:\r
5863       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5864       fromX = fromY = -1;\r
5865       break;\r
5866 \r
5867     case EP_WhiteMarshall:\r
5868       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5869       fromX = fromY = -1;\r
5870       break;\r
5871 \r
5872     case EP_WhiteKing:\r
5873       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5874       fromX = fromY = -1;\r
5875       break;\r
5876 \r
5877     case EP_BlackPawn:\r
5878       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5879       fromX = fromY = -1;\r
5880       break;\r
5881 \r
5882     case EP_BlackKnight:\r
5883       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5884       fromX = fromY = -1;\r
5885       break;\r
5886 \r
5887     case EP_BlackBishop:\r
5888       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5889       fromX = fromY = -1;\r
5890       break;\r
5891 \r
5892     case EP_BlackRook:\r
5893       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5894       fromX = fromY = -1;\r
5895       break;\r
5896 \r
5897     case EP_BlackQueen:\r
5898       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5899       fromX = fromY = -1;\r
5900       break;\r
5901 \r
5902     case EP_BlackFerz:\r
5903       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5904       fromX = fromY = -1;\r
5905       break;\r
5906 \r
5907     case EP_BlackWazir:\r
5908       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5909       fromX = fromY = -1;\r
5910       break;\r
5911 \r
5912     case EP_BlackAlfil:\r
5913       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5914       fromX = fromY = -1;\r
5915       break;\r
5916 \r
5917     case EP_BlackCannon:\r
5918       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5919       fromX = fromY = -1;\r
5920       break;\r
5921 \r
5922     case EP_BlackCardinal:\r
5923       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5924       fromX = fromY = -1;\r
5925       break;\r
5926 \r
5927     case EP_BlackMarshall:\r
5928       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5929       fromX = fromY = -1;\r
5930       break;\r
5931 \r
5932     case EP_BlackKing:\r
5933       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5934       fromX = fromY = -1;\r
5935       break;\r
5936 \r
5937     case EP_EmptySquare:\r
5938       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5939       fromX = fromY = -1;\r
5940       break;\r
5941 \r
5942     case EP_ClearBoard:\r
5943       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5944       fromX = fromY = -1;\r
5945       break;\r
5946 \r
5947     case EP_White:\r
5948       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5949       fromX = fromY = -1;\r
5950       break;\r
5951 \r
5952     case EP_Black:\r
5953       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5954       fromX = fromY = -1;\r
5955       break;\r
5956 \r
5957     case EP_Promote:\r
5958       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5959       fromX = fromY = -1;\r
5960       break;\r
5961 \r
5962     case EP_Demote:\r
5963       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5964       fromX = fromY = -1;\r
5965       break;\r
5966 \r
5967     case DP_Pawn:\r
5968       DropMenuEvent(WhitePawn, fromX, fromY);\r
5969       fromX = fromY = -1;\r
5970       break;\r
5971 \r
5972     case DP_Knight:\r
5973       DropMenuEvent(WhiteKnight, fromX, fromY);\r
5974       fromX = fromY = -1;\r
5975       break;\r
5976 \r
5977     case DP_Bishop:\r
5978       DropMenuEvent(WhiteBishop, fromX, fromY);\r
5979       fromX = fromY = -1;\r
5980       break;\r
5981 \r
5982     case DP_Rook:\r
5983       DropMenuEvent(WhiteRook, fromX, fromY);\r
5984       fromX = fromY = -1;\r
5985       break;\r
5986 \r
5987     case DP_Queen:\r
5988       DropMenuEvent(WhiteQueen, fromX, fromY);\r
5989       fromX = fromY = -1;\r
5990       break;\r
5991 \r
5992     default:\r
5993       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5994     }\r
5995     break;\r
5996 \r
5997   case WM_TIMER:\r
5998     switch (wParam) {\r
5999     case CLOCK_TIMER_ID:\r
6000       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6001       clockTimerEvent = 0;\r
6002       DecrementClocks(); /* call into back end */\r
6003       break;\r
6004     case LOAD_GAME_TIMER_ID:\r
6005       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6006       loadGameTimerEvent = 0;\r
6007       AutoPlayGameLoop(); /* call into back end */\r
6008       break;\r
6009     case ANALYSIS_TIMER_ID:\r
6010       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6011                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6012         AnalysisPeriodicEvent(0);\r
6013       } else {\r
6014         KillTimer(hwnd, analysisTimerEvent);\r
6015         analysisTimerEvent = 0;\r
6016       }\r
6017       break;\r
6018     case DELAYED_TIMER_ID:\r
6019       KillTimer(hwnd, delayedTimerEvent);\r
6020       delayedTimerEvent = 0;\r
6021       delayedTimerCallback();\r
6022       break;\r
6023     }\r
6024     break;\r
6025 \r
6026   case WM_USER_Input:\r
6027     InputEvent(hwnd, message, wParam, lParam);\r
6028     break;\r
6029 \r
6030   /* [AS] Also move "attached" child windows */\r
6031   case WM_WINDOWPOSCHANGING:\r
6032 \r
6033     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6034         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6035 \r
6036         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6037             /* Window is moving */\r
6038             RECT rcMain;\r
6039 \r
6040 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6041             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
6042             rcMain.right  = wpMain.x + wpMain.width;\r
6043             rcMain.top    = wpMain.y;\r
6044             rcMain.bottom = wpMain.y + wpMain.height;\r
6045             \r
6046             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6047             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6048             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6049             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6050             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6051             wpMain.x = lpwp->x;\r
6052             wpMain.y = lpwp->y;\r
6053         }\r
6054     }\r
6055     break;\r
6056 \r
6057   /* [AS] Snapping */\r
6058   case WM_ENTERSIZEMOVE:\r
6059     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6060     if (hwnd == hwndMain) {\r
6061       doingSizing = TRUE;\r
6062       lastSizing = 0;\r
6063     }\r
6064     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6065     break;\r
6066 \r
6067   case WM_SIZING:\r
6068     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6069     if (hwnd == hwndMain) {\r
6070       lastSizing = wParam;\r
6071     }\r
6072     break;\r
6073 \r
6074   case WM_MOVING:\r
6075     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6076       return OnMoving( &sd, hwnd, wParam, lParam );\r
6077 \r
6078   case WM_EXITSIZEMOVE:\r
6079     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6080     if (hwnd == hwndMain) {\r
6081       RECT client;\r
6082       doingSizing = FALSE;\r
6083       InvalidateRect(hwnd, &boardRect, FALSE);\r
6084       GetClientRect(hwnd, &client);\r
6085       ResizeBoard(client.right, client.bottom, lastSizing);\r
6086       lastSizing = 0;\r
6087       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6088     }\r
6089     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6090     break;\r
6091 \r
6092   case WM_DESTROY: /* message: window being destroyed */\r
6093     PostQuitMessage(0);\r
6094     break;\r
6095 \r
6096   case WM_CLOSE:\r
6097     if (hwnd == hwndMain) {\r
6098       ExitEvent(0);\r
6099     }\r
6100     break;\r
6101 \r
6102   default:      /* Passes it on if unprocessed */\r
6103     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6104   }\r
6105   return 0;\r
6106 }\r
6107 \r
6108 /*---------------------------------------------------------------------------*\\r
6109  *\r
6110  * Misc utility routines\r
6111  *\r
6112 \*---------------------------------------------------------------------------*/\r
6113 \r
6114 /*\r
6115  * Decent random number generator, at least not as bad as Windows\r
6116  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6117  */\r
6118 unsigned int randstate;\r
6119 \r
6120 int\r
6121 myrandom(void)\r
6122 {\r
6123   randstate = randstate * 1664525 + 1013904223;\r
6124   return (int) randstate & 0x7fffffff;\r
6125 }\r
6126 \r
6127 void\r
6128 mysrandom(unsigned int seed)\r
6129 {\r
6130   randstate = seed;\r
6131 }\r
6132 \r
6133 \r
6134 /* \r
6135  * returns TRUE if user selects a different color, FALSE otherwise \r
6136  */\r
6137 \r
6138 BOOL\r
6139 ChangeColor(HWND hwnd, COLORREF *which)\r
6140 {\r
6141   static BOOL firstTime = TRUE;\r
6142   static DWORD customColors[16];\r
6143   CHOOSECOLOR cc;\r
6144   COLORREF newcolor;\r
6145   int i;\r
6146   ColorClass ccl;\r
6147 \r
6148   if (firstTime) {\r
6149     /* Make initial colors in use available as custom colors */\r
6150     /* Should we put the compiled-in defaults here instead? */\r
6151     i = 0;\r
6152     customColors[i++] = lightSquareColor & 0xffffff;\r
6153     customColors[i++] = darkSquareColor & 0xffffff;\r
6154     customColors[i++] = whitePieceColor & 0xffffff;\r
6155     customColors[i++] = blackPieceColor & 0xffffff;\r
6156     customColors[i++] = highlightSquareColor & 0xffffff;\r
6157     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6158 \r
6159     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6160       customColors[i++] = textAttribs[ccl].color;\r
6161     }\r
6162     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6163     firstTime = FALSE;\r
6164   }\r
6165 \r
6166   cc.lStructSize = sizeof(cc);\r
6167   cc.hwndOwner = hwnd;\r
6168   cc.hInstance = NULL;\r
6169   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6170   cc.lpCustColors = (LPDWORD) customColors;\r
6171   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6172 \r
6173   if (!ChooseColor(&cc)) return FALSE;\r
6174 \r
6175   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6176   if (newcolor == *which) return FALSE;\r
6177   *which = newcolor;\r
6178   return TRUE;\r
6179 \r
6180   /*\r
6181   InitDrawingColors();\r
6182   InvalidateRect(hwnd, &boardRect, FALSE);\r
6183   */\r
6184 }\r
6185 \r
6186 BOOLEAN\r
6187 MyLoadSound(MySound *ms)\r
6188 {\r
6189   BOOL ok = FALSE;\r
6190   struct stat st;\r
6191   FILE *f;\r
6192 \r
6193   if (ms->data) free(ms->data);\r
6194   ms->data = NULL;\r
6195 \r
6196   switch (ms->name[0]) {\r
6197   case NULLCHAR:\r
6198     /* Silence */\r
6199     ok = TRUE;\r
6200     break;\r
6201   case '$':\r
6202     /* System sound from Control Panel.  Don't preload here. */\r
6203     ok = TRUE;\r
6204     break;\r
6205   case '!':\r
6206     if (ms->name[1] == NULLCHAR) {\r
6207       /* "!" alone = silence */\r
6208       ok = TRUE;\r
6209     } else {\r
6210       /* Builtin wave resource.  Error if not found. */\r
6211       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6212       if (h == NULL) break;\r
6213       ms->data = (void *)LoadResource(hInst, h);\r
6214       if (h == NULL) break;\r
6215       ok = TRUE;\r
6216     }\r
6217     break;\r
6218   default:\r
6219     /* .wav file.  Error if not found. */\r
6220     f = fopen(ms->name, "rb");\r
6221     if (f == NULL) break;\r
6222     if (fstat(fileno(f), &st) < 0) break;\r
6223     ms->data = malloc(st.st_size);\r
6224     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6225     fclose(f);\r
6226     ok = TRUE;\r
6227     break;\r
6228   }\r
6229   if (!ok) {\r
6230     char buf[MSG_SIZ];\r
6231     sprintf(buf, "Error loading sound %s", ms->name);\r
6232     DisplayError(buf, GetLastError());\r
6233   }\r
6234   return ok;\r
6235 }\r
6236 \r
6237 BOOLEAN\r
6238 MyPlaySound(MySound *ms)\r
6239 {\r
6240   BOOLEAN ok = FALSE;\r
6241 \r
6242   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6243   switch (ms->name[0]) {\r
6244   case NULLCHAR:\r
6245         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6246     /* Silence */\r
6247     ok = TRUE;\r
6248     break;\r
6249   case '$':\r
6250     /* System sound from Control Panel (deprecated feature).\r
6251        "$" alone or an unset sound name gets default beep (still in use). */\r
6252     if (ms->name[1]) {\r
6253       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6254     }\r
6255     if (!ok) ok = MessageBeep(MB_OK);\r
6256     break; \r
6257   case '!':\r
6258     /* Builtin wave resource, or "!" alone for silence */\r
6259     if (ms->name[1]) {\r
6260       if (ms->data == NULL) return FALSE;\r
6261       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6262     } else {\r
6263       ok = TRUE;\r
6264     }\r
6265     break;\r
6266   default:\r
6267     /* .wav file.  Error if not found. */\r
6268     if (ms->data == NULL) return FALSE;\r
6269     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6270     break;\r
6271   }\r
6272   /* Don't print an error: this can happen innocently if the sound driver\r
6273      is busy; for instance, if another instance of WinBoard is playing\r
6274      a sound at about the same time. */\r
6275   return ok;\r
6276 }\r
6277 \r
6278 \r
6279 LRESULT CALLBACK\r
6280 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6281 {\r
6282   BOOL ok;\r
6283   OPENFILENAME *ofn;\r
6284   static UINT *number; /* gross that this is static */\r
6285 \r
6286   switch (message) {\r
6287   case WM_INITDIALOG: /* message: initialize dialog box */\r
6288     /* Center the dialog over the application window */\r
6289     ofn = (OPENFILENAME *) lParam;\r
6290     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6291       number = (UINT *) ofn->lCustData;\r
6292       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6293     } else {\r
6294       number = NULL;\r
6295     }\r
6296     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6297     return FALSE;  /* Allow for further processing */\r
6298 \r
6299   case WM_COMMAND:\r
6300     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6301       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6302     }\r
6303     return FALSE;  /* Allow for further processing */\r
6304   }\r
6305   return FALSE;\r
6306 }\r
6307 \r
6308 UINT APIENTRY\r
6309 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6310 {\r
6311   static UINT *number;\r
6312   OPENFILENAME *ofname;\r
6313   OFNOTIFY *ofnot;\r
6314   switch (uiMsg) {\r
6315   case WM_INITDIALOG:\r
6316     ofname = (OPENFILENAME *)lParam;\r
6317     number = (UINT *)(ofname->lCustData);\r
6318     break;\r
6319   case WM_NOTIFY:\r
6320     ofnot = (OFNOTIFY *)lParam;\r
6321     if (ofnot->hdr.code == CDN_FILEOK) {\r
6322       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6323     }\r
6324     break;\r
6325   }\r
6326   return 0;\r
6327 }\r
6328 \r
6329 \r
6330 FILE *\r
6331 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6332                char *nameFilt, char *dlgTitle, UINT *number,\r
6333                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6334 {\r
6335   OPENFILENAME openFileName;\r
6336   char buf1[MSG_SIZ];\r
6337   FILE *f;\r
6338 \r
6339   if (fileName == NULL) fileName = buf1;\r
6340   if (defName == NULL) {\r
6341     strcpy(fileName, "*.");\r
6342     strcat(fileName, defExt);\r
6343   } else {\r
6344     strcpy(fileName, defName);\r
6345   }\r
6346   if (fileTitle) strcpy(fileTitle, "");\r
6347   if (number) *number = 0;\r
6348 \r
6349   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6350   openFileName.hwndOwner         = hwnd;\r
6351   openFileName.hInstance         = (HANDLE) hInst;\r
6352   openFileName.lpstrFilter       = nameFilt;\r
6353   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6354   openFileName.nMaxCustFilter    = 0L;\r
6355   openFileName.nFilterIndex      = 1L;\r
6356   openFileName.lpstrFile         = fileName;\r
6357   openFileName.nMaxFile          = MSG_SIZ;\r
6358   openFileName.lpstrFileTitle    = fileTitle;\r
6359   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6360   openFileName.lpstrInitialDir   = NULL;\r
6361   openFileName.lpstrTitle        = dlgTitle;\r
6362   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6363     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6364     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6365     | (oldDialog ? 0 : OFN_EXPLORER);\r
6366   openFileName.nFileOffset       = 0;\r
6367   openFileName.nFileExtension    = 0;\r
6368   openFileName.lpstrDefExt       = defExt;\r
6369   openFileName.lCustData         = (LONG) number;\r
6370   openFileName.lpfnHook          = oldDialog ?\r
6371     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6372   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6373 \r
6374   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6375                         GetOpenFileName(&openFileName)) {\r
6376     /* open the file */\r
6377     f = fopen(openFileName.lpstrFile, write);\r
6378     if (f == NULL) {\r
6379       MessageBox(hwnd, "File open failed", NULL,\r
6380                  MB_OK|MB_ICONEXCLAMATION);\r
6381       return NULL;\r
6382     }\r
6383   } else {\r
6384     int err = CommDlgExtendedError();\r
6385     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6386     return FALSE;\r
6387   }\r
6388   return f;\r
6389 }\r
6390 \r
6391 \r
6392 \r
6393 VOID APIENTRY\r
6394 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6395 {\r
6396   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6397 \r
6398   /*\r
6399    * Get the first pop-up menu in the menu template. This is the\r
6400    * menu that TrackPopupMenu displays.\r
6401    */\r
6402   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6403 \r
6404   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6405 \r
6406   /*\r
6407    * TrackPopup uses screen coordinates, so convert the\r
6408    * coordinates of the mouse click to screen coordinates.\r
6409    */\r
6410   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6411 \r
6412   /* Draw and track the floating pop-up menu. */\r
6413   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6414                  pt.x, pt.y, 0, hwnd, NULL);\r
6415 \r
6416   /* Destroy the menu.*/\r
6417   DestroyMenu(hmenu);\r
6418 }\r
6419    \r
6420 typedef struct {\r
6421   HWND hDlg, hText;\r
6422   int sizeX, sizeY, newSizeX, newSizeY;\r
6423   HDWP hdwp;\r
6424 } ResizeEditPlusButtonsClosure;\r
6425 \r
6426 BOOL CALLBACK\r
6427 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6428 {\r
6429   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6430   RECT rect;\r
6431   POINT pt;\r
6432 \r
6433   if (hChild == cl->hText) return TRUE;\r
6434   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6435   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6436   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6437   ScreenToClient(cl->hDlg, &pt);\r
6438   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6439     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6440   return TRUE;\r
6441 }\r
6442 \r
6443 /* Resize a dialog that has a (rich) edit field filling most of\r
6444    the top, with a row of buttons below */\r
6445 VOID\r
6446 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6447 {\r
6448   RECT rectText;\r
6449   int newTextHeight, newTextWidth;\r
6450   ResizeEditPlusButtonsClosure cl;\r
6451   \r
6452   /*if (IsIconic(hDlg)) return;*/\r
6453   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6454   \r
6455   cl.hdwp = BeginDeferWindowPos(8);\r
6456 \r
6457   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6458   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6459   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6460   if (newTextHeight < 0) {\r
6461     newSizeY += -newTextHeight;\r
6462     newTextHeight = 0;\r
6463   }\r
6464   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6465     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6466 \r
6467   cl.hDlg = hDlg;\r
6468   cl.hText = hText;\r
6469   cl.sizeX = sizeX;\r
6470   cl.sizeY = sizeY;\r
6471   cl.newSizeX = newSizeX;\r
6472   cl.newSizeY = newSizeY;\r
6473   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6474 \r
6475   EndDeferWindowPos(cl.hdwp);\r
6476 }\r
6477 \r
6478 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6479 {\r
6480     RECT    rChild, rParent;\r
6481     int     wChild, hChild, wParent, hParent;\r
6482     int     wScreen, hScreen, xNew, yNew;\r
6483     HDC     hdc;\r
6484 \r
6485     /* Get the Height and Width of the child window */\r
6486     GetWindowRect (hwndChild, &rChild);\r
6487     wChild = rChild.right - rChild.left;\r
6488     hChild = rChild.bottom - rChild.top;\r
6489 \r
6490     /* Get the Height and Width of the parent window */\r
6491     GetWindowRect (hwndParent, &rParent);\r
6492     wParent = rParent.right - rParent.left;\r
6493     hParent = rParent.bottom - rParent.top;\r
6494 \r
6495     /* Get the display limits */\r
6496     hdc = GetDC (hwndChild);\r
6497     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6498     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6499     ReleaseDC(hwndChild, hdc);\r
6500 \r
6501     /* Calculate new X position, then adjust for screen */\r
6502     xNew = rParent.left + ((wParent - wChild) /2);\r
6503     if (xNew < 0) {\r
6504         xNew = 0;\r
6505     } else if ((xNew+wChild) > wScreen) {\r
6506         xNew = wScreen - wChild;\r
6507     }\r
6508 \r
6509     /* Calculate new Y position, then adjust for screen */\r
6510     if( mode == 0 ) {\r
6511         yNew = rParent.top  + ((hParent - hChild) /2);\r
6512     }\r
6513     else {\r
6514         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6515     }\r
6516 \r
6517     if (yNew < 0) {\r
6518         yNew = 0;\r
6519     } else if ((yNew+hChild) > hScreen) {\r
6520         yNew = hScreen - hChild;\r
6521     }\r
6522 \r
6523     /* Set it, and return */\r
6524     return SetWindowPos (hwndChild, NULL,\r
6525                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6526 }\r
6527 \r
6528 /* Center one window over another */\r
6529 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6530 {\r
6531     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6532 }\r
6533 \r
6534 /*---------------------------------------------------------------------------*\\r
6535  *\r
6536  * Startup Dialog functions\r
6537  *\r
6538 \*---------------------------------------------------------------------------*/\r
6539 void\r
6540 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6541 {\r
6542   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6543 \r
6544   while (*cd != NULL) {\r
6545     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6546     cd++;\r
6547   }\r
6548 }\r
6549 \r
6550 void\r
6551 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6552 {\r
6553   char buf1[ARG_MAX];\r
6554   int len;\r
6555 \r
6556   if (str[0] == '@') {\r
6557     FILE* f = fopen(str + 1, "r");\r
6558     if (f == NULL) {\r
6559       DisplayFatalError(str + 1, errno, 2);\r
6560       return;\r
6561     }\r
6562     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6563     fclose(f);\r
6564     buf1[len] = NULLCHAR;\r
6565     str = buf1;\r
6566   }\r
6567 \r
6568   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6569 \r
6570   for (;;) {\r
6571     char buf[MSG_SIZ];\r
6572     char *end = strchr(str, '\n');\r
6573     if (end == NULL) return;\r
6574     memcpy(buf, str, end - str);\r
6575     buf[end - str] = NULLCHAR;\r
6576     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6577     str = end + 1;\r
6578   }\r
6579 }\r
6580 \r
6581 void\r
6582 SetStartupDialogEnables(HWND hDlg)\r
6583 {\r
6584   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6585     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6586     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6587   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6588     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6589   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6590     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6591   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6592     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6593   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6594     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6595     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6596     IsDlgButtonChecked(hDlg, OPT_View));\r
6597 }\r
6598 \r
6599 char *\r
6600 QuoteForFilename(char *filename)\r
6601 {\r
6602   int dquote, space;\r
6603   dquote = strchr(filename, '"') != NULL;\r
6604   space = strchr(filename, ' ') != NULL;\r
6605   if (dquote || space) {\r
6606     if (dquote) {\r
6607       return "'";\r
6608     } else {\r
6609       return "\"";\r
6610     }\r
6611   } else {\r
6612     return "";\r
6613   }\r
6614 }\r
6615 \r
6616 VOID\r
6617 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6618 {\r
6619   char buf[MSG_SIZ];\r
6620   char *q;\r
6621 \r
6622   InitComboStringsFromOption(hwndCombo, nthnames);\r
6623   q = QuoteForFilename(nthcp);\r
6624   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6625   if (*nthdir != NULLCHAR) {\r
6626     q = QuoteForFilename(nthdir);\r
6627     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6628   }\r
6629   if (*nthcp == NULLCHAR) {\r
6630     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6631   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6632     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6633     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6634   }\r
6635 }\r
6636 \r
6637 LRESULT CALLBACK\r
6638 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6639 {\r
6640   char buf[MSG_SIZ];\r
6641   HANDLE hwndCombo;\r
6642   char *p;\r
6643 \r
6644   switch (message) {\r
6645   case WM_INITDIALOG:\r
6646     /* Center the dialog */\r
6647     CenterWindow (hDlg, GetDesktopWindow());\r
6648     /* Initialize the dialog items */\r
6649     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6650                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6651                   firstChessProgramNames);\r
6652     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6653                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6654                   secondChessProgramNames);\r
6655     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6656     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6657     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6658     if (*appData.icsHelper != NULLCHAR) {\r
6659       char *q = QuoteForFilename(appData.icsHelper);\r
6660       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6661     }\r
6662     if (*appData.icsHost == NULLCHAR) {\r
6663       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6664       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6665     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6666       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6667       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6668     }\r
6669 \r
6670     if (appData.icsActive) {\r
6671       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6672     }\r
6673     else if (appData.noChessProgram) {\r
6674       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6675     }\r
6676     else {\r
6677       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6678     }\r
6679 \r
6680     SetStartupDialogEnables(hDlg);\r
6681     return TRUE;\r
6682 \r
6683   case WM_COMMAND:\r
6684     switch (LOWORD(wParam)) {\r
6685     case IDOK:\r
6686       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6687         strcpy(buf, "/fcp=");\r
6688         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6689         p = buf;\r
6690         ParseArgs(StringGet, &p);\r
6691         strcpy(buf, "/scp=");\r
6692         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6693         p = buf;\r
6694         ParseArgs(StringGet, &p);\r
6695         appData.noChessProgram = FALSE;\r
6696         appData.icsActive = FALSE;\r
6697       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6698         strcpy(buf, "/ics /icshost=");\r
6699         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6700         p = buf;\r
6701         ParseArgs(StringGet, &p);\r
6702         if (appData.zippyPlay) {\r
6703           strcpy(buf, "/fcp=");\r
6704           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6705           p = buf;\r
6706           ParseArgs(StringGet, &p);\r
6707         }\r
6708       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6709         appData.noChessProgram = TRUE;\r
6710         appData.icsActive = FALSE;\r
6711       } else {\r
6712         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6713                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6714         return TRUE;\r
6715       }\r
6716       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6717         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6718         p = buf;\r
6719         ParseArgs(StringGet, &p);\r
6720       }\r
6721       EndDialog(hDlg, TRUE);\r
6722       return TRUE;\r
6723 \r
6724     case IDCANCEL:\r
6725       ExitEvent(0);\r
6726       return TRUE;\r
6727 \r
6728     case IDM_HELPCONTENTS:\r
6729       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6730         MessageBox (GetFocus(),\r
6731                     "Unable to activate help",\r
6732                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6733       }\r
6734       break;\r
6735 \r
6736     default:\r
6737       SetStartupDialogEnables(hDlg);\r
6738       break;\r
6739     }\r
6740     break;\r
6741   }\r
6742   return FALSE;\r
6743 }\r
6744 \r
6745 /*---------------------------------------------------------------------------*\\r
6746  *\r
6747  * About box dialog functions\r
6748  *\r
6749 \*---------------------------------------------------------------------------*/\r
6750 \r
6751 /* Process messages for "About" dialog box */\r
6752 LRESULT CALLBACK\r
6753 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6754 {\r
6755   switch (message) {\r
6756   case WM_INITDIALOG: /* message: initialize dialog box */\r
6757     /* Center the dialog over the application window */\r
6758     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6759     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6760     JAWS_COPYRIGHT\r
6761     return (TRUE);\r
6762 \r
6763   case WM_COMMAND: /* message: received a command */\r
6764     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6765         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6766       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6767       return (TRUE);\r
6768     }\r
6769     break;\r
6770   }\r
6771   return (FALSE);\r
6772 }\r
6773 \r
6774 /*---------------------------------------------------------------------------*\\r
6775  *\r
6776  * Comment Dialog functions\r
6777  *\r
6778 \*---------------------------------------------------------------------------*/\r
6779 \r
6780 LRESULT CALLBACK\r
6781 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6782 {\r
6783   static HANDLE hwndText = NULL;\r
6784   int len, newSizeX, newSizeY, flags;\r
6785   static int sizeX, sizeY;\r
6786   char *str;\r
6787   RECT rect;\r
6788   MINMAXINFO *mmi;\r
6789 \r
6790   switch (message) {\r
6791   case WM_INITDIALOG: /* message: initialize dialog box */\r
6792     /* Initialize the dialog items */\r
6793     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6794     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6795     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6796     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6797     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6798     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6799     SetWindowText(hDlg, commentTitle);\r
6800     if (editComment) {\r
6801       SetFocus(hwndText);\r
6802     } else {\r
6803       SetFocus(GetDlgItem(hDlg, IDOK));\r
6804     }\r
6805     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6806                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6807                 MAKELPARAM(FALSE, 0));\r
6808     /* Size and position the dialog */\r
6809     if (!commentDialog) {\r
6810       commentDialog = hDlg;\r
6811       flags = SWP_NOZORDER;\r
6812       GetClientRect(hDlg, &rect);\r
6813       sizeX = rect.right;\r
6814       sizeY = rect.bottom;\r
6815       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6816           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6817         WINDOWPLACEMENT wp;\r
6818         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6819         wp.length = sizeof(WINDOWPLACEMENT);\r
6820         wp.flags = 0;\r
6821         wp.showCmd = SW_SHOW;\r
6822         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6823         wp.rcNormalPosition.left = wpComment.x;\r
6824         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6825         wp.rcNormalPosition.top = wpComment.y;\r
6826         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6827         SetWindowPlacement(hDlg, &wp);\r
6828 \r
6829         GetClientRect(hDlg, &rect);\r
6830         newSizeX = rect.right;\r
6831         newSizeY = rect.bottom;\r
6832         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6833                               newSizeX, newSizeY);\r
6834         sizeX = newSizeX;\r
6835         sizeY = newSizeY;\r
6836       }\r
6837     }\r
6838     return FALSE;\r
6839 \r
6840   case WM_COMMAND: /* message: received a command */\r
6841     switch (LOWORD(wParam)) {\r
6842     case IDOK:\r
6843       if (editComment) {\r
6844         char *p, *q;\r
6845         /* Read changed options from the dialog box */\r
6846         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6847         len = GetWindowTextLength(hwndText);\r
6848         str = (char *) malloc(len + 1);\r
6849         GetWindowText(hwndText, str, len + 1);\r
6850         p = q = str;\r
6851         while (*q) {\r
6852           if (*q == '\r')\r
6853             q++;\r
6854           else\r
6855             *p++ = *q++;\r
6856         }\r
6857         *p = NULLCHAR;\r
6858         ReplaceComment(commentIndex, str);\r
6859         free(str);\r
6860       }\r
6861       CommentPopDown();\r
6862       return TRUE;\r
6863 \r
6864     case IDCANCEL:\r
6865     case OPT_CancelComment:\r
6866       CommentPopDown();\r
6867       return TRUE;\r
6868 \r
6869     case OPT_ClearComment:\r
6870       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6871       break;\r
6872 \r
6873     case OPT_EditComment:\r
6874       EditCommentEvent();\r
6875       return TRUE;\r
6876 \r
6877     default:\r
6878       break;\r
6879     }\r
6880     break;\r
6881 \r
6882   case WM_SIZE:\r
6883     newSizeX = LOWORD(lParam);\r
6884     newSizeY = HIWORD(lParam);\r
6885     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6886     sizeX = newSizeX;\r
6887     sizeY = newSizeY;\r
6888     break;\r
6889 \r
6890   case WM_GETMINMAXINFO:\r
6891     /* Prevent resizing window too small */\r
6892     mmi = (MINMAXINFO *) lParam;\r
6893     mmi->ptMinTrackSize.x = 100;\r
6894     mmi->ptMinTrackSize.y = 100;\r
6895     break;\r
6896   }\r
6897   return FALSE;\r
6898 }\r
6899 \r
6900 VOID\r
6901 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6902 {\r
6903   FARPROC lpProc;\r
6904   char *p, *q;\r
6905 \r
6906   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6907 \r
6908   if (str == NULL) str = "";\r
6909   p = (char *) malloc(2 * strlen(str) + 2);\r
6910   q = p;\r
6911   while (*str) {\r
6912     if (*str == '\n') *q++ = '\r';\r
6913     *q++ = *str++;\r
6914   }\r
6915   *q = NULLCHAR;\r
6916   if (commentText != NULL) free(commentText);\r
6917 \r
6918   commentIndex = index;\r
6919   commentTitle = title;\r
6920   commentText = p;\r
6921   editComment = edit;\r
6922 \r
6923   if (commentDialog) {\r
6924     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6925     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6926   } else {\r
6927     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6928     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6929                  hwndMain, (DLGPROC)lpProc);\r
6930     FreeProcInstance(lpProc);\r
6931   }\r
6932   commentUp = TRUE;\r
6933 }\r
6934 \r
6935 \r
6936 /*---------------------------------------------------------------------------*\\r
6937  *\r
6938  * Type-in move dialog functions\r
6939  * \r
6940 \*---------------------------------------------------------------------------*/\r
6941 \r
6942 LRESULT CALLBACK\r
6943 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6944 {\r
6945   char move[MSG_SIZ];\r
6946   HWND hInput;\r
6947   ChessMove moveType;\r
6948   int fromX, fromY, toX, toY;\r
6949   char promoChar;\r
6950 \r
6951   switch (message) {\r
6952   case WM_INITDIALOG:\r
6953     move[0] = (char) lParam;\r
6954     move[1] = NULLCHAR;\r
6955     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6956     hInput = GetDlgItem(hDlg, OPT_Move);\r
6957     SetWindowText(hInput, move);\r
6958     SetFocus(hInput);\r
6959     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6960     return FALSE;\r
6961 \r
6962   case WM_COMMAND:\r
6963     switch (LOWORD(wParam)) {\r
6964     case IDOK:\r
6965       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6966       { int n; Board board;\r
6967         // [HGM] FENedit\r
6968         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
6969                 EditPositionPasteFEN(move);\r
6970                 EndDialog(hDlg, TRUE);\r
6971                 return TRUE;\r
6972         }\r
6973         // [HGM] movenum: allow move number to be typed in any mode\r
6974         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
6975           ToNrEvent(2*n-1);\r
6976           EndDialog(hDlg, TRUE);\r
6977           return TRUE;\r
6978         }\r
6979       }\r
6980       if (gameMode != EditGame && currentMove != forwardMostMove && \r
6981         gameMode != Training) {\r
6982         DisplayMoveError("Displayed move is not current");\r
6983       } else {\r
6984 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
6985         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6986           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
6987         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
6988         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
6989           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
6990           if (gameMode != Training)\r
6991               forwardMostMove = currentMove;\r
6992           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
6993         } else {\r
6994           DisplayMoveError("Could not parse move");\r
6995         }\r
6996       }\r
6997       EndDialog(hDlg, TRUE);\r
6998       return TRUE;\r
6999     case IDCANCEL:\r
7000       EndDialog(hDlg, FALSE);\r
7001       return TRUE;\r
7002     default:\r
7003       break;\r
7004     }\r
7005     break;\r
7006   }\r
7007   return FALSE;\r
7008 }\r
7009 \r
7010 VOID\r
7011 PopUpMoveDialog(char firstchar)\r
7012 {\r
7013     FARPROC lpProc;\r
7014     \r
7015     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7016         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7017         gameMode == AnalyzeMode || gameMode == EditGame || \r
7018         gameMode == EditPosition || gameMode == IcsExamining ||\r
7019         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7020         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7021                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7022                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7023         gameMode == Training) {\r
7024       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7025       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7026         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7027       FreeProcInstance(lpProc);\r
7028     }\r
7029 }\r
7030 \r
7031 /*---------------------------------------------------------------------------*\\r
7032  *\r
7033  * Type-in name dialog functions\r
7034  * \r
7035 \*---------------------------------------------------------------------------*/\r
7036 \r
7037 LRESULT CALLBACK\r
7038 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7039 {\r
7040   char move[MSG_SIZ];\r
7041   HWND hInput;\r
7042 \r
7043   switch (message) {\r
7044   case WM_INITDIALOG:\r
7045     move[0] = (char) lParam;\r
7046     move[1] = NULLCHAR;\r
7047     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7048     hInput = GetDlgItem(hDlg, OPT_Name);\r
7049     SetWindowText(hInput, move);\r
7050     SetFocus(hInput);\r
7051     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7052     return FALSE;\r
7053 \r
7054   case WM_COMMAND:\r
7055     switch (LOWORD(wParam)) {\r
7056     case IDOK:\r
7057       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7058       appData.userName = strdup(move);\r
7059       SetUserLogo();\r
7060 \r
7061       EndDialog(hDlg, TRUE);\r
7062       return TRUE;\r
7063     case IDCANCEL:\r
7064       EndDialog(hDlg, FALSE);\r
7065       return TRUE;\r
7066     default:\r
7067       break;\r
7068     }\r
7069     break;\r
7070   }\r
7071   return FALSE;\r
7072 }\r
7073 \r
7074 VOID\r
7075 PopUpNameDialog(char firstchar)\r
7076 {\r
7077     FARPROC lpProc;\r
7078     \r
7079       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7080       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7081         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7082       FreeProcInstance(lpProc);\r
7083 }\r
7084 \r
7085 /*---------------------------------------------------------------------------*\\r
7086  *\r
7087  *  Error dialogs\r
7088  * \r
7089 \*---------------------------------------------------------------------------*/\r
7090 \r
7091 /* Nonmodal error box */\r
7092 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7093                              WPARAM wParam, LPARAM lParam);\r
7094 \r
7095 VOID\r
7096 ErrorPopUp(char *title, char *content)\r
7097 {\r
7098   FARPROC lpProc;\r
7099   char *p, *q;\r
7100   BOOLEAN modal = hwndMain == NULL;\r
7101 \r
7102   p = content;\r
7103   q = errorMessage;\r
7104   while (*p) {\r
7105     if (*p == '\n') {\r
7106       if (modal) {\r
7107         *q++ = ' ';\r
7108         p++;\r
7109       } else {\r
7110         *q++ = '\r';\r
7111         *q++ = *p++;\r
7112       }\r
7113     } else {\r
7114       *q++ = *p++;\r
7115     }\r
7116   }\r
7117   *q = NULLCHAR;\r
7118   strncpy(errorTitle, title, sizeof(errorTitle));\r
7119   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7120   \r
7121   if (modal) {\r
7122     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7123   } else {\r
7124     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7125     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7126                  hwndMain, (DLGPROC)lpProc);\r
7127     FreeProcInstance(lpProc);\r
7128   }\r
7129 }\r
7130 \r
7131 VOID\r
7132 ErrorPopDown()\r
7133 {\r
7134   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7135   if (errorDialog == NULL) return;\r
7136   DestroyWindow(errorDialog);\r
7137   errorDialog = NULL;\r
7138 }\r
7139 \r
7140 LRESULT CALLBACK\r
7141 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7142 {\r
7143   HANDLE hwndText;\r
7144   RECT rChild;\r
7145 \r
7146   switch (message) {\r
7147   case WM_INITDIALOG:\r
7148     GetWindowRect(hDlg, &rChild);\r
7149 \r
7150     /*\r
7151     SetWindowPos(hDlg, NULL, rChild.left,\r
7152       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7153       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7154     */\r
7155 \r
7156     /* \r
7157         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7158         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7159         and it doesn't work when you resize the dialog.\r
7160         For now, just give it a default position.\r
7161     */\r
7162     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7163 \r
7164     errorDialog = hDlg;\r
7165     SetWindowText(hDlg, errorTitle);\r
7166     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7167     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7168     return FALSE;\r
7169 \r
7170   case WM_COMMAND:\r
7171     switch (LOWORD(wParam)) {\r
7172     case IDOK:\r
7173     case IDCANCEL:\r
7174       if (errorDialog == hDlg) errorDialog = NULL;\r
7175       DestroyWindow(hDlg);\r
7176       return TRUE;\r
7177 \r
7178     default:\r
7179       break;\r
7180     }\r
7181     break;\r
7182   }\r
7183   return FALSE;\r
7184 }\r
7185 \r
7186 #ifdef GOTHIC\r
7187 HWND gothicDialog = NULL;\r
7188 \r
7189 LRESULT CALLBACK\r
7190 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7191 {\r
7192   HANDLE hwndText;\r
7193   RECT rChild;\r
7194   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7195 \r
7196   switch (message) {\r
7197   case WM_INITDIALOG:\r
7198     GetWindowRect(hDlg, &rChild);\r
7199 \r
7200     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
7201                                                              SWP_NOZORDER);\r
7202 \r
7203     /* \r
7204         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7205         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7206         and it doesn't work when you resize the dialog.\r
7207         For now, just give it a default position.\r
7208     */\r
7209     gothicDialog = hDlg;\r
7210     SetWindowText(hDlg, errorTitle);\r
7211     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7212     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7213     return FALSE;\r
7214 \r
7215   case WM_COMMAND:\r
7216     switch (LOWORD(wParam)) {\r
7217     case IDOK:\r
7218     case IDCANCEL:\r
7219       if (errorDialog == hDlg) errorDialog = NULL;\r
7220       DestroyWindow(hDlg);\r
7221       return TRUE;\r
7222 \r
7223     default:\r
7224       break;\r
7225     }\r
7226     break;\r
7227   }\r
7228   return FALSE;\r
7229 }\r
7230 \r
7231 VOID\r
7232 GothicPopUp(char *title, VariantClass variant)\r
7233 {\r
7234   FARPROC lpProc;\r
7235   static char *lastTitle;\r
7236 \r
7237   strncpy(errorTitle, title, sizeof(errorTitle));\r
7238   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7239 \r
7240   if(lastTitle != title && gothicDialog != NULL) {\r
7241     DestroyWindow(gothicDialog);\r
7242     gothicDialog = NULL;\r
7243   }\r
7244   if(variant != VariantNormal && gothicDialog == NULL) {\r
7245     title = lastTitle;\r
7246     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7247     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7248                  hwndMain, (DLGPROC)lpProc);\r
7249     FreeProcInstance(lpProc);\r
7250   }\r
7251 }\r
7252 #endif\r
7253 \r
7254 /*---------------------------------------------------------------------------*\\r
7255  *\r
7256  *  Ics Interaction console functions\r
7257  *\r
7258 \*---------------------------------------------------------------------------*/\r
7259 \r
7260 #define HISTORY_SIZE 64\r
7261 static char *history[HISTORY_SIZE];\r
7262 int histIn = 0, histP = 0;\r
7263 \r
7264 VOID\r
7265 SaveInHistory(char *cmd)\r
7266 {\r
7267   if (history[histIn] != NULL) {\r
7268     free(history[histIn]);\r
7269     history[histIn] = NULL;\r
7270   }\r
7271   if (*cmd == NULLCHAR) return;\r
7272   history[histIn] = StrSave(cmd);\r
7273   histIn = (histIn + 1) % HISTORY_SIZE;\r
7274   if (history[histIn] != NULL) {\r
7275     free(history[histIn]);\r
7276     history[histIn] = NULL;\r
7277   }\r
7278   histP = histIn;\r
7279 }\r
7280 \r
7281 char *\r
7282 PrevInHistory(char *cmd)\r
7283 {\r
7284   int newhp;\r
7285   if (histP == histIn) {\r
7286     if (history[histIn] != NULL) free(history[histIn]);\r
7287     history[histIn] = StrSave(cmd);\r
7288   }\r
7289   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7290   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7291   histP = newhp;\r
7292   return history[histP];\r
7293 }\r
7294 \r
7295 char *\r
7296 NextInHistory()\r
7297 {\r
7298   if (histP == histIn) return NULL;\r
7299   histP = (histP + 1) % HISTORY_SIZE;\r
7300   return history[histP];\r
7301 }\r
7302 \r
7303 typedef struct {\r
7304   char *item;\r
7305   char *command;\r
7306   BOOLEAN getname;\r
7307   BOOLEAN immediate;\r
7308 } IcsTextMenuEntry;\r
7309 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7310 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7311 \r
7312 void\r
7313 ParseIcsTextMenu(char *icsTextMenuString)\r
7314 {\r
7315 //  int flags = 0;\r
7316   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7317   char *p = icsTextMenuString;\r
7318   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7319     free(e->item);\r
7320     e->item = NULL;\r
7321     if (e->command != NULL) {\r
7322       free(e->command);\r
7323       e->command = NULL;\r
7324     }\r
7325     e++;\r
7326   }\r
7327   e = icsTextMenuEntry;\r
7328   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7329     if (*p == ';' || *p == '\n') {\r
7330       e->item = strdup("-");\r
7331       e->command = NULL;\r
7332       p++;\r
7333     } else if (*p == '-') {\r
7334       e->item = strdup("-");\r
7335       e->command = NULL;\r
7336       p++;\r
7337       if (*p) p++;\r
7338     } else {\r
7339       char *q, *r, *s, *t;\r
7340       char c;\r
7341       q = strchr(p, ',');\r
7342       if (q == NULL) break;\r
7343       *q = NULLCHAR;\r
7344       r = strchr(q + 1, ',');\r
7345       if (r == NULL) break;\r
7346       *r = NULLCHAR;\r
7347       s = strchr(r + 1, ',');\r
7348       if (s == NULL) break;\r
7349       *s = NULLCHAR;\r
7350       c = ';';\r
7351       t = strchr(s + 1, c);\r
7352       if (t == NULL) {\r
7353         c = '\n';\r
7354         t = strchr(s + 1, c);\r
7355       }\r
7356       if (t != NULL) *t = NULLCHAR;\r
7357       e->item = strdup(p);\r
7358       e->command = strdup(q + 1);\r
7359       e->getname = *(r + 1) != '0';\r
7360       e->immediate = *(s + 1) != '0';\r
7361       *q = ',';\r
7362       *r = ',';\r
7363       *s = ',';\r
7364       if (t == NULL) break;\r
7365       *t = c;\r
7366       p = t + 1;\r
7367     }\r
7368     e++;\r
7369   } \r
7370 }\r
7371 \r
7372 HMENU\r
7373 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7374 {\r
7375   HMENU hmenu, h;\r
7376   int i = 0;\r
7377   hmenu = LoadMenu(hInst, "TextMenu");\r
7378   h = GetSubMenu(hmenu, 0);\r
7379   while (e->item) {\r
7380     if (strcmp(e->item, "-") == 0) {\r
7381       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7382     } else {\r
7383       if (e->item[0] == '|') {\r
7384         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7385                    IDM_CommandX + i, &e->item[1]);\r
7386       } else {\r
7387         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7388       }\r
7389     }\r
7390     e++;\r
7391     i++;\r
7392   } \r
7393   return hmenu;\r
7394 }\r
7395 \r
7396 WNDPROC consoleTextWindowProc;\r
7397 \r
7398 void\r
7399 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7400 {\r
7401   char buf[MSG_SIZ], name[MSG_SIZ];\r
7402   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7403   CHARRANGE sel;\r
7404 \r
7405   if (!getname) {\r
7406     SetWindowText(hInput, command);\r
7407     if (immediate) {\r
7408       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7409     } else {\r
7410       sel.cpMin = 999999;\r
7411       sel.cpMax = 999999;\r
7412       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7413       SetFocus(hInput);\r
7414     }\r
7415     return;\r
7416   }    \r
7417   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7418   if (sel.cpMin == sel.cpMax) {\r
7419     /* Expand to surrounding word */\r
7420     TEXTRANGE tr;\r
7421     do {\r
7422       tr.chrg.cpMax = sel.cpMin;\r
7423       tr.chrg.cpMin = --sel.cpMin;\r
7424       if (sel.cpMin < 0) break;\r
7425       tr.lpstrText = name;\r
7426       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7427     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7428     sel.cpMin++;\r
7429 \r
7430     do {\r
7431       tr.chrg.cpMin = sel.cpMax;\r
7432       tr.chrg.cpMax = ++sel.cpMax;\r
7433       tr.lpstrText = name;\r
7434       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7435     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7436     sel.cpMax--;\r
7437 \r
7438     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7439       MessageBeep(MB_ICONEXCLAMATION);\r
7440       return;\r
7441     }\r
7442     tr.chrg = sel;\r
7443     tr.lpstrText = name;\r
7444     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7445   } else {\r
7446     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7447       MessageBeep(MB_ICONEXCLAMATION);\r
7448       return;\r
7449     }\r
7450     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7451   }\r
7452   if (immediate) {\r
7453     sprintf(buf, "%s %s", command, name);\r
7454     SetWindowText(hInput, buf);\r
7455     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7456   } else {\r
7457     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7458     SetWindowText(hInput, buf);\r
7459     sel.cpMin = 999999;\r
7460     sel.cpMax = 999999;\r
7461     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7462     SetFocus(hInput);\r
7463   }\r
7464 }\r
7465 \r
7466 LRESULT CALLBACK \r
7467 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7468 {\r
7469   HWND hInput;\r
7470   CHARRANGE sel;\r
7471 \r
7472   switch (message) {\r
7473   case WM_KEYDOWN:\r
7474     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7475     switch (wParam) {\r
7476     case VK_PRIOR:\r
7477       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7478       return 0;\r
7479     case VK_NEXT:\r
7480       sel.cpMin = 999999;\r
7481       sel.cpMax = 999999;\r
7482       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7483       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7484       return 0;\r
7485     }\r
7486     break;\r
7487   case WM_CHAR:\r
7488    if(wParam != '\022') {\r
7489     if (wParam == '\t') {\r
7490       if (GetKeyState(VK_SHIFT) < 0) {\r
7491         /* shifted */\r
7492         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7493         if (buttonDesc[0].hwnd) {\r
7494           SetFocus(buttonDesc[0].hwnd);\r
7495         } else {\r
7496           SetFocus(hwndMain);\r
7497         }\r
7498       } else {\r
7499         /* unshifted */\r
7500         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7501       }\r
7502     } else {\r
7503       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7504       JAWS_DELETE( SetFocus(hInput); )\r
7505       SendMessage(hInput, message, wParam, lParam);\r
7506     }\r
7507     return 0;\r
7508    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7509   case WM_RBUTTONUP:\r
7510     if (GetKeyState(VK_SHIFT) & ~1) {\r
7511       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7512         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7513     } else {\r
7514       POINT pt;\r
7515       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7516       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7517       if (sel.cpMin == sel.cpMax) {\r
7518         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7519         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7520       }\r
7521       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7522         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7523       }\r
7524       pt.x = LOWORD(lParam);\r
7525       pt.y = HIWORD(lParam);\r
7526       MenuPopup(hwnd, pt, hmenu, -1);\r
7527     }\r
7528     return 0;\r
7529   case WM_PASTE:\r
7530     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7531     SetFocus(hInput);\r
7532     return SendMessage(hInput, message, wParam, lParam);\r
7533   case WM_MBUTTONDOWN:\r
7534     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7535   case WM_RBUTTONDOWN:\r
7536     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7537       /* Move selection here if it was empty */\r
7538       POINT pt;\r
7539       pt.x = LOWORD(lParam);\r
7540       pt.y = HIWORD(lParam);\r
7541       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7542       if (sel.cpMin == sel.cpMax) {\r
7543         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7544         sel.cpMax = sel.cpMin;\r
7545         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7546       }\r
7547       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7548     }\r
7549     return 0;\r
7550   case WM_COMMAND:\r
7551     switch (LOWORD(wParam)) {\r
7552     case IDM_QuickPaste:\r
7553       {\r
7554         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7555         if (sel.cpMin == sel.cpMax) {\r
7556           MessageBeep(MB_ICONEXCLAMATION);\r
7557           return 0;\r
7558         }\r
7559         SendMessage(hwnd, WM_COPY, 0, 0);\r
7560         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7561         SendMessage(hInput, WM_PASTE, 0, 0);\r
7562         SetFocus(hInput);\r
7563         return 0;\r
7564       }\r
7565     case IDM_Cut:\r
7566       SendMessage(hwnd, WM_CUT, 0, 0);\r
7567       return 0;\r
7568     case IDM_Paste:\r
7569       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7570       return 0;\r
7571     case IDM_Copy:\r
7572       SendMessage(hwnd, WM_COPY, 0, 0);\r
7573       return 0;\r
7574     default:\r
7575       {\r
7576         int i = LOWORD(wParam) - IDM_CommandX;\r
7577         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7578             icsTextMenuEntry[i].command != NULL) {\r
7579           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7580                    icsTextMenuEntry[i].getname,\r
7581                    icsTextMenuEntry[i].immediate);\r
7582           return 0;\r
7583         }\r
7584       }\r
7585       break;\r
7586     }\r
7587     break;\r
7588   }\r
7589   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7590 }\r
7591 \r
7592 WNDPROC consoleInputWindowProc;\r
7593 \r
7594 LRESULT CALLBACK\r
7595 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7596 {\r
7597   char buf[MSG_SIZ];\r
7598   char *p;\r
7599   static BOOL sendNextChar = FALSE;\r
7600   static BOOL quoteNextChar = FALSE;\r
7601   InputSource *is = consoleInputSource;\r
7602   CHARFORMAT cf;\r
7603   CHARRANGE sel;\r
7604 \r
7605   switch (message) {\r
7606   case WM_CHAR:\r
7607     if (!appData.localLineEditing || sendNextChar) {\r
7608       is->buf[0] = (CHAR) wParam;\r
7609       is->count = 1;\r
7610       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7611       sendNextChar = FALSE;\r
7612       return 0;\r
7613     }\r
7614     if (quoteNextChar) {\r
7615       buf[0] = (char) wParam;\r
7616       buf[1] = NULLCHAR;\r
7617       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7618       quoteNextChar = FALSE;\r
7619       return 0;\r
7620     }\r
7621     switch (wParam) {\r
7622     case '\r':   /* Enter key */\r
7623       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7624       if (consoleEcho) SaveInHistory(is->buf);\r
7625       is->buf[is->count++] = '\n';\r
7626       is->buf[is->count] = NULLCHAR;\r
7627       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7628       if (consoleEcho) {\r
7629         ConsoleOutput(is->buf, is->count, TRUE);\r
7630       } else if (appData.localLineEditing) {\r
7631         ConsoleOutput("\n", 1, TRUE);\r
7632       }\r
7633       /* fall thru */\r
7634     case '\033': /* Escape key */\r
7635       SetWindowText(hwnd, "");\r
7636       cf.cbSize = sizeof(CHARFORMAT);\r
7637       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7638       if (consoleEcho) {\r
7639         cf.crTextColor = textAttribs[ColorNormal].color;\r
7640       } else {\r
7641         cf.crTextColor = COLOR_ECHOOFF;\r
7642       }\r
7643       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7644       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7645       return 0;\r
7646     case '\t':   /* Tab key */\r
7647       if (GetKeyState(VK_SHIFT) < 0) {\r
7648         /* shifted */\r
7649         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7650       } else {\r
7651         /* unshifted */\r
7652         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7653         if (buttonDesc[0].hwnd) {\r
7654           SetFocus(buttonDesc[0].hwnd);\r
7655         } else {\r
7656           SetFocus(hwndMain);\r
7657         }\r
7658       }\r
7659       return 0;\r
7660     case '\023': /* Ctrl+S */\r
7661       sendNextChar = TRUE;\r
7662       return 0;\r
7663     case '\021': /* Ctrl+Q */\r
7664       quoteNextChar = TRUE;\r
7665       return 0;\r
7666     JAWS_REPLAY\r
7667     default:\r
7668       break;\r
7669     }\r
7670     break;\r
7671   case WM_KEYDOWN:\r
7672     switch (wParam) {\r
7673     case VK_UP:\r
7674       GetWindowText(hwnd, buf, MSG_SIZ);\r
7675       p = PrevInHistory(buf);\r
7676       if (p != NULL) {\r
7677         SetWindowText(hwnd, p);\r
7678         sel.cpMin = 999999;\r
7679         sel.cpMax = 999999;\r
7680         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7681         return 0;\r
7682       }\r
7683       break;\r
7684     case VK_DOWN:\r
7685       p = NextInHistory();\r
7686       if (p != NULL) {\r
7687         SetWindowText(hwnd, p);\r
7688         sel.cpMin = 999999;\r
7689         sel.cpMax = 999999;\r
7690         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7691         return 0;\r
7692       }\r
7693       break;\r
7694     case VK_HOME:\r
7695     case VK_END:\r
7696       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7697       /* fall thru */\r
7698     case VK_PRIOR:\r
7699     case VK_NEXT:\r
7700       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7701       return 0;\r
7702     }\r
7703     break;\r
7704   case WM_MBUTTONDOWN:\r
7705     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7706       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7707     break;\r
7708   case WM_RBUTTONUP:\r
7709     if (GetKeyState(VK_SHIFT) & ~1) {\r
7710       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7711         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7712     } else {\r
7713       POINT pt;\r
7714       HMENU hmenu;\r
7715       hmenu = LoadMenu(hInst, "InputMenu");\r
7716       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7717       if (sel.cpMin == sel.cpMax) {\r
7718         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7719         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7720       }\r
7721       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7722         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7723       }\r
7724       pt.x = LOWORD(lParam);\r
7725       pt.y = HIWORD(lParam);\r
7726       MenuPopup(hwnd, pt, hmenu, -1);\r
7727     }\r
7728     return 0;\r
7729   case WM_COMMAND:\r
7730     switch (LOWORD(wParam)) { \r
7731     case IDM_Undo:\r
7732       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7733       return 0;\r
7734     case IDM_SelectAll:\r
7735       sel.cpMin = 0;\r
7736       sel.cpMax = -1; /*999999?*/\r
7737       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7738       return 0;\r
7739     case IDM_Cut:\r
7740       SendMessage(hwnd, WM_CUT, 0, 0);\r
7741       return 0;\r
7742     case IDM_Paste:\r
7743       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7744       return 0;\r
7745     case IDM_Copy:\r
7746       SendMessage(hwnd, WM_COPY, 0, 0);\r
7747       return 0;\r
7748     }\r
7749     break;\r
7750   }\r
7751   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7752 }\r
7753 \r
7754 #define CO_MAX  100000\r
7755 #define CO_TRIM   1000\r
7756 \r
7757 LRESULT CALLBACK\r
7758 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7759 {\r
7760   static SnapData sd;\r
7761   HWND hText, hInput;\r
7762   RECT rect;\r
7763   static int sizeX, sizeY;\r
7764   int newSizeX, newSizeY;\r
7765   MINMAXINFO *mmi;\r
7766   WORD wMask;\r
7767 \r
7768   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7769   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7770 \r
7771   switch (message) {\r
7772   case WM_NOTIFY:\r
7773     if (((NMHDR*)lParam)->code == EN_LINK)\r
7774     {\r
7775       ENLINK *pLink = (ENLINK*)lParam;\r
7776       if (pLink->msg == WM_LBUTTONUP)\r
7777       {\r
7778         TEXTRANGE tr;\r
7779 \r
7780         tr.chrg = pLink->chrg;\r
7781         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7782         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7783         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7784         free(tr.lpstrText);\r
7785       }\r
7786     }\r
7787     break;\r
7788   case WM_INITDIALOG: /* message: initialize dialog box */\r
7789     hwndConsole = hDlg;\r
7790     SetFocus(hInput);\r
7791     consoleTextWindowProc = (WNDPROC)\r
7792       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7793     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7794     consoleInputWindowProc = (WNDPROC)\r
7795       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7796     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7797     Colorize(ColorNormal, TRUE);\r
7798     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7799     ChangedConsoleFont();\r
7800     GetClientRect(hDlg, &rect);\r
7801     sizeX = rect.right;\r
7802     sizeY = rect.bottom;\r
7803     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7804         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7805       WINDOWPLACEMENT wp;\r
7806       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7807       wp.length = sizeof(WINDOWPLACEMENT);\r
7808       wp.flags = 0;\r
7809       wp.showCmd = SW_SHOW;\r
7810       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7811       wp.rcNormalPosition.left = wpConsole.x;\r
7812       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7813       wp.rcNormalPosition.top = wpConsole.y;\r
7814       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7815       SetWindowPlacement(hDlg, &wp);\r
7816     }\r
7817 \r
7818    // [HGM] Chessknight's change 2004-07-13\r
7819    else { /* Determine Defaults */\r
7820        WINDOWPLACEMENT wp;\r
7821        wpConsole.x = wpMain.width + 1;\r
7822        wpConsole.y = wpMain.y;\r
7823        wpConsole.width = screenWidth -  wpMain.width;\r
7824        wpConsole.height = wpMain.height;\r
7825        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7826        wp.length = sizeof(WINDOWPLACEMENT);\r
7827        wp.flags = 0;\r
7828        wp.showCmd = SW_SHOW;\r
7829        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7830        wp.rcNormalPosition.left = wpConsole.x;\r
7831        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7832        wp.rcNormalPosition.top = wpConsole.y;\r
7833        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7834        SetWindowPlacement(hDlg, &wp);\r
7835     }\r
7836 \r
7837    // Allow hText to highlight URLs and send notifications on them\r
7838    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7839    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7840    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7841    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7842 \r
7843     return FALSE;\r
7844 \r
7845   case WM_SETFOCUS:\r
7846     SetFocus(hInput);\r
7847     return 0;\r
7848 \r
7849   case WM_CLOSE:\r
7850     ExitEvent(0);\r
7851     /* not reached */\r
7852     break;\r
7853 \r
7854   case WM_SIZE:\r
7855     if (IsIconic(hDlg)) break;\r
7856     newSizeX = LOWORD(lParam);\r
7857     newSizeY = HIWORD(lParam);\r
7858     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7859       RECT rectText, rectInput;\r
7860       POINT pt;\r
7861       int newTextHeight, newTextWidth;\r
7862       GetWindowRect(hText, &rectText);\r
7863       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7864       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7865       if (newTextHeight < 0) {\r
7866         newSizeY += -newTextHeight;\r
7867         newTextHeight = 0;\r
7868       }\r
7869       SetWindowPos(hText, NULL, 0, 0,\r
7870         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7871       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7872       pt.x = rectInput.left;\r
7873       pt.y = rectInput.top + newSizeY - sizeY;\r
7874       ScreenToClient(hDlg, &pt);\r
7875       SetWindowPos(hInput, NULL, \r
7876         pt.x, pt.y, /* needs client coords */   \r
7877         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7878         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7879     }\r
7880     sizeX = newSizeX;\r
7881     sizeY = newSizeY;\r
7882     break;\r
7883 \r
7884   case WM_GETMINMAXINFO:\r
7885     /* Prevent resizing window too small */\r
7886     mmi = (MINMAXINFO *) lParam;\r
7887     mmi->ptMinTrackSize.x = 100;\r
7888     mmi->ptMinTrackSize.y = 100;\r
7889     break;\r
7890 \r
7891   /* [AS] Snapping */\r
7892   case WM_ENTERSIZEMOVE:\r
7893     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7894 \r
7895   case WM_SIZING:\r
7896     return OnSizing( &sd, hDlg, wParam, lParam );\r
7897 \r
7898   case WM_MOVING:\r
7899     return OnMoving( &sd, hDlg, wParam, lParam );\r
7900 \r
7901   case WM_EXITSIZEMOVE:\r
7902         UpdateICSWidth(hText);\r
7903     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7904   }\r
7905 \r
7906   return DefWindowProc(hDlg, message, wParam, lParam);\r
7907 }\r
7908 \r
7909 \r
7910 VOID\r
7911 ConsoleCreate()\r
7912 {\r
7913   HWND hCons;\r
7914   if (hwndConsole) return;\r
7915   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7916   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7917 }\r
7918 \r
7919 \r
7920 VOID\r
7921 ConsoleOutput(char* data, int length, int forceVisible)\r
7922 {\r
7923   HWND hText;\r
7924   int trim, exlen;\r
7925   char *p, *q;\r
7926   char buf[CO_MAX+1];\r
7927   POINT pEnd;\r
7928   RECT rect;\r
7929   static int delayLF = 0;\r
7930   CHARRANGE savesel, sel;\r
7931 \r
7932   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7933   p = data;\r
7934   q = buf;\r
7935   if (delayLF) {\r
7936     *q++ = '\r';\r
7937     *q++ = '\n';\r
7938     delayLF = 0;\r
7939   }\r
7940   while (length--) {\r
7941     if (*p == '\n') {\r
7942       if (*++p) {\r
7943         *q++ = '\r';\r
7944         *q++ = '\n';\r
7945       } else {\r
7946         delayLF = 1;\r
7947       }\r
7948     } else if (*p == '\007') {\r
7949        MyPlaySound(&sounds[(int)SoundBell]);\r
7950        p++;\r
7951     } else {\r
7952       *q++ = *p++;\r
7953     }\r
7954   }\r
7955   *q = NULLCHAR;\r
7956   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7957   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7958   /* Save current selection */\r
7959   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7960   exlen = GetWindowTextLength(hText);\r
7961   /* Find out whether current end of text is visible */\r
7962   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7963   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7964   /* Trim existing text if it's too long */\r
7965   if (exlen + (q - buf) > CO_MAX) {\r
7966     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7967     sel.cpMin = 0;\r
7968     sel.cpMax = trim;\r
7969     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7970     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7971     exlen -= trim;\r
7972     savesel.cpMin -= trim;\r
7973     savesel.cpMax -= trim;\r
7974     if (exlen < 0) exlen = 0;\r
7975     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7976     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7977   }\r
7978   /* Append the new text */\r
7979   sel.cpMin = exlen;\r
7980   sel.cpMax = exlen;\r
7981   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7982   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7983   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7984   if (forceVisible || exlen == 0 ||\r
7985       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7986        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7987     /* Scroll to make new end of text visible if old end of text\r
7988        was visible or new text is an echo of user typein */\r
7989     sel.cpMin = 9999999;\r
7990     sel.cpMax = 9999999;\r
7991     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7992     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7993     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7994     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7995   }\r
7996   if (savesel.cpMax == exlen || forceVisible) {\r
7997     /* Move insert point to new end of text if it was at the old\r
7998        end of text or if the new text is an echo of user typein */\r
7999     sel.cpMin = 9999999;\r
8000     sel.cpMax = 9999999;\r
8001     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8002   } else {\r
8003     /* Restore previous selection */\r
8004     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8005   }\r
8006   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8007 }\r
8008 \r
8009 /*---------*/\r
8010 \r
8011 \r
8012 void\r
8013 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8014 {\r
8015   char buf[100];\r
8016   char *str;\r
8017   COLORREF oldFg, oldBg;\r
8018   HFONT oldFont;\r
8019   RECT rect;\r
8020 \r
8021   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8022 \r
8023   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8024   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8025   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8026 \r
8027   rect.left = x;\r
8028   rect.right = x + squareSize;\r
8029   rect.top  = y;\r
8030   rect.bottom = y + squareSize;\r
8031   str = buf;\r
8032 \r
8033   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8034                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8035              y, ETO_CLIPPED|ETO_OPAQUE,\r
8036              &rect, str, strlen(str), NULL);\r
8037 \r
8038   (void) SetTextColor(hdc, oldFg);\r
8039   (void) SetBkColor(hdc, oldBg);\r
8040   (void) SelectObject(hdc, oldFont);\r
8041 }\r
8042 \r
8043 void\r
8044 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8045               RECT *rect, char *color, char *flagFell)\r
8046 {\r
8047   char buf[100];\r
8048   char *str;\r
8049   COLORREF oldFg, oldBg;\r
8050   HFONT oldFont;\r
8051 \r
8052   if (appData.clockMode) {\r
8053     if (tinyLayout)\r
8054       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8055     else\r
8056       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8057     str = buf;\r
8058   } else {\r
8059     str = color;\r
8060   }\r
8061 \r
8062   if (highlight) {\r
8063     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8064     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8065   } else {\r
8066     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8067     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8068   }\r
8069   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8070 \r
8071   JAWS_SILENCE\r
8072 \r
8073   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8074              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8075              rect, str, strlen(str), NULL);\r
8076   if(logoHeight > 0 && appData.clockMode) {\r
8077       RECT r;\r
8078       sprintf(buf, "%s %s", buf+7, flagFell);\r
8079       r.top = rect->top + logoHeight/2;\r
8080       r.left = rect->left;\r
8081       r.right = rect->right;\r
8082       r.bottom = rect->bottom;\r
8083       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8084                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8085                  &r, str, strlen(str), NULL);\r
8086   }\r
8087   (void) SetTextColor(hdc, oldFg);\r
8088   (void) SetBkColor(hdc, oldBg);\r
8089   (void) SelectObject(hdc, oldFont);\r
8090 }\r
8091 \r
8092 \r
8093 int\r
8094 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8095            OVERLAPPED *ovl)\r
8096 {\r
8097   int ok, err;\r
8098 \r
8099   /* [AS]  */\r
8100   if( count <= 0 ) {\r
8101     if (appData.debugMode) {\r
8102       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8103     }\r
8104 \r
8105     return ERROR_INVALID_USER_BUFFER;\r
8106   }\r
8107 \r
8108   ResetEvent(ovl->hEvent);\r
8109   ovl->Offset = ovl->OffsetHigh = 0;\r
8110   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8111   if (ok) {\r
8112     err = NO_ERROR;\r
8113   } else {\r
8114     err = GetLastError();\r
8115     if (err == ERROR_IO_PENDING) {\r
8116       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8117       if (ok)\r
8118         err = NO_ERROR;\r
8119       else\r
8120         err = GetLastError();\r
8121     }\r
8122   }\r
8123   return err;\r
8124 }\r
8125 \r
8126 int\r
8127 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8128             OVERLAPPED *ovl)\r
8129 {\r
8130   int ok, err;\r
8131 \r
8132   ResetEvent(ovl->hEvent);\r
8133   ovl->Offset = ovl->OffsetHigh = 0;\r
8134   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8135   if (ok) {\r
8136     err = NO_ERROR;\r
8137   } else {\r
8138     err = GetLastError();\r
8139     if (err == ERROR_IO_PENDING) {\r
8140       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8141       if (ok)\r
8142         err = NO_ERROR;\r
8143       else\r
8144         err = GetLastError();\r
8145     }\r
8146   }\r
8147   return err;\r
8148 }\r
8149 \r
8150 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8151 void CheckForInputBufferFull( InputSource * is )\r
8152 {\r
8153     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8154         /* Look for end of line */\r
8155         char * p = is->buf;\r
8156         \r
8157         while( p < is->next && *p != '\n' ) {\r
8158             p++;\r
8159         }\r
8160 \r
8161         if( p >= is->next ) {\r
8162             if (appData.debugMode) {\r
8163                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8164             }\r
8165 \r
8166             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8167             is->count = (DWORD) -1;\r
8168             is->next = is->buf;\r
8169         }\r
8170     }\r
8171 }\r
8172 \r
8173 DWORD\r
8174 InputThread(LPVOID arg)\r
8175 {\r
8176   InputSource *is;\r
8177   OVERLAPPED ovl;\r
8178 \r
8179   is = (InputSource *) arg;\r
8180   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8181   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8182   while (is->hThread != NULL) {\r
8183     is->error = DoReadFile(is->hFile, is->next,\r
8184                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8185                            &is->count, &ovl);\r
8186     if (is->error == NO_ERROR) {\r
8187       is->next += is->count;\r
8188     } else {\r
8189       if (is->error == ERROR_BROKEN_PIPE) {\r
8190         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8191         is->count = 0;\r
8192       } else {\r
8193         is->count = (DWORD) -1;\r
8194         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8195         break; \r
8196       }\r
8197     }\r
8198 \r
8199     CheckForInputBufferFull( is );\r
8200 \r
8201     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8202 \r
8203     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8204 \r
8205     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8206   }\r
8207 \r
8208   CloseHandle(ovl.hEvent);\r
8209   CloseHandle(is->hFile);\r
8210 \r
8211   if (appData.debugMode) {\r
8212     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8213   }\r
8214 \r
8215   return 0;\r
8216 }\r
8217 \r
8218 \r
8219 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8220 DWORD\r
8221 NonOvlInputThread(LPVOID arg)\r
8222 {\r
8223   InputSource *is;\r
8224   char *p, *q;\r
8225   int i;\r
8226   char prev;\r
8227 \r
8228   is = (InputSource *) arg;\r
8229   while (is->hThread != NULL) {\r
8230     is->error = ReadFile(is->hFile, is->next,\r
8231                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8232                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8233     if (is->error == NO_ERROR) {\r
8234       /* Change CRLF to LF */\r
8235       if (is->next > is->buf) {\r
8236         p = is->next - 1;\r
8237         i = is->count + 1;\r
8238       } else {\r
8239         p = is->next;\r
8240         i = is->count;\r
8241       }\r
8242       q = p;\r
8243       prev = NULLCHAR;\r
8244       while (i > 0) {\r
8245         if (prev == '\r' && *p == '\n') {\r
8246           *(q-1) = '\n';\r
8247           is->count--;\r
8248         } else { \r
8249           *q++ = *p;\r
8250         }\r
8251         prev = *p++;\r
8252         i--;\r
8253       }\r
8254       *q = NULLCHAR;\r
8255       is->next = q;\r
8256     } else {\r
8257       if (is->error == ERROR_BROKEN_PIPE) {\r
8258         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8259         is->count = 0; \r
8260       } else {\r
8261         is->count = (DWORD) -1;\r
8262       }\r
8263     }\r
8264 \r
8265     CheckForInputBufferFull( is );\r
8266 \r
8267     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8268 \r
8269     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8270 \r
8271     if (is->count < 0) break;  /* Quit on error */\r
8272   }\r
8273   CloseHandle(is->hFile);\r
8274   return 0;\r
8275 }\r
8276 \r
8277 DWORD\r
8278 SocketInputThread(LPVOID arg)\r
8279 {\r
8280   InputSource *is;\r
8281 \r
8282   is = (InputSource *) arg;\r
8283   while (is->hThread != NULL) {\r
8284     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8285     if ((int)is->count == SOCKET_ERROR) {\r
8286       is->count = (DWORD) -1;\r
8287       is->error = WSAGetLastError();\r
8288     } else {\r
8289       is->error = NO_ERROR;\r
8290       is->next += is->count;\r
8291       if (is->count == 0 && is->second == is) {\r
8292         /* End of file on stderr; quit with no message */\r
8293         break;\r
8294       }\r
8295     }\r
8296     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8297 \r
8298     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8299 \r
8300     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8301   }\r
8302   return 0;\r
8303 }\r
8304 \r
8305 VOID\r
8306 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8307 {\r
8308   InputSource *is;\r
8309 \r
8310   is = (InputSource *) lParam;\r
8311   if (is->lineByLine) {\r
8312     /* Feed in lines one by one */\r
8313     char *p = is->buf;\r
8314     char *q = p;\r
8315     while (q < is->next) {\r
8316       if (*q++ == '\n') {\r
8317         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8318         p = q;\r
8319       }\r
8320     }\r
8321     \r
8322     /* Move any partial line to the start of the buffer */\r
8323     q = is->buf;\r
8324     while (p < is->next) {\r
8325       *q++ = *p++;\r
8326     }\r
8327     is->next = q;\r
8328 \r
8329     if (is->error != NO_ERROR || is->count == 0) {\r
8330       /* Notify backend of the error.  Note: If there was a partial\r
8331          line at the end, it is not flushed through. */\r
8332       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8333     }\r
8334   } else {\r
8335     /* Feed in the whole chunk of input at once */\r
8336     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8337     is->next = is->buf;\r
8338   }\r
8339 }\r
8340 \r
8341 /*---------------------------------------------------------------------------*\\r
8342  *\r
8343  *  Menu enables. Used when setting various modes.\r
8344  *\r
8345 \*---------------------------------------------------------------------------*/\r
8346 \r
8347 typedef struct {\r
8348   int item;\r
8349   int flags;\r
8350 } Enables;\r
8351 \r
8352 VOID\r
8353 GreyRevert(Boolean grey)\r
8354 { // [HGM] vari: for retracting variations in local mode\r
8355   HMENU hmenu = GetMenu(hwndMain);\r
8356   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8357 }\r
8358 \r
8359 VOID\r
8360 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8361 {\r
8362   while (enab->item > 0) {\r
8363     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8364     enab++;\r
8365   }\r
8366 }\r
8367 \r
8368 Enables gnuEnables[] = {\r
8369   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8370   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8371   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8372   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8373   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8374   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8375   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8376   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8377   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8378   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8379   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8380   { -1, -1 }\r
8381 };\r
8382 \r
8383 Enables icsEnables[] = {\r
8384   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8385   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8386   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8387   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8388   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8389   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8390   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8391   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8392   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8393   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8394   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8395   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8396   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8397   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8398   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8399   { -1, -1 }\r
8400 };\r
8401 \r
8402 #ifdef ZIPPY\r
8403 Enables zippyEnables[] = {\r
8404   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8405   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8406   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8407   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8408   { -1, -1 }\r
8409 };\r
8410 #endif\r
8411 \r
8412 Enables ncpEnables[] = {\r
8413   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8414   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8415   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8416   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8417   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8418   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8419   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8420   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8421   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8422   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8423   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8424   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8425   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8426   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8427   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8428   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8429   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8430   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8431   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8432   { -1, -1 }\r
8433 };\r
8434 \r
8435 Enables trainingOnEnables[] = {\r
8436   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8437   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8438   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8439   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8440   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8441   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8442   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8443   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8444   { -1, -1 }\r
8445 };\r
8446 \r
8447 Enables trainingOffEnables[] = {\r
8448   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8449   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8450   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8451   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8452   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8453   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8454   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8455   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8456   { -1, -1 }\r
8457 };\r
8458 \r
8459 /* These modify either ncpEnables or gnuEnables */\r
8460 Enables cmailEnables[] = {\r
8461   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8462   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8463   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8464   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8465   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8466   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8467   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8468   { -1, -1 }\r
8469 };\r
8470 \r
8471 Enables machineThinkingEnables[] = {\r
8472   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8473   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8474   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8475   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8476   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8477   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8478   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8479   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8480   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8481   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8482   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8483   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8484   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8485   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8486   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8487   { -1, -1 }\r
8488 };\r
8489 \r
8490 Enables userThinkingEnables[] = {\r
8491   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8492   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8493   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8494   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8495   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8496   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8497   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8498   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8499   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8500   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8501   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8502   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8503   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8504   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8505   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8506   { -1, -1 }\r
8507 };\r
8508 \r
8509 /*---------------------------------------------------------------------------*\\r
8510  *\r
8511  *  Front-end interface functions exported by XBoard.\r
8512  *  Functions appear in same order as prototypes in frontend.h.\r
8513  * \r
8514 \*---------------------------------------------------------------------------*/\r
8515 VOID\r
8516 ModeHighlight()\r
8517 {\r
8518   static UINT prevChecked = 0;\r
8519   static int prevPausing = 0;\r
8520   UINT nowChecked;\r
8521 \r
8522   if (pausing != prevPausing) {\r
8523     prevPausing = pausing;\r
8524     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8525                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8526     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8527   }\r
8528 \r
8529   switch (gameMode) {\r
8530   case BeginningOfGame:\r
8531     if (appData.icsActive)\r
8532       nowChecked = IDM_IcsClient;\r
8533     else if (appData.noChessProgram)\r
8534       nowChecked = IDM_EditGame;\r
8535     else\r
8536       nowChecked = IDM_MachineBlack;\r
8537     break;\r
8538   case MachinePlaysBlack:\r
8539     nowChecked = IDM_MachineBlack;\r
8540     break;\r
8541   case MachinePlaysWhite:\r
8542     nowChecked = IDM_MachineWhite;\r
8543     break;\r
8544   case TwoMachinesPlay:\r
8545     nowChecked = IDM_TwoMachines;\r
8546     break;\r
8547   case AnalyzeMode:\r
8548     nowChecked = IDM_AnalysisMode;\r
8549     break;\r
8550   case AnalyzeFile:\r
8551     nowChecked = IDM_AnalyzeFile;\r
8552     break;\r
8553   case EditGame:\r
8554     nowChecked = IDM_EditGame;\r
8555     break;\r
8556   case PlayFromGameFile:\r
8557     nowChecked = IDM_LoadGame;\r
8558     break;\r
8559   case EditPosition:\r
8560     nowChecked = IDM_EditPosition;\r
8561     break;\r
8562   case Training:\r
8563     nowChecked = IDM_Training;\r
8564     break;\r
8565   case IcsPlayingWhite:\r
8566   case IcsPlayingBlack:\r
8567   case IcsObserving:\r
8568   case IcsIdle:\r
8569     nowChecked = IDM_IcsClient;\r
8570     break;\r
8571   default:\r
8572   case EndOfGame:\r
8573     nowChecked = 0;\r
8574     break;\r
8575   }\r
8576   if (prevChecked != 0)\r
8577     (void) CheckMenuItem(GetMenu(hwndMain),\r
8578                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8579   if (nowChecked != 0)\r
8580     (void) CheckMenuItem(GetMenu(hwndMain),\r
8581                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8582 \r
8583   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8584     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8585                           MF_BYCOMMAND|MF_ENABLED);\r
8586   } else {\r
8587     (void) EnableMenuItem(GetMenu(hwndMain), \r
8588                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8589   }\r
8590 \r
8591   prevChecked = nowChecked;\r
8592 \r
8593   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8594   if (appData.icsActive) {\r
8595        if (appData.icsEngineAnalyze) {\r
8596                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8597                        MF_BYCOMMAND|MF_CHECKED);\r
8598        } else {\r
8599                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8600                        MF_BYCOMMAND|MF_UNCHECKED);\r
8601        }\r
8602   }\r
8603 }\r
8604 \r
8605 VOID\r
8606 SetICSMode()\r
8607 {\r
8608   HMENU hmenu = GetMenu(hwndMain);\r
8609   SetMenuEnables(hmenu, icsEnables);\r
8610   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8611     MF_BYPOSITION|MF_ENABLED);\r
8612 #ifdef ZIPPY\r
8613   if (appData.zippyPlay) {\r
8614     SetMenuEnables(hmenu, zippyEnables);\r
8615     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8616          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8617           MF_BYCOMMAND|MF_ENABLED);\r
8618   }\r
8619 #endif\r
8620 }\r
8621 \r
8622 VOID\r
8623 SetGNUMode()\r
8624 {\r
8625   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8626 }\r
8627 \r
8628 VOID\r
8629 SetNCPMode()\r
8630 {\r
8631   HMENU hmenu = GetMenu(hwndMain);\r
8632   SetMenuEnables(hmenu, ncpEnables);\r
8633   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8634     MF_BYPOSITION|MF_GRAYED);\r
8635     DrawMenuBar(hwndMain);\r
8636 }\r
8637 \r
8638 VOID\r
8639 SetCmailMode()\r
8640 {\r
8641   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8642 }\r
8643 \r
8644 VOID \r
8645 SetTrainingModeOn()\r
8646 {\r
8647   int i;\r
8648   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8649   for (i = 0; i < N_BUTTONS; i++) {\r
8650     if (buttonDesc[i].hwnd != NULL)\r
8651       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8652   }\r
8653   CommentPopDown();\r
8654 }\r
8655 \r
8656 VOID SetTrainingModeOff()\r
8657 {\r
8658   int i;\r
8659   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8660   for (i = 0; i < N_BUTTONS; i++) {\r
8661     if (buttonDesc[i].hwnd != NULL)\r
8662       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8663   }\r
8664 }\r
8665 \r
8666 \r
8667 VOID\r
8668 SetUserThinkingEnables()\r
8669 {\r
8670   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8671 }\r
8672 \r
8673 VOID\r
8674 SetMachineThinkingEnables()\r
8675 {\r
8676   HMENU hMenu = GetMenu(hwndMain);\r
8677   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8678 \r
8679   SetMenuEnables(hMenu, machineThinkingEnables);\r
8680 \r
8681   if (gameMode == MachinePlaysBlack) {\r
8682     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8683   } else if (gameMode == MachinePlaysWhite) {\r
8684     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8685   } else if (gameMode == TwoMachinesPlay) {\r
8686     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8687   }\r
8688 }\r
8689 \r
8690 \r
8691 VOID\r
8692 DisplayTitle(char *str)\r
8693 {\r
8694   char title[MSG_SIZ], *host;\r
8695   if (str[0] != NULLCHAR) {\r
8696     strcpy(title, str);\r
8697   } else if (appData.icsActive) {\r
8698     if (appData.icsCommPort[0] != NULLCHAR)\r
8699       host = "ICS";\r
8700     else \r
8701       host = appData.icsHost;\r
8702     sprintf(title, "%s: %s", szTitle, host);\r
8703   } else if (appData.noChessProgram) {\r
8704     strcpy(title, szTitle);\r
8705   } else {\r
8706     strcpy(title, szTitle);\r
8707     strcat(title, ": ");\r
8708     strcat(title, first.tidy);\r
8709   }\r
8710   SetWindowText(hwndMain, title);\r
8711 }\r
8712 \r
8713 \r
8714 VOID\r
8715 DisplayMessage(char *str1, char *str2)\r
8716 {\r
8717   HDC hdc;\r
8718   HFONT oldFont;\r
8719   int remain = MESSAGE_TEXT_MAX - 1;\r
8720   int len;\r
8721 \r
8722   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8723   messageText[0] = NULLCHAR;\r
8724   if (*str1) {\r
8725     len = strlen(str1);\r
8726     if (len > remain) len = remain;\r
8727     strncpy(messageText, str1, len);\r
8728     messageText[len] = NULLCHAR;\r
8729     remain -= len;\r
8730   }\r
8731   if (*str2 && remain >= 2) {\r
8732     if (*str1) {\r
8733       strcat(messageText, "  ");\r
8734       remain -= 2;\r
8735     }\r
8736     len = strlen(str2);\r
8737     if (len > remain) len = remain;\r
8738     strncat(messageText, str2, len);\r
8739   }\r
8740   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8741 \r
8742   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8743 \r
8744   SAYMACHINEMOVE();\r
8745 \r
8746   hdc = GetDC(hwndMain);\r
8747   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8748   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8749              &messageRect, messageText, strlen(messageText), NULL);\r
8750   (void) SelectObject(hdc, oldFont);\r
8751   (void) ReleaseDC(hwndMain, hdc);\r
8752 }\r
8753 \r
8754 VOID\r
8755 DisplayError(char *str, int error)\r
8756 {\r
8757   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8758   int len;\r
8759 \r
8760   if (error == 0) {\r
8761     strcpy(buf, str);\r
8762   } else {\r
8763     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8764                         NULL, error, LANG_NEUTRAL,\r
8765                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8766     if (len > 0) {\r
8767       sprintf(buf, "%s:\n%s", str, buf2);\r
8768     } else {\r
8769       ErrorMap *em = errmap;\r
8770       while (em->err != 0 && em->err != error) em++;\r
8771       if (em->err != 0) {\r
8772         sprintf(buf, "%s:\n%s", str, em->msg);\r
8773       } else {\r
8774         sprintf(buf, "%s:\nError code %d", str, error);\r
8775       }\r
8776     }\r
8777   }\r
8778   \r
8779   ErrorPopUp("Error", buf);\r
8780 }\r
8781 \r
8782 \r
8783 VOID\r
8784 DisplayMoveError(char *str)\r
8785 {\r
8786   fromX = fromY = -1;\r
8787   ClearHighlights();\r
8788   DrawPosition(FALSE, NULL);\r
8789   if (appData.popupMoveErrors) {\r
8790     ErrorPopUp("Error", str);\r
8791   } else {\r
8792     DisplayMessage(str, "");\r
8793     moveErrorMessageUp = TRUE;\r
8794   }\r
8795 }\r
8796 \r
8797 VOID\r
8798 DisplayFatalError(char *str, int error, int exitStatus)\r
8799 {\r
8800   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8801   int len;\r
8802   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8803 \r
8804   if (error != 0) {\r
8805     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8806                         NULL, error, LANG_NEUTRAL,\r
8807                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8808     if (len > 0) {\r
8809       sprintf(buf, "%s:\n%s", str, buf2);\r
8810     } else {\r
8811       ErrorMap *em = errmap;\r
8812       while (em->err != 0 && em->err != error) em++;\r
8813       if (em->err != 0) {\r
8814         sprintf(buf, "%s:\n%s", str, em->msg);\r
8815       } else {\r
8816         sprintf(buf, "%s:\nError code %d", str, error);\r
8817       }\r
8818     }\r
8819     str = buf;\r
8820   }\r
8821   if (appData.debugMode) {\r
8822     fprintf(debugFP, "%s: %s\n", label, str);\r
8823   }\r
8824   if (appData.popupExitMessage) {\r
8825     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8826                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8827   }\r
8828   ExitEvent(exitStatus);\r
8829 }\r
8830 \r
8831 \r
8832 VOID\r
8833 DisplayInformation(char *str)\r
8834 {\r
8835   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8836 }\r
8837 \r
8838 \r
8839 VOID\r
8840 DisplayNote(char *str)\r
8841 {\r
8842   ErrorPopUp("Note", str);\r
8843 }\r
8844 \r
8845 \r
8846 typedef struct {\r
8847   char *title, *question, *replyPrefix;\r
8848   ProcRef pr;\r
8849 } QuestionParams;\r
8850 \r
8851 LRESULT CALLBACK\r
8852 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8853 {\r
8854   static QuestionParams *qp;\r
8855   char reply[MSG_SIZ];\r
8856   int len, err;\r
8857 \r
8858   switch (message) {\r
8859   case WM_INITDIALOG:\r
8860     qp = (QuestionParams *) lParam;\r
8861     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8862     SetWindowText(hDlg, qp->title);\r
8863     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8864     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8865     return FALSE;\r
8866 \r
8867   case WM_COMMAND:\r
8868     switch (LOWORD(wParam)) {\r
8869     case IDOK:\r
8870       strcpy(reply, qp->replyPrefix);\r
8871       if (*reply) strcat(reply, " ");\r
8872       len = strlen(reply);\r
8873       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8874       strcat(reply, "\n");\r
8875       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8876       EndDialog(hDlg, TRUE);\r
8877       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8878       return TRUE;\r
8879     case IDCANCEL:\r
8880       EndDialog(hDlg, FALSE);\r
8881       return TRUE;\r
8882     default:\r
8883       break;\r
8884     }\r
8885     break;\r
8886   }\r
8887   return FALSE;\r
8888 }\r
8889 \r
8890 VOID\r
8891 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8892 {\r
8893     QuestionParams qp;\r
8894     FARPROC lpProc;\r
8895     \r
8896     qp.title = title;\r
8897     qp.question = question;\r
8898     qp.replyPrefix = replyPrefix;\r
8899     qp.pr = pr;\r
8900     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8901     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8902       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8903     FreeProcInstance(lpProc);\r
8904 }\r
8905 \r
8906 /* [AS] Pick FRC position */\r
8907 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8908 {\r
8909     static int * lpIndexFRC;\r
8910     BOOL index_is_ok;\r
8911     char buf[16];\r
8912 \r
8913     switch( message )\r
8914     {\r
8915     case WM_INITDIALOG:\r
8916         lpIndexFRC = (int *) lParam;\r
8917 \r
8918         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8919 \r
8920         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8921         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8922         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8923         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8924 \r
8925         break;\r
8926 \r
8927     case WM_COMMAND:\r
8928         switch( LOWORD(wParam) ) {\r
8929         case IDOK:\r
8930             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8931             EndDialog( hDlg, 0 );\r
8932             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8933             return TRUE;\r
8934         case IDCANCEL:\r
8935             EndDialog( hDlg, 1 );   \r
8936             return TRUE;\r
8937         case IDC_NFG_Edit:\r
8938             if( HIWORD(wParam) == EN_CHANGE ) {\r
8939                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8940 \r
8941                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8942             }\r
8943             return TRUE;\r
8944         case IDC_NFG_Random:\r
8945             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8946             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8947             return TRUE;\r
8948         }\r
8949 \r
8950         break;\r
8951     }\r
8952 \r
8953     return FALSE;\r
8954 }\r
8955 \r
8956 int NewGameFRC()\r
8957 {\r
8958     int result;\r
8959     int index = appData.defaultFrcPosition;\r
8960     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8961 \r
8962     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8963 \r
8964     if( result == 0 ) {\r
8965         appData.defaultFrcPosition = index;\r
8966     }\r
8967 \r
8968     return result;\r
8969 }\r
8970 \r
8971 /* [AS] Game list options */\r
8972 typedef struct {\r
8973     char id;\r
8974     char * name;\r
8975 } GLT_Item;\r
8976 \r
8977 static GLT_Item GLT_ItemInfo[] = {\r
8978     { GLT_EVENT,      "Event" },\r
8979     { GLT_SITE,       "Site" },\r
8980     { GLT_DATE,       "Date" },\r
8981     { GLT_ROUND,      "Round" },\r
8982     { GLT_PLAYERS,    "Players" },\r
8983     { GLT_RESULT,     "Result" },\r
8984     { GLT_WHITE_ELO,  "White Rating" },\r
8985     { GLT_BLACK_ELO,  "Black Rating" },\r
8986     { GLT_TIME_CONTROL,"Time Control" },\r
8987     { GLT_VARIANT,    "Variant" },\r
8988     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8989     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
8990     { 0, 0 }\r
8991 };\r
8992 \r
8993 const char * GLT_FindItem( char id )\r
8994 {\r
8995     const char * result = 0;\r
8996 \r
8997     GLT_Item * list = GLT_ItemInfo;\r
8998 \r
8999     while( list->id != 0 ) {\r
9000         if( list->id == id ) {\r
9001             result = list->name;\r
9002             break;\r
9003         }\r
9004 \r
9005         list++;\r
9006     }\r
9007 \r
9008     return result;\r
9009 }\r
9010 \r
9011 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9012 {\r
9013     const char * name = GLT_FindItem( id );\r
9014 \r
9015     if( name != 0 ) {\r
9016         if( index >= 0 ) {\r
9017             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9018         }\r
9019         else {\r
9020             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9021         }\r
9022     }\r
9023 }\r
9024 \r
9025 void GLT_TagsToList( HWND hDlg, char * tags )\r
9026 {\r
9027     char * pc = tags;\r
9028 \r
9029     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9030 \r
9031     while( *pc ) {\r
9032         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9033         pc++;\r
9034     }\r
9035 \r
9036     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9037 \r
9038     pc = GLT_ALL_TAGS;\r
9039 \r
9040     while( *pc ) {\r
9041         if( strchr( tags, *pc ) == 0 ) {\r
9042             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9043         }\r
9044         pc++;\r
9045     }\r
9046 \r
9047     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9048 }\r
9049 \r
9050 char GLT_ListItemToTag( HWND hDlg, int index )\r
9051 {\r
9052     char result = '\0';\r
9053     char name[128];\r
9054 \r
9055     GLT_Item * list = GLT_ItemInfo;\r
9056 \r
9057     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9058         while( list->id != 0 ) {\r
9059             if( strcmp( list->name, name ) == 0 ) {\r
9060                 result = list->id;\r
9061                 break;\r
9062             }\r
9063 \r
9064             list++;\r
9065         }\r
9066     }\r
9067 \r
9068     return result;\r
9069 }\r
9070 \r
9071 void GLT_MoveSelection( HWND hDlg, int delta )\r
9072 {\r
9073     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9074     int idx2 = idx1 + delta;\r
9075     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9076 \r
9077     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9078         char buf[128];\r
9079 \r
9080         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9081         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9082         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9083         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9084     }\r
9085 }\r
9086 \r
9087 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9088 {\r
9089     static char glt[64];\r
9090     static char * lpUserGLT;\r
9091 \r
9092     switch( message )\r
9093     {\r
9094     case WM_INITDIALOG:\r
9095         lpUserGLT = (char *) lParam;\r
9096         \r
9097         strcpy( glt, lpUserGLT );\r
9098 \r
9099         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9100 \r
9101         /* Initialize list */\r
9102         GLT_TagsToList( hDlg, glt );\r
9103 \r
9104         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9105 \r
9106         break;\r
9107 \r
9108     case WM_COMMAND:\r
9109         switch( LOWORD(wParam) ) {\r
9110         case IDOK:\r
9111             {\r
9112                 char * pc = lpUserGLT;\r
9113                 int idx = 0;\r
9114 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9115                 char id;\r
9116 \r
9117                 do {\r
9118                     id = GLT_ListItemToTag( hDlg, idx );\r
9119 \r
9120                     *pc++ = id;\r
9121                     idx++;\r
9122                 } while( id != '\0' );\r
9123             }\r
9124             EndDialog( hDlg, 0 );\r
9125             return TRUE;\r
9126         case IDCANCEL:\r
9127             EndDialog( hDlg, 1 );\r
9128             return TRUE;\r
9129 \r
9130         case IDC_GLT_Default:\r
9131             strcpy( glt, GLT_DEFAULT_TAGS );\r
9132             GLT_TagsToList( hDlg, glt );\r
9133             return TRUE;\r
9134 \r
9135         case IDC_GLT_Restore:\r
9136             strcpy( glt, lpUserGLT );\r
9137             GLT_TagsToList( hDlg, glt );\r
9138             return TRUE;\r
9139 \r
9140         case IDC_GLT_Up:\r
9141             GLT_MoveSelection( hDlg, -1 );\r
9142             return TRUE;\r
9143 \r
9144         case IDC_GLT_Down:\r
9145             GLT_MoveSelection( hDlg, +1 );\r
9146             return TRUE;\r
9147         }\r
9148 \r
9149         break;\r
9150     }\r
9151 \r
9152     return FALSE;\r
9153 }\r
9154 \r
9155 int GameListOptions()\r
9156 {\r
9157     char glt[64];\r
9158     int result;\r
9159     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9160 \r
9161     strcpy( glt, appData.gameListTags );\r
9162 \r
9163     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9164 \r
9165     if( result == 0 ) {\r
9166         /* [AS] Memory leak here! */\r
9167         appData.gameListTags = strdup( glt ); \r
9168     }\r
9169 \r
9170     return result;\r
9171 }\r
9172 \r
9173 \r
9174 VOID\r
9175 DisplayIcsInteractionTitle(char *str)\r
9176 {\r
9177   char consoleTitle[MSG_SIZ];\r
9178 \r
9179   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9180   SetWindowText(hwndConsole, consoleTitle);\r
9181 }\r
9182 \r
9183 void\r
9184 DrawPosition(int fullRedraw, Board board)\r
9185 {\r
9186   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9187 }\r
9188 \r
9189 void NotifyFrontendLogin()\r
9190 {\r
9191         if (hwndConsole)\r
9192                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9193 }\r
9194 \r
9195 VOID\r
9196 ResetFrontEnd()\r
9197 {\r
9198   fromX = fromY = -1;\r
9199   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9200     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9201     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9202     dragInfo.lastpos = dragInfo.pos;\r
9203     dragInfo.start.x = dragInfo.start.y = -1;\r
9204     dragInfo.from = dragInfo.start;\r
9205     ReleaseCapture();\r
9206     DrawPosition(TRUE, NULL);\r
9207   }\r
9208 }\r
9209 \r
9210 \r
9211 VOID\r
9212 CommentPopUp(char *title, char *str)\r
9213 {\r
9214   HWND hwnd = GetActiveWindow();\r
9215   EitherCommentPopUp(0, title, str, FALSE);\r
9216   SAY(str);\r
9217   SetActiveWindow(hwnd);\r
9218 }\r
9219 \r
9220 VOID\r
9221 CommentPopDown(void)\r
9222 {\r
9223   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9224   if (commentDialog) {\r
9225     ShowWindow(commentDialog, SW_HIDE);\r
9226   }\r
9227   commentUp = FALSE;\r
9228 }\r
9229 \r
9230 VOID\r
9231 EditCommentPopUp(int index, char *title, char *str)\r
9232 {\r
9233   EitherCommentPopUp(index, title, str, TRUE);\r
9234 }\r
9235 \r
9236 \r
9237 VOID\r
9238 RingBell()\r
9239 {\r
9240   MyPlaySound(&sounds[(int)SoundMove]);\r
9241 }\r
9242 \r
9243 VOID PlayIcsWinSound()\r
9244 {\r
9245   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9246 }\r
9247 \r
9248 VOID PlayIcsLossSound()\r
9249 {\r
9250   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9251 }\r
9252 \r
9253 VOID PlayIcsDrawSound()\r
9254 {\r
9255   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9256 }\r
9257 \r
9258 VOID PlayIcsUnfinishedSound()\r
9259 {\r
9260   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9261 }\r
9262 \r
9263 VOID\r
9264 PlayAlarmSound()\r
9265 {\r
9266   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9267 }\r
9268 \r
9269 \r
9270 VOID\r
9271 EchoOn()\r
9272 {\r
9273   HWND hInput;\r
9274   consoleEcho = TRUE;\r
9275   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9276   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9277   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9278 }\r
9279 \r
9280 \r
9281 VOID\r
9282 EchoOff()\r
9283 {\r
9284   CHARFORMAT cf;\r
9285   HWND hInput;\r
9286   consoleEcho = FALSE;\r
9287   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9288   /* This works OK: set text and background both to the same color */\r
9289   cf = consoleCF;\r
9290   cf.crTextColor = COLOR_ECHOOFF;\r
9291   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9292   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9293 }\r
9294 \r
9295 /* No Raw()...? */\r
9296 \r
9297 void Colorize(ColorClass cc, int continuation)\r
9298 {\r
9299   currentColorClass = cc;\r
9300   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9301   consoleCF.crTextColor = textAttribs[cc].color;\r
9302   consoleCF.dwEffects = textAttribs[cc].effects;\r
9303   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9304 }\r
9305 \r
9306 char *\r
9307 UserName()\r
9308 {\r
9309   static char buf[MSG_SIZ];\r
9310   DWORD bufsiz = MSG_SIZ;\r
9311 \r
9312   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9313         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9314   }\r
9315   if (!GetUserName(buf, &bufsiz)) {\r
9316     /*DisplayError("Error getting user name", GetLastError());*/\r
9317     strcpy(buf, "User");\r
9318   }\r
9319   return buf;\r
9320 }\r
9321 \r
9322 char *\r
9323 HostName()\r
9324 {\r
9325   static char buf[MSG_SIZ];\r
9326   DWORD bufsiz = MSG_SIZ;\r
9327 \r
9328   if (!GetComputerName(buf, &bufsiz)) {\r
9329     /*DisplayError("Error getting host name", GetLastError());*/\r
9330     strcpy(buf, "Unknown");\r
9331   }\r
9332   return buf;\r
9333 }\r
9334 \r
9335 \r
9336 int\r
9337 ClockTimerRunning()\r
9338 {\r
9339   return clockTimerEvent != 0;\r
9340 }\r
9341 \r
9342 int\r
9343 StopClockTimer()\r
9344 {\r
9345   if (clockTimerEvent == 0) return FALSE;\r
9346   KillTimer(hwndMain, clockTimerEvent);\r
9347   clockTimerEvent = 0;\r
9348   return TRUE;\r
9349 }\r
9350 \r
9351 void\r
9352 StartClockTimer(long millisec)\r
9353 {\r
9354   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9355                              (UINT) millisec, NULL);\r
9356 }\r
9357 \r
9358 void\r
9359 DisplayWhiteClock(long timeRemaining, int highlight)\r
9360 {\r
9361   HDC hdc;\r
9362   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9363 \r
9364   if(appData.noGUI) return;\r
9365   hdc = GetDC(hwndMain);\r
9366   if (!IsIconic(hwndMain)) {\r
9367     DisplayAClock(hdc, timeRemaining, highlight, \r
9368                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9369   }\r
9370   if (highlight && iconCurrent == iconBlack) {\r
9371     iconCurrent = iconWhite;\r
9372     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9373     if (IsIconic(hwndMain)) {\r
9374       DrawIcon(hdc, 2, 2, iconCurrent);\r
9375     }\r
9376   }\r
9377   (void) ReleaseDC(hwndMain, hdc);\r
9378   if (hwndConsole)\r
9379     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9380 }\r
9381 \r
9382 void\r
9383 DisplayBlackClock(long timeRemaining, int highlight)\r
9384 {\r
9385   HDC hdc;\r
9386   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9387 \r
9388   if(appData.noGUI) return;\r
9389   hdc = GetDC(hwndMain);\r
9390   if (!IsIconic(hwndMain)) {\r
9391     DisplayAClock(hdc, timeRemaining, highlight, \r
9392                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9393   }\r
9394   if (highlight && iconCurrent == iconWhite) {\r
9395     iconCurrent = iconBlack;\r
9396     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9397     if (IsIconic(hwndMain)) {\r
9398       DrawIcon(hdc, 2, 2, iconCurrent);\r
9399     }\r
9400   }\r
9401   (void) ReleaseDC(hwndMain, hdc);\r
9402   if (hwndConsole)\r
9403     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9404 }\r
9405 \r
9406 \r
9407 int\r
9408 LoadGameTimerRunning()\r
9409 {\r
9410   return loadGameTimerEvent != 0;\r
9411 }\r
9412 \r
9413 int\r
9414 StopLoadGameTimer()\r
9415 {\r
9416   if (loadGameTimerEvent == 0) return FALSE;\r
9417   KillTimer(hwndMain, loadGameTimerEvent);\r
9418   loadGameTimerEvent = 0;\r
9419   return TRUE;\r
9420 }\r
9421 \r
9422 void\r
9423 StartLoadGameTimer(long millisec)\r
9424 {\r
9425   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9426                                 (UINT) millisec, NULL);\r
9427 }\r
9428 \r
9429 void\r
9430 AutoSaveGame()\r
9431 {\r
9432   char *defName;\r
9433   FILE *f;\r
9434   char fileTitle[MSG_SIZ];\r
9435 \r
9436   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9437   f = OpenFileDialog(hwndMain, "a", defName,\r
9438                      appData.oldSaveStyle ? "gam" : "pgn",\r
9439                      GAME_FILT, \r
9440                      "Save Game to File", NULL, fileTitle, NULL);\r
9441   if (f != NULL) {\r
9442     SaveGame(f, 0, "");\r
9443     fclose(f);\r
9444   }\r
9445 }\r
9446 \r
9447 \r
9448 void\r
9449 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9450 {\r
9451   if (delayedTimerEvent != 0) {\r
9452     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9453       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9454     }\r
9455     KillTimer(hwndMain, delayedTimerEvent);\r
9456     delayedTimerEvent = 0;\r
9457     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9458     delayedTimerCallback();\r
9459   }\r
9460   delayedTimerCallback = cb;\r
9461   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9462                                 (UINT) millisec, NULL);\r
9463 }\r
9464 \r
9465 DelayedEventCallback\r
9466 GetDelayedEvent()\r
9467 {\r
9468   if (delayedTimerEvent) {\r
9469     return delayedTimerCallback;\r
9470   } else {\r
9471     return NULL;\r
9472   }\r
9473 }\r
9474 \r
9475 void\r
9476 CancelDelayedEvent()\r
9477 {\r
9478   if (delayedTimerEvent) {\r
9479     KillTimer(hwndMain, delayedTimerEvent);\r
9480     delayedTimerEvent = 0;\r
9481   }\r
9482 }\r
9483 \r
9484 DWORD GetWin32Priority(int nice)\r
9485 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9486 /*\r
9487 REALTIME_PRIORITY_CLASS     0x00000100\r
9488 HIGH_PRIORITY_CLASS         0x00000080\r
9489 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9490 NORMAL_PRIORITY_CLASS       0x00000020\r
9491 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9492 IDLE_PRIORITY_CLASS         0x00000040\r
9493 */\r
9494         if (nice < -15) return 0x00000080;\r
9495         if (nice < 0)   return 0x00008000;\r
9496         if (nice == 0)  return 0x00000020;\r
9497         if (nice < 15)  return 0x00004000;\r
9498         return 0x00000040;\r
9499 }\r
9500 \r
9501 /* Start a child process running the given program.\r
9502    The process's standard output can be read from "from", and its\r
9503    standard input can be written to "to".\r
9504    Exit with fatal error if anything goes wrong.\r
9505    Returns an opaque pointer that can be used to destroy the process\r
9506    later.\r
9507 */\r
9508 int\r
9509 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9510 {\r
9511 #define BUFSIZE 4096\r
9512 \r
9513   HANDLE hChildStdinRd, hChildStdinWr,\r
9514     hChildStdoutRd, hChildStdoutWr;\r
9515   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9516   SECURITY_ATTRIBUTES saAttr;\r
9517   BOOL fSuccess;\r
9518   PROCESS_INFORMATION piProcInfo;\r
9519   STARTUPINFO siStartInfo;\r
9520   ChildProc *cp;\r
9521   char buf[MSG_SIZ];\r
9522   DWORD err;\r
9523 \r
9524   if (appData.debugMode) {\r
9525     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9526   }\r
9527 \r
9528   *pr = NoProc;\r
9529 \r
9530   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9531   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9532   saAttr.bInheritHandle = TRUE;\r
9533   saAttr.lpSecurityDescriptor = NULL;\r
9534 \r
9535   /*\r
9536    * The steps for redirecting child's STDOUT:\r
9537    *     1. Create anonymous pipe to be STDOUT for child.\r
9538    *     2. Create a noninheritable duplicate of read handle,\r
9539    *         and close the inheritable read handle.\r
9540    */\r
9541 \r
9542   /* Create a pipe for the child's STDOUT. */\r
9543   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9544     return GetLastError();\r
9545   }\r
9546 \r
9547   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9548   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9549                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9550                              FALSE,     /* not inherited */\r
9551                              DUPLICATE_SAME_ACCESS);\r
9552   if (! fSuccess) {\r
9553     return GetLastError();\r
9554   }\r
9555   CloseHandle(hChildStdoutRd);\r
9556 \r
9557   /*\r
9558    * The steps for redirecting child's STDIN:\r
9559    *     1. Create anonymous pipe to be STDIN for child.\r
9560    *     2. Create a noninheritable duplicate of write handle,\r
9561    *         and close the inheritable write handle.\r
9562    */\r
9563 \r
9564   /* Create a pipe for the child's STDIN. */\r
9565   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9566     return GetLastError();\r
9567   }\r
9568 \r
9569   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9570   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9571                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9572                              FALSE,     /* not inherited */\r
9573                              DUPLICATE_SAME_ACCESS);\r
9574   if (! fSuccess) {\r
9575     return GetLastError();\r
9576   }\r
9577   CloseHandle(hChildStdinWr);\r
9578 \r
9579   /* Arrange to (1) look in dir for the child .exe file, and\r
9580    * (2) have dir be the child's working directory.  Interpret\r
9581    * dir relative to the directory WinBoard loaded from. */\r
9582   GetCurrentDirectory(MSG_SIZ, buf);\r
9583   SetCurrentDirectory(installDir);\r
9584   SetCurrentDirectory(dir);\r
9585 \r
9586   /* Now create the child process. */\r
9587 \r
9588   siStartInfo.cb = sizeof(STARTUPINFO);\r
9589   siStartInfo.lpReserved = NULL;\r
9590   siStartInfo.lpDesktop = NULL;\r
9591   siStartInfo.lpTitle = NULL;\r
9592   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9593   siStartInfo.cbReserved2 = 0;\r
9594   siStartInfo.lpReserved2 = NULL;\r
9595   siStartInfo.hStdInput = hChildStdinRd;\r
9596   siStartInfo.hStdOutput = hChildStdoutWr;\r
9597   siStartInfo.hStdError = hChildStdoutWr;\r
9598 \r
9599   fSuccess = CreateProcess(NULL,\r
9600                            cmdLine,        /* command line */\r
9601                            NULL,           /* process security attributes */\r
9602                            NULL,           /* primary thread security attrs */\r
9603                            TRUE,           /* handles are inherited */\r
9604                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9605                            NULL,           /* use parent's environment */\r
9606                            NULL,\r
9607                            &siStartInfo, /* STARTUPINFO pointer */\r
9608                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9609 \r
9610   err = GetLastError();\r
9611   SetCurrentDirectory(buf); /* return to prev directory */\r
9612   if (! fSuccess) {\r
9613     return err;\r
9614   }\r
9615 \r
9616   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9617     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9618     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9619   }\r
9620 \r
9621   /* Close the handles we don't need in the parent */\r
9622   CloseHandle(piProcInfo.hThread);\r
9623   CloseHandle(hChildStdinRd);\r
9624   CloseHandle(hChildStdoutWr);\r
9625 \r
9626   /* Prepare return value */\r
9627   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9628   cp->kind = CPReal;\r
9629   cp->hProcess = piProcInfo.hProcess;\r
9630   cp->pid = piProcInfo.dwProcessId;\r
9631   cp->hFrom = hChildStdoutRdDup;\r
9632   cp->hTo = hChildStdinWrDup;\r
9633 \r
9634   *pr = (void *) cp;\r
9635 \r
9636   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9637      2000 where engines sometimes don't see the initial command(s)\r
9638      from WinBoard and hang.  I don't understand how that can happen,\r
9639      but the Sleep is harmless, so I've put it in.  Others have also\r
9640      reported what may be the same problem, so hopefully this will fix\r
9641      it for them too.  */\r
9642   Sleep(500);\r
9643 \r
9644   return NO_ERROR;\r
9645 }\r
9646 \r
9647 \r
9648 void\r
9649 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9650 {\r
9651   ChildProc *cp; int result;\r
9652 \r
9653   cp = (ChildProc *) pr;\r
9654   if (cp == NULL) return;\r
9655 \r
9656   switch (cp->kind) {\r
9657   case CPReal:\r
9658     /* TerminateProcess is considered harmful, so... */\r
9659     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9660     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9661     /* The following doesn't work because the chess program\r
9662        doesn't "have the same console" as WinBoard.  Maybe\r
9663        we could arrange for this even though neither WinBoard\r
9664        nor the chess program uses a console for stdio? */\r
9665     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9666 \r
9667     /* [AS] Special termination modes for misbehaving programs... */\r
9668     if( signal == 9 ) { \r
9669         result = TerminateProcess( cp->hProcess, 0 );\r
9670 \r
9671         if ( appData.debugMode) {\r
9672             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9673         }\r
9674     }\r
9675     else if( signal == 10 ) {\r
9676         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9677 \r
9678         if( dw != WAIT_OBJECT_0 ) {\r
9679             result = TerminateProcess( cp->hProcess, 0 );\r
9680 \r
9681             if ( appData.debugMode) {\r
9682                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9683             }\r
9684 \r
9685         }\r
9686     }\r
9687 \r
9688     CloseHandle(cp->hProcess);\r
9689     break;\r
9690 \r
9691   case CPComm:\r
9692     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9693     break;\r
9694 \r
9695   case CPSock:\r
9696     closesocket(cp->sock);\r
9697     WSACleanup();\r
9698     break;\r
9699 \r
9700   case CPRcmd:\r
9701     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9702     closesocket(cp->sock);\r
9703     closesocket(cp->sock2);\r
9704     WSACleanup();\r
9705     break;\r
9706   }\r
9707   free(cp);\r
9708 }\r
9709 \r
9710 void\r
9711 InterruptChildProcess(ProcRef pr)\r
9712 {\r
9713   ChildProc *cp;\r
9714 \r
9715   cp = (ChildProc *) pr;\r
9716   if (cp == NULL) return;\r
9717   switch (cp->kind) {\r
9718   case CPReal:\r
9719     /* The following doesn't work because the chess program\r
9720        doesn't "have the same console" as WinBoard.  Maybe\r
9721        we could arrange for this even though neither WinBoard\r
9722        nor the chess program uses a console for stdio */\r
9723     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9724     break;\r
9725 \r
9726   case CPComm:\r
9727   case CPSock:\r
9728     /* Can't interrupt */\r
9729     break;\r
9730 \r
9731   case CPRcmd:\r
9732     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9733     break;\r
9734   }\r
9735 }\r
9736 \r
9737 \r
9738 int\r
9739 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9740 {\r
9741   char cmdLine[MSG_SIZ];\r
9742 \r
9743   if (port[0] == NULLCHAR) {\r
9744     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9745   } else {\r
9746     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9747   }\r
9748   return StartChildProcess(cmdLine, "", pr);\r
9749 }\r
9750 \r
9751 \r
9752 /* Code to open TCP sockets */\r
9753 \r
9754 int\r
9755 OpenTCP(char *host, char *port, ProcRef *pr)\r
9756 {\r
9757   ChildProc *cp;\r
9758   int err;\r
9759   SOCKET s;\r
9760   struct sockaddr_in sa, mysa;\r
9761   struct hostent FAR *hp;\r
9762   unsigned short uport;\r
9763   WORD wVersionRequested;\r
9764   WSADATA wsaData;\r
9765 \r
9766   /* Initialize socket DLL */\r
9767   wVersionRequested = MAKEWORD(1, 1);\r
9768   err = WSAStartup(wVersionRequested, &wsaData);\r
9769   if (err != 0) return err;\r
9770 \r
9771   /* Make socket */\r
9772   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9773     err = WSAGetLastError();\r
9774     WSACleanup();\r
9775     return err;\r
9776   }\r
9777 \r
9778   /* Bind local address using (mostly) don't-care values.\r
9779    */\r
9780   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9781   mysa.sin_family = AF_INET;\r
9782   mysa.sin_addr.s_addr = INADDR_ANY;\r
9783   uport = (unsigned short) 0;\r
9784   mysa.sin_port = htons(uport);\r
9785   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9786       == SOCKET_ERROR) {\r
9787     err = WSAGetLastError();\r
9788     WSACleanup();\r
9789     return err;\r
9790   }\r
9791 \r
9792   /* Resolve remote host name */\r
9793   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9794   if (!(hp = gethostbyname(host))) {\r
9795     unsigned int b0, b1, b2, b3;\r
9796 \r
9797     err = WSAGetLastError();\r
9798 \r
9799     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9800       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9801       hp->h_addrtype = AF_INET;\r
9802       hp->h_length = 4;\r
9803       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9804       hp->h_addr_list[0] = (char *) malloc(4);\r
9805       hp->h_addr_list[0][0] = (char) b0;\r
9806       hp->h_addr_list[0][1] = (char) b1;\r
9807       hp->h_addr_list[0][2] = (char) b2;\r
9808       hp->h_addr_list[0][3] = (char) b3;\r
9809     } else {\r
9810       WSACleanup();\r
9811       return err;\r
9812     }\r
9813   }\r
9814   sa.sin_family = hp->h_addrtype;\r
9815   uport = (unsigned short) atoi(port);\r
9816   sa.sin_port = htons(uport);\r
9817   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9818 \r
9819   /* Make connection */\r
9820   if (connect(s, (struct sockaddr *) &sa,\r
9821               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9822     err = WSAGetLastError();\r
9823     WSACleanup();\r
9824     return err;\r
9825   }\r
9826 \r
9827   /* Prepare return value */\r
9828   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9829   cp->kind = CPSock;\r
9830   cp->sock = s;\r
9831   *pr = (ProcRef *) cp;\r
9832 \r
9833   return NO_ERROR;\r
9834 }\r
9835 \r
9836 int\r
9837 OpenCommPort(char *name, ProcRef *pr)\r
9838 {\r
9839   HANDLE h;\r
9840   COMMTIMEOUTS ct;\r
9841   ChildProc *cp;\r
9842   char fullname[MSG_SIZ];\r
9843 \r
9844   if (*name != '\\')\r
9845     sprintf(fullname, "\\\\.\\%s", name);\r
9846   else\r
9847     strcpy(fullname, name);\r
9848 \r
9849   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9850                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9851   if (h == (HANDLE) -1) {\r
9852     return GetLastError();\r
9853   }\r
9854   hCommPort = h;\r
9855 \r
9856   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9857 \r
9858   /* Accumulate characters until a 100ms pause, then parse */\r
9859   ct.ReadIntervalTimeout = 100;\r
9860   ct.ReadTotalTimeoutMultiplier = 0;\r
9861   ct.ReadTotalTimeoutConstant = 0;\r
9862   ct.WriteTotalTimeoutMultiplier = 0;\r
9863   ct.WriteTotalTimeoutConstant = 0;\r
9864   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9865 \r
9866   /* Prepare return value */\r
9867   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9868   cp->kind = CPComm;\r
9869   cp->hFrom = h;\r
9870   cp->hTo = h;\r
9871   *pr = (ProcRef *) cp;\r
9872 \r
9873   return NO_ERROR;\r
9874 }\r
9875 \r
9876 int\r
9877 OpenLoopback(ProcRef *pr)\r
9878 {\r
9879   DisplayFatalError("Not implemented", 0, 1);\r
9880   return NO_ERROR;\r
9881 }\r
9882 \r
9883 \r
9884 int\r
9885 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9886 {\r
9887   ChildProc *cp;\r
9888   int err;\r
9889   SOCKET s, s2, s3;\r
9890   struct sockaddr_in sa, mysa;\r
9891   struct hostent FAR *hp;\r
9892   unsigned short uport;\r
9893   WORD wVersionRequested;\r
9894   WSADATA wsaData;\r
9895   int fromPort;\r
9896   char stderrPortStr[MSG_SIZ];\r
9897 \r
9898   /* Initialize socket DLL */\r
9899   wVersionRequested = MAKEWORD(1, 1);\r
9900   err = WSAStartup(wVersionRequested, &wsaData);\r
9901   if (err != 0) return err;\r
9902 \r
9903   /* Resolve remote host name */\r
9904   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9905   if (!(hp = gethostbyname(host))) {\r
9906     unsigned int b0, b1, b2, b3;\r
9907 \r
9908     err = WSAGetLastError();\r
9909 \r
9910     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9911       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9912       hp->h_addrtype = AF_INET;\r
9913       hp->h_length = 4;\r
9914       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9915       hp->h_addr_list[0] = (char *) malloc(4);\r
9916       hp->h_addr_list[0][0] = (char) b0;\r
9917       hp->h_addr_list[0][1] = (char) b1;\r
9918       hp->h_addr_list[0][2] = (char) b2;\r
9919       hp->h_addr_list[0][3] = (char) b3;\r
9920     } else {\r
9921       WSACleanup();\r
9922       return err;\r
9923     }\r
9924   }\r
9925   sa.sin_family = hp->h_addrtype;\r
9926   uport = (unsigned short) 514;\r
9927   sa.sin_port = htons(uport);\r
9928   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9929 \r
9930   /* Bind local socket to unused "privileged" port address\r
9931    */\r
9932   s = INVALID_SOCKET;\r
9933   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9934   mysa.sin_family = AF_INET;\r
9935   mysa.sin_addr.s_addr = INADDR_ANY;\r
9936   for (fromPort = 1023;; fromPort--) {\r
9937     if (fromPort < 0) {\r
9938       WSACleanup();\r
9939       return WSAEADDRINUSE;\r
9940     }\r
9941     if (s == INVALID_SOCKET) {\r
9942       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9943         err = WSAGetLastError();\r
9944         WSACleanup();\r
9945         return err;\r
9946       }\r
9947     }\r
9948     uport = (unsigned short) fromPort;\r
9949     mysa.sin_port = htons(uport);\r
9950     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9951         == SOCKET_ERROR) {\r
9952       err = WSAGetLastError();\r
9953       if (err == WSAEADDRINUSE) continue;\r
9954       WSACleanup();\r
9955       return err;\r
9956     }\r
9957     if (connect(s, (struct sockaddr *) &sa,\r
9958       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9959       err = WSAGetLastError();\r
9960       if (err == WSAEADDRINUSE) {\r
9961         closesocket(s);\r
9962         s = -1;\r
9963         continue;\r
9964       }\r
9965       WSACleanup();\r
9966       return err;\r
9967     }\r
9968     break;\r
9969   }\r
9970 \r
9971   /* Bind stderr local socket to unused "privileged" port address\r
9972    */\r
9973   s2 = INVALID_SOCKET;\r
9974   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9975   mysa.sin_family = AF_INET;\r
9976   mysa.sin_addr.s_addr = INADDR_ANY;\r
9977   for (fromPort = 1023;; fromPort--) {\r
9978     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9979     if (fromPort < 0) {\r
9980       (void) closesocket(s);\r
9981       WSACleanup();\r
9982       return WSAEADDRINUSE;\r
9983     }\r
9984     if (s2 == INVALID_SOCKET) {\r
9985       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9986         err = WSAGetLastError();\r
9987         closesocket(s);\r
9988         WSACleanup();\r
9989         return err;\r
9990       }\r
9991     }\r
9992     uport = (unsigned short) fromPort;\r
9993     mysa.sin_port = htons(uport);\r
9994     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9995         == SOCKET_ERROR) {\r
9996       err = WSAGetLastError();\r
9997       if (err == WSAEADDRINUSE) continue;\r
9998       (void) closesocket(s);\r
9999       WSACleanup();\r
10000       return err;\r
10001     }\r
10002     if (listen(s2, 1) == SOCKET_ERROR) {\r
10003       err = WSAGetLastError();\r
10004       if (err == WSAEADDRINUSE) {\r
10005         closesocket(s2);\r
10006         s2 = INVALID_SOCKET;\r
10007         continue;\r
10008       }\r
10009       (void) closesocket(s);\r
10010       (void) closesocket(s2);\r
10011       WSACleanup();\r
10012       return err;\r
10013     }\r
10014     break;\r
10015   }\r
10016   prevStderrPort = fromPort; // remember port used\r
10017   sprintf(stderrPortStr, "%d", fromPort);\r
10018 \r
10019   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10020     err = WSAGetLastError();\r
10021     (void) closesocket(s);\r
10022     (void) closesocket(s2);\r
10023     WSACleanup();\r
10024     return err;\r
10025   }\r
10026 \r
10027   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10028     err = WSAGetLastError();\r
10029     (void) closesocket(s);\r
10030     (void) closesocket(s2);\r
10031     WSACleanup();\r
10032     return err;\r
10033   }\r
10034   if (*user == NULLCHAR) user = UserName();\r
10035   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10036     err = WSAGetLastError();\r
10037     (void) closesocket(s);\r
10038     (void) closesocket(s2);\r
10039     WSACleanup();\r
10040     return err;\r
10041   }\r
10042   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10043     err = WSAGetLastError();\r
10044     (void) closesocket(s);\r
10045     (void) closesocket(s2);\r
10046     WSACleanup();\r
10047     return err;\r
10048   }\r
10049 \r
10050   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10051     err = WSAGetLastError();\r
10052     (void) closesocket(s);\r
10053     (void) closesocket(s2);\r
10054     WSACleanup();\r
10055     return err;\r
10056   }\r
10057   (void) closesocket(s2);  /* Stop listening */\r
10058 \r
10059   /* Prepare return value */\r
10060   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10061   cp->kind = CPRcmd;\r
10062   cp->sock = s;\r
10063   cp->sock2 = s3;\r
10064   *pr = (ProcRef *) cp;\r
10065 \r
10066   return NO_ERROR;\r
10067 }\r
10068 \r
10069 \r
10070 InputSourceRef\r
10071 AddInputSource(ProcRef pr, int lineByLine,\r
10072                InputCallback func, VOIDSTAR closure)\r
10073 {\r
10074   InputSource *is, *is2 = NULL;\r
10075   ChildProc *cp = (ChildProc *) pr;\r
10076 \r
10077   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10078   is->lineByLine = lineByLine;\r
10079   is->func = func;\r
10080   is->closure = closure;\r
10081   is->second = NULL;\r
10082   is->next = is->buf;\r
10083   if (pr == NoProc) {\r
10084     is->kind = CPReal;\r
10085     consoleInputSource = is;\r
10086   } else {\r
10087     is->kind = cp->kind;\r
10088     /* \r
10089         [AS] Try to avoid a race condition if the thread is given control too early:\r
10090         we create all threads suspended so that the is->hThread variable can be\r
10091         safely assigned, then let the threads start with ResumeThread.\r
10092     */\r
10093     switch (cp->kind) {\r
10094     case CPReal:\r
10095       is->hFile = cp->hFrom;\r
10096       cp->hFrom = NULL; /* now owned by InputThread */\r
10097       is->hThread =\r
10098         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10099                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10100       break;\r
10101 \r
10102     case CPComm:\r
10103       is->hFile = cp->hFrom;\r
10104       cp->hFrom = NULL; /* now owned by InputThread */\r
10105       is->hThread =\r
10106         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10107                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10108       break;\r
10109 \r
10110     case CPSock:\r
10111       is->sock = cp->sock;\r
10112       is->hThread =\r
10113         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10114                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10115       break;\r
10116 \r
10117     case CPRcmd:\r
10118       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10119       *is2 = *is;\r
10120       is->sock = cp->sock;\r
10121       is->second = is2;\r
10122       is2->sock = cp->sock2;\r
10123       is2->second = is2;\r
10124       is->hThread =\r
10125         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10126                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10127       is2->hThread =\r
10128         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10129                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10130       break;\r
10131     }\r
10132 \r
10133     if( is->hThread != NULL ) {\r
10134         ResumeThread( is->hThread );\r
10135     }\r
10136 \r
10137     if( is2 != NULL && is2->hThread != NULL ) {\r
10138         ResumeThread( is2->hThread );\r
10139     }\r
10140   }\r
10141 \r
10142   return (InputSourceRef) is;\r
10143 }\r
10144 \r
10145 void\r
10146 RemoveInputSource(InputSourceRef isr)\r
10147 {\r
10148   InputSource *is;\r
10149 \r
10150   is = (InputSource *) isr;\r
10151   is->hThread = NULL;  /* tell thread to stop */\r
10152   CloseHandle(is->hThread);\r
10153   if (is->second != NULL) {\r
10154     is->second->hThread = NULL;\r
10155     CloseHandle(is->second->hThread);\r
10156   }\r
10157 }\r
10158 \r
10159 int no_wrap(char *message, int count)\r
10160 {\r
10161     ConsoleOutput(message, count, FALSE);\r
10162     return count;\r
10163 }\r
10164 \r
10165 int\r
10166 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10167 {\r
10168   DWORD dOutCount;\r
10169   int outCount = SOCKET_ERROR;\r
10170   ChildProc *cp = (ChildProc *) pr;\r
10171   static OVERLAPPED ovl;\r
10172   static int line = 0;\r
10173 \r
10174   if (pr == NoProc)\r
10175   {\r
10176     if (appData.noJoin || !appData.useInternalWrap)\r
10177       return no_wrap(message, count);\r
10178     else\r
10179     {\r
10180       int width = get_term_width();\r
10181       int len = wrap(NULL, message, count, width, &line);\r
10182       char *msg = malloc(len);\r
10183       int dbgchk;\r
10184 \r
10185       if (!msg)\r
10186         return no_wrap(message, count);\r
10187       else\r
10188       {\r
10189         dbgchk = wrap(msg, message, count, width, &line);\r
10190         if (dbgchk != len && appData.debugMode)\r
10191             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
10192         ConsoleOutput(msg, len, FALSE);\r
10193         free(msg);\r
10194         return len;\r
10195       }\r
10196     }\r
10197   }\r
10198 \r
10199   if (ovl.hEvent == NULL) {\r
10200     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10201   }\r
10202   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10203 \r
10204   switch (cp->kind) {\r
10205   case CPSock:\r
10206   case CPRcmd:\r
10207     outCount = send(cp->sock, message, count, 0);\r
10208     if (outCount == SOCKET_ERROR) {\r
10209       *outError = WSAGetLastError();\r
10210     } else {\r
10211       *outError = NO_ERROR;\r
10212     }\r
10213     break;\r
10214 \r
10215   case CPReal:\r
10216     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10217                   &dOutCount, NULL)) {\r
10218       *outError = NO_ERROR;\r
10219       outCount = (int) dOutCount;\r
10220     } else {\r
10221       *outError = GetLastError();\r
10222     }\r
10223     break;\r
10224 \r
10225   case CPComm:\r
10226     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10227                             &dOutCount, &ovl);\r
10228     if (*outError == NO_ERROR) {\r
10229       outCount = (int) dOutCount;\r
10230     }\r
10231     break;\r
10232   }\r
10233   return outCount;\r
10234 }\r
10235 \r
10236 int\r
10237 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10238                        long msdelay)\r
10239 {\r
10240   /* Ignore delay, not implemented for WinBoard */\r
10241   return OutputToProcess(pr, message, count, outError);\r
10242 }\r
10243 \r
10244 \r
10245 void\r
10246 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10247                         char *buf, int count, int error)\r
10248 {\r
10249   DisplayFatalError("Not implemented", 0, 1);\r
10250 }\r
10251 \r
10252 /* see wgamelist.c for Game List functions */\r
10253 /* see wedittags.c for Edit Tags functions */\r
10254 \r
10255 \r
10256 VOID\r
10257 ICSInitScript()\r
10258 {\r
10259   FILE *f;\r
10260   char buf[MSG_SIZ];\r
10261   char *dummy;\r
10262 \r
10263   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10264     f = fopen(buf, "r");\r
10265     if (f != NULL) {\r
10266       ProcessICSInitScript(f);\r
10267       fclose(f);\r
10268     }\r
10269   }\r
10270 }\r
10271 \r
10272 \r
10273 VOID\r
10274 StartAnalysisClock()\r
10275 {\r
10276   if (analysisTimerEvent) return;\r
10277   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10278                                         (UINT) 2000, NULL);\r
10279 }\r
10280 \r
10281 VOID\r
10282 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10283 {\r
10284   highlightInfo.sq[0].x = fromX;\r
10285   highlightInfo.sq[0].y = fromY;\r
10286   highlightInfo.sq[1].x = toX;\r
10287   highlightInfo.sq[1].y = toY;\r
10288 }\r
10289 \r
10290 VOID\r
10291 ClearHighlights()\r
10292 {\r
10293   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10294     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10295 }\r
10296 \r
10297 VOID\r
10298 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10299 {\r
10300   premoveHighlightInfo.sq[0].x = fromX;\r
10301   premoveHighlightInfo.sq[0].y = fromY;\r
10302   premoveHighlightInfo.sq[1].x = toX;\r
10303   premoveHighlightInfo.sq[1].y = toY;\r
10304 }\r
10305 \r
10306 VOID\r
10307 ClearPremoveHighlights()\r
10308 {\r
10309   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10310     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10311 }\r
10312 \r
10313 VOID\r
10314 ShutDownFrontEnd()\r
10315 {\r
10316   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10317   DeleteClipboardTempFiles();\r
10318 }\r
10319 \r
10320 void\r
10321 BoardToTop()\r
10322 {\r
10323     if (IsIconic(hwndMain))\r
10324       ShowWindow(hwndMain, SW_RESTORE);\r
10325 \r
10326     SetActiveWindow(hwndMain);\r
10327 }\r
10328 \r
10329 /*\r
10330  * Prototypes for animation support routines\r
10331  */\r
10332 static void ScreenSquare(int column, int row, POINT * pt);\r
10333 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10334      POINT frames[], int * nFrames);\r
10335 \r
10336 \r
10337 void\r
10338 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10339 {       // [HGM] atomic: animate blast wave\r
10340         int i;\r
10341 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10342         explodeInfo.fromX = fromX;\r
10343         explodeInfo.fromY = fromY;\r
10344         explodeInfo.toX = toX;\r
10345         explodeInfo.toY = toY;\r
10346         for(i=1; i<nFrames; i++) {\r
10347             explodeInfo.radius = (i*180)/(nFrames-1);\r
10348             DrawPosition(FALSE, NULL);\r
10349             Sleep(appData.animSpeed);\r
10350         }\r
10351         explodeInfo.radius = 0;\r
10352         DrawPosition(TRUE, NULL);\r
10353 }\r
10354 \r
10355 #define kFactor 4\r
10356 \r
10357 void\r
10358 AnimateMove(board, fromX, fromY, toX, toY)\r
10359      Board board;\r
10360      int fromX;\r
10361      int fromY;\r
10362      int toX;\r
10363      int toY;\r
10364 {\r
10365   ChessSquare piece;\r
10366   POINT start, finish, mid;\r
10367   POINT frames[kFactor * 2 + 1];\r
10368   int nFrames, n;\r
10369 \r
10370   if (!appData.animate) return;\r
10371   if (doingSizing) return;\r
10372   if (fromY < 0 || fromX < 0) return;\r
10373   piece = board[fromY][fromX];\r
10374   if (piece >= EmptySquare) return;\r
10375 \r
10376   ScreenSquare(fromX, fromY, &start);\r
10377   ScreenSquare(toX, toY, &finish);\r
10378 \r
10379   /* All pieces except knights move in straight line */\r
10380   if (piece != WhiteKnight && piece != BlackKnight) {\r
10381     mid.x = start.x + (finish.x - start.x) / 2;\r
10382     mid.y = start.y + (finish.y - start.y) / 2;\r
10383   } else {\r
10384     /* Knight: make diagonal movement then straight */\r
10385     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10386        mid.x = start.x + (finish.x - start.x) / 2;\r
10387        mid.y = finish.y;\r
10388      } else {\r
10389        mid.x = finish.x;\r
10390        mid.y = start.y + (finish.y - start.y) / 2;\r
10391      }\r
10392   }\r
10393   \r
10394   /* Don't use as many frames for very short moves */\r
10395   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10396     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10397   else\r
10398     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10399 \r
10400   animInfo.from.x = fromX;\r
10401   animInfo.from.y = fromY;\r
10402   animInfo.to.x = toX;\r
10403   animInfo.to.y = toY;\r
10404   animInfo.lastpos = start;\r
10405   animInfo.piece = piece;\r
10406   for (n = 0; n < nFrames; n++) {\r
10407     animInfo.pos = frames[n];\r
10408     DrawPosition(FALSE, NULL);\r
10409     animInfo.lastpos = animInfo.pos;\r
10410     Sleep(appData.animSpeed);\r
10411   }\r
10412   animInfo.pos = finish;\r
10413   DrawPosition(FALSE, NULL);\r
10414   animInfo.piece = EmptySquare;\r
10415   if(gameInfo.variant == VariantAtomic && \r
10416      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10417         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10418 }\r
10419 \r
10420 /*      Convert board position to corner of screen rect and color       */\r
10421 \r
10422 static void\r
10423 ScreenSquare(column, row, pt)\r
10424      int column; int row; POINT * pt;\r
10425 {\r
10426   if (flipView) {\r
10427     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10428     pt->y = lineGap + row * (squareSize + lineGap);\r
10429   } else {\r
10430     pt->x = lineGap + column * (squareSize + lineGap);\r
10431     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10432   }\r
10433 }\r
10434 \r
10435 /*      Generate a series of frame coords from start->mid->finish.\r
10436         The movement rate doubles until the half way point is\r
10437         reached, then halves back down to the final destination,\r
10438         which gives a nice slow in/out effect. The algorithmn\r
10439         may seem to generate too many intermediates for short\r
10440         moves, but remember that the purpose is to attract the\r
10441         viewers attention to the piece about to be moved and\r
10442         then to where it ends up. Too few frames would be less\r
10443         noticeable.                                             */\r
10444 \r
10445 static void\r
10446 Tween(start, mid, finish, factor, frames, nFrames)\r
10447      POINT * start; POINT * mid;\r
10448      POINT * finish; int factor;\r
10449      POINT frames[]; int * nFrames;\r
10450 {\r
10451   int n, fraction = 1, count = 0;\r
10452 \r
10453   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10454   for (n = 0; n < factor; n++)\r
10455     fraction *= 2;\r
10456   for (n = 0; n < factor; n++) {\r
10457     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10458     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10459     count ++;\r
10460     fraction = fraction / 2;\r
10461   }\r
10462   \r
10463   /* Midpoint */\r
10464   frames[count] = *mid;\r
10465   count ++;\r
10466   \r
10467   /* Slow out, stepping 1/2, then 1/4, ... */\r
10468   fraction = 2;\r
10469   for (n = 0; n < factor; n++) {\r
10470     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10471     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10472     count ++;\r
10473     fraction = fraction * 2;\r
10474   }\r
10475   *nFrames = count;\r
10476 }\r
10477 \r
10478 void\r
10479 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10480 {\r
10481     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10482 \r
10483     EvalGraphSet( first, last, current, pvInfoList );\r
10484 }\r