ee5fa0c8b81899c3bfc01975cf5f5ca8d8701f7d
[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 #define XBOARD FALSE\r
907 \r
908 ArgDescriptor argDescriptors[] = {\r
909   /* positional arguments */\r
910   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE, INVALID },\r
911   { "", ArgNone, NULL, FALSE, INVALID },\r
912   /* keyword arguments */\r
913   JAWS_ARGS\r
914   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE, INVALID },\r
915   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE, INVALID },\r
916   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE, INVALID },\r
917   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE, INVALID },\r
918   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE, INVALID },\r
919   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE, INVALID },\r
920   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE, INVALID },\r
921   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE, INVALID },\r
922   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE, INVALID },\r
923   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE, INVALID },\r
924   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE, INVALID },\r
925   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE, INVALID },\r
926   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE, (ArgIniType) MOVES_PER_SESSION },\r
927   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE, INVALID },\r
928   { "initString", ArgString, (LPVOID) &appData.initString, FALSE, INVALID },\r
929   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE, (ArgIniType) INIT_STRING },\r
930   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE, (ArgIniType) INIT_STRING },\r
931   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
932     FALSE, (ArgIniType) COMPUTER_STRING },\r
933   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
934     FALSE, (ArgIniType) COMPUTER_STRING },\r
935   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
936     FALSE, (ArgIniType) FIRST_CHESS_PROGRAM },\r
937   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE, INVALID },\r
938   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
939     FALSE, (ArgIniType) SECOND_CHESS_PROGRAM },\r
940   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE, INVALID },\r
941   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE, FALSE },\r
942   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE, FALSE },\r
943   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE, INVALID },\r
944   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE, INVALID },\r
945   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE, FALSE },\r
946   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE, INVALID },\r
947   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE, INVALID },\r
948   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE, INVALID },\r
949   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE, (ArgIniType) FIRST_HOST },\r
950   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE, INVALID },\r
951   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE, (ArgIniType) SECOND_HOST },\r
952   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE, INVALID },\r
953   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE, (ArgIniType) FIRST_DIRECTORY },\r
954   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE, INVALID },\r
955   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE, (ArgIniType) SECOND_DIRECTORY },\r
956   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE, INVALID },\r
957 \r
958   /* some options only used by the XBoard front end, and ignored in WinBoard         */\r
959   /* Their saving is controlled by XBOARD, which in WinBoard is defined as FALSE */\r
960   { "internetChessServerInputBox", ArgBoolean, (LPVOID) &appData.icsInputBox, XBOARD, (ArgIniType) FALSE },\r
961   { "icsinput", ArgTrue, (LPVOID) &appData.icsInputBox, FALSE, INVALID },\r
962   { "xicsinput", ArgFalse, (LPVOID) &appData.icsInputBox, FALSE, INVALID },\r
963   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE, (ArgIniType) "" },\r
964   { "soundProgram", ArgFilename, (LPVOID) &appData.soundProgram, XBOARD, (ArgIniType) "play" },\r
965   { "fontSizeTolerance", ArgInt, (LPVOID) &appData.fontSizeTolerance, XBOARD, (ArgIniType) 4 },\r
966   { "lowTimeWarningColor", ArgColor, (LPVOID) &appData.lowTimeWarningColor, XBOARD, \r
967         (ArgIniType) LOWTIMEWARNING_COLOR },\r
968   { "lowTimeWarning", ArgBoolean, (LPVOID) &appData.lowTimeWarning, XBOARD, (ArgIniType) FALSE },\r
969   { "titleInWindow", ArgBoolean, (LPVOID) &appData.titleInWindow, XBOARD, (ArgIniType) FALSE },\r
970   { "title", ArgTrue, (LPVOID) &appData.titleInWindow, FALSE, INVALID },\r
971   { "xtitle", ArgFalse, (LPVOID) &appData.titleInWindow, FALSE, INVALID },\r
972   { "flashCount", ArgInt, (LPVOID) &appData.flashCount, XBOARD, (ArgIniType) FLASH_COUNT },\r
973   { "flashRate", ArgInt, (LPVOID) &appData.flashRate, XBOARD, (ArgIniType) FLASH_RATE },\r
974   { "pixmapDirectory", ArgFilename, (LPVOID) &appData.pixmapDirectory, XBOARD, (ArgIniType) "" },\r
975   { "pixmap", ArgFilename, (LPVOID) &appData.pixmapDirectory, FALSE, INVALID },\r
976   { "bitmapDirectory", ArgFilename, (LPVOID) &appData.bitmapDirectory, XBOARD, (ArgIniType) "" },\r
977   { "bm", ArgFilename, (LPVOID) &appData.bitmapDirectory, FALSE, INVALID },\r
978   { "msLoginDelay", ArgInt, (LPVOID) &appData.msLoginDelay, XBOARD, (ArgIniType) MS_LOGIN_DELAY },\r
979   { "pasteSelection", ArgBoolean, (LPVOID) &appData.pasteSelection, XBOARD, (ArgIniType) FALSE },\r
980 \r
981   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE, (ArgIniType) REMOTE_SHELL },\r
982   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE, INVALID },\r
983   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE, INVALID },\r
984   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE, INVALID },\r
985   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE, INVALID },\r
986   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE, INVALID },\r
987   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE, (ArgIniType) TIME_CONTROL },\r
988   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE, INVALID },\r
989   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE, (ArgIniType) TIME_INCREMENT },\r
990   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE, INVALID },\r
991   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE, INVALID },\r
992   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE, (ArgIniType) FALSE },\r
993   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE, INVALID },\r
994   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE, INVALID },\r
995   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE, (ArgIniType) "" },\r
996   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE, INVALID },\r
997   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE, (ArgIniType) ICS_PORT },\r
998   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE, INVALID },\r
999   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE, (ArgIniType) ICS_COMM_PORT },\r
1000   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE, INVALID },\r
1001   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE, INVALID },\r
1002   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE, INVALID },\r
1003   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE, (ArgIniType) ICS_LOGON },\r
1004   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE, INVALID },\r
1005   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
1006   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
1007   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
1008   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE, INVALID },\r
1009   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE, (ArgIniType) TELNET_PROGRAM },\r
1010   { "internetChessserverHelper", ArgFilename, (LPVOID) &appData.icsHelper, \r
1011         FALSE, INVALID }, // for XB\r
1012   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE, (ArgIniType) "" },\r
1013   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE, (ArgIniType) "" },\r
1014   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE, (ArgIniType) "" },\r
1015   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE, INVALID },\r
1016   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE, (ArgIniType) 0 },\r
1017   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE, INVALID },\r
1018   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE, (ArgIniType) "" },\r
1019   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE, INVALID },\r
1020   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE, (ArgIniType) FALSE },\r
1021   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE, INVALID },\r
1022   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE, INVALID },\r
1023   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE, INVALID },\r
1024   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE, (ArgIniType) "" },\r
1025   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE, INVALID },\r
1026   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE, (ArgIniType) 1 },\r
1027   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE, INVALID },\r
1028   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE, (ArgIniType) "" },\r
1029   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE, INVALID },\r
1030   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE, (ArgIniType) FALSE },\r
1031   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE, INVALID },\r
1032   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE, INVALID },\r
1033   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE, INVALID },\r
1034   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE, (ArgIniType) 0 },\r
1035   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE, INVALID },\r
1036   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE, (ArgIniType) FALSE },\r
1037   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE, INVALID },\r
1038   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE, INVALID },\r
1039   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE, INVALID },\r
1040   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE, (ArgIniType) FALSE },\r
1041   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE, INVALID },\r
1042   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE, INVALID },\r
1043   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE, INVALID },\r
1044   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE, (ArgIniType) TRUE },\r
1045   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE, INVALID },\r
1046   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE, INVALID },\r
1047   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE, INVALID },\r
1048   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE, (ArgIniType) "" },\r
1049   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE, INVALID },\r
1050   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE, (ArgIniType) 0 },\r
1051   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE, INVALID },\r
1052   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE, (ArgIniType) FALSE },\r
1053   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE, INVALID },\r
1054   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE, INVALID },\r
1055   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE, INVALID },\r
1056   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE, (ArgIniType) FALSE },\r
1057   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE, INVALID },\r
1058   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE, INVALID },\r
1059   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE, INVALID },\r
1060   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE, (ArgIniType) TRUE },\r
1061   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE, INVALID },\r
1062   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE, INVALID },\r
1063   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE, INVALID },\r
1064   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE, (ArgIniType) TRUE },\r
1065   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE, INVALID },\r
1066   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE, INVALID },\r
1067   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE, INVALID },\r
1068   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE, (ArgIniType) TRUE },\r
1069   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE, INVALID },\r
1070   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE, INVALID },\r
1071   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE, INVALID },\r
1072   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE, (ArgIniType) FALSE },\r
1073   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE, INVALID },\r
1074   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE, INVALID },\r
1075   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE, INVALID },\r
1076   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1077     FALSE, INVALID }, /* only so that old WinBoard.ini files from betas can be read */\r
1078   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE, INVALID },\r
1079   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE, INVALID },\r
1080   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE, INVALID },\r
1081   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE, INVALID },\r
1082   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE, INVALID },\r
1083   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE, INVALID },\r
1084   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE, INVALID }, /* [AS] */\r
1085   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1086     TRUE, (ArgIniType) -1 }, /* must come after all fonts */\r
1087   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE, INVALID },\r
1088   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1089     FALSE, (ArgIniType) TRUE }, /* historical; kept only so old winboard.ini files will parse */\r
1090   { "bell", ArgTrue, (LPVOID) &appData.ringBellAfterMoves, FALSE, INVALID }, // for XB\r
1091   { "xbell", ArgFalse, (LPVOID) &appData.ringBellAfterMoves, FALSE, INVALID }, // for XB\r
1092   { "movesound", ArgTrue, (LPVOID) &appData.ringBellAfterMoves, FALSE, INVALID }, // for XB\r
1093   { "xmovesound", ArgFalse, (LPVOID) &appData.ringBellAfterMoves, FALSE, INVALID }, // for XB\r
1094   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE, INVALID },\r
1095   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE, INVALID },\r
1096   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE, INVALID },\r
1097   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE, INVALID },\r
1098   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE, (ArgIniType) FALSE },\r
1099   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE, INVALID },\r
1100   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE, INVALID },\r
1101   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE, INVALID },\r
1102   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE, (ArgIniType) FALSE },\r
1103   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE, INVALID },\r
1104   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE, INVALID },\r
1105   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE, INVALID },\r
1106   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE, (ArgIniType) FALSE },\r
1107   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE, INVALID },\r
1108   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE, INVALID },\r
1109   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE, INVALID },\r
1110   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE, (ArgIniType) FALSE },\r
1111   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE, INVALID },\r
1112   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE, INVALID },\r
1113   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE, INVALID },\r
1114   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE, (ArgIniType) TRUE },\r
1115   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE, INVALID },\r
1116   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE, INVALID },\r
1117   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE, INVALID },\r
1118   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE, (ArgIniType) TRUE },\r
1119   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE, INVALID },\r
1120   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE, INVALID },\r
1121   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE, INVALID },\r
1122   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE, (ArgIniType) FALSE },\r
1123   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE, INVALID },\r
1124   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE, INVALID },\r
1125   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE, INVALID },\r
1126   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE, (ArgIniType) FALSE },\r
1127   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE, INVALID },\r
1128   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE, INVALID },\r
1129   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE, INVALID },\r
1130   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE, (ArgIniType) FALSE },\r
1131   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE, INVALID },\r
1132   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE, INVALID },\r
1133   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE, INVALID },\r
1134   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE, (ArgIniType) TRUE },\r
1135   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE, INVALID },\r
1136   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE, INVALID },\r
1137   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE, INVALID },\r
1138   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE, (ArgIniType) TRUE },\r
1139   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE, INVALID },\r
1140   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE, INVALID },\r
1141   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE, INVALID },\r
1142   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE, (ArgIniType) TRUE },\r
1143   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE, INVALID },\r
1144   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE, INVALID },\r
1145   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE, INVALID },\r
1146   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE, (ArgIniType) FALSE },\r
1147   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE, INVALID },\r
1148   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE, INVALID },\r
1149   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE, INVALID },\r
1150   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE, (ArgIniType) "" },\r
1151   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE, (ArgIniType) FALSE },\r
1152   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE, INVALID },\r
1153   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE, INVALID },\r
1154   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE, INVALID },\r
1155   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE, (ArgIniType) "" },\r
1156   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE, (ArgIniType) TRUE},\r
1157   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1158   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1159   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1160   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE, (ArgIniType) 5000},\r
1161   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE, (ArgIniType) TRUE},\r
1162   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE, INVALID },\r
1163   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE, INVALID },\r
1164   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE, INVALID },\r
1165   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE, (ArgIniType) TRUE },\r
1166   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE, INVALID },\r
1167   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE, INVALID },\r
1168   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE, INVALID },\r
1169   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE, (ArgIniType) 10 },\r
1170   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE, (ArgIniType) TRUE },\r
1171   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE, INVALID },\r
1172   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE, INVALID },\r
1173   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE, INVALID },\r
1174   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE, (ArgIniType) FALSE },\r
1175   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE, INVALID },\r
1176   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE, INVALID },\r
1177   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE, INVALID },\r
1178   { "highlightLastMove", ArgBoolean,\r
1179     (LPVOID) &appData.highlightLastMove, TRUE, (ArgIniType) TRUE },\r
1180   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE, INVALID },\r
1181   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE, INVALID },\r
1182   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE, INVALID },\r
1183   { "highlightDragging", ArgBoolean,\r
1184     (LPVOID) &appData.highlightDragging, TRUE, INVALID },\r
1185   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE, INVALID },\r
1186   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE, INVALID },\r
1187   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE, INVALID },\r
1188   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE, (ArgIniType) TRUE },\r
1189   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE, INVALID },\r
1190   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE, INVALID },\r
1191   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE, INVALID },\r
1192   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE, INVALID },\r
1193   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE, INVALID },\r
1194   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE, INVALID },\r
1195   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE, INVALID },\r
1196   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE, INVALID },\r
1197   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE, INVALID },\r
1198   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE, INVALID },\r
1199   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE, INVALID },\r
1200   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE, INVALID },\r
1201   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE, INVALID },\r
1202   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE, INVALID },\r
1203   { "soundShout", ArgFilename, (LPVOID) &appData.soundShout, TRUE, (ArgIniType) "" },\r
1204   { "soundSShout", ArgFilename, (LPVOID) &appData.soundSShout, TRUE, (ArgIniType) "" },\r
1205   { "soundCShout", ArgFilename, (LPVOID) &appData.soundSShout, TRUE, (ArgIniType) "" }, // for XB\r
1206   { "soundChannel1", ArgFilename, (LPVOID) &appData.soundChannel1, TRUE, (ArgIniType) "" },\r
1207   { "soundChannel", ArgFilename, (LPVOID) &appData.soundChannel, TRUE, (ArgIniType) "" },\r
1208   { "soundKibitz", ArgFilename, (LPVOID) &appData.soundKibitz, TRUE, (ArgIniType) "" },\r
1209   { "soundTell", ArgFilename, (LPVOID) &appData.soundTell, TRUE, (ArgIniType) "" },\r
1210   { "soundChallenge", ArgFilename, (LPVOID) &appData.soundChallenge, TRUE, (ArgIniType) "" },\r
1211   { "soundRequest", ArgFilename, (LPVOID) &appData.soundRequest, TRUE, (ArgIniType) "" },\r
1212   { "soundSeek", ArgFilename, (LPVOID) &appData.soundSeek, TRUE, (ArgIniType) "" },\r
1213   { "soundMove", ArgFilename, (LPVOID) &appData.soundMove, TRUE, (ArgIniType) "" },\r
1214   { "soundBell", ArgFilename, (LPVOID) &appData.soundBell, TRUE, (ArgIniType) SOUND_BELL },\r
1215   { "soundIcsWin", ArgFilename, (LPVOID) &appData.soundIcsWin, TRUE, (ArgIniType) "" },\r
1216   { "soundIcsLoss", ArgFilename, (LPVOID) &appData.soundIcsLoss, TRUE, (ArgIniType) "" },\r
1217   { "soundIcsDraw", ArgFilename, (LPVOID) &appData.soundIcsDraw, TRUE, (ArgIniType) "" },\r
1218   { "soundIcsUnfinished", ArgFilename, (LPVOID) &appData.soundIcsUnfinished, TRUE, (ArgIniType) "" },\r
1219   { "soundIcsAlarm", ArgFilename, (LPVOID) &appData.soundIcsAlarm, TRUE, (ArgIniType) "" },\r
1220   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE, (ArgIniType) TRUE },\r
1221   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE, INVALID },\r
1222   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE, INVALID },\r
1223   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE, INVALID },\r
1224   { "reuseChessPrograms", ArgBoolean,\r
1225     (LPVOID) &appData.reuseFirst, FALSE, INVALID }, /* backward compat only */\r
1226   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE, (ArgIniType) TRUE },\r
1227   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE, INVALID },\r
1228   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE, INVALID },\r
1229   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE, INVALID },\r
1230   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE, INVALID },\r
1231   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE, (ArgIniType) SETTINGS_FILE },\r
1232   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE, INVALID },\r
1233   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE, (ArgIniType) TRUE },\r
1234   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE, (ArgIniType) FALSE },\r
1235   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE, INVALID },\r
1236   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE, INVALID },\r
1237   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE, INVALID },\r
1238   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE, (ArgIniType) ICS_TEXT_MENU_DEFAULT },\r
1239   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE, (ArgIniType) ICS_NAMES },\r
1240   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1241     TRUE, (ArgIniType) FCP_NAMES },\r
1242   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1243     TRUE, (ArgIniType) SCP_NAMES },\r
1244   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE, (ArgIniType) "" },\r
1245   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE, INVALID },\r
1246   { "variant", ArgString, (LPVOID) &appData.variant, FALSE, (ArgIniType) "normal" },\r
1247   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE, (ArgIniType) PROTOVER },\r
1248   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE, (ArgIniType) PROTOVER },\r
1249   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE, (ArgIniType) TRUE },\r
1250   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE, INVALID },\r
1251   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE, INVALID },\r
1252   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE, INVALID },\r
1253 \r
1254   /* [AS] New features */\r
1255   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE, (ArgIniType) FALSE },\r
1256   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE, (ArgIniType) FALSE },\r
1257   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE, (ArgIniType) FALSE },\r
1258   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE, (ArgIniType) FALSE },\r
1259   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE, (ArgIniType) "" },\r
1260   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE, (ArgIniType) "" },\r
1261   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE, (ArgIniType) BACK_TEXTURE_MODE_PLAIN },\r
1262   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE, (ArgIniType) BACK_TEXTURE_MODE_PLAIN },\r
1263   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE, (ArgIniType) "" },\r
1264   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE, (ArgIniType) "" },\r
1265   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE, (ArgIniType) 0 },\r
1266   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE, (ArgIniType) 0 },\r
1267   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE, (ArgIniType) 0 },\r
1268   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE, (ArgIniType) 0 },\r
1269   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE, (ArgIniType) 80 },\r
1270   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE, (ArgIniType) 1 },\r
1271   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE, (ArgIniType) 0 },\r
1272   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE, (ArgIniType) 0 },\r
1273   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE, (ArgIniType) 0 },\r
1274   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE, (ArgIniType) "winboard.debug" },\r
1275   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE, INVALID },\r
1276   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE, (ArgIniType) "Computer Chess Game" },\r
1277   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE, (ArgIniType) -1 },\r
1278   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE, (ArgIniType) GLT_DEFAULT_TAGS },\r
1279   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE, (ArgIniType) TRUE },\r
1280   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE, (ArgIniType) TRUE },\r
1281   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE, INVALID },\r
1282   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE, INVALID },\r
1283   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE, (ArgIniType) FALSE },\r
1284   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE, INVALID },\r
1285   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE, (ArgIniType) TRUE },\r
1286   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE, (ArgIniType) TRUE },\r
1287   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE, (ArgIniType) TRUE },\r
1288   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE, (ArgIniType) TRUE },\r
1289   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE, (ArgIniType) FALSE },\r
1290   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE, INVALID },\r
1291   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE, (ArgIniType) FALSE },\r
1292   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE, INVALID },\r
1293   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE, (ArgIniType) TRUE },\r
1294   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE, INVALID },\r
1295   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE, INVALID },\r
1296   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE, (ArgIniType) TRUE },\r
1297   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE, INVALID },\r
1298   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE, INVALID },\r
1299   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE, (ArgIniType) "" },\r
1300   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE, (ArgIniType) FALSE },\r
1301   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE, (ArgIniType) "" },\r
1302   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE, (ArgIniType) 64 }, \r
1303   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE, (ArgIniType) 4 },\r
1304   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE, (ArgIniType) "c:\\egtb" },\r
1305 \r
1306   /* [HGM] board-size, adjudication and misc. options */\r
1307   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE, (ArgIniType) -1 },\r
1308   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE, (ArgIniType) -1 },\r
1309   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE, (ArgIniType) -1 },\r
1310   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE, (ArgIniType) 10000 },\r
1311   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE, INVALID },\r
1312   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE, (ArgIniType) FALSE },\r
1313   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE, (ArgIniType) FALSE },\r
1314   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE, (ArgIniType) FALSE },\r
1315   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE, (ArgIniType) FALSE },\r
1316   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE, (ArgIniType) FALSE },\r
1317   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE, (ArgIniType) FALSE },\r
1318   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE, (ArgIniType) FALSE },\r
1319   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE, (ArgIniType) FALSE },\r
1320   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE, (ArgIniType) FALSE },\r
1321   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE, (ArgIniType) 51 },\r
1322   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE, (ArgIniType) 6 },\r
1323   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE, INVALID },\r
1324   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE, (ArgIniType) 1 },\r
1325   { "userName", ArgString, (LPVOID) &appData.userName, FALSE, INVALID },\r
1326   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE, INVALID },\r
1327   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE, INVALID },\r
1328   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE, (ArgIniType) 1 },\r
1329   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE, (ArgIniType) "" },\r
1330   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE, INVALID },\r
1331   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE, INVALID },\r
1332   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE, INVALID },\r
1333   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE, INVALID },\r
1334   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE, (ArgIniType) "" },\r
1335   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE, (ArgIniType) "" },\r
1336   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE, (ArgIniType) "" },\r
1337   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE, (ArgIniType) "" },\r
1338   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE, INVALID },\r
1339   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE, INVALID },\r
1340   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE, INVALID },\r
1341 \r
1342 #ifdef ZIPPY\r
1343   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE, (ArgIniType) ZIPPY_TALK },\r
1344   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE, INVALID },\r
1345   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE, INVALID },\r
1346   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE, INVALID },\r
1347   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE, (ArgIniType) ZIPPY_PLAY },\r
1348   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE, INVALID },\r
1349   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE, INVALID },\r
1350   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE, INVALID },\r
1351   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE, (ArgIniType) ZIPPY_LINES },\r
1352   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE, (ArgIniType) ZIPPY_PINHEAD },\r
1353   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE, (ArgIniType) ZIPPY_PASSWORD },\r
1354   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE, (ArgIniType) ZIPPY_PASSWORD2 },\r
1355   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1356     FALSE, (ArgIniType) ZIPPY_WRONG_PASSWORD },\r
1357   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE, (ArgIniType) ZIPPY_ACCEPT_ONLY },\r
1358   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE, (ArgIniType) ZIPPY_USE_I },\r
1359   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE, INVALID },\r
1360   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE, INVALID },\r
1361   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE, INVALID },\r
1362   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE, (ArgIniType) ZIPPY_BUGHOUSE },\r
1363   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1364     FALSE, (ArgIniType) ZIPPY_NOPLAY_CRAFTY },\r
1365   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE, INVALID },\r
1366   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE, INVALID },\r
1367   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE, INVALID },\r
1368   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE, (ArgIniType) ZIPPY_GAME_END },\r
1369   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE, (ArgIniType) ZIPPY_GAME_START },\r
1370   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE, (ArgIniType) ZIPPY_ADJOURN },\r
1371   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE, INVALID },\r
1372   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE, INVALID },\r
1373   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE, INVALID },\r
1374   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE, (ArgIniType) ZIPPY_ABORT },\r
1375   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE, INVALID },\r
1376   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE, INVALID },\r
1377   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE, INVALID },\r
1378   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE, (ArgIniType) ZIPPY_VARIANTS },\r
1379   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE, (ArgIniType) ZIPPY_MAX_GAMES},\r
1380   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE, (ArgIniType) ZIPPY_REPLAY_TIMEOUT },\r
1381   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE, INVALID },\r
1382   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1383   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE, INVALID },\r
1384 #endif\r
1385   /* [HGM] options for broadcasting and time odds */\r
1386   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE, (ArgIniType) NULL },\r
1387   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE, (ArgIniType) FALSE },\r
1388   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE, (ArgIniType) 15 },\r
1389   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE, (ArgIniType) 1 },\r
1390   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE, (ArgIniType) 1 },\r
1391   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE, INVALID },\r
1392   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE, (ArgIniType) 1 },\r
1393   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE, (ArgIniType) 1 },\r
1394   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE, (ArgIniType) -1 },\r
1395   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE, (ArgIniType) -1 },\r
1396   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE, INVALID },\r
1397   { "keepLineBreaksICS", ArgBoolean, (LPVOID) &appData.noJoin, TRUE, INVALID },\r
1398   { "wrapContinuationSequence", ArgString, (LPVOID) &appData.wrapContSeq, FALSE, INVALID },\r
1399   { "useInternalWrap", ArgTrue, (LPVOID) &appData.useInternalWrap, FALSE, INVALID }, /* noJoin usurps this if set */\r
1400   \r
1401   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1402   { "minX", ArgZ, (LPVOID) &minX, FALSE, INVALID }, // [HGM] placement: to make suer auxialary windows can be placed\r
1403   { "minY", ArgZ, (LPVOID) &minY, FALSE, INVALID },\r
1404   { "winWidth",  ArgInt, (LPVOID) &wpMain.width,  TRUE, INVALID }, // [HGM] placement: dummies to remember right & bottom\r
1405   { "winHeight", ArgInt, (LPVOID) &wpMain.height, TRUE, INVALID }, //       for attaching auxiliary windows to them\r
1406   { "x", ArgInt, (LPVOID) &wpMain.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1407   { "y", ArgInt, (LPVOID) &wpMain.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1408   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1409   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1410   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1411   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1412   { "analysisX", ArgX,   (LPVOID) &dummy, FALSE, INVALID }, // [HGM] placement: analysis window no longer exists\r
1413   { "analysisY", ArgY,   (LPVOID) &dummy, FALSE, INVALID }, //       provided for compatibility with old ini files\r
1414   { "analysisW", ArgInt, (LPVOID) &dummy, FALSE, INVALID },\r
1415   { "analysisH", ArgInt, (LPVOID) &dummy, FALSE, INVALID },\r
1416   { "commentX", ArgX,   (LPVOID) &wpComment.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1417   { "commentY", ArgY,   (LPVOID) &wpComment.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1418   { "commentW", ArgInt, (LPVOID) &wpComment.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1419   { "commentH", ArgInt, (LPVOID) &wpComment.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1420   { "tagsX", ArgX,   (LPVOID) &wpTags.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1421   { "tagsY", ArgY,   (LPVOID) &wpTags.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1422   { "tagsW", ArgInt, (LPVOID) &wpTags.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1423   { "tagsH", ArgInt, (LPVOID) &wpTags.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1424   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1425   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1426   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1427   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1428   /* [AS] Layout stuff */\r
1429   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE, (ArgIniType) TRUE },\r
1430   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1431   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1432   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1433   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1434 \r
1435   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE, (ArgIniType) TRUE },\r
1436   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1437   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1438   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1439   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1440 \r
1441   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE, (ArgIniType) TRUE },\r
1442   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1443   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1444   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1445   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE, (ArgIniType) CW_USEDEFAULT },\r
1446 \r
1447   { NULL, ArgNone, NULL, FALSE, INVALID }\r
1448 };\r
1449 \r
1450 \r
1451 /* Kludge for indirection files on command line */\r
1452 char* lastIndirectionFilename;\r
1453 ArgDescriptor argDescriptorIndirection =\r
1454 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1455 \r
1456 \r
1457 VOID\r
1458 ExitArgError(char *msg, char *badArg)\r
1459 {\r
1460   char buf[MSG_SIZ];\r
1461 \r
1462   sprintf(buf, "%s %s", msg, badArg);\r
1463   DisplayFatalError(buf, 0, 2);\r
1464   exit(2);\r
1465 }\r
1466 \r
1467 /* Command line font name parser.  NULL name means do nothing.\r
1468    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1469    For backward compatibility, syntax without the colon is also\r
1470    accepted, but font names with digits in them won't work in that case.\r
1471 */\r
1472 VOID\r
1473 ParseFontName(char *name, MyFontParams *mfp)\r
1474 {\r
1475   char *p, *q;\r
1476   if (name == NULL) return;\r
1477   p = name;\r
1478   q = strchr(p, ':');\r
1479   if (q) {\r
1480     if (q - p >= sizeof(mfp->faceName))\r
1481       ExitArgError("Font name too long:", name);\r
1482     memcpy(mfp->faceName, p, q - p);\r
1483     mfp->faceName[q - p] = NULLCHAR;\r
1484     p = q + 1;\r
1485   } else {\r
1486     q = mfp->faceName;\r
1487     while (*p && !isdigit(*p)) {\r
1488       *q++ = *p++;\r
1489       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1490         ExitArgError("Font name too long:", name);\r
1491     }\r
1492     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1493     *q = NULLCHAR;\r
1494   }\r
1495   if (!*p) ExitArgError("Font point size missing:", name);\r
1496   mfp->pointSize = (float) atof(p);\r
1497   mfp->bold = (strchr(p, 'b') != NULL);\r
1498   mfp->italic = (strchr(p, 'i') != NULL);\r
1499   mfp->underline = (strchr(p, 'u') != NULL);\r
1500   mfp->strikeout = (strchr(p, 's') != NULL);\r
1501   mfp->charset = DEFAULT_CHARSET;\r
1502   q = strchr(p, 'c');\r
1503   if (q)\r
1504     mfp->charset = (BYTE) atoi(q+1);\r
1505 }\r
1506 \r
1507 /* Color name parser.\r
1508    X version accepts X color names, but this one\r
1509    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1510 COLORREF\r
1511 ParseColorName(char *name)\r
1512 {\r
1513   int red, green, blue, count;\r
1514   char buf[MSG_SIZ];\r
1515 \r
1516   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1517   if (count != 3) {\r
1518     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1519       &red, &green, &blue);\r
1520   }\r
1521   if (count != 3) {\r
1522     sprintf(buf, "Can't parse color name %s", name);\r
1523     DisplayError(buf, 0);\r
1524     return RGB(0, 0, 0);\r
1525   }\r
1526   return PALETTERGB(red, green, blue);\r
1527 }\r
1528 \r
1529 \r
1530 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1531 {\r
1532   char *e = argValue;\r
1533   int eff = 0;\r
1534 \r
1535   while (*e) {\r
1536     if (*e == 'b')      eff |= CFE_BOLD;\r
1537     else if (*e == 'i') eff |= CFE_ITALIC;\r
1538     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1539     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1540     else if (*e == '#' || isdigit(*e)) break;\r
1541     e++;\r
1542   }\r
1543   *effects = eff;\r
1544   *color   = ParseColorName(e);\r
1545 }\r
1546 \r
1547 \r
1548 BoardSize\r
1549 ParseBoardSize(char *name)\r
1550 {\r
1551   BoardSize bs = SizeTiny;\r
1552   while (sizeInfo[bs].name != NULL) {\r
1553     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1554     bs++;\r
1555   }\r
1556   ExitArgError("Unrecognized board size value", name);\r
1557   return bs; /* not reached */\r
1558 }\r
1559 \r
1560 \r
1561 char\r
1562 StringGet(void *getClosure)\r
1563 {\r
1564   char **p = (char **) getClosure;\r
1565   return *((*p)++);\r
1566 }\r
1567 \r
1568 char\r
1569 FileGet(void *getClosure)\r
1570 {\r
1571   int c;\r
1572   FILE* f = (FILE*) getClosure;\r
1573 \r
1574   c = getc(f);\r
1575   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1576   if (c == EOF)\r
1577     return NULLCHAR;\r
1578   else\r
1579     return (char) c;\r
1580 }\r
1581 \r
1582 /* Parse settings file named "name". If file found, return the\r
1583    full name in fullname and return TRUE; else return FALSE */\r
1584 BOOLEAN\r
1585 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1586 {\r
1587   char *dummy;\r
1588   FILE *f;\r
1589   int ok; char buf[MSG_SIZ];\r
1590 \r
1591   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1592   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1593     sprintf(buf, "%s.ini", name);\r
1594     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1595   }\r
1596   if (ok) {\r
1597     f = fopen(fullname, "r");\r
1598     if (f != NULL) {\r
1599       ParseArgs(FileGet, f);\r
1600       fclose(f);\r
1601       return TRUE;\r
1602     }\r
1603   }\r
1604   return FALSE;\r
1605 }\r
1606 \r
1607 VOID\r
1608 ParseArgs(GetFunc get, void *cl)\r
1609 {\r
1610   char argName[ARG_MAX];\r
1611   char argValue[ARG_MAX];\r
1612   ArgDescriptor *ad;\r
1613   char start;\r
1614   char *q;\r
1615   int i, octval;\r
1616   char ch;\r
1617   int posarg = 0;\r
1618 \r
1619   ch = get(cl);\r
1620   for (;;) {\r
1621     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1622     if (ch == NULLCHAR) break;\r
1623     if (ch == ';') {\r
1624       /* Comment to end of line */\r
1625       ch = get(cl);\r
1626       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1627       continue;\r
1628     } else if (ch == '/' || ch == '-') {\r
1629       /* Switch */\r
1630       q = argName;\r
1631       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1632              ch != '\n' && ch != '\t') {\r
1633         *q++ = ch;\r
1634         ch = get(cl);\r
1635       }\r
1636       *q = NULLCHAR;\r
1637 \r
1638       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1639         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1640 \r
1641       if (ad->argName == NULL)\r
1642         ExitArgError("Unrecognized argument", argName);\r
1643 \r
1644     } else if (ch == '@') {\r
1645       /* Indirection file */\r
1646       ad = &argDescriptorIndirection;\r
1647       ch = get(cl);\r
1648     } else {\r
1649       /* Positional argument */\r
1650       ad = &argDescriptors[posarg++];\r
1651       strcpy(argName, ad->argName);\r
1652     }\r
1653 \r
1654     if (ad->argType == ArgTrue) {\r
1655       *(Boolean *) ad->argLoc = TRUE;\r
1656       continue;\r
1657     }\r
1658     if (ad->argType == ArgFalse) {\r
1659       *(Boolean *) ad->argLoc = FALSE;\r
1660       continue;\r
1661     }\r
1662 \r
1663     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1664     if (ch == NULLCHAR || ch == '\n') {\r
1665       ExitArgError("No value provided for argument", argName);\r
1666     }\r
1667     q = argValue;\r
1668     if (ch == '{') {\r
1669       // Quoting with { }.  No characters have to (or can) be escaped.\r
1670       // Thus the string cannot contain a '}' character.\r
1671       start = ch;\r
1672       ch = get(cl);\r
1673       while (start) {\r
1674         switch (ch) {\r
1675         case NULLCHAR:\r
1676           start = NULLCHAR;\r
1677           break;\r
1678           \r
1679         case '}':\r
1680           ch = get(cl);\r
1681           start = NULLCHAR;\r
1682           break;\r
1683 \r
1684         default:\r
1685           *q++ = ch;\r
1686           ch = get(cl);\r
1687           break;\r
1688         }\r
1689       }   \r
1690     } else if (ch == '\'' || ch == '"') {\r
1691       // Quoting with ' ' or " ", with \ as escape character.\r
1692       // Inconvenient for long strings that may contain Windows filenames.\r
1693       start = ch;\r
1694       ch = get(cl);\r
1695       while (start) {\r
1696         switch (ch) {\r
1697         case NULLCHAR:\r
1698           start = NULLCHAR;\r
1699           break;\r
1700 \r
1701         default:\r
1702         not_special:\r
1703           *q++ = ch;\r
1704           ch = get(cl);\r
1705           break;\r
1706 \r
1707         case '\'':\r
1708         case '\"':\r
1709           if (ch == start) {\r
1710             ch = get(cl);\r
1711             start = NULLCHAR;\r
1712             break;\r
1713           } else {\r
1714             goto not_special;\r
1715           }\r
1716 \r
1717         case '\\':\r
1718           if (ad->argType == ArgFilename\r
1719               || ad->argType == ArgSettingsFilename) {\r
1720               goto not_special;\r
1721           }\r
1722           ch = get(cl);\r
1723           switch (ch) {\r
1724           case NULLCHAR:\r
1725             ExitArgError("Incomplete \\ escape in value for", argName);\r
1726             break;\r
1727           case 'n':\r
1728             *q++ = '\n';\r
1729             ch = get(cl);\r
1730             break;\r
1731           case 'r':\r
1732             *q++ = '\r';\r
1733             ch = get(cl);\r
1734             break;\r
1735           case 't':\r
1736             *q++ = '\t';\r
1737             ch = get(cl);\r
1738             break;\r
1739           case 'b':\r
1740             *q++ = '\b';\r
1741             ch = get(cl);\r
1742             break;\r
1743           case 'f':\r
1744             *q++ = '\f';\r
1745             ch = get(cl);\r
1746             break;\r
1747           default:\r
1748             octval = 0;\r
1749             for (i = 0; i < 3; i++) {\r
1750               if (ch >= '0' && ch <= '7') {\r
1751                 octval = octval*8 + (ch - '0');\r
1752                 ch = get(cl);\r
1753               } else {\r
1754                 break;\r
1755               }\r
1756             }\r
1757             if (i > 0) {\r
1758               *q++ = (char) octval;\r
1759             } else {\r
1760               *q++ = ch;\r
1761               ch = get(cl);\r
1762             }\r
1763             break;\r
1764           }\r
1765           break;\r
1766         }\r
1767       }\r
1768     } else {\r
1769       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1770         *q++ = ch;\r
1771         ch = get(cl);\r
1772       }\r
1773     }\r
1774     *q = NULLCHAR;\r
1775 \r
1776     switch (ad->argType) {\r
1777     case ArgInt:\r
1778       *(int *) ad->argLoc = atoi(argValue);\r
1779       break;\r
1780 \r
1781     case ArgX:\r
1782       *(int *) ad->argLoc = atoi(argValue) + wpMain.x; // [HGM] placement: translate stored relative to absolute \r
1783       break;\r
1784 \r
1785     case ArgY:\r
1786       *(int *) ad->argLoc = atoi(argValue) + wpMain.y; // (this is really kludgey, it should be done where used...)\r
1787       break;\r
1788 \r
1789     case ArgZ:\r
1790       *(int *) ad->argLoc = atoi(argValue);\r
1791       EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY); \r
1792       break;\r
1793 \r
1794     case ArgFloat:\r
1795       *(float *) ad->argLoc = (float) atof(argValue);\r
1796       break;\r
1797 \r
1798     case ArgString:\r
1799     case ArgFilename:\r
1800       *(char **) ad->argLoc = strdup(argValue);\r
1801       break;\r
1802 \r
1803     case ArgSettingsFilename:\r
1804       {\r
1805         char fullname[MSG_SIZ];\r
1806         if (ParseSettingsFile(argValue, fullname)) {\r
1807           if (ad->argLoc != NULL) {\r
1808             *(char **) ad->argLoc = strdup(fullname);\r
1809           }\r
1810         } else {\r
1811           if (ad->argLoc != NULL) {\r
1812           } else {\r
1813             ExitArgError("Failed to open indirection file", argValue);\r
1814           }\r
1815         }\r
1816       }\r
1817       break;\r
1818 \r
1819     case ArgBoolean:\r
1820       switch (argValue[0]) {\r
1821       case 't':\r
1822       case 'T':\r
1823         *(Boolean *) ad->argLoc = TRUE;\r
1824         break;\r
1825       case 'f':\r
1826       case 'F':\r
1827         *(Boolean *) ad->argLoc = FALSE;\r
1828         break;\r
1829       default:\r
1830         ExitArgError("Unrecognized boolean argument value", argValue);\r
1831         break;\r
1832       }\r
1833       break;\r
1834 \r
1835     case ArgColor:\r
1836       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1837       break;\r
1838 \r
1839     case ArgAttribs: {\r
1840       ColorClass cc = (ColorClass)ad->argLoc;\r
1841       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1842       }\r
1843       break;\r
1844       \r
1845     case ArgBoardSize:\r
1846       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1847       break;\r
1848 \r
1849     case ArgFont:\r
1850       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1851       break;\r
1852 \r
1853     case ArgCommSettings:\r
1854       ParseCommSettings(argValue, &dcb);\r
1855       break;\r
1856 \r
1857     case ArgNone:\r
1858       ExitArgError("Unrecognized argument", argValue);\r
1859       break;\r
1860     case ArgTrue:\r
1861     case ArgFalse: ;\r
1862     }\r
1863   }\r
1864 }\r
1865 \r
1866 VOID\r
1867 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1868 {\r
1869   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1870   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1871   DeleteDC(hdc);\r
1872   lf->lfWidth = 0;\r
1873   lf->lfEscapement = 0;\r
1874   lf->lfOrientation = 0;\r
1875   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1876   lf->lfItalic = mfp->italic;\r
1877   lf->lfUnderline = mfp->underline;\r
1878   lf->lfStrikeOut = mfp->strikeout;\r
1879   lf->lfCharSet = mfp->charset;\r
1880   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1881   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1882   lf->lfQuality = DEFAULT_QUALITY;\r
1883   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1884   strcpy(lf->lfFaceName, mfp->faceName);\r
1885 }\r
1886 \r
1887 VOID\r
1888 CreateFontInMF(MyFont *mf)\r
1889 {\r
1890   LFfromMFP(&mf->lf, &mf->mfp);\r
1891   if (mf->hf) DeleteObject(mf->hf);\r
1892   mf->hf = CreateFontIndirect(&mf->lf);\r
1893 }\r
1894 \r
1895 VOID\r
1896 SetDefaultTextAttribs()\r
1897 {\r
1898   ColorClass cc;\r
1899   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1900     ParseAttribs(&textAttribs[cc].color, \r
1901                  &textAttribs[cc].effects, \r
1902                  defaultTextAttribs[cc]);\r
1903   }\r
1904 }\r
1905 \r
1906 VOID\r
1907 SetDefaultSounds()\r
1908 { // [HGM] only sounds for which no option exists\r
1909   ColorClass cc;\r
1910   for (cc = ColorNormal; cc < NColorClasses; cc++) {\r
1911     textAttribs[cc].sound.name = strdup("");\r
1912     textAttribs[cc].sound.data = NULL;\r
1913   }\r
1914 }\r
1915 \r
1916 VOID\r
1917 LoadAllSounds()\r
1918 { // [HGM] import name from appData first\r
1919   ColorClass cc;\r
1920   SoundClass sc;\r
1921   for (cc = (ColorClass)0; cc < ColorNormal; cc++) {\r
1922     textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);\r
1923     textAttribs[cc].sound.data = NULL;\r
1924     MyLoadSound(&textAttribs[cc].sound);\r
1925   }\r
1926   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1927     sounds[sc].name = strdup((&appData.soundMove)[sc]);\r
1928     sounds[sc].data = NULL;\r
1929     MyLoadSound(&sounds[sc]);\r
1930   }\r
1931 }\r
1932 \r
1933 void\r
1934 SetDefaultsFromList()\r
1935 { // [HGM] ini: take defaults from argDescriptor list\r
1936   int i;\r
1937 \r
1938   for(i=0; argDescriptors[i].argName != NULL; i++) {\r
1939     if(argDescriptors[i].defaultValue != INVALID)\r
1940       switch(argDescriptors[i].argType) {\r
1941         case ArgBoolean:\r
1942         case ArgTrue:\r
1943         case ArgFalse:\r
1944           *(Boolean *) argDescriptors[i].argLoc = (int)argDescriptors[i].defaultValue;\r
1945           break;\r
1946         case ArgInt:\r
1947         case ArgX:\r
1948         case ArgY:\r
1949         case ArgZ:\r
1950           *(int *) argDescriptors[i].argLoc = (int)argDescriptors[i].defaultValue;\r
1951           break;\r
1952         case ArgString:\r
1953         case ArgFilename:\r
1954         case ArgSettingsFilename:\r
1955           *(char **) argDescriptors[i].argLoc = (char *)argDescriptors[i].defaultValue;\r
1956           break;\r
1957         case ArgBoardSize:\r
1958           *(BoardSize *) argDescriptors[i].argLoc = (BoardSize)argDescriptors[i].defaultValue;\r
1959           break;\r
1960         case ArgFloat: // floats cannot be casted to int without precision loss\r
1961         default: ; // some arg types cannot be initialized through table\r
1962     }\r
1963   }\r
1964 }\r
1965 \r
1966 VOID\r
1967 InitAppData(LPSTR lpCmdLine)\r
1968 {\r
1969   int i, j;\r
1970   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1971   char *dummy, *p;\r
1972 \r
1973   programName = szAppName;\r
1974 \r
1975   /* Initialize to defaults */\r
1976   SetDefaultsFromList(); // this sets most defaults\r
1977 \r
1978   // some parameters for which there are no options!\r
1979   appData.Iconic = FALSE; /*unused*/\r
1980   appData.cmailGameName = "";\r
1981   appData.icsEngineAnalyze = FALSE;\r
1982 \r
1983   // float: casting to int is not harmless, so default cannot be contained in table\r
1984   appData.timeDelay = TIME_DELAY;\r
1985 \r
1986   // colors have platform-dependent option format and internal representation\r
1987   // their setting and parsing must remain in front-end\r
1988   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1989   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1990   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1991   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1992   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1993   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1994   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1995   // the following must be moved out of appData to front-end variables\r
1996   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1997   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1998   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1999 \r
2000   // some complex, platform-dependent stuff\r
2001   SetDefaultTextAttribs();\r
2002   SetDefaultSounds();\r
2003 \r
2004   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2005   dcb.DCBlength = sizeof(DCB);\r
2006   dcb.BaudRate = 9600;\r
2007   dcb.fBinary = TRUE;\r
2008   dcb.fParity = FALSE;\r
2009   dcb.fOutxCtsFlow = FALSE;\r
2010   dcb.fOutxDsrFlow = FALSE;\r
2011   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2012   dcb.fDsrSensitivity = FALSE;\r
2013   dcb.fTXContinueOnXoff = TRUE;\r
2014   dcb.fOutX = FALSE;\r
2015   dcb.fInX = FALSE;\r
2016   dcb.fNull = FALSE;\r
2017   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2018   dcb.fAbortOnError = FALSE;\r
2019   dcb.ByteSize = 7;\r
2020   dcb.Parity = SPACEPARITY;\r
2021   dcb.StopBits = ONESTOPBIT;\r
2022 \r
2023   /* Point font array elements to structures and\r
2024      parse default font names */\r
2025   for (i=0; i<NUM_FONTS; i++) {\r
2026     for (j=0; j<NUM_SIZES; j++) {\r
2027       font[j][i] = &fontRec[j][i];\r
2028       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2029     }\r
2030   }\r
2031   \r
2032   /* Parse default settings file if any */\r
2033   if (ParseSettingsFile(settingsFileName, buf)) {\r
2034     settingsFileName = strdup(buf);\r
2035   }\r
2036 \r
2037   /* Parse command line */\r
2038   ParseArgs(StringGet, &lpCmdLine);\r
2039 \r
2040   /* [HGM] make sure board size is acceptable */\r
2041   if(appData.NrFiles > BOARD_FILES ||\r
2042      appData.NrRanks > BOARD_RANKS   )\r
2043       DisplayFatalError("Recompile with BOARD_RANKS or BOARD_FILES, to support this size", 0, 2);\r
2044 \r
2045   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2046    * with options from the command line, we now make an even higher priority\r
2047    * overrule by WB options attached to the engine command line. This so that\r
2048    * tournament managers can use WB options (such as /timeOdds) that follow\r
2049    * the engines.\r
2050    */\r
2051   if(appData.firstChessProgram != NULL) {\r
2052       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2053       static char *f = "first";\r
2054       char buf[MSG_SIZ], *q = buf;\r
2055       if(p != NULL) { // engine command line contains WinBoard options\r
2056           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2057           ParseArgs(StringGet, &q);\r
2058           p[-1] = 0; // cut them offengine command line\r
2059       }\r
2060   }\r
2061   // now do same for second chess program\r
2062   if(appData.secondChessProgram != NULL) {\r
2063       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2064       static char *s = "second";\r
2065       char buf[MSG_SIZ], *q = buf;\r
2066       if(p != NULL) { // engine command line contains WinBoard options\r
2067           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2068           ParseArgs(StringGet, &q);\r
2069           p[-1] = 0; // cut them offengine command line\r
2070       }\r
2071   }\r
2072 \r
2073 \r
2074   /* Propagate options that affect others */\r
2075   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2076   if (appData.icsActive || appData.noChessProgram) {\r
2077      chessProgram = FALSE;  /* not local chess program mode */\r
2078   }\r
2079 \r
2080   /* Open startup dialog if needed */\r
2081   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2082       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2083       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2084                         *appData.secondChessProgram == NULLCHAR))) {\r
2085     FARPROC lpProc;\r
2086     \r
2087     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2088     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2089     FreeProcInstance(lpProc);\r
2090   }\r
2091 \r
2092   /* Make sure save files land in the right (?) directory */\r
2093   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2094     appData.saveGameFile = strdup(buf);\r
2095   }\r
2096   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2097     appData.savePositionFile = strdup(buf);\r
2098   }\r
2099 \r
2100   /* Finish initialization for fonts and sounds */\r
2101   for (i=0; i<NUM_FONTS; i++) {\r
2102     for (j=0; j<NUM_SIZES; j++) {\r
2103       CreateFontInMF(font[j][i]);\r
2104     }\r
2105   }\r
2106   /* xboard, and older WinBoards, controlled the move sound with the\r
2107      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2108      always turn the option on (so that the backend will call us),\r
2109      then let the user turn the sound off by setting it to silence if\r
2110      desired.  To accommodate old winboard.ini files saved by old\r
2111      versions of WinBoard, we also turn off the sound if the option\r
2112      was initially set to false. */\r
2113   if (!appData.ringBellAfterMoves) {\r
2114     sounds[(int)SoundMove].name = strdup("");\r
2115     appData.ringBellAfterMoves = TRUE;\r
2116   }\r
2117   GetCurrentDirectory(MSG_SIZ, currDir);\r
2118   SetCurrentDirectory(installDir);\r
2119   LoadAllSounds();\r
2120   SetCurrentDirectory(currDir);\r
2121 \r
2122   p = icsTextMenuString;\r
2123   if (p[0] == '@') {\r
2124     FILE* f = fopen(p + 1, "r");\r
2125     if (f == NULL) {\r
2126       DisplayFatalError(p + 1, errno, 2);\r
2127       return;\r
2128     }\r
2129     i = fread(buf, 1, sizeof(buf)-1, f);\r
2130     fclose(f);\r
2131     buf[i] = NULLCHAR;\r
2132     p = buf;\r
2133   }\r
2134   ParseIcsTextMenu(strdup(p));\r
2135 }\r
2136 \r
2137 \r
2138 VOID\r
2139 InitMenuChecks()\r
2140 {\r
2141   HMENU hmenu = GetMenu(hwndMain);\r
2142 \r
2143   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2144                         MF_BYCOMMAND|((appData.icsActive &&\r
2145                                        *appData.icsCommPort != NULLCHAR) ?\r
2146                                       MF_ENABLED : MF_GRAYED));\r
2147   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2148                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2149                                      MF_CHECKED : MF_UNCHECKED));\r
2150 }\r
2151 \r
2152 // [HGM] args: these three cases taken out to stay in front-end\r
2153 void\r
2154 SaveFontArg(FILE *f, ArgDescriptor *ad)\r
2155 {       // in WinBoard every board size has its own font, and the "argLoc" identifies the table,\r
2156         // while the curent board size determines the element. This system should be ported to XBoard.\r
2157         // What the table contains pointers to, and how to print the font description, remains platform-dependent\r
2158         int bs;\r
2159         for (bs=0; bs<NUM_SIZES; bs++) {\r
2160           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2161           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2162           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
2163             ad->argName, mfp->faceName, mfp->pointSize,\r
2164             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2165             mfp->bold ? "b" : "",\r
2166             mfp->italic ? "i" : "",\r
2167             mfp->underline ? "u" : "",\r
2168             mfp->strikeout ? "s" : "",\r
2169             (int)mfp->charset);\r
2170         }\r
2171       }\r
2172 \r
2173 VOID\r
2174 ExportSounds()\r
2175 { // [HGM] copy the names from the internal WB variables to appData\r
2176   ColorClass cc;\r
2177   SoundClass sc;\r
2178   for (cc = (ColorClass)0; cc < ColorNormal; cc++)\r
2179     (&appData.soundShout)[cc] = textAttribs[cc].sound.name;\r
2180   for (sc = (SoundClass)0; sc < NSoundClasses; sc++)\r
2181     (&appData.soundMove)[sc] = sounds[sc].name;\r
2182 }\r
2183 \r
2184 void\r
2185 SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
2186 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though\r
2187         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2188         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2189           (ta->effects & CFE_BOLD) ? "b" : "",\r
2190           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2191           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2192           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2193           (ta->effects) ? " " : "",\r
2194           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2195       }\r
2196 \r
2197 void\r
2198 SaveColor(FILE *f, ArgDescriptor *ad)\r
2199 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?\r
2200         COLORREF color = *(COLORREF *)ad->argLoc;\r
2201         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2202           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2203 }\r
2204 \r
2205 int\r
2206 MainWindowUp()\r
2207 { // [HGM] args: allows testing if main window is realized from back-end\r
2208   return hwndMain != NULL;\r
2209 }\r
2210 \r
2211 VOID\r
2212 SaveSettings(char* name)\r
2213 {\r
2214   FILE *f;\r
2215   ArgDescriptor *ad;\r
2216   char dir[MSG_SIZ];\r
2217 \r
2218   if (!MainWindowUp()) return;\r
2219 \r
2220   GetCurrentDirectory(MSG_SIZ, dir);\r
2221   SetCurrentDirectory(installDir);\r
2222   f = fopen(name, "w");\r
2223   SetCurrentDirectory(dir);\r
2224   if (f == NULL) {\r
2225     DisplayError(name, errno);\r
2226     return;\r
2227   }\r
2228   fprintf(f, ";\n");\r
2229   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2230   fprintf(f, ";\n");\r
2231   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2232   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2233   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2234   fprintf(f, ";\n");\r
2235 \r
2236   GetActualPlacement(hwndMain, &wpMain);\r
2237   GetActualPlacement(hwndConsole, &wpConsole);\r
2238   GetActualPlacement(commentDialog, &wpComment);\r
2239   GetActualPlacement(editTagsDialog, &wpTags);\r
2240   GetActualPlacement(gameListDialog, &wpGameList);\r
2241 \r
2242   /* [AS] Move history */\r
2243   wpMoveHistory.visible = MoveHistoryIsUp();\r
2244   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
2245 \r
2246   /* [AS] Eval graph */\r
2247   wpEvalGraph.visible = EvalGraphIsUp();\r
2248   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
2249 \r
2250   /* [AS] Engine output */\r
2251   wpEngineOutput.visible = EngineOutputIsUp();\r
2252   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
2253 \r
2254   // [HGM] in WB we have to copy sound names to appData first\r
2255   ExportSounds();\r
2256 \r
2257   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2258     if (!ad->save) continue;\r
2259     switch (ad->argType) {\r
2260     case ArgString:\r
2261       {\r
2262         char *p = *(char **)ad->argLoc;\r
2263         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2264           /* Quote multiline values or \-containing values\r
2265              with { } if possible */\r
2266           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2267         } else {\r
2268           /* Else quote with " " */\r
2269           fprintf(f, "/%s=\"", ad->argName);\r
2270           while (*p) {\r
2271             if (*p == '\n') fprintf(f, "\n");\r
2272             else if (*p == '\r') fprintf(f, "\\r");\r
2273             else if (*p == '\t') fprintf(f, "\\t");\r
2274             else if (*p == '\b') fprintf(f, "\\b");\r
2275             else if (*p == '\f') fprintf(f, "\\f");\r
2276             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2277             else if (*p == '\"') fprintf(f, "\\\"");\r
2278             else if (*p == '\\') fprintf(f, "\\\\");\r
2279             else putc(*p, f);\r
2280             p++;\r
2281           }\r
2282           fprintf(f, "\"\n");\r
2283         }\r
2284       }\r
2285       break;\r
2286     case ArgInt:\r
2287     case ArgZ:\r
2288       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2289       break;\r
2290     case ArgX:\r
2291       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - wpMain.x); // [HGM] placement: stor relative value\r
2292       break;\r
2293     case ArgY:\r
2294       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - wpMain.y);\r
2295       break;\r
2296     case ArgFloat:\r
2297       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2298       break;\r
2299     case ArgBoolean:\r
2300       fprintf(f, "/%s=%s\n", ad->argName, \r
2301         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2302       break;\r
2303     case ArgTrue:\r
2304       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2305       break;\r
2306     case ArgFalse:\r
2307       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2308       break;\r
2309     case ArgColor:\r
2310       SaveColor(f, ad);\r
2311       break;\r
2312     case ArgAttribs:\r
2313       break;\r
2314     case ArgFilename:\r
2315       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2316         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2317       } else {\r
2318         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2319       }\r
2320       break;\r
2321     case ArgBoardSize:\r
2322       fprintf(f, "/%s=%s\n", ad->argName,\r
2323               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2324       break;\r
2325     case ArgFont:\r
2326       SaveFontArg(f, ad);\r
2327       break;\r
2328     case ArgCommSettings:\r
2329       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2330     case ArgNone:\r
2331     case ArgSettingsFilename: ;\r
2332     }\r
2333   }\r
2334   fclose(f);\r
2335 }\r
2336 \r
2337 \r
2338 \r
2339 /*---------------------------------------------------------------------------*\\r
2340  *\r
2341  * GDI board drawing routines\r
2342  *\r
2343 \*---------------------------------------------------------------------------*/\r
2344 \r
2345 /* [AS] Draw square using background texture */\r
2346 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2347 {\r
2348     XFORM   x;\r
2349 \r
2350     if( mode == 0 ) {\r
2351         return; /* Should never happen! */\r
2352     }\r
2353 \r
2354     SetGraphicsMode( dst, GM_ADVANCED );\r
2355 \r
2356     switch( mode ) {\r
2357     case 1:\r
2358         /* Identity */\r
2359         break;\r
2360     case 2:\r
2361         /* X reflection */\r
2362         x.eM11 = -1.0;\r
2363         x.eM12 = 0;\r
2364         x.eM21 = 0;\r
2365         x.eM22 = 1.0;\r
2366         x.eDx = (FLOAT) dw + dx - 1;\r
2367         x.eDy = 0;\r
2368         dx = 0;\r
2369         SetWorldTransform( dst, &x );\r
2370         break;\r
2371     case 3:\r
2372         /* Y reflection */\r
2373         x.eM11 = 1.0;\r
2374         x.eM12 = 0;\r
2375         x.eM21 = 0;\r
2376         x.eM22 = -1.0;\r
2377         x.eDx = 0;\r
2378         x.eDy = (FLOAT) dh + dy - 1;\r
2379         dy = 0;\r
2380         SetWorldTransform( dst, &x );\r
2381         break;\r
2382     case 4:\r
2383         /* X/Y flip */\r
2384         x.eM11 = 0;\r
2385         x.eM12 = 1.0;\r
2386         x.eM21 = 1.0;\r
2387         x.eM22 = 0;\r
2388         x.eDx = (FLOAT) dx;\r
2389         x.eDy = (FLOAT) dy;\r
2390         dx = 0;\r
2391         dy = 0;\r
2392         SetWorldTransform( dst, &x );\r
2393         break;\r
2394     }\r
2395 \r
2396     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2397 \r
2398     x.eM11 = 1.0;\r
2399     x.eM12 = 0;\r
2400     x.eM21 = 0;\r
2401     x.eM22 = 1.0;\r
2402     x.eDx = 0;\r
2403     x.eDy = 0;\r
2404     SetWorldTransform( dst, &x );\r
2405 \r
2406     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2407 }\r
2408 \r
2409 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2410 enum {\r
2411     PM_WP = (int) WhitePawn, \r
2412     PM_WN = (int) WhiteKnight, \r
2413     PM_WB = (int) WhiteBishop, \r
2414     PM_WR = (int) WhiteRook, \r
2415     PM_WQ = (int) WhiteQueen, \r
2416     PM_WF = (int) WhiteFerz, \r
2417     PM_WW = (int) WhiteWazir, \r
2418     PM_WE = (int) WhiteAlfil, \r
2419     PM_WM = (int) WhiteMan, \r
2420     PM_WO = (int) WhiteCannon, \r
2421     PM_WU = (int) WhiteUnicorn, \r
2422     PM_WH = (int) WhiteNightrider, \r
2423     PM_WA = (int) WhiteAngel, \r
2424     PM_WC = (int) WhiteMarshall, \r
2425     PM_WAB = (int) WhiteCardinal, \r
2426     PM_WD = (int) WhiteDragon, \r
2427     PM_WL = (int) WhiteLance, \r
2428     PM_WS = (int) WhiteCobra, \r
2429     PM_WV = (int) WhiteFalcon, \r
2430     PM_WSG = (int) WhiteSilver, \r
2431     PM_WG = (int) WhiteGrasshopper, \r
2432     PM_WK = (int) WhiteKing,\r
2433     PM_BP = (int) BlackPawn, \r
2434     PM_BN = (int) BlackKnight, \r
2435     PM_BB = (int) BlackBishop, \r
2436     PM_BR = (int) BlackRook, \r
2437     PM_BQ = (int) BlackQueen, \r
2438     PM_BF = (int) BlackFerz, \r
2439     PM_BW = (int) BlackWazir, \r
2440     PM_BE = (int) BlackAlfil, \r
2441     PM_BM = (int) BlackMan,\r
2442     PM_BO = (int) BlackCannon, \r
2443     PM_BU = (int) BlackUnicorn, \r
2444     PM_BH = (int) BlackNightrider, \r
2445     PM_BA = (int) BlackAngel, \r
2446     PM_BC = (int) BlackMarshall, \r
2447     PM_BG = (int) BlackGrasshopper, \r
2448     PM_BAB = (int) BlackCardinal,\r
2449     PM_BD = (int) BlackDragon,\r
2450     PM_BL = (int) BlackLance,\r
2451     PM_BS = (int) BlackCobra,\r
2452     PM_BV = (int) BlackFalcon,\r
2453     PM_BSG = (int) BlackSilver,\r
2454     PM_BK = (int) BlackKing\r
2455 };\r
2456 \r
2457 static HFONT hPieceFont = NULL;\r
2458 static HBITMAP hPieceMask[(int) EmptySquare];\r
2459 static HBITMAP hPieceFace[(int) EmptySquare];\r
2460 static int fontBitmapSquareSize = 0;\r
2461 static char pieceToFontChar[(int) EmptySquare] =\r
2462                               { 'p', 'n', 'b', 'r', 'q', \r
2463                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2464                       'k', 'o', 'm', 'v', 't', 'w', \r
2465                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2466                                                               'l' };\r
2467 \r
2468 extern BOOL SetCharTable( char *table, const char * map );\r
2469 /* [HGM] moved to backend.c */\r
2470 \r
2471 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2472 {\r
2473     HBRUSH hbrush;\r
2474     BYTE r1 = GetRValue( color );\r
2475     BYTE g1 = GetGValue( color );\r
2476     BYTE b1 = GetBValue( color );\r
2477     BYTE r2 = r1 / 2;\r
2478     BYTE g2 = g1 / 2;\r
2479     BYTE b2 = b1 / 2;\r
2480     RECT rc;\r
2481 \r
2482     /* Create a uniform background first */\r
2483     hbrush = CreateSolidBrush( color );\r
2484     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2485     FillRect( hdc, &rc, hbrush );\r
2486     DeleteObject( hbrush );\r
2487     \r
2488     if( mode == 1 ) {\r
2489         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2490         int steps = squareSize / 2;\r
2491         int i;\r
2492 \r
2493         for( i=0; i<steps; i++ ) {\r
2494             BYTE r = r1 - (r1-r2) * i / steps;\r
2495             BYTE g = g1 - (g1-g2) * i / steps;\r
2496             BYTE b = b1 - (b1-b2) * i / steps;\r
2497 \r
2498             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2499             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2500             FillRect( hdc, &rc, hbrush );\r
2501             DeleteObject(hbrush);\r
2502         }\r
2503     }\r
2504     else if( mode == 2 ) {\r
2505         /* Diagonal gradient, good more or less for every piece */\r
2506         POINT triangle[3];\r
2507         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2508         HBRUSH hbrush_old;\r
2509         int steps = squareSize;\r
2510         int i;\r
2511 \r
2512         triangle[0].x = squareSize - steps;\r
2513         triangle[0].y = squareSize;\r
2514         triangle[1].x = squareSize;\r
2515         triangle[1].y = squareSize;\r
2516         triangle[2].x = squareSize;\r
2517         triangle[2].y = squareSize - steps;\r
2518 \r
2519         for( i=0; i<steps; i++ ) {\r
2520             BYTE r = r1 - (r1-r2) * i / steps;\r
2521             BYTE g = g1 - (g1-g2) * i / steps;\r
2522             BYTE b = b1 - (b1-b2) * i / steps;\r
2523 \r
2524             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2525             hbrush_old = SelectObject( hdc, hbrush );\r
2526             Polygon( hdc, triangle, 3 );\r
2527             SelectObject( hdc, hbrush_old );\r
2528             DeleteObject(hbrush);\r
2529             triangle[0].x++;\r
2530             triangle[2].y++;\r
2531         }\r
2532 \r
2533         SelectObject( hdc, hpen );\r
2534     }\r
2535 }\r
2536 \r
2537 /*\r
2538     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2539     seems to work ok. The main problem here is to find the "inside" of a chess\r
2540     piece: follow the steps as explained below.\r
2541 */\r
2542 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2543 {\r
2544     HBITMAP hbm;\r
2545     HBITMAP hbm_old;\r
2546     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2547     RECT rc;\r
2548     SIZE sz;\r
2549     POINT pt;\r
2550     int backColor = whitePieceColor; \r
2551     int foreColor = blackPieceColor;\r
2552     \r
2553     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2554         backColor = appData.fontBackColorWhite;\r
2555         foreColor = appData.fontForeColorWhite;\r
2556     }\r
2557     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2558         backColor = appData.fontBackColorBlack;\r
2559         foreColor = appData.fontForeColorBlack;\r
2560     }\r
2561 \r
2562     /* Mask */\r
2563     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2564 \r
2565     hbm_old = SelectObject( hdc, hbm );\r
2566 \r
2567     rc.left = 0;\r
2568     rc.top = 0;\r
2569     rc.right = squareSize;\r
2570     rc.bottom = squareSize;\r
2571 \r
2572     /* Step 1: background is now black */\r
2573     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2574 \r
2575     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2576 \r
2577     pt.x = (squareSize - sz.cx) / 2;\r
2578     pt.y = (squareSize - sz.cy) / 2;\r
2579 \r
2580     SetBkMode( hdc, TRANSPARENT );\r
2581     SetTextColor( hdc, chroma );\r
2582     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2583     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2584 \r
2585     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2586     /* Step 3: the area outside the piece is filled with white */\r
2587 //    FloodFill( hdc, 0, 0, chroma );\r
2588     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2589     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2590     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2591     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2592     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2593     /* \r
2594         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2595         but if the start point is not inside the piece we're lost!\r
2596         There should be a better way to do this... if we could create a region or path\r
2597         from the fill operation we would be fine for example.\r
2598     */\r
2599 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2600     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2601 \r
2602     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2603         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2604         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2605 \r
2606         SelectObject( dc2, bm2 );\r
2607         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2608         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2609         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2610         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2611         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2612 \r
2613         DeleteDC( dc2 );\r
2614         DeleteObject( bm2 );\r
2615     }\r
2616 \r
2617     SetTextColor( hdc, 0 );\r
2618     /* \r
2619         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2620         draw the piece again in black for safety.\r
2621     */\r
2622     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2623 \r
2624     SelectObject( hdc, hbm_old );\r
2625 \r
2626     if( hPieceMask[index] != NULL ) {\r
2627         DeleteObject( hPieceMask[index] );\r
2628     }\r
2629 \r
2630     hPieceMask[index] = hbm;\r
2631 \r
2632     /* Face */\r
2633     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2634 \r
2635     SelectObject( hdc, hbm );\r
2636 \r
2637     {\r
2638         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2639         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2640         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2641 \r
2642         SelectObject( dc1, hPieceMask[index] );\r
2643         SelectObject( dc2, bm2 );\r
2644         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2645         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2646         \r
2647         /* \r
2648             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2649             the piece background and deletes (makes transparent) the rest.\r
2650             Thanks to that mask, we are free to paint the background with the greates\r
2651             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2652             We use this, to make gradients and give the pieces a "roundish" look.\r
2653         */\r
2654         SetPieceBackground( hdc, backColor, 2 );\r
2655         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2656 \r
2657         DeleteDC( dc2 );\r
2658         DeleteDC( dc1 );\r
2659         DeleteObject( bm2 );\r
2660     }\r
2661 \r
2662     SetTextColor( hdc, foreColor );\r
2663     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2664 \r
2665     SelectObject( hdc, hbm_old );\r
2666 \r
2667     if( hPieceFace[index] != NULL ) {\r
2668         DeleteObject( hPieceFace[index] );\r
2669     }\r
2670 \r
2671     hPieceFace[index] = hbm;\r
2672 }\r
2673 \r
2674 static int TranslatePieceToFontPiece( int piece )\r
2675 {\r
2676     switch( piece ) {\r
2677     case BlackPawn:\r
2678         return PM_BP;\r
2679     case BlackKnight:\r
2680         return PM_BN;\r
2681     case BlackBishop:\r
2682         return PM_BB;\r
2683     case BlackRook:\r
2684         return PM_BR;\r
2685     case BlackQueen:\r
2686         return PM_BQ;\r
2687     case BlackKing:\r
2688         return PM_BK;\r
2689     case WhitePawn:\r
2690         return PM_WP;\r
2691     case WhiteKnight:\r
2692         return PM_WN;\r
2693     case WhiteBishop:\r
2694         return PM_WB;\r
2695     case WhiteRook:\r
2696         return PM_WR;\r
2697     case WhiteQueen:\r
2698         return PM_WQ;\r
2699     case WhiteKing:\r
2700         return PM_WK;\r
2701 \r
2702     case BlackAngel:\r
2703         return PM_BA;\r
2704     case BlackMarshall:\r
2705         return PM_BC;\r
2706     case BlackFerz:\r
2707         return PM_BF;\r
2708     case BlackNightrider:\r
2709         return PM_BH;\r
2710     case BlackAlfil:\r
2711         return PM_BE;\r
2712     case BlackWazir:\r
2713         return PM_BW;\r
2714     case BlackUnicorn:\r
2715         return PM_BU;\r
2716     case BlackCannon:\r
2717         return PM_BO;\r
2718     case BlackGrasshopper:\r
2719         return PM_BG;\r
2720     case BlackMan:\r
2721         return PM_BM;\r
2722     case BlackSilver:\r
2723         return PM_BSG;\r
2724     case BlackLance:\r
2725         return PM_BL;\r
2726     case BlackFalcon:\r
2727         return PM_BV;\r
2728     case BlackCobra:\r
2729         return PM_BS;\r
2730     case BlackCardinal:\r
2731         return PM_BAB;\r
2732     case BlackDragon:\r
2733         return PM_BD;\r
2734 \r
2735     case WhiteAngel:\r
2736         return PM_WA;\r
2737     case WhiteMarshall:\r
2738         return PM_WC;\r
2739     case WhiteFerz:\r
2740         return PM_WF;\r
2741     case WhiteNightrider:\r
2742         return PM_WH;\r
2743     case WhiteAlfil:\r
2744         return PM_WE;\r
2745     case WhiteWazir:\r
2746         return PM_WW;\r
2747     case WhiteUnicorn:\r
2748         return PM_WU;\r
2749     case WhiteCannon:\r
2750         return PM_WO;\r
2751     case WhiteGrasshopper:\r
2752         return PM_WG;\r
2753     case WhiteMan:\r
2754         return PM_WM;\r
2755     case WhiteSilver:\r
2756         return PM_WSG;\r
2757     case WhiteLance:\r
2758         return PM_WL;\r
2759     case WhiteFalcon:\r
2760         return PM_WV;\r
2761     case WhiteCobra:\r
2762         return PM_WS;\r
2763     case WhiteCardinal:\r
2764         return PM_WAB;\r
2765     case WhiteDragon:\r
2766         return PM_WD;\r
2767     }\r
2768 \r
2769     return 0;\r
2770 }\r
2771 \r
2772 void CreatePiecesFromFont()\r
2773 {\r
2774     LOGFONT lf;\r
2775     HDC hdc_window = NULL;\r
2776     HDC hdc = NULL;\r
2777     HFONT hfont_old;\r
2778     int fontHeight;\r
2779     int i;\r
2780 \r
2781     if( fontBitmapSquareSize < 0 ) {\r
2782         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2783         return;\r
2784     }\r
2785 \r
2786     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2787         fontBitmapSquareSize = -1;\r
2788         return;\r
2789     }\r
2790 \r
2791     if( fontBitmapSquareSize != squareSize ) {\r
2792         hdc_window = GetDC( hwndMain );\r
2793         hdc = CreateCompatibleDC( hdc_window );\r
2794 \r
2795         if( hPieceFont != NULL ) {\r
2796             DeleteObject( hPieceFont );\r
2797         }\r
2798         else {\r
2799             for( i=0; i<=(int)BlackKing; i++ ) {\r
2800                 hPieceMask[i] = NULL;\r
2801                 hPieceFace[i] = NULL;\r
2802             }\r
2803         }\r
2804 \r
2805         fontHeight = 75;\r
2806 \r
2807         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2808             fontHeight = appData.fontPieceSize;\r
2809         }\r
2810 \r
2811         fontHeight = (fontHeight * squareSize) / 100;\r
2812 \r
2813         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2814         lf.lfWidth = 0;\r
2815         lf.lfEscapement = 0;\r
2816         lf.lfOrientation = 0;\r
2817         lf.lfWeight = FW_NORMAL;\r
2818         lf.lfItalic = 0;\r
2819         lf.lfUnderline = 0;\r
2820         lf.lfStrikeOut = 0;\r
2821         lf.lfCharSet = DEFAULT_CHARSET;\r
2822         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2823         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2824         lf.lfQuality = PROOF_QUALITY;\r
2825         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2826         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2827         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2828 \r
2829         hPieceFont = CreateFontIndirect( &lf );\r
2830 \r
2831         if( hPieceFont == NULL ) {\r
2832             fontBitmapSquareSize = -2;\r
2833         }\r
2834         else {\r
2835             /* Setup font-to-piece character table */\r
2836             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2837                 /* No (or wrong) global settings, try to detect the font */\r
2838                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2839                     /* Alpha */\r
2840                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2841                 }\r
2842                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2843                     /* DiagramTT* family */\r
2844                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2845                 }\r
2846                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2847                     /* Fairy symbols */\r
2848                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2849                 }\r
2850                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2851                     /* Good Companion (Some characters get warped as literal :-( */\r
2852                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2853                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2854                     SetCharTable(pieceToFontChar, s);\r
2855                 }\r
2856                 else {\r
2857                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2858                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2859                 }\r
2860             }\r
2861 \r
2862             /* Create bitmaps */\r
2863             hfont_old = SelectObject( hdc, hPieceFont );\r
2864             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2865                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2866                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2867 \r
2868             SelectObject( hdc, hfont_old );\r
2869 \r
2870             fontBitmapSquareSize = squareSize;\r
2871         }\r
2872     }\r
2873 \r
2874     if( hdc != NULL ) {\r
2875         DeleteDC( hdc );\r
2876     }\r
2877 \r
2878     if( hdc_window != NULL ) {\r
2879         ReleaseDC( hwndMain, hdc_window );\r
2880     }\r
2881 }\r
2882 \r
2883 HBITMAP\r
2884 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2885 {\r
2886   char name[128];\r
2887 \r
2888   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2889   if (gameInfo.event &&\r
2890       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2891       strcmp(name, "k80s") == 0) {\r
2892     strcpy(name, "tim");\r
2893   }\r
2894   return LoadBitmap(hinst, name);\r
2895 }\r
2896 \r
2897 \r
2898 /* Insert a color into the program's logical palette\r
2899    structure.  This code assumes the given color is\r
2900    the result of the RGB or PALETTERGB macro, and it\r
2901    knows how those macros work (which is documented).\r
2902 */\r
2903 VOID\r
2904 InsertInPalette(COLORREF color)\r
2905 {\r
2906   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2907 \r
2908   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2909     DisplayFatalError("Too many colors", 0, 1);\r
2910     pLogPal->palNumEntries--;\r
2911     return;\r
2912   }\r
2913 \r
2914   pe->peFlags = (char) 0;\r
2915   pe->peRed = (char) (0xFF & color);\r
2916   pe->peGreen = (char) (0xFF & (color >> 8));\r
2917   pe->peBlue = (char) (0xFF & (color >> 16));\r
2918   return;\r
2919 }\r
2920 \r
2921 \r
2922 VOID\r
2923 InitDrawingColors()\r
2924 {\r
2925   if (pLogPal == NULL) {\r
2926     /* Allocate enough memory for a logical palette with\r
2927      * PALETTESIZE entries and set the size and version fields\r
2928      * of the logical palette structure.\r
2929      */\r
2930     pLogPal = (NPLOGPALETTE)\r
2931       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2932                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2933     pLogPal->palVersion    = 0x300;\r
2934   }\r
2935   pLogPal->palNumEntries = 0;\r
2936 \r
2937   InsertInPalette(lightSquareColor);\r
2938   InsertInPalette(darkSquareColor);\r
2939   InsertInPalette(whitePieceColor);\r
2940   InsertInPalette(blackPieceColor);\r
2941   InsertInPalette(highlightSquareColor);\r
2942   InsertInPalette(premoveHighlightColor);\r
2943 \r
2944   /*  create a logical color palette according the information\r
2945    *  in the LOGPALETTE structure.\r
2946    */\r
2947   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2948 \r
2949   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2950   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2951   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2952   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2953   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2954   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2955   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
2956   /* [AS] Force rendering of the font-based pieces */\r
2957   if( fontBitmapSquareSize > 0 ) {\r
2958     fontBitmapSquareSize = 0;\r
2959   }\r
2960 }\r
2961 \r
2962 \r
2963 int\r
2964 BoardWidth(int boardSize, int n)\r
2965 { /* [HGM] argument n added to allow different width and height */\r
2966   int lineGap = sizeInfo[boardSize].lineGap;\r
2967 \r
2968   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2969       lineGap = appData.overrideLineGap;\r
2970   }\r
2971 \r
2972   return (n + 1) * lineGap +\r
2973           n * sizeInfo[boardSize].squareSize;\r
2974 }\r
2975 \r
2976 /* Respond to board resize by dragging edge */\r
2977 VOID\r
2978 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2979 {\r
2980   BoardSize newSize = NUM_SIZES - 1;\r
2981   static int recurse = 0;\r
2982   if (IsIconic(hwndMain)) return;\r
2983   if (recurse > 0) return;\r
2984   recurse++;\r
2985   while (newSize > 0) {\r
2986         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
2987         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
2988            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2989     newSize--;\r
2990   } \r
2991   boardSize = newSize;\r
2992   InitDrawingSizes(boardSize, flags);\r
2993   recurse--;\r
2994 }\r
2995 \r
2996 \r
2997 \r
2998 VOID\r
2999 InitDrawingSizes(BoardSize boardSize, int flags)\r
3000 {\r
3001   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3002   ChessSquare piece;\r
3003   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3004   HDC hdc;\r
3005   SIZE clockSize, messageSize;\r
3006   HFONT oldFont;\r
3007   char buf[MSG_SIZ];\r
3008   char *str;\r
3009   HMENU hmenu = GetMenu(hwndMain);\r
3010   RECT crect, wrect, oldRect;\r
3011   int offby;\r
3012   LOGBRUSH logbrush;\r
3013 \r
3014   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3015   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3016 \r
3017   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3018   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3019 \r
3020   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
3021   oldRect.top = wpMain.y;\r
3022   oldRect.right = wpMain.x + wpMain.width;\r
3023   oldRect.bottom = wpMain.y + wpMain.height;\r
3024 \r
3025   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3026   smallLayout = sizeInfo[boardSize].smallLayout;\r
3027   squareSize = sizeInfo[boardSize].squareSize;\r
3028   lineGap = sizeInfo[boardSize].lineGap;\r
3029   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3030 \r
3031   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3032       lineGap = appData.overrideLineGap;\r
3033   }\r
3034 \r
3035   if (tinyLayout != oldTinyLayout) {\r
3036     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3037     if (tinyLayout) {\r
3038       style &= ~WS_SYSMENU;\r
3039       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3040                  "&Minimize\tCtrl+F4");\r
3041     } else {\r
3042       style |= WS_SYSMENU;\r
3043       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3044     }\r
3045     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3046 \r
3047     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3048       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3049         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3050     }\r
3051     DrawMenuBar(hwndMain);\r
3052   }\r
3053 \r
3054   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3055   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3056 \r
3057   /* Get text area sizes */\r
3058   hdc = GetDC(hwndMain);\r
3059   if (appData.clockMode) {\r
3060     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3061   } else {\r
3062     sprintf(buf, "White");\r
3063   }\r
3064   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3065   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3066   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3067   str = "We only care about the height here";\r
3068   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3069   SelectObject(hdc, oldFont);\r
3070   ReleaseDC(hwndMain, hdc);\r
3071 \r
3072   /* Compute where everything goes */\r
3073   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3074         /* [HGM] logo: if either logo is on, reserve space for it */\r
3075         logoHeight =  2*clockSize.cy;\r
3076         leftLogoRect.left   = OUTER_MARGIN;\r
3077         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3078         leftLogoRect.top    = OUTER_MARGIN;\r
3079         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3080 \r
3081         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3082         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3083         rightLogoRect.top    = OUTER_MARGIN;\r
3084         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3085 \r
3086 \r
3087     whiteRect.left = leftLogoRect.right;\r
3088     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3089     whiteRect.top = OUTER_MARGIN;\r
3090     whiteRect.bottom = whiteRect.top + logoHeight;\r
3091 \r
3092     blackRect.right = rightLogoRect.left;\r
3093     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3094     blackRect.top = whiteRect.top;\r
3095     blackRect.bottom = whiteRect.bottom;\r
3096   } else {\r
3097     whiteRect.left = OUTER_MARGIN;\r
3098     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3099     whiteRect.top = OUTER_MARGIN;\r
3100     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3101 \r
3102     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3103     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3104     blackRect.top = whiteRect.top;\r
3105     blackRect.bottom = whiteRect.bottom;\r
3106   }\r
3107 \r
3108   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3109   if (appData.showButtonBar) {\r
3110     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3111       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3112   } else {\r
3113     messageRect.right = OUTER_MARGIN + boardWidth;\r
3114   }\r
3115   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3116   messageRect.bottom = messageRect.top + messageSize.cy;\r
3117 \r
3118   boardRect.left = OUTER_MARGIN;\r
3119   boardRect.right = boardRect.left + boardWidth;\r
3120   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3121   boardRect.bottom = boardRect.top + boardHeight;\r
3122 \r
3123   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3124   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3125   oldBoardSize = boardSize;\r
3126   oldTinyLayout = tinyLayout;\r
3127   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3128   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3129     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3130   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3131   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3132   wpMain.height = winH; //       without disturbing window attachments\r
3133   GetWindowRect(hwndMain, &wrect);\r
3134   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
3135                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3136 \r
3137   // [HGM] placement: let attached windows follow size change.\r
3138   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
3139   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
3140   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
3141   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
3142   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
3143 \r
3144   /* compensate if menu bar wrapped */\r
3145   GetClientRect(hwndMain, &crect);\r
3146   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3147   wpMain.height += offby;\r
3148   switch (flags) {\r
3149   case WMSZ_TOPLEFT:\r
3150     SetWindowPos(hwndMain, NULL, \r
3151                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
3152                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3153     break;\r
3154 \r
3155   case WMSZ_TOPRIGHT:\r
3156   case WMSZ_TOP:\r
3157     SetWindowPos(hwndMain, NULL, \r
3158                  wrect.left, wrect.bottom - wpMain.height, \r
3159                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3160     break;\r
3161 \r
3162   case WMSZ_BOTTOMLEFT:\r
3163   case WMSZ_LEFT:\r
3164     SetWindowPos(hwndMain, NULL, \r
3165                  wrect.right - wpMain.width, wrect.top, \r
3166                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3167     break;\r
3168 \r
3169   case WMSZ_BOTTOMRIGHT:\r
3170   case WMSZ_BOTTOM:\r
3171   case WMSZ_RIGHT:\r
3172   default:\r
3173     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
3174                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3175     break;\r
3176   }\r
3177 \r
3178   hwndPause = NULL;\r
3179   for (i = 0; i < N_BUTTONS; i++) {\r
3180     if (buttonDesc[i].hwnd != NULL) {\r
3181       DestroyWindow(buttonDesc[i].hwnd);\r
3182       buttonDesc[i].hwnd = NULL;\r
3183     }\r
3184     if (appData.showButtonBar) {\r
3185       buttonDesc[i].hwnd =\r
3186         CreateWindow("BUTTON", buttonDesc[i].label,\r
3187                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3188                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3189                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3190                      (HMENU) buttonDesc[i].id,\r
3191                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3192       if (tinyLayout) {\r
3193         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3194                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3195                     MAKELPARAM(FALSE, 0));\r
3196       }\r
3197       if (buttonDesc[i].id == IDM_Pause)\r
3198         hwndPause = buttonDesc[i].hwnd;\r
3199       buttonDesc[i].wndproc = (WNDPROC)\r
3200         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3201     }\r
3202   }\r
3203   if (gridPen != NULL) DeleteObject(gridPen);\r
3204   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3205   if (premovePen != NULL) DeleteObject(premovePen);\r
3206   if (lineGap != 0) {\r
3207     logbrush.lbStyle = BS_SOLID;\r
3208     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3209     gridPen =\r
3210       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3211                    lineGap, &logbrush, 0, NULL);\r
3212     logbrush.lbColor = highlightSquareColor;\r
3213     highlightPen =\r
3214       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3215                    lineGap, &logbrush, 0, NULL);\r
3216 \r
3217     logbrush.lbColor = premoveHighlightColor; \r
3218     premovePen =\r
3219       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3220                    lineGap, &logbrush, 0, NULL);\r
3221 \r
3222     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3223     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3224       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3225       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3226         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3227       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3228         BOARD_WIDTH * (squareSize + lineGap);\r
3229       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3230     }\r
3231     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3232       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3233       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3234         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3235         lineGap / 2 + (i * (squareSize + lineGap));\r
3236       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3237         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3238       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3239     }\r
3240   }\r
3241 \r
3242   /* [HGM] Licensing requirement */\r
3243 #ifdef GOTHIC\r
3244   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3245 #endif\r
3246 #ifdef FALCON\r
3247   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3248 #endif\r
3249   GothicPopUp( "", VariantNormal);\r
3250 \r
3251 \r
3252 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3253 \r
3254   /* Load piece bitmaps for this board size */\r
3255   for (i=0; i<=2; i++) {\r
3256     for (piece = WhitePawn;\r
3257          (int) piece < (int) BlackPawn;\r
3258          piece = (ChessSquare) ((int) piece + 1)) {\r
3259       if (pieceBitmap[i][piece] != NULL)\r
3260         DeleteObject(pieceBitmap[i][piece]);\r
3261     }\r
3262   }\r
3263 \r
3264   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3265   // Orthodox Chess pieces\r
3266   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3267   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3268   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3269   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3270   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3271   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3272   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3273   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3274   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3275   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3276   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3277   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3278   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3279   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3280   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3281   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3282     // in Shogi, Hijack the unused Queen for Lance\r
3283     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3284     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3285     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3286   } else {\r
3287     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3288     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3289     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3290   }\r
3291 \r
3292   if(squareSize <= 72 && squareSize >= 33) { \r
3293     /* A & C are available in most sizes now */\r
3294     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3295       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3296       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3297       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3298       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3299       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3300       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3301       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3302       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3303       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3304       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3305       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3306       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3307     } else { // Smirf-like\r
3308       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3309       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3310       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3311     }\r
3312     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3313       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3314       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3315       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3316     } else { // WinBoard standard\r
3317       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3318       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3319       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3320     }\r
3321   }\r
3322 \r
3323 \r
3324   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3325     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3326     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3327     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3328     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3329     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3330     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3331     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3332     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3333     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3334     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3335     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3336     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3337     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3338     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3339     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3340     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3341     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3342     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3343     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3344     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3345     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3346     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3347     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3348     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3349     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3350     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3351     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3352     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3353     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3354     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3355 \r
3356     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3357       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3358       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3359       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3360       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3361       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3362       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3363       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3364       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3365       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3366       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3367       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3368       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3369     } else {\r
3370       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3371       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3372       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3373       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3374       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3375       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3376       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3377       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3378       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3379       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3380       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3381       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3382     }\r
3383 \r
3384   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3385     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3386     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3387     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3388     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3389     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3390     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3391     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3392     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3393     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3394     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3395     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3396     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3397     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3398     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3399   }\r
3400 \r
3401 \r
3402   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3403   /* special Shogi support in this size */\r
3404   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3405       for (piece = WhitePawn;\r
3406            (int) piece < (int) BlackPawn;\r
3407            piece = (ChessSquare) ((int) piece + 1)) {\r
3408         if (pieceBitmap[i][piece] != NULL)\r
3409           DeleteObject(pieceBitmap[i][piece]);\r
3410       }\r
3411     }\r
3412   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3413   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3414   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3415   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3416   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3417   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3418   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3419   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3420   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3421   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3422   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3423   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3424   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3425   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3426   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3427   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3428   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3429   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3430   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3431   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3432   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3433   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3434   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3435   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3436   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3437   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3438   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3439   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3440   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3441   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3442   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3443   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3444   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3445   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3446   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3447   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3448   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3449   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3450   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3451   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3452   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3453   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3454   minorSize = 0;\r
3455   }\r
3456 }\r
3457 \r
3458 HBITMAP\r
3459 PieceBitmap(ChessSquare p, int kind)\r
3460 {\r
3461   if ((int) p >= (int) BlackPawn)\r
3462     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3463 \r
3464   return pieceBitmap[kind][(int) p];\r
3465 }\r
3466 \r
3467 /***************************************************************/\r
3468 \r
3469 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3470 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3471 /*\r
3472 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3473 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3474 */\r
3475 \r
3476 VOID\r
3477 SquareToPos(int row, int column, int * x, int * y)\r
3478 {\r
3479   if (flipView) {\r
3480     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3481     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3482   } else {\r
3483     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3484     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3485   }\r
3486 }\r
3487 \r
3488 VOID\r
3489 DrawCoordsOnDC(HDC hdc)\r
3490 {\r
3491   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
3492   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
3493   char str[2] = { NULLCHAR, NULLCHAR };\r
3494   int oldMode, oldAlign, x, y, start, i;\r
3495   HFONT oldFont;\r
3496   HBRUSH oldBrush;\r
3497 \r
3498   if (!appData.showCoords)\r
3499     return;\r
3500 \r
3501   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3502 \r
3503   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3504   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3505   oldAlign = GetTextAlign(hdc);\r
3506   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3507 \r
3508   y = boardRect.top + lineGap;\r
3509   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3510 \r
3511   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3512   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3513     str[0] = files[start + i];\r
3514     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3515     y += squareSize + lineGap;\r
3516   }\r
3517 \r
3518   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3519 \r
3520   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3521   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3522     str[0] = ranks[start + i];\r
3523     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3524     x += squareSize + lineGap;\r
3525   }    \r
3526 \r
3527   SelectObject(hdc, oldBrush);\r
3528   SetBkMode(hdc, oldMode);\r
3529   SetTextAlign(hdc, oldAlign);\r
3530   SelectObject(hdc, oldFont);\r
3531 }\r
3532 \r
3533 VOID\r
3534 DrawGridOnDC(HDC hdc)\r
3535 {\r
3536   HPEN oldPen;\r
3537  \r
3538   if (lineGap != 0) {\r
3539     oldPen = SelectObject(hdc, gridPen);\r
3540     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3541     SelectObject(hdc, oldPen);\r
3542   }\r
3543 }\r
3544 \r
3545 #define HIGHLIGHT_PEN 0\r
3546 #define PREMOVE_PEN   1\r
3547 \r
3548 VOID\r
3549 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3550 {\r
3551   int x1, y1;\r
3552   HPEN oldPen, hPen;\r
3553   if (lineGap == 0) return;\r
3554   if (flipView) {\r
3555     x1 = boardRect.left +\r
3556       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3557     y1 = boardRect.top +\r
3558       lineGap/2 + y * (squareSize + lineGap);\r
3559   } else {\r
3560     x1 = boardRect.left +\r
3561       lineGap/2 + x * (squareSize + lineGap);\r
3562     y1 = boardRect.top +\r
3563       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3564   }\r
3565   hPen = pen ? premovePen : highlightPen;\r
3566   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3567   MoveToEx(hdc, x1, y1, NULL);\r
3568   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3569   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3570   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3571   LineTo(hdc, x1, y1);\r
3572   SelectObject(hdc, oldPen);\r
3573 }\r
3574 \r
3575 VOID\r
3576 DrawHighlightsOnDC(HDC hdc)\r
3577 {\r
3578   int i;\r
3579   for (i=0; i<2; i++) {\r
3580     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3581       DrawHighlightOnDC(hdc, TRUE,\r
3582                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3583                         HIGHLIGHT_PEN);\r
3584   }\r
3585   for (i=0; i<2; i++) {\r
3586     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3587         premoveHighlightInfo.sq[i].y >= 0) {\r
3588         DrawHighlightOnDC(hdc, TRUE,\r
3589                           premoveHighlightInfo.sq[i].x, \r
3590                           premoveHighlightInfo.sq[i].y,\r
3591                           PREMOVE_PEN);\r
3592     }\r
3593   }\r
3594 }\r
3595 \r
3596 /* Note: sqcolor is used only in monoMode */\r
3597 /* Note that this code is largely duplicated in woptions.c,\r
3598    function DrawSampleSquare, so that needs to be updated too */\r
3599 VOID\r
3600 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3601 {\r
3602   HBITMAP oldBitmap;\r
3603   HBRUSH oldBrush;\r
3604   int tmpSize;\r
3605 \r
3606   if (appData.blindfold) return;\r
3607 \r
3608   /* [AS] Use font-based pieces if needed */\r
3609   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3610     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3611     CreatePiecesFromFont();\r
3612 \r
3613     if( fontBitmapSquareSize == squareSize ) {\r
3614         int index = TranslatePieceToFontPiece(piece);\r
3615 \r
3616         SelectObject( tmphdc, hPieceMask[ index ] );\r
3617 \r
3618         BitBlt( hdc,\r
3619             x, y,\r
3620             squareSize, squareSize,\r
3621             tmphdc,\r
3622             0, 0,\r
3623             SRCAND );\r
3624 \r
3625         SelectObject( tmphdc, hPieceFace[ index ] );\r
3626 \r
3627         BitBlt( hdc,\r
3628             x, y,\r
3629             squareSize, squareSize,\r
3630             tmphdc,\r
3631             0, 0,\r
3632             SRCPAINT );\r
3633 \r
3634         return;\r
3635     }\r
3636   }\r
3637 \r
3638   if (appData.monoMode) {\r
3639     SelectObject(tmphdc, PieceBitmap(piece, \r
3640       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3641     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3642            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3643   } else {\r
3644     tmpSize = squareSize;\r
3645     if(minorSize &&\r
3646         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3647          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3648       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3649       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3650       x += (squareSize - minorSize)>>1;\r
3651       y += squareSize - minorSize - 2;\r
3652       tmpSize = minorSize;\r
3653     }\r
3654     if (color || appData.allWhite ) {\r
3655       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3656       if( color )\r
3657               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3658       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3659       if(appData.upsideDown && color==flipView)\r
3660         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3661       else\r
3662         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3663       /* Use black for outline of white pieces */\r
3664       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3665       if(appData.upsideDown && color==flipView)\r
3666         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3667       else\r
3668         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3669     } else {\r
3670       /* Use square color for details of black pieces */\r
3671       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3672       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3673       if(appData.upsideDown && !flipView)\r
3674         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3675       else\r
3676         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3677     }\r
3678     SelectObject(hdc, oldBrush);\r
3679     SelectObject(tmphdc, oldBitmap);\r
3680   }\r
3681 }\r
3682 \r
3683 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3684 int GetBackTextureMode( int algo )\r
3685 {\r
3686     int result = BACK_TEXTURE_MODE_DISABLED;\r
3687 \r
3688     switch( algo ) \r
3689     {\r
3690         case BACK_TEXTURE_MODE_PLAIN:\r
3691             result = 1; /* Always use identity map */\r
3692             break;\r
3693         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3694             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3695             break;\r
3696     }\r
3697 \r
3698     return result;\r
3699 }\r
3700 \r
3701 /* \r
3702     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3703     to handle redraws cleanly (as random numbers would always be different).\r
3704 */\r
3705 VOID RebuildTextureSquareInfo()\r
3706 {\r
3707     BITMAP bi;\r
3708     int lite_w = 0;\r
3709     int lite_h = 0;\r
3710     int dark_w = 0;\r
3711     int dark_h = 0;\r
3712     int row;\r
3713     int col;\r
3714 \r
3715     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3716 \r
3717     if( liteBackTexture != NULL ) {\r
3718         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3719             lite_w = bi.bmWidth;\r
3720             lite_h = bi.bmHeight;\r
3721         }\r
3722     }\r
3723 \r
3724     if( darkBackTexture != NULL ) {\r
3725         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3726             dark_w = bi.bmWidth;\r
3727             dark_h = bi.bmHeight;\r
3728         }\r
3729     }\r
3730 \r
3731     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3732         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3733             if( (col + row) & 1 ) {\r
3734                 /* Lite square */\r
3735                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3736                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3737                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3738                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3739                 }\r
3740             }\r
3741             else {\r
3742                 /* Dark square */\r
3743                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3744                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3745                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3746                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3747                 }\r
3748             }\r
3749         }\r
3750     }\r
3751 }\r
3752 \r
3753 /* [AS] Arrow highlighting support */\r
3754 \r
3755 static int A_WIDTH = 5; /* Width of arrow body */\r
3756 \r
3757 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3758 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3759 \r
3760 static double Sqr( double x )\r
3761 {\r
3762     return x*x;\r
3763 }\r
3764 \r
3765 static int Round( double x )\r
3766 {\r
3767     return (int) (x + 0.5);\r
3768 }\r
3769 \r
3770 /* Draw an arrow between two points using current settings */\r
3771 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3772 {\r
3773     POINT arrow[7];\r
3774     double dx, dy, j, k, x, y;\r
3775 \r
3776     if( d_x == s_x ) {\r
3777         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3778 \r
3779         arrow[0].x = s_x + A_WIDTH;\r
3780         arrow[0].y = s_y;\r
3781 \r
3782         arrow[1].x = s_x + A_WIDTH;\r
3783         arrow[1].y = d_y - h;\r
3784 \r
3785         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3786         arrow[2].y = d_y - h;\r
3787 \r
3788         arrow[3].x = d_x;\r
3789         arrow[3].y = d_y;\r
3790 \r
3791         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3792         arrow[4].y = d_y - h;\r
3793 \r
3794         arrow[5].x = s_x - A_WIDTH;\r
3795         arrow[5].y = d_y - h;\r
3796 \r
3797         arrow[6].x = s_x - A_WIDTH;\r
3798         arrow[6].y = s_y;\r
3799     }\r
3800     else if( d_y == s_y ) {\r
3801         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3802 \r
3803         arrow[0].x = s_x;\r
3804         arrow[0].y = s_y + A_WIDTH;\r
3805 \r
3806         arrow[1].x = d_x - w;\r
3807         arrow[1].y = s_y + A_WIDTH;\r
3808 \r
3809         arrow[2].x = d_x - w;\r
3810         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3811 \r
3812         arrow[3].x = d_x;\r
3813         arrow[3].y = d_y;\r
3814 \r
3815         arrow[4].x = d_x - w;\r
3816         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3817 \r
3818         arrow[5].x = d_x - w;\r
3819         arrow[5].y = s_y - A_WIDTH;\r
3820 \r
3821         arrow[6].x = s_x;\r
3822         arrow[6].y = s_y - A_WIDTH;\r
3823     }\r
3824     else {\r
3825         /* [AS] Needed a lot of paper for this! :-) */\r
3826         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3827         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3828   \r
3829         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3830 \r
3831         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3832 \r
3833         x = s_x;\r
3834         y = s_y;\r
3835 \r
3836         arrow[0].x = Round(x - j);\r
3837         arrow[0].y = Round(y + j*dx);\r
3838 \r
3839         arrow[1].x = Round(x + j);\r
3840         arrow[1].y = Round(y - j*dx);\r
3841 \r
3842         if( d_x > s_x ) {\r
3843             x = (double) d_x - k;\r
3844             y = (double) d_y - k*dy;\r
3845         }\r
3846         else {\r
3847             x = (double) d_x + k;\r
3848             y = (double) d_y + k*dy;\r
3849         }\r
3850 \r
3851         arrow[2].x = Round(x + j);\r
3852         arrow[2].y = Round(y - j*dx);\r
3853 \r
3854         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3855         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3856 \r
3857         arrow[4].x = d_x;\r
3858         arrow[4].y = d_y;\r
3859 \r
3860         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3861         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3862 \r
3863         arrow[6].x = Round(x - j);\r
3864         arrow[6].y = Round(y + j*dx);\r
3865     }\r
3866 \r
3867     Polygon( hdc, arrow, 7 );\r
3868 }\r
3869 \r
3870 /* [AS] Draw an arrow between two squares */\r
3871 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3872 {\r
3873     int s_x, s_y, d_x, d_y;\r
3874     HPEN hpen;\r
3875     HPEN holdpen;\r
3876     HBRUSH hbrush;\r
3877     HBRUSH holdbrush;\r
3878     LOGBRUSH stLB;\r
3879 \r
3880     if( s_col == d_col && s_row == d_row ) {\r
3881         return;\r
3882     }\r
3883 \r
3884     /* Get source and destination points */\r
3885     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3886     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3887 \r
3888     if( d_y > s_y ) {\r
3889         d_y += squareSize / 4;\r
3890     }\r
3891     else if( d_y < s_y ) {\r
3892         d_y += 3 * squareSize / 4;\r
3893     }\r
3894     else {\r
3895         d_y += squareSize / 2;\r
3896     }\r
3897 \r
3898     if( d_x > s_x ) {\r
3899         d_x += squareSize / 4;\r
3900     }\r
3901     else if( d_x < s_x ) {\r
3902         d_x += 3 * squareSize / 4;\r
3903     }\r
3904     else {\r
3905         d_x += squareSize / 2;\r
3906     }\r
3907 \r
3908     s_x += squareSize / 2;\r
3909     s_y += squareSize / 2;\r
3910 \r
3911     /* Adjust width */\r
3912     A_WIDTH = squareSize / 14;\r
3913 \r
3914     /* Draw */\r
3915     stLB.lbStyle = BS_SOLID;\r
3916     stLB.lbColor = appData.highlightArrowColor;\r
3917     stLB.lbHatch = 0;\r
3918 \r
3919     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3920     holdpen = SelectObject( hdc, hpen );\r
3921     hbrush = CreateBrushIndirect( &stLB );\r
3922     holdbrush = SelectObject( hdc, hbrush );\r
3923 \r
3924     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3925 \r
3926     SelectObject( hdc, holdpen );\r
3927     SelectObject( hdc, holdbrush );\r
3928     DeleteObject( hpen );\r
3929     DeleteObject( hbrush );\r
3930 }\r
3931 \r
3932 BOOL HasHighlightInfo()\r
3933 {\r
3934     BOOL result = FALSE;\r
3935 \r
3936     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3937         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3938     {\r
3939         result = TRUE;\r
3940     }\r
3941 \r
3942     return result;\r
3943 }\r
3944 \r
3945 BOOL IsDrawArrowEnabled()\r
3946 {\r
3947     BOOL result = FALSE;\r
3948 \r
3949     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3950         result = TRUE;\r
3951     }\r
3952 \r
3953     return result;\r
3954 }\r
3955 \r
3956 VOID DrawArrowHighlight( HDC hdc )\r
3957 {\r
3958     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3959         DrawArrowBetweenSquares( hdc,\r
3960             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3961             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3962     }\r
3963 }\r
3964 \r
3965 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3966 {\r
3967     HRGN result = NULL;\r
3968 \r
3969     if( HasHighlightInfo() ) {\r
3970         int x1, y1, x2, y2;\r
3971         int sx, sy, dx, dy;\r
3972 \r
3973         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3974         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3975 \r
3976         sx = MIN( x1, x2 );\r
3977         sy = MIN( y1, y2 );\r
3978         dx = MAX( x1, x2 ) + squareSize;\r
3979         dy = MAX( y1, y2 ) + squareSize;\r
3980 \r
3981         result = CreateRectRgn( sx, sy, dx, dy );\r
3982     }\r
3983 \r
3984     return result;\r
3985 }\r
3986 \r
3987 /*\r
3988     Warning: this function modifies the behavior of several other functions. \r
3989     \r
3990     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3991     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3992     repaint is scattered all over the place, which is not good for features such as\r
3993     "arrow highlighting" that require a full repaint of the board.\r
3994 \r
3995     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3996     user interaction, when speed is not so important) but especially to avoid errors\r
3997     in the displayed graphics.\r
3998 \r
3999     In such patched places, I always try refer to this function so there is a single\r
4000     place to maintain knowledge.\r
4001     \r
4002     To restore the original behavior, just return FALSE unconditionally.\r
4003 */\r
4004 BOOL IsFullRepaintPreferrable()\r
4005 {\r
4006     BOOL result = FALSE;\r
4007 \r
4008     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4009         /* Arrow may appear on the board */\r
4010         result = TRUE;\r
4011     }\r
4012 \r
4013     return result;\r
4014 }\r
4015 \r
4016 /* \r
4017     This function is called by DrawPosition to know whether a full repaint must\r
4018     be forced or not.\r
4019 \r
4020     Only DrawPosition may directly call this function, which makes use of \r
4021     some state information. Other function should call DrawPosition specifying \r
4022     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4023 */\r
4024 BOOL DrawPositionNeedsFullRepaint()\r
4025 {\r
4026     BOOL result = FALSE;\r
4027 \r
4028     /* \r
4029         Probably a slightly better policy would be to trigger a full repaint\r
4030         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4031         but animation is fast enough that it's difficult to notice.\r
4032     */\r
4033     if( animInfo.piece == EmptySquare ) {\r
4034         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
4035             result = TRUE;\r
4036         }\r
4037     }\r
4038 \r
4039     return result;\r
4040 }\r
4041 \r
4042 VOID\r
4043 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4044 {\r
4045   int row, column, x, y, square_color, piece_color;\r
4046   ChessSquare piece;\r
4047   HBRUSH oldBrush;\r
4048   HDC texture_hdc = NULL;\r
4049 \r
4050   /* [AS] Initialize background textures if needed */\r
4051   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4052       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4053       if( backTextureSquareSize != squareSize \r
4054        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
4055           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
4056           backTextureSquareSize = squareSize;\r
4057           RebuildTextureSquareInfo();\r
4058       }\r
4059 \r
4060       texture_hdc = CreateCompatibleDC( hdc );\r
4061   }\r
4062 \r
4063   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4064     for (column = 0; column < BOARD_WIDTH; column++) {\r
4065   \r
4066       SquareToPos(row, column, &x, &y);\r
4067 \r
4068       piece = board[row][column];\r
4069 \r
4070       square_color = ((column + row) % 2) == 1;\r
4071       if( gameInfo.variant == VariantXiangqi ) {\r
4072           square_color = !InPalace(row, column);\r
4073           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4074           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4075       }\r
4076       piece_color = (int) piece < (int) BlackPawn;\r
4077 \r
4078 \r
4079       /* [HGM] holdings file: light square or black */\r
4080       if(column == BOARD_LEFT-2) {\r
4081             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4082                 square_color = 1;\r
4083             else {\r
4084                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4085                 continue;\r
4086             }\r
4087       } else\r
4088       if(column == BOARD_RGHT + 1 ) {\r
4089             if( row < gameInfo.holdingsSize )\r
4090                 square_color = 1;\r
4091             else {\r
4092                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4093                 continue;\r
4094             }\r
4095       }\r
4096       if(column == BOARD_LEFT-1 ) /* left align */\r
4097             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4098       else if( column == BOARD_RGHT) /* right align */\r
4099             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4100       else\r
4101       if (appData.monoMode) {\r
4102         if (piece == EmptySquare) {\r
4103           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4104                  square_color ? WHITENESS : BLACKNESS);\r
4105         } else {\r
4106           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4107         }\r
4108       } \r
4109       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4110           /* [AS] Draw the square using a texture bitmap */\r
4111           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4112           int r = row, c = column; // [HGM] do not flip board in flipView\r
4113           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4114 \r
4115           DrawTile( x, y, \r
4116               squareSize, squareSize, \r
4117               hdc, \r
4118               texture_hdc,\r
4119               backTextureSquareInfo[r][c].mode,\r
4120               backTextureSquareInfo[r][c].x,\r
4121               backTextureSquareInfo[r][c].y );\r
4122 \r
4123           SelectObject( texture_hdc, hbm );\r
4124 \r
4125           if (piece != EmptySquare) {\r
4126               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4127           }\r
4128       }\r
4129       else {\r
4130         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4131 \r
4132         oldBrush = SelectObject(hdc, brush );\r
4133         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4134         SelectObject(hdc, oldBrush);\r
4135         if (piece != EmptySquare)\r
4136           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4137       }\r
4138     }\r
4139   }\r
4140 \r
4141   if( texture_hdc != NULL ) {\r
4142     DeleteDC( texture_hdc );\r
4143   }\r
4144 }\r
4145 \r
4146 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4147 void fputDW(FILE *f, int x)\r
4148 {\r
4149         fputc(x     & 255, f);\r
4150         fputc(x>>8  & 255, f);\r
4151         fputc(x>>16 & 255, f);\r
4152         fputc(x>>24 & 255, f);\r
4153 }\r
4154 \r
4155 #define MAX_CLIPS 200   /* more than enough */\r
4156 \r
4157 VOID\r
4158 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4159 {\r
4160 //  HBITMAP bufferBitmap;\r
4161   BITMAP bi;\r
4162 //  RECT Rect;\r
4163   HDC tmphdc;\r
4164   HBITMAP hbm;\r
4165   int w = 100, h = 50;\r
4166 \r
4167   if(logo == NULL) return;\r
4168 //  GetClientRect(hwndMain, &Rect);\r
4169 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4170 //                                      Rect.bottom-Rect.top+1);\r
4171   tmphdc = CreateCompatibleDC(hdc);\r
4172   hbm = SelectObject(tmphdc, logo);\r
4173   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4174             w = bi.bmWidth;\r
4175             h = bi.bmHeight;\r
4176   }\r
4177   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4178                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4179   SelectObject(tmphdc, hbm);\r
4180   DeleteDC(tmphdc);\r
4181 }\r
4182 \r
4183 VOID\r
4184 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4185 {\r
4186   static Board lastReq, lastDrawn;\r
4187   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4188   static int lastDrawnFlipView = 0;\r
4189   static int lastReqValid = 0, lastDrawnValid = 0;\r
4190   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4191   HDC tmphdc;\r
4192   HDC hdcmem;\r
4193   HBITMAP bufferBitmap;\r
4194   HBITMAP oldBitmap;\r
4195   RECT Rect;\r
4196   HRGN clips[MAX_CLIPS];\r
4197   ChessSquare dragged_piece = EmptySquare;\r
4198 \r
4199   /* I'm undecided on this - this function figures out whether a full\r
4200    * repaint is necessary on its own, so there's no real reason to have the\r
4201    * caller tell it that.  I think this can safely be set to FALSE - but\r
4202    * if we trust the callers not to request full repaints unnessesarily, then\r
4203    * we could skip some clipping work.  In other words, only request a full\r
4204    * redraw when the majority of pieces have changed positions (ie. flip, \r
4205    * gamestart and similar)  --Hawk\r
4206    */\r
4207   Boolean fullrepaint = repaint;\r
4208 \r
4209   if( DrawPositionNeedsFullRepaint() ) {\r
4210       fullrepaint = TRUE;\r
4211   }\r
4212 \r
4213   if (board == NULL) {\r
4214     if (!lastReqValid) {\r
4215       return;\r
4216     }\r
4217     board = lastReq;\r
4218   } else {\r
4219     CopyBoard(lastReq, board);\r
4220     lastReqValid = 1;\r
4221   }\r
4222 \r
4223   if (doingSizing) {\r
4224     return;\r
4225   }\r
4226 \r
4227   if (IsIconic(hwndMain)) {\r
4228     return;\r
4229   }\r
4230 \r
4231   if (hdc == NULL) {\r
4232     hdc = GetDC(hwndMain);\r
4233     if (!appData.monoMode) {\r
4234       SelectPalette(hdc, hPal, FALSE);\r
4235       RealizePalette(hdc);\r
4236     }\r
4237     releaseDC = TRUE;\r
4238   } else {\r
4239     releaseDC = FALSE;\r
4240   }\r
4241 \r
4242   /* Create some work-DCs */\r
4243   hdcmem = CreateCompatibleDC(hdc);\r
4244   tmphdc = CreateCompatibleDC(hdc);\r
4245 \r
4246   /* If dragging is in progress, we temporarely remove the piece */\r
4247   /* [HGM] or temporarily decrease count if stacked              */\r
4248   /*       !! Moved to before board compare !!                   */\r
4249   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4250     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4251     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4252             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4253         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4254     } else \r
4255     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4256             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4257         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4258     } else \r
4259         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4260   }\r
4261 \r
4262   /* Figure out which squares need updating by comparing the \r
4263    * newest board with the last drawn board and checking if\r
4264    * flipping has changed.\r
4265    */\r
4266   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4267     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4268       for (column = 0; column < BOARD_WIDTH; column++) {\r
4269         if (lastDrawn[row][column] != board[row][column]) {\r
4270           SquareToPos(row, column, &x, &y);\r
4271           clips[num_clips++] =\r
4272             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4273         }\r
4274       }\r
4275     }\r
4276     for (i=0; i<2; i++) {\r
4277       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4278           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4279         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4280             lastDrawnHighlight.sq[i].y >= 0) {\r
4281           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4282                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4283           clips[num_clips++] =\r
4284             CreateRectRgn(x - lineGap, y - lineGap, \r
4285                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4286         }\r
4287         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4288           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4289           clips[num_clips++] =\r
4290             CreateRectRgn(x - lineGap, y - lineGap, \r
4291                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4292         }\r
4293       }\r
4294     }\r
4295     for (i=0; i<2; i++) {\r
4296       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4297           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4298         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4299             lastDrawnPremove.sq[i].y >= 0) {\r
4300           SquareToPos(lastDrawnPremove.sq[i].y,\r
4301                       lastDrawnPremove.sq[i].x, &x, &y);\r
4302           clips[num_clips++] =\r
4303             CreateRectRgn(x - lineGap, y - lineGap, \r
4304                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4305         }\r
4306         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4307             premoveHighlightInfo.sq[i].y >= 0) {\r
4308           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4309                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4310           clips[num_clips++] =\r
4311             CreateRectRgn(x - lineGap, y - lineGap, \r
4312                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4313         }\r
4314       }\r
4315     }\r
4316   } else {\r
4317     fullrepaint = TRUE;\r
4318   }\r
4319 \r
4320   /* Create a buffer bitmap - this is the actual bitmap\r
4321    * being written to.  When all the work is done, we can\r
4322    * copy it to the real DC (the screen).  This avoids\r
4323    * the problems with flickering.\r
4324    */\r
4325   GetClientRect(hwndMain, &Rect);\r
4326   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4327                                         Rect.bottom-Rect.top+1);\r
4328   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4329   if (!appData.monoMode) {\r
4330     SelectPalette(hdcmem, hPal, FALSE);\r
4331   }\r
4332 \r
4333   /* Create clips for dragging */\r
4334   if (!fullrepaint) {\r
4335     if (dragInfo.from.x >= 0) {\r
4336       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4337       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4338     }\r
4339     if (dragInfo.start.x >= 0) {\r
4340       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4341       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4342     }\r
4343     if (dragInfo.pos.x >= 0) {\r
4344       x = dragInfo.pos.x - squareSize / 2;\r
4345       y = dragInfo.pos.y - squareSize / 2;\r
4346       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4347     }\r
4348     if (dragInfo.lastpos.x >= 0) {\r
4349       x = dragInfo.lastpos.x - squareSize / 2;\r
4350       y = dragInfo.lastpos.y - squareSize / 2;\r
4351       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4352     }\r
4353   }\r
4354 \r
4355   /* Are we animating a move?  \r
4356    * If so, \r
4357    *   - remove the piece from the board (temporarely)\r
4358    *   - calculate the clipping region\r
4359    */\r
4360   if (!fullrepaint) {\r
4361     if (animInfo.piece != EmptySquare) {\r
4362       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4363       x = boardRect.left + animInfo.lastpos.x;\r
4364       y = boardRect.top + animInfo.lastpos.y;\r
4365       x2 = boardRect.left + animInfo.pos.x;\r
4366       y2 = boardRect.top + animInfo.pos.y;\r
4367       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4368       /* Slight kludge.  The real problem is that after AnimateMove is\r
4369          done, the position on the screen does not match lastDrawn.\r
4370          This currently causes trouble only on e.p. captures in\r
4371          atomic, where the piece moves to an empty square and then\r
4372          explodes.  The old and new positions both had an empty square\r
4373          at the destination, but animation has drawn a piece there and\r
4374          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4375       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4376     }\r
4377   }\r
4378 \r
4379   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4380   if (num_clips == 0)\r
4381     fullrepaint = TRUE;\r
4382 \r
4383   /* Set clipping on the memory DC */\r
4384   if (!fullrepaint) {\r
4385     SelectClipRgn(hdcmem, clips[0]);\r
4386     for (x = 1; x < num_clips; x++) {\r
4387       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4388         abort();  // this should never ever happen!\r
4389     }\r
4390   }\r
4391 \r
4392   /* Do all the drawing to the memory DC */\r
4393   if(explodeInfo.radius) { // [HGM] atomic\r
4394         HBRUSH oldBrush;\r
4395         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4396         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4397         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4398         x += squareSize/2;\r
4399         y += squareSize/2;\r
4400         if(!fullrepaint) {\r
4401           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4402           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4403         }\r
4404         DrawGridOnDC(hdcmem);\r
4405         DrawHighlightsOnDC(hdcmem);\r
4406         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4407         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4408         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4409         SelectObject(hdcmem, oldBrush);\r
4410   } else {\r
4411     DrawGridOnDC(hdcmem);\r
4412     DrawHighlightsOnDC(hdcmem);\r
4413     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4414   }\r
4415   if(logoHeight) {\r
4416         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4417         if(appData.autoLogo) {\r
4418           \r
4419           switch(gameMode) { // pick logos based on game mode\r
4420             case IcsObserving:\r
4421                 whiteLogo = second.programLogo; // ICS logo\r
4422                 blackLogo = second.programLogo;\r
4423             default:\r
4424                 break;\r
4425             case IcsPlayingWhite:\r
4426                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4427                 blackLogo = second.programLogo; // ICS logo\r
4428                 break;\r
4429             case IcsPlayingBlack:\r
4430                 whiteLogo = second.programLogo; // ICS logo\r
4431                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4432                 break;\r
4433             case TwoMachinesPlay:\r
4434                 if(first.twoMachinesColor[0] == 'b') {\r
4435                     whiteLogo = second.programLogo;\r
4436                     blackLogo = first.programLogo;\r
4437                 }\r
4438                 break;\r
4439             case MachinePlaysWhite:\r
4440                 blackLogo = userLogo;\r
4441                 break;\r
4442             case MachinePlaysBlack:\r
4443                 whiteLogo = userLogo;\r
4444                 blackLogo = first.programLogo;\r
4445           }\r
4446         }\r
4447         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4448         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4449   }\r
4450 \r
4451   if( appData.highlightMoveWithArrow ) {\r
4452     DrawArrowHighlight(hdcmem);\r
4453   }\r
4454 \r
4455   DrawCoordsOnDC(hdcmem);\r
4456 \r
4457   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4458                  /* to make sure lastDrawn contains what is actually drawn */\r
4459 \r
4460   /* Put the dragged piece back into place and draw it (out of place!) */\r
4461     if (dragged_piece != EmptySquare) {\r
4462     /* [HGM] or restack */\r
4463     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4464                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4465     else\r
4466     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4467                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4468     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4469     x = dragInfo.pos.x - squareSize / 2;\r
4470     y = dragInfo.pos.y - squareSize / 2;\r
4471     DrawPieceOnDC(hdcmem, dragged_piece,\r
4472                   ((int) dragged_piece < (int) BlackPawn), \r
4473                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4474   }   \r
4475   \r
4476   /* Put the animated piece back into place and draw it */\r
4477   if (animInfo.piece != EmptySquare) {\r
4478     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4479     x = boardRect.left + animInfo.pos.x;\r
4480     y = boardRect.top + animInfo.pos.y;\r
4481     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4482                   ((int) animInfo.piece < (int) BlackPawn),\r
4483                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4484   }\r
4485 \r
4486   /* Release the bufferBitmap by selecting in the old bitmap \r
4487    * and delete the memory DC\r
4488    */\r
4489   SelectObject(hdcmem, oldBitmap);\r
4490   DeleteDC(hdcmem);\r
4491 \r
4492   /* Set clipping on the target DC */\r
4493   if (!fullrepaint) {\r
4494     SelectClipRgn(hdc, clips[0]);\r
4495     for (x = 1; x < num_clips; x++) {\r
4496       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4497         abort();   // this should never ever happen!\r
4498     } \r
4499   }\r
4500 \r
4501   /* Copy the new bitmap onto the screen in one go.\r
4502    * This way we avoid any flickering\r
4503    */\r
4504   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4505   BitBlt(hdc, boardRect.left, boardRect.top,\r
4506          boardRect.right - boardRect.left,\r
4507          boardRect.bottom - boardRect.top,\r
4508          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4509   if(saveDiagFlag) { \r
4510     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4511     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4512 \r
4513     GetObject(bufferBitmap, sizeof(b), &b);\r
4514     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4515         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4516         bih.biWidth = b.bmWidth;\r
4517         bih.biHeight = b.bmHeight;\r
4518         bih.biPlanes = 1;\r
4519         bih.biBitCount = b.bmBitsPixel;\r
4520         bih.biCompression = 0;\r
4521         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4522         bih.biXPelsPerMeter = 0;\r
4523         bih.biYPelsPerMeter = 0;\r
4524         bih.biClrUsed = 0;\r
4525         bih.biClrImportant = 0;\r
4526 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4527 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4528         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4529 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4530 \r
4531         wb = b.bmWidthBytes;\r
4532         // count colors\r
4533         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4534                 int k = ((int*) pData)[i];\r
4535                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4536                 if(j >= 16) break;\r
4537                 color[j] = k;\r
4538                 if(j >= nrColors) nrColors = j+1;\r
4539         }\r
4540         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4541                 INT p = 0;\r
4542                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4543                     for(w=0; w<(wb>>2); w+=2) {\r
4544                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4545                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4546                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4547                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4548                         pData[p++] = m | j<<4;\r
4549                     }\r
4550                     while(p&3) pData[p++] = 0;\r
4551                 }\r
4552                 fac = 3;\r
4553                 wb = ((wb+31)>>5)<<2;\r
4554         }\r
4555         // write BITMAPFILEHEADER\r
4556         fprintf(diagFile, "BM");\r
4557         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4558         fputDW(diagFile, 0);\r
4559         fputDW(diagFile, 0x36 + (fac?64:0));\r
4560         // write BITMAPINFOHEADER\r
4561         fputDW(diagFile, 40);\r
4562         fputDW(diagFile, b.bmWidth);\r
4563         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4564         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4565         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4566         fputDW(diagFile, 0);\r
4567         fputDW(diagFile, 0);\r
4568         fputDW(diagFile, 0);\r
4569         fputDW(diagFile, 0);\r
4570         fputDW(diagFile, 0);\r
4571         fputDW(diagFile, 0);\r
4572         // write color table\r
4573         if(fac)\r
4574         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4575         // write bitmap data\r
4576         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4577                 fputc(pData[i], diagFile);\r
4578      }\r
4579   }\r
4580 \r
4581   SelectObject(tmphdc, oldBitmap);\r
4582 \r
4583   /* Massive cleanup */\r
4584   for (x = 0; x < num_clips; x++)\r
4585     DeleteObject(clips[x]);\r
4586 \r
4587   DeleteDC(tmphdc);\r
4588   DeleteObject(bufferBitmap);\r
4589 \r
4590   if (releaseDC) \r
4591     ReleaseDC(hwndMain, hdc);\r
4592   \r
4593   if (lastDrawnFlipView != flipView) {\r
4594     if (flipView)\r
4595       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4596     else\r
4597       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4598   }\r
4599 \r
4600 /*  CopyBoard(lastDrawn, board);*/\r
4601   lastDrawnHighlight = highlightInfo;\r
4602   lastDrawnPremove   = premoveHighlightInfo;\r
4603   lastDrawnFlipView = flipView;\r
4604   lastDrawnValid = 1;\r
4605 }\r
4606 \r
4607 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4608 int\r
4609 SaveDiagram(f)\r
4610      FILE *f;\r
4611 {\r
4612     saveDiagFlag = 1; diagFile = f;\r
4613     HDCDrawPosition(NULL, TRUE, NULL);\r
4614 \r
4615     saveDiagFlag = 0;\r
4616 \r
4617 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4618     \r
4619     fclose(f);\r
4620     return TRUE;\r
4621 }\r
4622 \r
4623 \r
4624 /*---------------------------------------------------------------------------*\\r
4625 | CLIENT PAINT PROCEDURE\r
4626 |   This is the main event-handler for the WM_PAINT message.\r
4627 |\r
4628 \*---------------------------------------------------------------------------*/\r
4629 VOID\r
4630 PaintProc(HWND hwnd)\r
4631 {\r
4632   HDC         hdc;\r
4633   PAINTSTRUCT ps;\r
4634   HFONT       oldFont;\r
4635 \r
4636   if((hdc = BeginPaint(hwnd, &ps))) {\r
4637     if (IsIconic(hwnd)) {\r
4638       DrawIcon(hdc, 2, 2, iconCurrent);\r
4639     } else {\r
4640       if (!appData.monoMode) {\r
4641         SelectPalette(hdc, hPal, FALSE);\r
4642         RealizePalette(hdc);\r
4643       }\r
4644       HDCDrawPosition(hdc, 1, NULL);\r
4645       oldFont =\r
4646         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4647       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4648                  ETO_CLIPPED|ETO_OPAQUE,\r
4649                  &messageRect, messageText, strlen(messageText), NULL);\r
4650       SelectObject(hdc, oldFont);\r
4651       DisplayBothClocks();\r
4652     }\r
4653     EndPaint(hwnd,&ps);\r
4654   }\r
4655 \r
4656   return;\r
4657 }\r
4658 \r
4659 \r
4660 /*\r
4661  * If the user selects on a border boundary, return -1; if off the board,\r
4662  *   return -2.  Otherwise map the event coordinate to the square.\r
4663  * The offset boardRect.left or boardRect.top must already have been\r
4664  *   subtracted from x.\r
4665  */\r
4666 int EventToSquare(x, limit)\r
4667      int x, limit;\r
4668 {\r
4669   if (x <= 0)\r
4670     return -2;\r
4671   if (x < lineGap)\r
4672     return -1;\r
4673   x -= lineGap;\r
4674   if ((x % (squareSize + lineGap)) >= squareSize)\r
4675     return -1;\r
4676   x /= (squareSize + lineGap);\r
4677     if (x >= limit)\r
4678     return -2;\r
4679   return x;\r
4680 }\r
4681 \r
4682 typedef struct {\r
4683   char piece;\r
4684   int command;\r
4685   char* name;\r
4686 } DropEnable;\r
4687 \r
4688 DropEnable dropEnables[] = {\r
4689   { 'P', DP_Pawn, "Pawn" },\r
4690   { 'N', DP_Knight, "Knight" },\r
4691   { 'B', DP_Bishop, "Bishop" },\r
4692   { 'R', DP_Rook, "Rook" },\r
4693   { 'Q', DP_Queen, "Queen" },\r
4694 };\r
4695 \r
4696 VOID\r
4697 SetupDropMenu(HMENU hmenu)\r
4698 {\r
4699   int i, count, enable;\r
4700   char *p;\r
4701   extern char white_holding[], black_holding[];\r
4702   char item[MSG_SIZ];\r
4703 \r
4704   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4705     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4706                dropEnables[i].piece);\r
4707     count = 0;\r
4708     while (p && *p++ == dropEnables[i].piece) count++;\r
4709     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4710     enable = count > 0 || !appData.testLegality\r
4711       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4712                       && !appData.icsActive);\r
4713     ModifyMenu(hmenu, dropEnables[i].command,\r
4714                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4715                dropEnables[i].command, item);\r
4716   }\r
4717 }\r
4718 \r
4719 void DragPieceBegin(int x, int y)\r
4720 {\r
4721       dragInfo.lastpos.x = boardRect.left + x;\r
4722       dragInfo.lastpos.y = boardRect.top + y;\r
4723       dragInfo.from.x = fromX;\r
4724       dragInfo.from.y = fromY;\r
4725       dragInfo.start = dragInfo.from;\r
4726       SetCapture(hwndMain);\r
4727 }\r
4728 \r
4729 void DragPieceEnd(int x, int y)\r
4730 {\r
4731     ReleaseCapture();\r
4732     dragInfo.start.x = dragInfo.start.y = -1;\r
4733     dragInfo.from = dragInfo.start;\r
4734     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4735 }\r
4736 \r
4737 /* Event handler for mouse messages */\r
4738 VOID\r
4739 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4740 {\r
4741   int x, y;\r
4742   POINT pt;\r
4743   static int recursive = 0;\r
4744   HMENU hmenu;\r
4745   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4746 \r
4747   if (recursive) {\r
4748     if (message == WM_MBUTTONUP) {\r
4749       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4750          to the middle button: we simulate pressing the left button too!\r
4751          */\r
4752       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4753       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4754     }\r
4755     return;\r
4756   }\r
4757   recursive++;\r
4758   \r
4759   pt.x = LOWORD(lParam);\r
4760   pt.y = HIWORD(lParam);\r
4761   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4762   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4763   if (!flipView && y >= 0) {\r
4764     y = BOARD_HEIGHT - 1 - y;\r
4765   }\r
4766   if (flipView && x >= 0) {\r
4767     x = BOARD_WIDTH - 1 - x;\r
4768   }\r
4769 \r
4770   switch (message) {\r
4771   case WM_LBUTTONDOWN:\r
4772       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4773         if (gameMode == EditPosition) {\r
4774           SetWhiteToPlayEvent();\r
4775         } else if (gameMode == IcsPlayingBlack ||\r
4776                    gameMode == MachinePlaysWhite) {\r
4777           CallFlagEvent();\r
4778         } else if (gameMode == EditGame) {\r
4779           AdjustClock(flipClock, -1);\r
4780         }\r
4781       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4782         if (gameMode == EditPosition) {\r
4783           SetBlackToPlayEvent();\r
4784         } else if (gameMode == IcsPlayingWhite ||\r
4785                    gameMode == MachinePlaysBlack) {\r
4786           CallFlagEvent();\r
4787         } else if (gameMode == EditGame) {\r
4788           AdjustClock(!flipClock, -1);\r
4789         }\r
4790       }\r
4791       dragInfo.start.x = dragInfo.start.y = -1;\r
4792       dragInfo.from = dragInfo.start;\r
4793     if(fromX == -1 && frozen) { // not sure where this is for\r
4794                 fromX = fromY = -1; \r
4795       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4796       break;\r
4797     }\r
4798       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4799       DrawPosition(TRUE, NULL);\r
4800     break;\r
4801 \r
4802   case WM_LBUTTONUP:\r
4803       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4804       DrawPosition(TRUE, NULL);\r
4805     break;\r
4806 \r
4807   case WM_MOUSEMOVE:\r
4808     if ((appData.animateDragging || appData.highlightDragging)\r
4809         && (wParam & MK_LBUTTON)\r
4810         && dragInfo.from.x >= 0) \r
4811     {\r
4812       BOOL full_repaint = FALSE;\r
4813 \r
4814       if (appData.animateDragging) {\r
4815         dragInfo.pos = pt;\r
4816       }\r
4817       if (appData.highlightDragging) {\r
4818         SetHighlights(fromX, fromY, x, y);\r
4819         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4820             full_repaint = TRUE;\r
4821         }\r
4822       }\r
4823       \r
4824       DrawPosition( full_repaint, NULL);\r
4825       \r
4826       dragInfo.lastpos = dragInfo.pos;\r
4827     }\r
4828     break;\r
4829 \r
4830   case WM_MOUSEWHEEL: // [DM]\r
4831     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4832        /* Mouse Wheel is being rolled forward\r
4833         * Play moves forward\r
4834         */\r
4835        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
4836                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4837        /* Mouse Wheel is being rolled backward\r
4838         * Play moves backward\r
4839         */\r
4840        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
4841                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4842     }\r
4843     break;\r
4844 \r
4845   case WM_MBUTTONDOWN:\r
4846   case WM_RBUTTONDOWN:\r
4847     ErrorPopDown();\r
4848     ReleaseCapture();\r
4849     fromX = fromY = -1;\r
4850     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4851     dragInfo.start.x = dragInfo.start.y = -1;\r
4852     dragInfo.from = dragInfo.start;\r
4853     dragInfo.lastpos = dragInfo.pos;\r
4854     if (appData.highlightDragging) {\r
4855       ClearHighlights();\r
4856     }\r
4857     if(y == -2) {\r
4858       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4859       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4860           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4861       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4862           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4863       }\r
4864     }\r
4865     DrawPosition(TRUE, NULL);\r
4866 \r
4867     switch (gameMode) {\r
4868     case EditPosition:\r
4869     case IcsExamining:\r
4870       if (x < 0 || y < 0) break;\r
4871       fromX = x;\r
4872       fromY = y;\r
4873       if (message == WM_MBUTTONDOWN) {\r
4874         buttonCount = 3;  /* even if system didn't think so */\r
4875         if (wParam & MK_SHIFT) \r
4876           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4877         else\r
4878           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4879       } else { /* message == WM_RBUTTONDOWN */\r
4880         /* Just have one menu, on the right button.  Windows users don't\r
4881            think to try the middle one, and sometimes other software steals\r
4882            it, or it doesn't really exist. */\r
4883         if(gameInfo.variant != VariantShogi)\r
4884             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4885         else\r
4886             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4887       }\r
4888       break;\r
4889     case IcsPlayingWhite:\r
4890     case IcsPlayingBlack:\r
4891     case EditGame:\r
4892     case MachinePlaysWhite:\r
4893     case MachinePlaysBlack:\r
4894       if (appData.testLegality &&\r
4895           gameInfo.variant != VariantBughouse &&\r
4896           gameInfo.variant != VariantCrazyhouse) break;\r
4897       if (x < 0 || y < 0) break;\r
4898       fromX = x;\r
4899       fromY = y;\r
4900       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
4901       SetupDropMenu(hmenu);\r
4902       MenuPopup(hwnd, pt, hmenu, -1);\r
4903       break;\r
4904     default:\r
4905       break;\r
4906     }\r
4907     break;\r
4908   }\r
4909 \r
4910   recursive--;\r
4911 }\r
4912 \r
4913 /* Preprocess messages for buttons in main window */\r
4914 LRESULT CALLBACK\r
4915 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4916 {\r
4917   int id = GetWindowLong(hwnd, GWL_ID);\r
4918   int i, dir;\r
4919 \r
4920   for (i=0; i<N_BUTTONS; i++) {\r
4921     if (buttonDesc[i].id == id) break;\r
4922   }\r
4923   if (i == N_BUTTONS) return 0;\r
4924   switch (message) {\r
4925   case WM_KEYDOWN:\r
4926     switch (wParam) {\r
4927     case VK_LEFT:\r
4928     case VK_RIGHT:\r
4929       dir = (wParam == VK_LEFT) ? -1 : 1;\r
4930       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
4931       return TRUE;\r
4932     }\r
4933     break;\r
4934   case WM_CHAR:\r
4935     switch (wParam) {\r
4936     case '\r':\r
4937       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
4938       return TRUE;\r
4939     default:\r
4940       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
4941         // [HGM] movenum: only letters or leading zero should go to ICS input\r
4942         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
4943         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
4944         SetFocus(h);\r
4945         SendMessage(h, WM_CHAR, wParam, lParam);\r
4946         return TRUE;\r
4947       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
4948         PopUpMoveDialog((char)wParam);\r
4949       }\r
4950       break;\r
4951     }\r
4952     break;\r
4953   }\r
4954   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
4955 }\r
4956 \r
4957 /* Process messages for Promotion dialog box */\r
4958 LRESULT CALLBACK\r
4959 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4960 {\r
4961   char promoChar;\r
4962 \r
4963   switch (message) {\r
4964   case WM_INITDIALOG: /* message: initialize dialog box */\r
4965     /* Center the dialog over the application window */\r
4966     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4967     ShowWindow(GetDlgItem(hDlg, PB_King), \r
4968       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
4969        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
4970                SW_SHOW : SW_HIDE);\r
4971     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
4972     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
4973        ((PieceToChar(WhiteAngel) >= 'A' &&\r
4974          PieceToChar(WhiteAngel) != '~') ||\r
4975         (PieceToChar(BlackAngel) >= 'A' &&\r
4976          PieceToChar(BlackAngel) != '~')   ) ?\r
4977                SW_SHOW : SW_HIDE);\r
4978     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
4979        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
4980          PieceToChar(WhiteMarshall) != '~') ||\r
4981         (PieceToChar(BlackMarshall) >= 'A' &&\r
4982          PieceToChar(BlackMarshall) != '~')   ) ?\r
4983                SW_SHOW : SW_HIDE);\r
4984     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
4985     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
4986        gameInfo.variant != VariantShogi ?\r
4987                SW_SHOW : SW_HIDE);\r
4988     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
4989        gameInfo.variant != VariantShogi ?\r
4990                SW_SHOW : SW_HIDE);\r
4991     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
4992        gameInfo.variant == VariantShogi ?\r
4993                SW_SHOW : SW_HIDE);\r
4994     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
4995        gameInfo.variant == VariantShogi ?\r
4996                SW_SHOW : SW_HIDE);\r
4997     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
4998        gameInfo.variant == VariantSuper ?\r
4999                SW_SHOW : SW_HIDE);\r
5000     return TRUE;\r
5001 \r
5002   case WM_COMMAND: /* message: received a command */\r
5003     switch (LOWORD(wParam)) {\r
5004     case IDCANCEL:\r
5005       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5006       ClearHighlights();\r
5007       DrawPosition(FALSE, NULL);\r
5008       return TRUE;\r
5009     case PB_King:\r
5010       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5011       break;\r
5012     case PB_Queen:\r
5013       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5014       break;\r
5015     case PB_Rook:\r
5016       promoChar = PieceToChar(BlackRook);\r
5017       break;\r
5018     case PB_Bishop:\r
5019       promoChar = PieceToChar(BlackBishop);\r
5020       break;\r
5021     case PB_Chancellor:\r
5022       promoChar = PieceToChar(BlackMarshall);\r
5023       break;\r
5024     case PB_Archbishop:\r
5025       promoChar = PieceToChar(BlackAngel);\r
5026       break;\r
5027     case PB_Knight:\r
5028       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5029       break;\r
5030     default:\r
5031       return FALSE;\r
5032     }\r
5033     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5034     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5035        only show the popup when we are already sure the move is valid or\r
5036        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5037        will figure out it is a promotion from the promoChar. */\r
5038     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
5039     fromX = fromY = -1;\r
5040     if (!appData.highlightLastMove) {\r
5041       ClearHighlights();\r
5042       DrawPosition(FALSE, NULL);\r
5043     }\r
5044     return TRUE;\r
5045   }\r
5046   return FALSE;\r
5047 }\r
5048 \r
5049 /* Pop up promotion dialog */\r
5050 VOID\r
5051 PromotionPopup(HWND hwnd)\r
5052 {\r
5053   FARPROC lpProc;\r
5054 \r
5055   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5056   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5057     hwnd, (DLGPROC)lpProc);\r
5058   FreeProcInstance(lpProc);\r
5059 }\r
5060 \r
5061 void\r
5062 PromotionPopUp()\r
5063 {\r
5064   DrawPosition(TRUE, NULL);\r
5065   PromotionPopup(hwndMain);\r
5066 }\r
5067 \r
5068 /* Toggle ShowThinking */\r
5069 VOID\r
5070 ToggleShowThinking()\r
5071 {\r
5072   appData.showThinking = !appData.showThinking;\r
5073   ShowThinkingEvent();\r
5074 }\r
5075 \r
5076 VOID\r
5077 LoadGameDialog(HWND hwnd, char* title)\r
5078 {\r
5079   UINT number = 0;\r
5080   FILE *f;\r
5081   char fileTitle[MSG_SIZ];\r
5082   f = OpenFileDialog(hwnd, "rb", "",\r
5083                      appData.oldSaveStyle ? "gam" : "pgn",\r
5084                      GAME_FILT,\r
5085                      title, &number, fileTitle, NULL);\r
5086   if (f != NULL) {\r
5087     cmailMsgLoaded = FALSE;\r
5088     if (number == 0) {\r
5089       int error = GameListBuild(f);\r
5090       if (error) {\r
5091         DisplayError("Cannot build game list", error);\r
5092       } else if (!ListEmpty(&gameList) &&\r
5093                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5094         GameListPopUp(f, fileTitle);\r
5095         return;\r
5096       }\r
5097       GameListDestroy();\r
5098       number = 1;\r
5099     }\r
5100     LoadGame(f, number, fileTitle, FALSE);\r
5101   }\r
5102 }\r
5103 \r
5104 int get_term_width()\r
5105 {\r
5106     HDC hdc;\r
5107     TEXTMETRIC tm;\r
5108     RECT rc;\r
5109     HFONT hfont, hold_font;\r
5110     LOGFONT lf;\r
5111     HWND hText;\r
5112 \r
5113     if (hwndConsole)\r
5114         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5115     else\r
5116         return 79;\r
5117 \r
5118     // get the text metrics\r
5119     hdc = GetDC(hText);\r
5120     lf = font[boardSize][CONSOLE_FONT]->lf;\r
5121     if (consoleCF.dwEffects & CFE_BOLD)\r
5122         lf.lfWeight = FW_BOLD;\r
5123     if (consoleCF.dwEffects & CFE_ITALIC)\r
5124         lf.lfItalic = TRUE;\r
5125     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
5126         lf.lfStrikeOut = TRUE;\r
5127     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
5128         lf.lfUnderline = TRUE;\r
5129     hfont = CreateFontIndirect(&lf);\r
5130     hold_font = SelectObject(hdc, hfont);\r
5131     GetTextMetrics(hdc, &tm);\r
5132     SelectObject(hdc, hold_font);\r
5133     DeleteObject(hfont);\r
5134     ReleaseDC(hText, hdc);\r
5135 \r
5136     // get the rectangle\r
5137     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5138 \r
5139     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
5140 }\r
5141 \r
5142 void UpdateICSWidth(HWND hText)\r
5143 {\r
5144     LONG old_width, new_width;\r
5145 \r
5146     new_width = get_term_width(hText, FALSE);\r
5147     old_width = GetWindowLong(hText, GWL_USERDATA);\r
5148     if (new_width != old_width)\r
5149     {\r
5150         ics_update_width(new_width);\r
5151         SetWindowLong(hText, GWL_USERDATA, new_width);\r
5152     }\r
5153 }\r
5154 \r
5155 VOID\r
5156 ChangedConsoleFont()\r
5157 {\r
5158   CHARFORMAT cfmt;\r
5159   CHARRANGE tmpsel, sel;\r
5160   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5161   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5162   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5163   PARAFORMAT paraf;\r
5164 \r
5165   cfmt.cbSize = sizeof(CHARFORMAT);\r
5166   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5167   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5168   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5169    * size.  This was undocumented in the version of MSVC++ that I had\r
5170    * when I wrote the code, but is apparently documented now.\r
5171    */\r
5172   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5173   cfmt.bCharSet = f->lf.lfCharSet;\r
5174   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5175   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5176   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5177   /* Why are the following seemingly needed too? */\r
5178   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5179   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5180   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5181   tmpsel.cpMin = 0;\r
5182   tmpsel.cpMax = -1; /*999999?*/\r
5183   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5184   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5185   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5186    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5187    */\r
5188   paraf.cbSize = sizeof(paraf);\r
5189   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5190   paraf.dxStartIndent = 0;\r
5191   paraf.dxOffset = WRAP_INDENT;\r
5192   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5193   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5194   UpdateICSWidth(hText);\r
5195 }\r
5196 \r
5197 /*---------------------------------------------------------------------------*\\r
5198  *\r
5199  * Window Proc for main window\r
5200  *\r
5201 \*---------------------------------------------------------------------------*/\r
5202 \r
5203 /* Process messages for main window, etc. */\r
5204 LRESULT CALLBACK\r
5205 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5206 {\r
5207   FARPROC lpProc;\r
5208   int wmId, wmEvent;\r
5209   char *defName;\r
5210   FILE *f;\r
5211   UINT number;\r
5212   char fileTitle[MSG_SIZ];\r
5213   char buf[MSG_SIZ];\r
5214   static SnapData sd;\r
5215 \r
5216   switch (message) {\r
5217 \r
5218   case WM_PAINT: /* message: repaint portion of window */\r
5219     PaintProc(hwnd);\r
5220     break;\r
5221 \r
5222   case WM_ERASEBKGND:\r
5223     if (IsIconic(hwnd)) {\r
5224       /* Cheat; change the message */\r
5225       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5226     } else {\r
5227       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5228     }\r
5229     break;\r
5230 \r
5231   case WM_LBUTTONDOWN:\r
5232   case WM_MBUTTONDOWN:\r
5233   case WM_RBUTTONDOWN:\r
5234   case WM_LBUTTONUP:\r
5235   case WM_MBUTTONUP:\r
5236   case WM_RBUTTONUP:\r
5237   case WM_MOUSEMOVE:\r
5238   case WM_MOUSEWHEEL:\r
5239     MouseEvent(hwnd, message, wParam, lParam);\r
5240     break;\r
5241 \r
5242   JAWS_KB_NAVIGATION\r
5243 \r
5244   case WM_CHAR:\r
5245     \r
5246     JAWS_ALT_INTERCEPT\r
5247 \r
5248     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
5249         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5250         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5251         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5252         SetFocus(h);\r
5253         SendMessage(h, message, wParam, lParam);\r
5254     } else if(lParam != KF_REPEAT) {\r
5255         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5256                 PopUpMoveDialog((char)wParam);\r
5257         } else if((char)wParam == 003) CopyGameToClipboard();\r
5258          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5259     }\r
5260 \r
5261     break;\r
5262 \r
5263   case WM_PALETTECHANGED:\r
5264     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5265       int nnew;\r
5266       HDC hdc = GetDC(hwndMain);\r
5267       SelectPalette(hdc, hPal, TRUE);\r
5268       nnew = RealizePalette(hdc);\r
5269       if (nnew > 0) {\r
5270         paletteChanged = TRUE;\r
5271         InvalidateRect(hwnd, &boardRect, FALSE);\r
5272       }\r
5273       ReleaseDC(hwnd, hdc);\r
5274     }\r
5275     break;\r
5276 \r
5277   case WM_QUERYNEWPALETTE:\r
5278     if (!appData.monoMode /*&& paletteChanged*/) {\r
5279       int nnew;\r
5280       HDC hdc = GetDC(hwndMain);\r
5281       paletteChanged = FALSE;\r
5282       SelectPalette(hdc, hPal, FALSE);\r
5283       nnew = RealizePalette(hdc);\r
5284       if (nnew > 0) {\r
5285         InvalidateRect(hwnd, &boardRect, FALSE);\r
5286       }\r
5287       ReleaseDC(hwnd, hdc);\r
5288       return TRUE;\r
5289     }\r
5290     return FALSE;\r
5291 \r
5292   case WM_COMMAND: /* message: command from application menu */\r
5293     wmId    = LOWORD(wParam);\r
5294     wmEvent = HIWORD(wParam);\r
5295 \r
5296     switch (wmId) {\r
5297     case IDM_NewGame:\r
5298       ResetGameEvent();\r
5299       SAY("new game enter a move to play against the computer with white");\r
5300       break;\r
5301 \r
5302     case IDM_NewGameFRC:\r
5303       if( NewGameFRC() == 0 ) {\r
5304         ResetGameEvent();\r
5305       }\r
5306       break;\r
5307 \r
5308     case IDM_NewVariant:\r
5309       NewVariantPopup(hwnd);\r
5310       break;\r
5311 \r
5312     case IDM_LoadGame:\r
5313       LoadGameDialog(hwnd, "Load Game from File");\r
5314       break;\r
5315 \r
5316     case IDM_LoadNextGame:\r
5317       ReloadGame(1);\r
5318       break;\r
5319 \r
5320     case IDM_LoadPrevGame:\r
5321       ReloadGame(-1);\r
5322       break;\r
5323 \r
5324     case IDM_ReloadGame:\r
5325       ReloadGame(0);\r
5326       break;\r
5327 \r
5328     case IDM_LoadPosition:\r
5329       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5330         Reset(FALSE, TRUE);\r
5331       }\r
5332       number = 1;\r
5333       f = OpenFileDialog(hwnd, "rb", "",\r
5334                          appData.oldSaveStyle ? "pos" : "fen",\r
5335                          POSITION_FILT,\r
5336                          "Load Position from File", &number, fileTitle, NULL);\r
5337       if (f != NULL) {\r
5338         LoadPosition(f, number, fileTitle);\r
5339       }\r
5340       break;\r
5341 \r
5342     case IDM_LoadNextPosition:\r
5343       ReloadPosition(1);\r
5344       break;\r
5345 \r
5346     case IDM_LoadPrevPosition:\r
5347       ReloadPosition(-1);\r
5348       break;\r
5349 \r
5350     case IDM_ReloadPosition:\r
5351       ReloadPosition(0);\r
5352       break;\r
5353 \r
5354     case IDM_SaveGame:\r
5355       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5356       f = OpenFileDialog(hwnd, "a", defName,\r
5357                          appData.oldSaveStyle ? "gam" : "pgn",\r
5358                          GAME_FILT,\r
5359                          "Save Game to File", NULL, fileTitle, NULL);\r
5360       if (f != NULL) {\r
5361         SaveGame(f, 0, "");\r
5362       }\r
5363       break;\r
5364 \r
5365     case IDM_SavePosition:\r
5366       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5367       f = OpenFileDialog(hwnd, "a", defName,\r
5368                          appData.oldSaveStyle ? "pos" : "fen",\r
5369                          POSITION_FILT,\r
5370                          "Save Position to File", NULL, fileTitle, NULL);\r
5371       if (f != NULL) {\r
5372         SavePosition(f, 0, "");\r
5373       }\r
5374       break;\r
5375 \r
5376     case IDM_SaveDiagram:\r
5377       defName = "diagram";\r
5378       f = OpenFileDialog(hwnd, "wb", defName,\r
5379                          "bmp",\r
5380                          DIAGRAM_FILT,\r
5381                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5382       if (f != NULL) {\r
5383         SaveDiagram(f);\r
5384       }\r
5385       break;\r
5386 \r
5387     case IDM_CopyGame:\r
5388       CopyGameToClipboard();\r
5389       break;\r
5390 \r
5391     case IDM_PasteGame:\r
5392       PasteGameFromClipboard();\r
5393       break;\r
5394 \r
5395     case IDM_CopyGameListToClipboard:\r
5396       CopyGameListToClipboard();\r
5397       break;\r
5398 \r
5399     /* [AS] Autodetect FEN or PGN data */\r
5400     case IDM_PasteAny:\r
5401       PasteGameOrFENFromClipboard();\r
5402       break;\r
5403 \r
5404     /* [AS] Move history */\r
5405     case IDM_ShowMoveHistory:\r
5406         if( MoveHistoryIsUp() ) {\r
5407             MoveHistoryPopDown();\r
5408         }\r
5409         else {\r
5410             MoveHistoryPopUp();\r
5411         }\r
5412         break;\r
5413 \r
5414     /* [AS] Eval graph */\r
5415     case IDM_ShowEvalGraph:\r
5416         if( EvalGraphIsUp() ) {\r
5417             EvalGraphPopDown();\r
5418         }\r
5419         else {\r
5420             EvalGraphPopUp();\r
5421             SetFocus(hwndMain);\r
5422         }\r
5423         break;\r
5424 \r
5425     /* [AS] Engine output */\r
5426     case IDM_ShowEngineOutput:\r
5427         if( EngineOutputIsUp() ) {\r
5428             EngineOutputPopDown();\r
5429         }\r
5430         else {\r
5431             EngineOutputPopUp();\r
5432         }\r
5433         break;\r
5434 \r
5435     /* [AS] User adjudication */\r
5436     case IDM_UserAdjudication_White:\r
5437         UserAdjudicationEvent( +1 );\r
5438         break;\r
5439 \r
5440     case IDM_UserAdjudication_Black:\r
5441         UserAdjudicationEvent( -1 );\r
5442         break;\r
5443 \r
5444     case IDM_UserAdjudication_Draw:\r
5445         UserAdjudicationEvent( 0 );\r
5446         break;\r
5447 \r
5448     /* [AS] Game list options dialog */\r
5449     case IDM_GameListOptions:\r
5450       GameListOptions();\r
5451       break;\r
5452 \r
5453     case IDM_NewChat:\r
5454       ChatPopUp();\r
5455       break;\r
5456 \r
5457     case IDM_CopyPosition:\r
5458       CopyFENToClipboard();\r
5459       break;\r
5460 \r
5461     case IDM_PastePosition:\r
5462       PasteFENFromClipboard();\r
5463       break;\r
5464 \r
5465     case IDM_MailMove:\r
5466       MailMoveEvent();\r
5467       break;\r
5468 \r
5469     case IDM_ReloadCMailMsg:\r
5470       Reset(TRUE, TRUE);\r
5471       ReloadCmailMsgEvent(FALSE);\r
5472       break;\r
5473 \r
5474     case IDM_Minimize:\r
5475       ShowWindow(hwnd, SW_MINIMIZE);\r
5476       break;\r
5477 \r
5478     case IDM_Exit:\r
5479       ExitEvent(0);\r
5480       break;\r
5481 \r
5482     case IDM_MachineWhite:\r
5483       MachineWhiteEvent();\r
5484       /*\r
5485        * refresh the tags dialog only if it's visible\r
5486        */\r
5487       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5488           char *tags;\r
5489           tags = PGNTags(&gameInfo);\r
5490           TagsPopUp(tags, CmailMsg());\r
5491           free(tags);\r
5492       }\r
5493       SAY("computer starts playing white");\r
5494       break;\r
5495 \r
5496     case IDM_MachineBlack:\r
5497       MachineBlackEvent();\r
5498       /*\r
5499        * refresh the tags dialog only if it's visible\r
5500        */\r
5501       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5502           char *tags;\r
5503           tags = PGNTags(&gameInfo);\r
5504           TagsPopUp(tags, CmailMsg());\r
5505           free(tags);\r
5506       }\r
5507       SAY("computer starts playing black");\r
5508       break;\r
5509 \r
5510     case IDM_TwoMachines:\r
5511       TwoMachinesEvent();\r
5512       /*\r
5513        * refresh the tags dialog only if it's visible\r
5514        */\r
5515       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5516           char *tags;\r
5517           tags = PGNTags(&gameInfo);\r
5518           TagsPopUp(tags, CmailMsg());\r
5519           free(tags);\r
5520       }\r
5521       SAY("programs start playing each other");\r
5522       break;\r
5523 \r
5524     case IDM_AnalysisMode:\r
5525       if (!first.analysisSupport) {\r
5526         sprintf(buf, "%s does not support analysis", first.tidy);\r
5527         DisplayError(buf, 0);\r
5528       } else {\r
5529         SAY("analyzing current position");\r
5530         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5531         if (appData.icsActive) {\r
5532                if (gameMode != IcsObserving) {\r
5533                        sprintf(buf, "You are not observing a game");\r
5534                        DisplayError(buf, 0);\r
5535                        /* secure check */\r
5536                        if (appData.icsEngineAnalyze) {\r
5537                                if (appData.debugMode) \r
5538                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5539                                ExitAnalyzeMode();\r
5540                                ModeHighlight();\r
5541                                break;\r
5542                        }\r
5543                        break;\r
5544                } else {\r
5545                        /* if enable, user want disable icsEngineAnalyze */\r
5546                        if (appData.icsEngineAnalyze) {\r
5547                                ExitAnalyzeMode();\r
5548                                ModeHighlight();\r
5549                                break;\r
5550                        }\r
5551                        appData.icsEngineAnalyze = TRUE;\r
5552                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5553                }\r
5554         } \r
5555         if (!appData.showThinking) ToggleShowThinking();\r
5556         AnalyzeModeEvent();\r
5557       }\r
5558       break;\r
5559 \r
5560     case IDM_AnalyzeFile:\r
5561       if (!first.analysisSupport) {\r
5562         char buf[MSG_SIZ];\r
5563         sprintf(buf, "%s does not support analysis", first.tidy);\r
5564         DisplayError(buf, 0);\r
5565       } else {\r
5566         if (!appData.showThinking) ToggleShowThinking();\r
5567         AnalyzeFileEvent();\r
5568         LoadGameDialog(hwnd, "Analyze Game from File");\r
5569         AnalysisPeriodicEvent(1);\r
5570       }\r
5571       break;\r
5572 \r
5573     case IDM_IcsClient:\r
5574       IcsClientEvent();\r
5575       break;\r
5576 \r
5577     case IDM_EditGame:\r
5578       EditGameEvent();\r
5579       SAY("edit game");\r
5580       break;\r
5581 \r
5582     case IDM_EditPosition:\r
5583       EditPositionEvent();\r
5584       SAY("to set up a position type a FEN");\r
5585       break;\r
5586 \r
5587     case IDM_Training:\r
5588       TrainingEvent();\r
5589       break;\r
5590 \r
5591     case IDM_ShowGameList:\r
5592       ShowGameListProc();\r
5593       break;\r
5594 \r
5595     case IDM_EditTags:\r
5596       EditTagsProc();\r
5597       break;\r
5598 \r
5599     case IDM_EditComment:\r
5600       if (commentUp && editComment) {\r
5601         CommentPopDown();\r
5602       } else {\r
5603         EditCommentEvent();\r
5604       }\r
5605       break;\r
5606 \r
5607     case IDM_Pause:\r
5608       PauseEvent();\r
5609       break;\r
5610 \r
5611     case IDM_Accept:\r
5612       AcceptEvent();\r
5613       break;\r
5614 \r
5615     case IDM_Decline:\r
5616       DeclineEvent();\r
5617       break;\r
5618 \r
5619     case IDM_Rematch:\r
5620       RematchEvent();\r
5621       break;\r
5622 \r
5623     case IDM_CallFlag:\r
5624       CallFlagEvent();\r
5625       break;\r
5626 \r
5627     case IDM_Draw:\r
5628       DrawEvent();\r
5629       break;\r
5630 \r
5631     case IDM_Adjourn:\r
5632       AdjournEvent();\r
5633       break;\r
5634 \r
5635     case IDM_Abort:\r
5636       AbortEvent();\r
5637       break;\r
5638 \r
5639     case IDM_Resign:\r
5640       ResignEvent();\r
5641       break;\r
5642 \r
5643     case IDM_StopObserving:\r
5644       StopObservingEvent();\r
5645       break;\r
5646 \r
5647     case IDM_StopExamining:\r
5648       StopExaminingEvent();\r
5649       break;\r
5650 \r
5651     case IDM_TypeInMove:\r
5652       PopUpMoveDialog('\000');\r
5653       break;\r
5654 \r
5655     case IDM_TypeInName:\r
5656       PopUpNameDialog('\000');\r
5657       break;\r
5658 \r
5659     case IDM_Backward:\r
5660       BackwardEvent();\r
5661       SetFocus(hwndMain);\r
5662       break;\r
5663 \r
5664     JAWS_MENU_ITEMS\r
5665 \r
5666     case IDM_Forward:\r
5667       ForwardEvent();\r
5668       SetFocus(hwndMain);\r
5669       break;\r
5670 \r
5671     case IDM_ToStart:\r
5672       ToStartEvent();\r
5673       SetFocus(hwndMain);\r
5674       break;\r
5675 \r
5676     case IDM_ToEnd:\r
5677       ToEndEvent();\r
5678       SetFocus(hwndMain);\r
5679       break;\r
5680 \r
5681     case IDM_Revert:\r
5682       RevertEvent();\r
5683       break;\r
5684 \r
5685     case IDM_TruncateGame:\r
5686       TruncateGameEvent();\r
5687       break;\r
5688 \r
5689     case IDM_MoveNow:\r
5690       MoveNowEvent();\r
5691       break;\r
5692 \r
5693     case IDM_RetractMove:\r
5694       RetractMoveEvent();\r
5695       break;\r
5696 \r
5697     case IDM_FlipView:\r
5698       flipView = !flipView;\r
5699       DrawPosition(FALSE, NULL);\r
5700       break;\r
5701 \r
5702     case IDM_FlipClock:\r
5703       flipClock = !flipClock;\r
5704       DisplayBothClocks();\r
5705       DrawPosition(FALSE, NULL);\r
5706       break;\r
5707 \r
5708     case IDM_MuteSounds:\r
5709       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5710       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5711                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5712       break;\r
5713 \r
5714     case IDM_GeneralOptions:\r
5715       GeneralOptionsPopup(hwnd);\r
5716       DrawPosition(TRUE, NULL);\r
5717       break;\r
5718 \r
5719     case IDM_BoardOptions:\r
5720       BoardOptionsPopup(hwnd);\r
5721       break;\r
5722 \r
5723     case IDM_EnginePlayOptions:\r
5724       EnginePlayOptionsPopup(hwnd);\r
5725       break;\r
5726 \r
5727     case IDM_Engine1Options:\r
5728       EngineOptionsPopup(hwnd, &first);\r
5729       break;\r
5730 \r
5731     case IDM_Engine2Options:\r
5732       EngineOptionsPopup(hwnd, &second);\r
5733       break;\r
5734 \r
5735     case IDM_OptionsUCI:\r
5736       UciOptionsPopup(hwnd);\r
5737       break;\r
5738 \r
5739     case IDM_IcsOptions:\r
5740       IcsOptionsPopup(hwnd);\r
5741       break;\r
5742 \r
5743     case IDM_Fonts:\r
5744       FontsOptionsPopup(hwnd);\r
5745       break;\r
5746 \r
5747     case IDM_Sounds:\r
5748       SoundOptionsPopup(hwnd);\r
5749       break;\r
5750 \r
5751     case IDM_CommPort:\r
5752       CommPortOptionsPopup(hwnd);\r
5753       break;\r
5754 \r
5755     case IDM_LoadOptions:\r
5756       LoadOptionsPopup(hwnd);\r
5757       break;\r
5758 \r
5759     case IDM_SaveOptions:\r
5760       SaveOptionsPopup(hwnd);\r
5761       break;\r
5762 \r
5763     case IDM_TimeControl:\r
5764       TimeControlOptionsPopup(hwnd);\r
5765       break;\r
5766 \r
5767     case IDM_SaveSettings:\r
5768       SaveSettings(settingsFileName);\r
5769       break;\r
5770 \r
5771     case IDM_SaveSettingsOnExit:\r
5772       saveSettingsOnExit = !saveSettingsOnExit;\r
5773       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5774                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5775                                          MF_CHECKED : MF_UNCHECKED));\r
5776       break;\r
5777 \r
5778     case IDM_Hint:\r
5779       HintEvent();\r
5780       break;\r
5781 \r
5782     case IDM_Book:\r
5783       BookEvent();\r
5784       break;\r
5785 \r
5786     case IDM_AboutGame:\r
5787       AboutGameEvent();\r
5788       break;\r
5789 \r
5790     case IDM_Debug:\r
5791       appData.debugMode = !appData.debugMode;\r
5792       if (appData.debugMode) {\r
5793         char dir[MSG_SIZ];\r
5794         GetCurrentDirectory(MSG_SIZ, dir);\r
5795         SetCurrentDirectory(installDir);\r
5796         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5797         SetCurrentDirectory(dir);\r
5798         setbuf(debugFP, NULL);\r
5799       } else {\r
5800         fclose(debugFP);\r
5801         debugFP = NULL;\r
5802       }\r
5803       break;\r
5804 \r
5805     case IDM_HELPCONTENTS:\r
5806       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5807           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5808           MessageBox (GetFocus(),\r
5809                     "Unable to activate help",\r
5810                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5811       }\r
5812       break;\r
5813 \r
5814     case IDM_HELPSEARCH:\r
5815         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5816             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5817         MessageBox (GetFocus(),\r
5818                     "Unable to activate help",\r
5819                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5820       }\r
5821       break;\r
5822 \r
5823     case IDM_HELPHELP:\r
5824       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5825         MessageBox (GetFocus(),\r
5826                     "Unable to activate help",\r
5827                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5828       }\r
5829       break;\r
5830 \r
5831     case IDM_ABOUT:\r
5832       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5833       DialogBox(hInst, \r
5834         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5835         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5836       FreeProcInstance(lpProc);\r
5837       break;\r
5838 \r
5839     case IDM_DirectCommand1:\r
5840       AskQuestionEvent("Direct Command",\r
5841                        "Send to chess program:", "", "1");\r
5842       break;\r
5843     case IDM_DirectCommand2:\r
5844       AskQuestionEvent("Direct Command",\r
5845                        "Send to second chess program:", "", "2");\r
5846       break;\r
5847 \r
5848     case EP_WhitePawn:\r
5849       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5850       fromX = fromY = -1;\r
5851       break;\r
5852 \r
5853     case EP_WhiteKnight:\r
5854       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5855       fromX = fromY = -1;\r
5856       break;\r
5857 \r
5858     case EP_WhiteBishop:\r
5859       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5860       fromX = fromY = -1;\r
5861       break;\r
5862 \r
5863     case EP_WhiteRook:\r
5864       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5865       fromX = fromY = -1;\r
5866       break;\r
5867 \r
5868     case EP_WhiteQueen:\r
5869       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5870       fromX = fromY = -1;\r
5871       break;\r
5872 \r
5873     case EP_WhiteFerz:\r
5874       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5875       fromX = fromY = -1;\r
5876       break;\r
5877 \r
5878     case EP_WhiteWazir:\r
5879       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5880       fromX = fromY = -1;\r
5881       break;\r
5882 \r
5883     case EP_WhiteAlfil:\r
5884       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5885       fromX = fromY = -1;\r
5886       break;\r
5887 \r
5888     case EP_WhiteCannon:\r
5889       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5890       fromX = fromY = -1;\r
5891       break;\r
5892 \r
5893     case EP_WhiteCardinal:\r
5894       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5895       fromX = fromY = -1;\r
5896       break;\r
5897 \r
5898     case EP_WhiteMarshall:\r
5899       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5900       fromX = fromY = -1;\r
5901       break;\r
5902 \r
5903     case EP_WhiteKing:\r
5904       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5905       fromX = fromY = -1;\r
5906       break;\r
5907 \r
5908     case EP_BlackPawn:\r
5909       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5910       fromX = fromY = -1;\r
5911       break;\r
5912 \r
5913     case EP_BlackKnight:\r
5914       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5915       fromX = fromY = -1;\r
5916       break;\r
5917 \r
5918     case EP_BlackBishop:\r
5919       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5920       fromX = fromY = -1;\r
5921       break;\r
5922 \r
5923     case EP_BlackRook:\r
5924       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5925       fromX = fromY = -1;\r
5926       break;\r
5927 \r
5928     case EP_BlackQueen:\r
5929       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5930       fromX = fromY = -1;\r
5931       break;\r
5932 \r
5933     case EP_BlackFerz:\r
5934       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5935       fromX = fromY = -1;\r
5936       break;\r
5937 \r
5938     case EP_BlackWazir:\r
5939       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5940       fromX = fromY = -1;\r
5941       break;\r
5942 \r
5943     case EP_BlackAlfil:\r
5944       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5945       fromX = fromY = -1;\r
5946       break;\r
5947 \r
5948     case EP_BlackCannon:\r
5949       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5950       fromX = fromY = -1;\r
5951       break;\r
5952 \r
5953     case EP_BlackCardinal:\r
5954       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5955       fromX = fromY = -1;\r
5956       break;\r
5957 \r
5958     case EP_BlackMarshall:\r
5959       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5960       fromX = fromY = -1;\r
5961       break;\r
5962 \r
5963     case EP_BlackKing:\r
5964       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5965       fromX = fromY = -1;\r
5966       break;\r
5967 \r
5968     case EP_EmptySquare:\r
5969       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5970       fromX = fromY = -1;\r
5971       break;\r
5972 \r
5973     case EP_ClearBoard:\r
5974       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5975       fromX = fromY = -1;\r
5976       break;\r
5977 \r
5978     case EP_White:\r
5979       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
5980       fromX = fromY = -1;\r
5981       break;\r
5982 \r
5983     case EP_Black:\r
5984       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
5985       fromX = fromY = -1;\r
5986       break;\r
5987 \r
5988     case EP_Promote:\r
5989       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
5990       fromX = fromY = -1;\r
5991       break;\r
5992 \r
5993     case EP_Demote:\r
5994       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
5995       fromX = fromY = -1;\r
5996       break;\r
5997 \r
5998     case DP_Pawn:\r
5999       DropMenuEvent(WhitePawn, fromX, fromY);\r
6000       fromX = fromY = -1;\r
6001       break;\r
6002 \r
6003     case DP_Knight:\r
6004       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6005       fromX = fromY = -1;\r
6006       break;\r
6007 \r
6008     case DP_Bishop:\r
6009       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6010       fromX = fromY = -1;\r
6011       break;\r
6012 \r
6013     case DP_Rook:\r
6014       DropMenuEvent(WhiteRook, fromX, fromY);\r
6015       fromX = fromY = -1;\r
6016       break;\r
6017 \r
6018     case DP_Queen:\r
6019       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6020       fromX = fromY = -1;\r
6021       break;\r
6022 \r
6023     default:\r
6024       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6025     }\r
6026     break;\r
6027 \r
6028   case WM_TIMER:\r
6029     switch (wParam) {\r
6030     case CLOCK_TIMER_ID:\r
6031       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6032       clockTimerEvent = 0;\r
6033       DecrementClocks(); /* call into back end */\r
6034       break;\r
6035     case LOAD_GAME_TIMER_ID:\r
6036       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6037       loadGameTimerEvent = 0;\r
6038       AutoPlayGameLoop(); /* call into back end */\r
6039       break;\r
6040     case ANALYSIS_TIMER_ID:\r
6041       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6042                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6043         AnalysisPeriodicEvent(0);\r
6044       } else {\r
6045         KillTimer(hwnd, analysisTimerEvent);\r
6046         analysisTimerEvent = 0;\r
6047       }\r
6048       break;\r
6049     case DELAYED_TIMER_ID:\r
6050       KillTimer(hwnd, delayedTimerEvent);\r
6051       delayedTimerEvent = 0;\r
6052       delayedTimerCallback();\r
6053       break;\r
6054     }\r
6055     break;\r
6056 \r
6057   case WM_USER_Input:\r
6058     InputEvent(hwnd, message, wParam, lParam);\r
6059     break;\r
6060 \r
6061   /* [AS] Also move "attached" child windows */\r
6062   case WM_WINDOWPOSCHANGING:\r
6063 \r
6064     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6065         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6066 \r
6067         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6068             /* Window is moving */\r
6069             RECT rcMain;\r
6070 \r
6071 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6072             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
6073             rcMain.right  = wpMain.x + wpMain.width;\r
6074             rcMain.top    = wpMain.y;\r
6075             rcMain.bottom = wpMain.y + wpMain.height;\r
6076             \r
6077             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6078             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6079             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6080             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6081             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6082             wpMain.x = lpwp->x;\r
6083             wpMain.y = lpwp->y;\r
6084         }\r
6085     }\r
6086     break;\r
6087 \r
6088   /* [AS] Snapping */\r
6089   case WM_ENTERSIZEMOVE:\r
6090     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6091     if (hwnd == hwndMain) {\r
6092       doingSizing = TRUE;\r
6093       lastSizing = 0;\r
6094     }\r
6095     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6096     break;\r
6097 \r
6098   case WM_SIZING:\r
6099     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6100     if (hwnd == hwndMain) {\r
6101       lastSizing = wParam;\r
6102     }\r
6103     break;\r
6104 \r
6105   case WM_MOVING:\r
6106     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6107       return OnMoving( &sd, hwnd, wParam, lParam );\r
6108 \r
6109   case WM_EXITSIZEMOVE:\r
6110     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6111     if (hwnd == hwndMain) {\r
6112       RECT client;\r
6113       doingSizing = FALSE;\r
6114       InvalidateRect(hwnd, &boardRect, FALSE);\r
6115       GetClientRect(hwnd, &client);\r
6116       ResizeBoard(client.right, client.bottom, lastSizing);\r
6117       lastSizing = 0;\r
6118       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6119     }\r
6120     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6121     break;\r
6122 \r
6123   case WM_DESTROY: /* message: window being destroyed */\r
6124     PostQuitMessage(0);\r
6125     break;\r
6126 \r
6127   case WM_CLOSE:\r
6128     if (hwnd == hwndMain) {\r
6129       ExitEvent(0);\r
6130     }\r
6131     break;\r
6132 \r
6133   default:      /* Passes it on if unprocessed */\r
6134     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6135   }\r
6136   return 0;\r
6137 }\r
6138 \r
6139 /*---------------------------------------------------------------------------*\\r
6140  *\r
6141  * Misc utility routines\r
6142  *\r
6143 \*---------------------------------------------------------------------------*/\r
6144 \r
6145 /*\r
6146  * Decent random number generator, at least not as bad as Windows\r
6147  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6148  */\r
6149 unsigned int randstate;\r
6150 \r
6151 int\r
6152 myrandom(void)\r
6153 {\r
6154   randstate = randstate * 1664525 + 1013904223;\r
6155   return (int) randstate & 0x7fffffff;\r
6156 }\r
6157 \r
6158 void\r
6159 mysrandom(unsigned int seed)\r
6160 {\r
6161   randstate = seed;\r
6162 }\r
6163 \r
6164 \r
6165 /* \r
6166  * returns TRUE if user selects a different color, FALSE otherwise \r
6167  */\r
6168 \r
6169 BOOL\r
6170 ChangeColor(HWND hwnd, COLORREF *which)\r
6171 {\r
6172   static BOOL firstTime = TRUE;\r
6173   static DWORD customColors[16];\r
6174   CHOOSECOLOR cc;\r
6175   COLORREF newcolor;\r
6176   int i;\r
6177   ColorClass ccl;\r
6178 \r
6179   if (firstTime) {\r
6180     /* Make initial colors in use available as custom colors */\r
6181     /* Should we put the compiled-in defaults here instead? */\r
6182     i = 0;\r
6183     customColors[i++] = lightSquareColor & 0xffffff;\r
6184     customColors[i++] = darkSquareColor & 0xffffff;\r
6185     customColors[i++] = whitePieceColor & 0xffffff;\r
6186     customColors[i++] = blackPieceColor & 0xffffff;\r
6187     customColors[i++] = highlightSquareColor & 0xffffff;\r
6188     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6189 \r
6190     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6191       customColors[i++] = textAttribs[ccl].color;\r
6192     }\r
6193     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6194     firstTime = FALSE;\r
6195   }\r
6196 \r
6197   cc.lStructSize = sizeof(cc);\r
6198   cc.hwndOwner = hwnd;\r
6199   cc.hInstance = NULL;\r
6200   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6201   cc.lpCustColors = (LPDWORD) customColors;\r
6202   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6203 \r
6204   if (!ChooseColor(&cc)) return FALSE;\r
6205 \r
6206   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6207   if (newcolor == *which) return FALSE;\r
6208   *which = newcolor;\r
6209   return TRUE;\r
6210 \r
6211   /*\r
6212   InitDrawingColors();\r
6213   InvalidateRect(hwnd, &boardRect, FALSE);\r
6214   */\r
6215 }\r
6216 \r
6217 BOOLEAN\r
6218 MyLoadSound(MySound *ms)\r
6219 {\r
6220   BOOL ok = FALSE;\r
6221   struct stat st;\r
6222   FILE *f;\r
6223 \r
6224   if (ms->data) free(ms->data);\r
6225   ms->data = NULL;\r
6226 \r
6227   switch (ms->name[0]) {\r
6228   case NULLCHAR:\r
6229     /* Silence */\r
6230     ok = TRUE;\r
6231     break;\r
6232   case '$':\r
6233     /* System sound from Control Panel.  Don't preload here. */\r
6234     ok = TRUE;\r
6235     break;\r
6236   case '!':\r
6237     if (ms->name[1] == NULLCHAR) {\r
6238       /* "!" alone = silence */\r
6239       ok = TRUE;\r
6240     } else {\r
6241       /* Builtin wave resource.  Error if not found. */\r
6242       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6243       if (h == NULL) break;\r
6244       ms->data = (void *)LoadResource(hInst, h);\r
6245       if (h == NULL) break;\r
6246       ok = TRUE;\r
6247     }\r
6248     break;\r
6249   default:\r
6250     /* .wav file.  Error if not found. */\r
6251     f = fopen(ms->name, "rb");\r
6252     if (f == NULL) break;\r
6253     if (fstat(fileno(f), &st) < 0) break;\r
6254     ms->data = malloc(st.st_size);\r
6255     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6256     fclose(f);\r
6257     ok = TRUE;\r
6258     break;\r
6259   }\r
6260   if (!ok) {\r
6261     char buf[MSG_SIZ];\r
6262     sprintf(buf, "Error loading sound %s", ms->name);\r
6263     DisplayError(buf, GetLastError());\r
6264   }\r
6265   return ok;\r
6266 }\r
6267 \r
6268 BOOLEAN\r
6269 MyPlaySound(MySound *ms)\r
6270 {\r
6271   BOOLEAN ok = FALSE;\r
6272 \r
6273   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6274   switch (ms->name[0]) {\r
6275   case NULLCHAR:\r
6276         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6277     /* Silence */\r
6278     ok = TRUE;\r
6279     break;\r
6280   case '$':\r
6281     /* System sound from Control Panel (deprecated feature).\r
6282        "$" alone or an unset sound name gets default beep (still in use). */\r
6283     if (ms->name[1]) {\r
6284       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6285     }\r
6286     if (!ok) ok = MessageBeep(MB_OK);\r
6287     break; \r
6288   case '!':\r
6289     /* Builtin wave resource, or "!" alone for silence */\r
6290     if (ms->name[1]) {\r
6291       if (ms->data == NULL) return FALSE;\r
6292       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6293     } else {\r
6294       ok = TRUE;\r
6295     }\r
6296     break;\r
6297   default:\r
6298     /* .wav file.  Error if not found. */\r
6299     if (ms->data == NULL) return FALSE;\r
6300     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6301     break;\r
6302   }\r
6303   /* Don't print an error: this can happen innocently if the sound driver\r
6304      is busy; for instance, if another instance of WinBoard is playing\r
6305      a sound at about the same time. */\r
6306   return ok;\r
6307 }\r
6308 \r
6309 \r
6310 LRESULT CALLBACK\r
6311 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6312 {\r
6313   BOOL ok;\r
6314   OPENFILENAME *ofn;\r
6315   static UINT *number; /* gross that this is static */\r
6316 \r
6317   switch (message) {\r
6318   case WM_INITDIALOG: /* message: initialize dialog box */\r
6319     /* Center the dialog over the application window */\r
6320     ofn = (OPENFILENAME *) lParam;\r
6321     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6322       number = (UINT *) ofn->lCustData;\r
6323       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6324     } else {\r
6325       number = NULL;\r
6326     }\r
6327     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6328     return FALSE;  /* Allow for further processing */\r
6329 \r
6330   case WM_COMMAND:\r
6331     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6332       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6333     }\r
6334     return FALSE;  /* Allow for further processing */\r
6335   }\r
6336   return FALSE;\r
6337 }\r
6338 \r
6339 UINT APIENTRY\r
6340 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6341 {\r
6342   static UINT *number;\r
6343   OPENFILENAME *ofname;\r
6344   OFNOTIFY *ofnot;\r
6345   switch (uiMsg) {\r
6346   case WM_INITDIALOG:\r
6347     ofname = (OPENFILENAME *)lParam;\r
6348     number = (UINT *)(ofname->lCustData);\r
6349     break;\r
6350   case WM_NOTIFY:\r
6351     ofnot = (OFNOTIFY *)lParam;\r
6352     if (ofnot->hdr.code == CDN_FILEOK) {\r
6353       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6354     }\r
6355     break;\r
6356   }\r
6357   return 0;\r
6358 }\r
6359 \r
6360 \r
6361 FILE *\r
6362 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6363                char *nameFilt, char *dlgTitle, UINT *number,\r
6364                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6365 {\r
6366   OPENFILENAME openFileName;\r
6367   char buf1[MSG_SIZ];\r
6368   FILE *f;\r
6369 \r
6370   if (fileName == NULL) fileName = buf1;\r
6371   if (defName == NULL) {\r
6372     strcpy(fileName, "*.");\r
6373     strcat(fileName, defExt);\r
6374   } else {\r
6375     strcpy(fileName, defName);\r
6376   }\r
6377   if (fileTitle) strcpy(fileTitle, "");\r
6378   if (number) *number = 0;\r
6379 \r
6380   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6381   openFileName.hwndOwner         = hwnd;\r
6382   openFileName.hInstance         = (HANDLE) hInst;\r
6383   openFileName.lpstrFilter       = nameFilt;\r
6384   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6385   openFileName.nMaxCustFilter    = 0L;\r
6386   openFileName.nFilterIndex      = 1L;\r
6387   openFileName.lpstrFile         = fileName;\r
6388   openFileName.nMaxFile          = MSG_SIZ;\r
6389   openFileName.lpstrFileTitle    = fileTitle;\r
6390   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6391   openFileName.lpstrInitialDir   = NULL;\r
6392   openFileName.lpstrTitle        = dlgTitle;\r
6393   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6394     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6395     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6396     | (oldDialog ? 0 : OFN_EXPLORER);\r
6397   openFileName.nFileOffset       = 0;\r
6398   openFileName.nFileExtension    = 0;\r
6399   openFileName.lpstrDefExt       = defExt;\r
6400   openFileName.lCustData         = (LONG) number;\r
6401   openFileName.lpfnHook          = oldDialog ?\r
6402     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6403   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6404 \r
6405   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6406                         GetOpenFileName(&openFileName)) {\r
6407     /* open the file */\r
6408     f = fopen(openFileName.lpstrFile, write);\r
6409     if (f == NULL) {\r
6410       MessageBox(hwnd, "File open failed", NULL,\r
6411                  MB_OK|MB_ICONEXCLAMATION);\r
6412       return NULL;\r
6413     }\r
6414   } else {\r
6415     int err = CommDlgExtendedError();\r
6416     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6417     return FALSE;\r
6418   }\r
6419   return f;\r
6420 }\r
6421 \r
6422 \r
6423 \r
6424 VOID APIENTRY\r
6425 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6426 {\r
6427   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6428 \r
6429   /*\r
6430    * Get the first pop-up menu in the menu template. This is the\r
6431    * menu that TrackPopupMenu displays.\r
6432    */\r
6433   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6434 \r
6435   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6436 \r
6437   /*\r
6438    * TrackPopup uses screen coordinates, so convert the\r
6439    * coordinates of the mouse click to screen coordinates.\r
6440    */\r
6441   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6442 \r
6443   /* Draw and track the floating pop-up menu. */\r
6444   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6445                  pt.x, pt.y, 0, hwnd, NULL);\r
6446 \r
6447   /* Destroy the menu.*/\r
6448   DestroyMenu(hmenu);\r
6449 }\r
6450    \r
6451 typedef struct {\r
6452   HWND hDlg, hText;\r
6453   int sizeX, sizeY, newSizeX, newSizeY;\r
6454   HDWP hdwp;\r
6455 } ResizeEditPlusButtonsClosure;\r
6456 \r
6457 BOOL CALLBACK\r
6458 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6459 {\r
6460   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6461   RECT rect;\r
6462   POINT pt;\r
6463 \r
6464   if (hChild == cl->hText) return TRUE;\r
6465   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6466   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6467   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6468   ScreenToClient(cl->hDlg, &pt);\r
6469   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6470     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6471   return TRUE;\r
6472 }\r
6473 \r
6474 /* Resize a dialog that has a (rich) edit field filling most of\r
6475    the top, with a row of buttons below */\r
6476 VOID\r
6477 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6478 {\r
6479   RECT rectText;\r
6480   int newTextHeight, newTextWidth;\r
6481   ResizeEditPlusButtonsClosure cl;\r
6482   \r
6483   /*if (IsIconic(hDlg)) return;*/\r
6484   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6485   \r
6486   cl.hdwp = BeginDeferWindowPos(8);\r
6487 \r
6488   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6489   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6490   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6491   if (newTextHeight < 0) {\r
6492     newSizeY += -newTextHeight;\r
6493     newTextHeight = 0;\r
6494   }\r
6495   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6496     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6497 \r
6498   cl.hDlg = hDlg;\r
6499   cl.hText = hText;\r
6500   cl.sizeX = sizeX;\r
6501   cl.sizeY = sizeY;\r
6502   cl.newSizeX = newSizeX;\r
6503   cl.newSizeY = newSizeY;\r
6504   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6505 \r
6506   EndDeferWindowPos(cl.hdwp);\r
6507 }\r
6508 \r
6509 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6510 {\r
6511     RECT    rChild, rParent;\r
6512     int     wChild, hChild, wParent, hParent;\r
6513     int     wScreen, hScreen, xNew, yNew;\r
6514     HDC     hdc;\r
6515 \r
6516     /* Get the Height and Width of the child window */\r
6517     GetWindowRect (hwndChild, &rChild);\r
6518     wChild = rChild.right - rChild.left;\r
6519     hChild = rChild.bottom - rChild.top;\r
6520 \r
6521     /* Get the Height and Width of the parent window */\r
6522     GetWindowRect (hwndParent, &rParent);\r
6523     wParent = rParent.right - rParent.left;\r
6524     hParent = rParent.bottom - rParent.top;\r
6525 \r
6526     /* Get the display limits */\r
6527     hdc = GetDC (hwndChild);\r
6528     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6529     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6530     ReleaseDC(hwndChild, hdc);\r
6531 \r
6532     /* Calculate new X position, then adjust for screen */\r
6533     xNew = rParent.left + ((wParent - wChild) /2);\r
6534     if (xNew < 0) {\r
6535         xNew = 0;\r
6536     } else if ((xNew+wChild) > wScreen) {\r
6537         xNew = wScreen - wChild;\r
6538     }\r
6539 \r
6540     /* Calculate new Y position, then adjust for screen */\r
6541     if( mode == 0 ) {\r
6542         yNew = rParent.top  + ((hParent - hChild) /2);\r
6543     }\r
6544     else {\r
6545         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6546     }\r
6547 \r
6548     if (yNew < 0) {\r
6549         yNew = 0;\r
6550     } else if ((yNew+hChild) > hScreen) {\r
6551         yNew = hScreen - hChild;\r
6552     }\r
6553 \r
6554     /* Set it, and return */\r
6555     return SetWindowPos (hwndChild, NULL,\r
6556                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6557 }\r
6558 \r
6559 /* Center one window over another */\r
6560 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6561 {\r
6562     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6563 }\r
6564 \r
6565 /*---------------------------------------------------------------------------*\\r
6566  *\r
6567  * Startup Dialog functions\r
6568  *\r
6569 \*---------------------------------------------------------------------------*/\r
6570 void\r
6571 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6572 {\r
6573   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6574 \r
6575   while (*cd != NULL) {\r
6576     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6577     cd++;\r
6578   }\r
6579 }\r
6580 \r
6581 void\r
6582 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6583 {\r
6584   char buf1[ARG_MAX];\r
6585   int len;\r
6586 \r
6587   if (str[0] == '@') {\r
6588     FILE* f = fopen(str + 1, "r");\r
6589     if (f == NULL) {\r
6590       DisplayFatalError(str + 1, errno, 2);\r
6591       return;\r
6592     }\r
6593     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6594     fclose(f);\r
6595     buf1[len] = NULLCHAR;\r
6596     str = buf1;\r
6597   }\r
6598 \r
6599   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6600 \r
6601   for (;;) {\r
6602     char buf[MSG_SIZ];\r
6603     char *end = strchr(str, '\n');\r
6604     if (end == NULL) return;\r
6605     memcpy(buf, str, end - str);\r
6606     buf[end - str] = NULLCHAR;\r
6607     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6608     str = end + 1;\r
6609   }\r
6610 }\r
6611 \r
6612 void\r
6613 SetStartupDialogEnables(HWND hDlg)\r
6614 {\r
6615   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6616     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6617     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6618   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6619     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6620   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6621     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6622   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6623     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6624   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6625     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6626     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6627     IsDlgButtonChecked(hDlg, OPT_View));\r
6628 }\r
6629 \r
6630 char *\r
6631 QuoteForFilename(char *filename)\r
6632 {\r
6633   int dquote, space;\r
6634   dquote = strchr(filename, '"') != NULL;\r
6635   space = strchr(filename, ' ') != NULL;\r
6636   if (dquote || space) {\r
6637     if (dquote) {\r
6638       return "'";\r
6639     } else {\r
6640       return "\"";\r
6641     }\r
6642   } else {\r
6643     return "";\r
6644   }\r
6645 }\r
6646 \r
6647 VOID\r
6648 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6649 {\r
6650   char buf[MSG_SIZ];\r
6651   char *q;\r
6652 \r
6653   InitComboStringsFromOption(hwndCombo, nthnames);\r
6654   q = QuoteForFilename(nthcp);\r
6655   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6656   if (*nthdir != NULLCHAR) {\r
6657     q = QuoteForFilename(nthdir);\r
6658     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6659   }\r
6660   if (*nthcp == NULLCHAR) {\r
6661     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6662   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6663     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6664     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6665   }\r
6666 }\r
6667 \r
6668 LRESULT CALLBACK\r
6669 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6670 {\r
6671   char buf[MSG_SIZ];\r
6672   HANDLE hwndCombo;\r
6673   char *p;\r
6674 \r
6675   switch (message) {\r
6676   case WM_INITDIALOG:\r
6677     /* Center the dialog */\r
6678     CenterWindow (hDlg, GetDesktopWindow());\r
6679     /* Initialize the dialog items */\r
6680     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6681                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6682                   firstChessProgramNames);\r
6683     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6684                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6685                   secondChessProgramNames);\r
6686     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6687     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6688     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6689     if (*appData.icsHelper != NULLCHAR) {\r
6690       char *q = QuoteForFilename(appData.icsHelper);\r
6691       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6692     }\r
6693     if (*appData.icsHost == NULLCHAR) {\r
6694       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6695       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6696     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6697       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6698       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6699     }\r
6700 \r
6701     if (appData.icsActive) {\r
6702       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6703     }\r
6704     else if (appData.noChessProgram) {\r
6705       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6706     }\r
6707     else {\r
6708       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6709     }\r
6710 \r
6711     SetStartupDialogEnables(hDlg);\r
6712     return TRUE;\r
6713 \r
6714   case WM_COMMAND:\r
6715     switch (LOWORD(wParam)) {\r
6716     case IDOK:\r
6717       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6718         strcpy(buf, "/fcp=");\r
6719         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6720         p = buf;\r
6721         ParseArgs(StringGet, &p);\r
6722         strcpy(buf, "/scp=");\r
6723         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6724         p = buf;\r
6725         ParseArgs(StringGet, &p);\r
6726         appData.noChessProgram = FALSE;\r
6727         appData.icsActive = FALSE;\r
6728       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6729         strcpy(buf, "/ics /icshost=");\r
6730         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6731         p = buf;\r
6732         ParseArgs(StringGet, &p);\r
6733         if (appData.zippyPlay) {\r
6734           strcpy(buf, "/fcp=");\r
6735           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6736           p = buf;\r
6737           ParseArgs(StringGet, &p);\r
6738         }\r
6739       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6740         appData.noChessProgram = TRUE;\r
6741         appData.icsActive = FALSE;\r
6742       } else {\r
6743         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6744                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6745         return TRUE;\r
6746       }\r
6747       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6748         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6749         p = buf;\r
6750         ParseArgs(StringGet, &p);\r
6751       }\r
6752       EndDialog(hDlg, TRUE);\r
6753       return TRUE;\r
6754 \r
6755     case IDCANCEL:\r
6756       ExitEvent(0);\r
6757       return TRUE;\r
6758 \r
6759     case IDM_HELPCONTENTS:\r
6760       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6761         MessageBox (GetFocus(),\r
6762                     "Unable to activate help",\r
6763                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6764       }\r
6765       break;\r
6766 \r
6767     default:\r
6768       SetStartupDialogEnables(hDlg);\r
6769       break;\r
6770     }\r
6771     break;\r
6772   }\r
6773   return FALSE;\r
6774 }\r
6775 \r
6776 /*---------------------------------------------------------------------------*\\r
6777  *\r
6778  * About box dialog functions\r
6779  *\r
6780 \*---------------------------------------------------------------------------*/\r
6781 \r
6782 /* Process messages for "About" dialog box */\r
6783 LRESULT CALLBACK\r
6784 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6785 {\r
6786   switch (message) {\r
6787   case WM_INITDIALOG: /* message: initialize dialog box */\r
6788     /* Center the dialog over the application window */\r
6789     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6790     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6791     JAWS_COPYRIGHT\r
6792     return (TRUE);\r
6793 \r
6794   case WM_COMMAND: /* message: received a command */\r
6795     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6796         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6797       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6798       return (TRUE);\r
6799     }\r
6800     break;\r
6801   }\r
6802   return (FALSE);\r
6803 }\r
6804 \r
6805 /*---------------------------------------------------------------------------*\\r
6806  *\r
6807  * Comment Dialog functions\r
6808  *\r
6809 \*---------------------------------------------------------------------------*/\r
6810 \r
6811 LRESULT CALLBACK\r
6812 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6813 {\r
6814   static HANDLE hwndText = NULL;\r
6815   int len, newSizeX, newSizeY, flags;\r
6816   static int sizeX, sizeY;\r
6817   char *str;\r
6818   RECT rect;\r
6819   MINMAXINFO *mmi;\r
6820 \r
6821   switch (message) {\r
6822   case WM_INITDIALOG: /* message: initialize dialog box */\r
6823     /* Initialize the dialog items */\r
6824     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6825     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6826     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6827     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6828     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6829     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6830     SetWindowText(hDlg, commentTitle);\r
6831     if (editComment) {\r
6832       SetFocus(hwndText);\r
6833     } else {\r
6834       SetFocus(GetDlgItem(hDlg, IDOK));\r
6835     }\r
6836     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6837                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6838                 MAKELPARAM(FALSE, 0));\r
6839     /* Size and position the dialog */\r
6840     if (!commentDialog) {\r
6841       commentDialog = hDlg;\r
6842       flags = SWP_NOZORDER;\r
6843       GetClientRect(hDlg, &rect);\r
6844       sizeX = rect.right;\r
6845       sizeY = rect.bottom;\r
6846       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6847           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6848         WINDOWPLACEMENT wp;\r
6849         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6850         wp.length = sizeof(WINDOWPLACEMENT);\r
6851         wp.flags = 0;\r
6852         wp.showCmd = SW_SHOW;\r
6853         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6854         wp.rcNormalPosition.left = wpComment.x;\r
6855         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6856         wp.rcNormalPosition.top = wpComment.y;\r
6857         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6858         SetWindowPlacement(hDlg, &wp);\r
6859 \r
6860         GetClientRect(hDlg, &rect);\r
6861         newSizeX = rect.right;\r
6862         newSizeY = rect.bottom;\r
6863         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6864                               newSizeX, newSizeY);\r
6865         sizeX = newSizeX;\r
6866         sizeY = newSizeY;\r
6867       }\r
6868     }\r
6869     return FALSE;\r
6870 \r
6871   case WM_COMMAND: /* message: received a command */\r
6872     switch (LOWORD(wParam)) {\r
6873     case IDOK:\r
6874       if (editComment) {\r
6875         char *p, *q;\r
6876         /* Read changed options from the dialog box */\r
6877         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6878         len = GetWindowTextLength(hwndText);\r
6879         str = (char *) malloc(len + 1);\r
6880         GetWindowText(hwndText, str, len + 1);\r
6881         p = q = str;\r
6882         while (*q) {\r
6883           if (*q == '\r')\r
6884             q++;\r
6885           else\r
6886             *p++ = *q++;\r
6887         }\r
6888         *p = NULLCHAR;\r
6889         ReplaceComment(commentIndex, str);\r
6890         free(str);\r
6891       }\r
6892       CommentPopDown();\r
6893       return TRUE;\r
6894 \r
6895     case IDCANCEL:\r
6896     case OPT_CancelComment:\r
6897       CommentPopDown();\r
6898       return TRUE;\r
6899 \r
6900     case OPT_ClearComment:\r
6901       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6902       break;\r
6903 \r
6904     case OPT_EditComment:\r
6905       EditCommentEvent();\r
6906       return TRUE;\r
6907 \r
6908     default:\r
6909       break;\r
6910     }\r
6911     break;\r
6912 \r
6913   case WM_SIZE:\r
6914     newSizeX = LOWORD(lParam);\r
6915     newSizeY = HIWORD(lParam);\r
6916     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6917     sizeX = newSizeX;\r
6918     sizeY = newSizeY;\r
6919     break;\r
6920 \r
6921   case WM_GETMINMAXINFO:\r
6922     /* Prevent resizing window too small */\r
6923     mmi = (MINMAXINFO *) lParam;\r
6924     mmi->ptMinTrackSize.x = 100;\r
6925     mmi->ptMinTrackSize.y = 100;\r
6926     break;\r
6927   }\r
6928   return FALSE;\r
6929 }\r
6930 \r
6931 VOID\r
6932 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6933 {\r
6934   FARPROC lpProc;\r
6935   char *p, *q;\r
6936 \r
6937   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6938 \r
6939   if (str == NULL) str = "";\r
6940   p = (char *) malloc(2 * strlen(str) + 2);\r
6941   q = p;\r
6942   while (*str) {\r
6943     if (*str == '\n') *q++ = '\r';\r
6944     *q++ = *str++;\r
6945   }\r
6946   *q = NULLCHAR;\r
6947   if (commentText != NULL) free(commentText);\r
6948 \r
6949   commentIndex = index;\r
6950   commentTitle = title;\r
6951   commentText = p;\r
6952   editComment = edit;\r
6953 \r
6954   if (commentDialog) {\r
6955     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6956     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
6957   } else {\r
6958     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6959     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6960                  hwndMain, (DLGPROC)lpProc);\r
6961     FreeProcInstance(lpProc);\r
6962   }\r
6963   commentUp = TRUE;\r
6964 }\r
6965 \r
6966 \r
6967 /*---------------------------------------------------------------------------*\\r
6968  *\r
6969  * Type-in move dialog functions\r
6970  * \r
6971 \*---------------------------------------------------------------------------*/\r
6972 \r
6973 LRESULT CALLBACK\r
6974 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6975 {\r
6976   char move[MSG_SIZ];\r
6977   HWND hInput;\r
6978   ChessMove moveType;\r
6979   int fromX, fromY, toX, toY;\r
6980   char promoChar;\r
6981 \r
6982   switch (message) {\r
6983   case WM_INITDIALOG:\r
6984     move[0] = (char) lParam;\r
6985     move[1] = NULLCHAR;\r
6986     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
6987     hInput = GetDlgItem(hDlg, OPT_Move);\r
6988     SetWindowText(hInput, move);\r
6989     SetFocus(hInput);\r
6990     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
6991     return FALSE;\r
6992 \r
6993   case WM_COMMAND:\r
6994     switch (LOWORD(wParam)) {\r
6995     case IDOK:\r
6996       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
6997       { int n; Board board;\r
6998         // [HGM] FENedit\r
6999         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7000                 EditPositionPasteFEN(move);\r
7001                 EndDialog(hDlg, TRUE);\r
7002                 return TRUE;\r
7003         }\r
7004         // [HGM] movenum: allow move number to be typed in any mode\r
7005         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7006           ToNrEvent(2*n-1);\r
7007           EndDialog(hDlg, TRUE);\r
7008           return TRUE;\r
7009         }\r
7010       }\r
7011       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7012         gameMode != Training) {\r
7013         DisplayMoveError("Displayed move is not current");\r
7014       } else {\r
7015 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7016         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7017           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7018         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7019         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7020           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7021           if (gameMode != Training)\r
7022               forwardMostMove = currentMove;\r
7023           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7024         } else {\r
7025           DisplayMoveError("Could not parse move");\r
7026         }\r
7027       }\r
7028       EndDialog(hDlg, TRUE);\r
7029       return TRUE;\r
7030     case IDCANCEL:\r
7031       EndDialog(hDlg, FALSE);\r
7032       return TRUE;\r
7033     default:\r
7034       break;\r
7035     }\r
7036     break;\r
7037   }\r
7038   return FALSE;\r
7039 }\r
7040 \r
7041 VOID\r
7042 PopUpMoveDialog(char firstchar)\r
7043 {\r
7044     FARPROC lpProc;\r
7045     \r
7046     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7047         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7048         gameMode == AnalyzeMode || gameMode == EditGame || \r
7049         gameMode == EditPosition || gameMode == IcsExamining ||\r
7050         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7051         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7052                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7053                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7054         gameMode == Training) {\r
7055       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7056       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7057         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7058       FreeProcInstance(lpProc);\r
7059     }\r
7060 }\r
7061 \r
7062 /*---------------------------------------------------------------------------*\\r
7063  *\r
7064  * Type-in name dialog functions\r
7065  * \r
7066 \*---------------------------------------------------------------------------*/\r
7067 \r
7068 LRESULT CALLBACK\r
7069 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7070 {\r
7071   char move[MSG_SIZ];\r
7072   HWND hInput;\r
7073 \r
7074   switch (message) {\r
7075   case WM_INITDIALOG:\r
7076     move[0] = (char) lParam;\r
7077     move[1] = NULLCHAR;\r
7078     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7079     hInput = GetDlgItem(hDlg, OPT_Name);\r
7080     SetWindowText(hInput, move);\r
7081     SetFocus(hInput);\r
7082     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7083     return FALSE;\r
7084 \r
7085   case WM_COMMAND:\r
7086     switch (LOWORD(wParam)) {\r
7087     case IDOK:\r
7088       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7089       appData.userName = strdup(move);\r
7090       SetUserLogo();\r
7091 \r
7092       EndDialog(hDlg, TRUE);\r
7093       return TRUE;\r
7094     case IDCANCEL:\r
7095       EndDialog(hDlg, FALSE);\r
7096       return TRUE;\r
7097     default:\r
7098       break;\r
7099     }\r
7100     break;\r
7101   }\r
7102   return FALSE;\r
7103 }\r
7104 \r
7105 VOID\r
7106 PopUpNameDialog(char firstchar)\r
7107 {\r
7108     FARPROC lpProc;\r
7109     \r
7110       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7111       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7112         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7113       FreeProcInstance(lpProc);\r
7114 }\r
7115 \r
7116 /*---------------------------------------------------------------------------*\\r
7117  *\r
7118  *  Error dialogs\r
7119  * \r
7120 \*---------------------------------------------------------------------------*/\r
7121 \r
7122 /* Nonmodal error box */\r
7123 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7124                              WPARAM wParam, LPARAM lParam);\r
7125 \r
7126 VOID\r
7127 ErrorPopUp(char *title, char *content)\r
7128 {\r
7129   FARPROC lpProc;\r
7130   char *p, *q;\r
7131   BOOLEAN modal = hwndMain == NULL;\r
7132 \r
7133   p = content;\r
7134   q = errorMessage;\r
7135   while (*p) {\r
7136     if (*p == '\n') {\r
7137       if (modal) {\r
7138         *q++ = ' ';\r
7139         p++;\r
7140       } else {\r
7141         *q++ = '\r';\r
7142         *q++ = *p++;\r
7143       }\r
7144     } else {\r
7145       *q++ = *p++;\r
7146     }\r
7147   }\r
7148   *q = NULLCHAR;\r
7149   strncpy(errorTitle, title, sizeof(errorTitle));\r
7150   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7151   \r
7152   if (modal) {\r
7153     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7154   } else {\r
7155     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7156     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7157                  hwndMain, (DLGPROC)lpProc);\r
7158     FreeProcInstance(lpProc);\r
7159   }\r
7160 }\r
7161 \r
7162 VOID\r
7163 ErrorPopDown()\r
7164 {\r
7165   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7166   if (errorDialog == NULL) return;\r
7167   DestroyWindow(errorDialog);\r
7168   errorDialog = NULL;\r
7169 }\r
7170 \r
7171 LRESULT CALLBACK\r
7172 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7173 {\r
7174   HANDLE hwndText;\r
7175   RECT rChild;\r
7176 \r
7177   switch (message) {\r
7178   case WM_INITDIALOG:\r
7179     GetWindowRect(hDlg, &rChild);\r
7180 \r
7181     /*\r
7182     SetWindowPos(hDlg, NULL, rChild.left,\r
7183       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7184       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7185     */\r
7186 \r
7187     /* \r
7188         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7189         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7190         and it doesn't work when you resize the dialog.\r
7191         For now, just give it a default position.\r
7192     */\r
7193     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7194 \r
7195     errorDialog = hDlg;\r
7196     SetWindowText(hDlg, errorTitle);\r
7197     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7198     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7199     return FALSE;\r
7200 \r
7201   case WM_COMMAND:\r
7202     switch (LOWORD(wParam)) {\r
7203     case IDOK:\r
7204     case IDCANCEL:\r
7205       if (errorDialog == hDlg) errorDialog = NULL;\r
7206       DestroyWindow(hDlg);\r
7207       return TRUE;\r
7208 \r
7209     default:\r
7210       break;\r
7211     }\r
7212     break;\r
7213   }\r
7214   return FALSE;\r
7215 }\r
7216 \r
7217 #ifdef GOTHIC\r
7218 HWND gothicDialog = NULL;\r
7219 \r
7220 LRESULT CALLBACK\r
7221 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7222 {\r
7223   HANDLE hwndText;\r
7224   RECT rChild;\r
7225   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7226 \r
7227   switch (message) {\r
7228   case WM_INITDIALOG:\r
7229     GetWindowRect(hDlg, &rChild);\r
7230 \r
7231     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
7232                                                              SWP_NOZORDER);\r
7233 \r
7234     /* \r
7235         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7236         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7237         and it doesn't work when you resize the dialog.\r
7238         For now, just give it a default position.\r
7239     */\r
7240     gothicDialog = hDlg;\r
7241     SetWindowText(hDlg, errorTitle);\r
7242     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7243     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7244     return FALSE;\r
7245 \r
7246   case WM_COMMAND:\r
7247     switch (LOWORD(wParam)) {\r
7248     case IDOK:\r
7249     case IDCANCEL:\r
7250       if (errorDialog == hDlg) errorDialog = NULL;\r
7251       DestroyWindow(hDlg);\r
7252       return TRUE;\r
7253 \r
7254     default:\r
7255       break;\r
7256     }\r
7257     break;\r
7258   }\r
7259   return FALSE;\r
7260 }\r
7261 \r
7262 VOID\r
7263 GothicPopUp(char *title, VariantClass variant)\r
7264 {\r
7265   FARPROC lpProc;\r
7266   static char *lastTitle;\r
7267 \r
7268   strncpy(errorTitle, title, sizeof(errorTitle));\r
7269   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7270 \r
7271   if(lastTitle != title && gothicDialog != NULL) {\r
7272     DestroyWindow(gothicDialog);\r
7273     gothicDialog = NULL;\r
7274   }\r
7275   if(variant != VariantNormal && gothicDialog == NULL) {\r
7276     title = lastTitle;\r
7277     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7278     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7279                  hwndMain, (DLGPROC)lpProc);\r
7280     FreeProcInstance(lpProc);\r
7281   }\r
7282 }\r
7283 #endif\r
7284 \r
7285 /*---------------------------------------------------------------------------*\\r
7286  *\r
7287  *  Ics Interaction console functions\r
7288  *\r
7289 \*---------------------------------------------------------------------------*/\r
7290 \r
7291 #define HISTORY_SIZE 64\r
7292 static char *history[HISTORY_SIZE];\r
7293 int histIn = 0, histP = 0;\r
7294 \r
7295 VOID\r
7296 SaveInHistory(char *cmd)\r
7297 {\r
7298   if (history[histIn] != NULL) {\r
7299     free(history[histIn]);\r
7300     history[histIn] = NULL;\r
7301   }\r
7302   if (*cmd == NULLCHAR) return;\r
7303   history[histIn] = StrSave(cmd);\r
7304   histIn = (histIn + 1) % HISTORY_SIZE;\r
7305   if (history[histIn] != NULL) {\r
7306     free(history[histIn]);\r
7307     history[histIn] = NULL;\r
7308   }\r
7309   histP = histIn;\r
7310 }\r
7311 \r
7312 char *\r
7313 PrevInHistory(char *cmd)\r
7314 {\r
7315   int newhp;\r
7316   if (histP == histIn) {\r
7317     if (history[histIn] != NULL) free(history[histIn]);\r
7318     history[histIn] = StrSave(cmd);\r
7319   }\r
7320   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7321   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7322   histP = newhp;\r
7323   return history[histP];\r
7324 }\r
7325 \r
7326 char *\r
7327 NextInHistory()\r
7328 {\r
7329   if (histP == histIn) return NULL;\r
7330   histP = (histP + 1) % HISTORY_SIZE;\r
7331   return history[histP];\r
7332 }\r
7333 \r
7334 typedef struct {\r
7335   char *item;\r
7336   char *command;\r
7337   BOOLEAN getname;\r
7338   BOOLEAN immediate;\r
7339 } IcsTextMenuEntry;\r
7340 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7341 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7342 \r
7343 void\r
7344 ParseIcsTextMenu(char *icsTextMenuString)\r
7345 {\r
7346 //  int flags = 0;\r
7347   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7348   char *p = icsTextMenuString;\r
7349   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7350     free(e->item);\r
7351     e->item = NULL;\r
7352     if (e->command != NULL) {\r
7353       free(e->command);\r
7354       e->command = NULL;\r
7355     }\r
7356     e++;\r
7357   }\r
7358   e = icsTextMenuEntry;\r
7359   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7360     if (*p == ';' || *p == '\n') {\r
7361       e->item = strdup("-");\r
7362       e->command = NULL;\r
7363       p++;\r
7364     } else if (*p == '-') {\r
7365       e->item = strdup("-");\r
7366       e->command = NULL;\r
7367       p++;\r
7368       if (*p) p++;\r
7369     } else {\r
7370       char *q, *r, *s, *t;\r
7371       char c;\r
7372       q = strchr(p, ',');\r
7373       if (q == NULL) break;\r
7374       *q = NULLCHAR;\r
7375       r = strchr(q + 1, ',');\r
7376       if (r == NULL) break;\r
7377       *r = NULLCHAR;\r
7378       s = strchr(r + 1, ',');\r
7379       if (s == NULL) break;\r
7380       *s = NULLCHAR;\r
7381       c = ';';\r
7382       t = strchr(s + 1, c);\r
7383       if (t == NULL) {\r
7384         c = '\n';\r
7385         t = strchr(s + 1, c);\r
7386       }\r
7387       if (t != NULL) *t = NULLCHAR;\r
7388       e->item = strdup(p);\r
7389       e->command = strdup(q + 1);\r
7390       e->getname = *(r + 1) != '0';\r
7391       e->immediate = *(s + 1) != '0';\r
7392       *q = ',';\r
7393       *r = ',';\r
7394       *s = ',';\r
7395       if (t == NULL) break;\r
7396       *t = c;\r
7397       p = t + 1;\r
7398     }\r
7399     e++;\r
7400   } \r
7401 }\r
7402 \r
7403 HMENU\r
7404 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7405 {\r
7406   HMENU hmenu, h;\r
7407   int i = 0;\r
7408   hmenu = LoadMenu(hInst, "TextMenu");\r
7409   h = GetSubMenu(hmenu, 0);\r
7410   while (e->item) {\r
7411     if (strcmp(e->item, "-") == 0) {\r
7412       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7413     } else {\r
7414       if (e->item[0] == '|') {\r
7415         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7416                    IDM_CommandX + i, &e->item[1]);\r
7417       } else {\r
7418         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7419       }\r
7420     }\r
7421     e++;\r
7422     i++;\r
7423   } \r
7424   return hmenu;\r
7425 }\r
7426 \r
7427 WNDPROC consoleTextWindowProc;\r
7428 \r
7429 void\r
7430 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7431 {\r
7432   char buf[MSG_SIZ], name[MSG_SIZ];\r
7433   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7434   CHARRANGE sel;\r
7435 \r
7436   if (!getname) {\r
7437     SetWindowText(hInput, command);\r
7438     if (immediate) {\r
7439       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7440     } else {\r
7441       sel.cpMin = 999999;\r
7442       sel.cpMax = 999999;\r
7443       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7444       SetFocus(hInput);\r
7445     }\r
7446     return;\r
7447   }    \r
7448   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7449   if (sel.cpMin == sel.cpMax) {\r
7450     /* Expand to surrounding word */\r
7451     TEXTRANGE tr;\r
7452     do {\r
7453       tr.chrg.cpMax = sel.cpMin;\r
7454       tr.chrg.cpMin = --sel.cpMin;\r
7455       if (sel.cpMin < 0) break;\r
7456       tr.lpstrText = name;\r
7457       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7458     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7459     sel.cpMin++;\r
7460 \r
7461     do {\r
7462       tr.chrg.cpMin = sel.cpMax;\r
7463       tr.chrg.cpMax = ++sel.cpMax;\r
7464       tr.lpstrText = name;\r
7465       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7466     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7467     sel.cpMax--;\r
7468 \r
7469     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7470       MessageBeep(MB_ICONEXCLAMATION);\r
7471       return;\r
7472     }\r
7473     tr.chrg = sel;\r
7474     tr.lpstrText = name;\r
7475     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7476   } else {\r
7477     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7478       MessageBeep(MB_ICONEXCLAMATION);\r
7479       return;\r
7480     }\r
7481     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7482   }\r
7483   if (immediate) {\r
7484     sprintf(buf, "%s %s", command, name);\r
7485     SetWindowText(hInput, buf);\r
7486     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7487   } else {\r
7488     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7489     SetWindowText(hInput, buf);\r
7490     sel.cpMin = 999999;\r
7491     sel.cpMax = 999999;\r
7492     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7493     SetFocus(hInput);\r
7494   }\r
7495 }\r
7496 \r
7497 LRESULT CALLBACK \r
7498 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7499 {\r
7500   HWND hInput;\r
7501   CHARRANGE sel;\r
7502 \r
7503   switch (message) {\r
7504   case WM_KEYDOWN:\r
7505     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7506     switch (wParam) {\r
7507     case VK_PRIOR:\r
7508       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7509       return 0;\r
7510     case VK_NEXT:\r
7511       sel.cpMin = 999999;\r
7512       sel.cpMax = 999999;\r
7513       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7514       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7515       return 0;\r
7516     }\r
7517     break;\r
7518   case WM_CHAR:\r
7519    if(wParam != '\022') {\r
7520     if (wParam == '\t') {\r
7521       if (GetKeyState(VK_SHIFT) < 0) {\r
7522         /* shifted */\r
7523         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7524         if (buttonDesc[0].hwnd) {\r
7525           SetFocus(buttonDesc[0].hwnd);\r
7526         } else {\r
7527           SetFocus(hwndMain);\r
7528         }\r
7529       } else {\r
7530         /* unshifted */\r
7531         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7532       }\r
7533     } else {\r
7534       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7535       JAWS_DELETE( SetFocus(hInput); )\r
7536       SendMessage(hInput, message, wParam, lParam);\r
7537     }\r
7538     return 0;\r
7539    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7540   case WM_RBUTTONUP:\r
7541     if (GetKeyState(VK_SHIFT) & ~1) {\r
7542       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7543         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7544     } else {\r
7545       POINT pt;\r
7546       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7547       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7548       if (sel.cpMin == sel.cpMax) {\r
7549         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7550         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7551       }\r
7552       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7553         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7554       }\r
7555       pt.x = LOWORD(lParam);\r
7556       pt.y = HIWORD(lParam);\r
7557       MenuPopup(hwnd, pt, hmenu, -1);\r
7558     }\r
7559     return 0;\r
7560   case WM_PASTE:\r
7561     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7562     SetFocus(hInput);\r
7563     return SendMessage(hInput, message, wParam, lParam);\r
7564   case WM_MBUTTONDOWN:\r
7565     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7566   case WM_RBUTTONDOWN:\r
7567     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7568       /* Move selection here if it was empty */\r
7569       POINT pt;\r
7570       pt.x = LOWORD(lParam);\r
7571       pt.y = HIWORD(lParam);\r
7572       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7573       if (sel.cpMin == sel.cpMax) {\r
7574         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7575         sel.cpMax = sel.cpMin;\r
7576         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7577       }\r
7578       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7579     }\r
7580     return 0;\r
7581   case WM_COMMAND:\r
7582     switch (LOWORD(wParam)) {\r
7583     case IDM_QuickPaste:\r
7584       {\r
7585         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7586         if (sel.cpMin == sel.cpMax) {\r
7587           MessageBeep(MB_ICONEXCLAMATION);\r
7588           return 0;\r
7589         }\r
7590         SendMessage(hwnd, WM_COPY, 0, 0);\r
7591         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7592         SendMessage(hInput, WM_PASTE, 0, 0);\r
7593         SetFocus(hInput);\r
7594         return 0;\r
7595       }\r
7596     case IDM_Cut:\r
7597       SendMessage(hwnd, WM_CUT, 0, 0);\r
7598       return 0;\r
7599     case IDM_Paste:\r
7600       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7601       return 0;\r
7602     case IDM_Copy:\r
7603       SendMessage(hwnd, WM_COPY, 0, 0);\r
7604       return 0;\r
7605     default:\r
7606       {\r
7607         int i = LOWORD(wParam) - IDM_CommandX;\r
7608         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7609             icsTextMenuEntry[i].command != NULL) {\r
7610           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7611                    icsTextMenuEntry[i].getname,\r
7612                    icsTextMenuEntry[i].immediate);\r
7613           return 0;\r
7614         }\r
7615       }\r
7616       break;\r
7617     }\r
7618     break;\r
7619   }\r
7620   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7621 }\r
7622 \r
7623 WNDPROC consoleInputWindowProc;\r
7624 \r
7625 LRESULT CALLBACK\r
7626 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7627 {\r
7628   char buf[MSG_SIZ];\r
7629   char *p;\r
7630   static BOOL sendNextChar = FALSE;\r
7631   static BOOL quoteNextChar = FALSE;\r
7632   InputSource *is = consoleInputSource;\r
7633   CHARFORMAT cf;\r
7634   CHARRANGE sel;\r
7635 \r
7636   switch (message) {\r
7637   case WM_CHAR:\r
7638     if (!appData.localLineEditing || sendNextChar) {\r
7639       is->buf[0] = (CHAR) wParam;\r
7640       is->count = 1;\r
7641       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7642       sendNextChar = FALSE;\r
7643       return 0;\r
7644     }\r
7645     if (quoteNextChar) {\r
7646       buf[0] = (char) wParam;\r
7647       buf[1] = NULLCHAR;\r
7648       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7649       quoteNextChar = FALSE;\r
7650       return 0;\r
7651     }\r
7652     switch (wParam) {\r
7653     case '\r':   /* Enter key */\r
7654       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7655       if (consoleEcho) SaveInHistory(is->buf);\r
7656       is->buf[is->count++] = '\n';\r
7657       is->buf[is->count] = NULLCHAR;\r
7658       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7659       if (consoleEcho) {\r
7660         ConsoleOutput(is->buf, is->count, TRUE);\r
7661       } else if (appData.localLineEditing) {\r
7662         ConsoleOutput("\n", 1, TRUE);\r
7663       }\r
7664       /* fall thru */\r
7665     case '\033': /* Escape key */\r
7666       SetWindowText(hwnd, "");\r
7667       cf.cbSize = sizeof(CHARFORMAT);\r
7668       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7669       if (consoleEcho) {\r
7670         cf.crTextColor = textAttribs[ColorNormal].color;\r
7671       } else {\r
7672         cf.crTextColor = COLOR_ECHOOFF;\r
7673       }\r
7674       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7675       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7676       return 0;\r
7677     case '\t':   /* Tab key */\r
7678       if (GetKeyState(VK_SHIFT) < 0) {\r
7679         /* shifted */\r
7680         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7681       } else {\r
7682         /* unshifted */\r
7683         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7684         if (buttonDesc[0].hwnd) {\r
7685           SetFocus(buttonDesc[0].hwnd);\r
7686         } else {\r
7687           SetFocus(hwndMain);\r
7688         }\r
7689       }\r
7690       return 0;\r
7691     case '\023': /* Ctrl+S */\r
7692       sendNextChar = TRUE;\r
7693       return 0;\r
7694     case '\021': /* Ctrl+Q */\r
7695       quoteNextChar = TRUE;\r
7696       return 0;\r
7697     JAWS_REPLAY\r
7698     default:\r
7699       break;\r
7700     }\r
7701     break;\r
7702   case WM_KEYDOWN:\r
7703     switch (wParam) {\r
7704     case VK_UP:\r
7705       GetWindowText(hwnd, buf, MSG_SIZ);\r
7706       p = PrevInHistory(buf);\r
7707       if (p != NULL) {\r
7708         SetWindowText(hwnd, p);\r
7709         sel.cpMin = 999999;\r
7710         sel.cpMax = 999999;\r
7711         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7712         return 0;\r
7713       }\r
7714       break;\r
7715     case VK_DOWN:\r
7716       p = NextInHistory();\r
7717       if (p != NULL) {\r
7718         SetWindowText(hwnd, p);\r
7719         sel.cpMin = 999999;\r
7720         sel.cpMax = 999999;\r
7721         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7722         return 0;\r
7723       }\r
7724       break;\r
7725     case VK_HOME:\r
7726     case VK_END:\r
7727       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7728       /* fall thru */\r
7729     case VK_PRIOR:\r
7730     case VK_NEXT:\r
7731       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7732       return 0;\r
7733     }\r
7734     break;\r
7735   case WM_MBUTTONDOWN:\r
7736     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7737       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7738     break;\r
7739   case WM_RBUTTONUP:\r
7740     if (GetKeyState(VK_SHIFT) & ~1) {\r
7741       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7742         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7743     } else {\r
7744       POINT pt;\r
7745       HMENU hmenu;\r
7746       hmenu = LoadMenu(hInst, "InputMenu");\r
7747       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7748       if (sel.cpMin == sel.cpMax) {\r
7749         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7750         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7751       }\r
7752       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7753         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7754       }\r
7755       pt.x = LOWORD(lParam);\r
7756       pt.y = HIWORD(lParam);\r
7757       MenuPopup(hwnd, pt, hmenu, -1);\r
7758     }\r
7759     return 0;\r
7760   case WM_COMMAND:\r
7761     switch (LOWORD(wParam)) { \r
7762     case IDM_Undo:\r
7763       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7764       return 0;\r
7765     case IDM_SelectAll:\r
7766       sel.cpMin = 0;\r
7767       sel.cpMax = -1; /*999999?*/\r
7768       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7769       return 0;\r
7770     case IDM_Cut:\r
7771       SendMessage(hwnd, WM_CUT, 0, 0);\r
7772       return 0;\r
7773     case IDM_Paste:\r
7774       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7775       return 0;\r
7776     case IDM_Copy:\r
7777       SendMessage(hwnd, WM_COPY, 0, 0);\r
7778       return 0;\r
7779     }\r
7780     break;\r
7781   }\r
7782   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7783 }\r
7784 \r
7785 #define CO_MAX  100000\r
7786 #define CO_TRIM   1000\r
7787 \r
7788 LRESULT CALLBACK\r
7789 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7790 {\r
7791   static SnapData sd;\r
7792   HWND hText, hInput;\r
7793   RECT rect;\r
7794   static int sizeX, sizeY;\r
7795   int newSizeX, newSizeY;\r
7796   MINMAXINFO *mmi;\r
7797   WORD wMask;\r
7798 \r
7799   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7800   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7801 \r
7802   switch (message) {\r
7803   case WM_NOTIFY:\r
7804     if (((NMHDR*)lParam)->code == EN_LINK)\r
7805     {\r
7806       ENLINK *pLink = (ENLINK*)lParam;\r
7807       if (pLink->msg == WM_LBUTTONUP)\r
7808       {\r
7809         TEXTRANGE tr;\r
7810 \r
7811         tr.chrg = pLink->chrg;\r
7812         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7813         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7814         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7815         free(tr.lpstrText);\r
7816       }\r
7817     }\r
7818     break;\r
7819   case WM_INITDIALOG: /* message: initialize dialog box */\r
7820     hwndConsole = hDlg;\r
7821     SetFocus(hInput);\r
7822     consoleTextWindowProc = (WNDPROC)\r
7823       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7824     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7825     consoleInputWindowProc = (WNDPROC)\r
7826       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7827     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7828     Colorize(ColorNormal, TRUE);\r
7829     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7830     ChangedConsoleFont();\r
7831     GetClientRect(hDlg, &rect);\r
7832     sizeX = rect.right;\r
7833     sizeY = rect.bottom;\r
7834     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7835         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7836       WINDOWPLACEMENT wp;\r
7837       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7838       wp.length = sizeof(WINDOWPLACEMENT);\r
7839       wp.flags = 0;\r
7840       wp.showCmd = SW_SHOW;\r
7841       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7842       wp.rcNormalPosition.left = wpConsole.x;\r
7843       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7844       wp.rcNormalPosition.top = wpConsole.y;\r
7845       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7846       SetWindowPlacement(hDlg, &wp);\r
7847     }\r
7848 \r
7849    // [HGM] Chessknight's change 2004-07-13\r
7850    else { /* Determine Defaults */\r
7851        WINDOWPLACEMENT wp;\r
7852        wpConsole.x = wpMain.width + 1;\r
7853        wpConsole.y = wpMain.y;\r
7854        wpConsole.width = screenWidth -  wpMain.width;\r
7855        wpConsole.height = wpMain.height;\r
7856        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7857        wp.length = sizeof(WINDOWPLACEMENT);\r
7858        wp.flags = 0;\r
7859        wp.showCmd = SW_SHOW;\r
7860        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7861        wp.rcNormalPosition.left = wpConsole.x;\r
7862        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7863        wp.rcNormalPosition.top = wpConsole.y;\r
7864        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7865        SetWindowPlacement(hDlg, &wp);\r
7866     }\r
7867 \r
7868    // Allow hText to highlight URLs and send notifications on them\r
7869    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7870    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7871    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7872    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7873 \r
7874     return FALSE;\r
7875 \r
7876   case WM_SETFOCUS:\r
7877     SetFocus(hInput);\r
7878     return 0;\r
7879 \r
7880   case WM_CLOSE:\r
7881     ExitEvent(0);\r
7882     /* not reached */\r
7883     break;\r
7884 \r
7885   case WM_SIZE:\r
7886     if (IsIconic(hDlg)) break;\r
7887     newSizeX = LOWORD(lParam);\r
7888     newSizeY = HIWORD(lParam);\r
7889     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7890       RECT rectText, rectInput;\r
7891       POINT pt;\r
7892       int newTextHeight, newTextWidth;\r
7893       GetWindowRect(hText, &rectText);\r
7894       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7895       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7896       if (newTextHeight < 0) {\r
7897         newSizeY += -newTextHeight;\r
7898         newTextHeight = 0;\r
7899       }\r
7900       SetWindowPos(hText, NULL, 0, 0,\r
7901         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7902       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7903       pt.x = rectInput.left;\r
7904       pt.y = rectInput.top + newSizeY - sizeY;\r
7905       ScreenToClient(hDlg, &pt);\r
7906       SetWindowPos(hInput, NULL, \r
7907         pt.x, pt.y, /* needs client coords */   \r
7908         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7909         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7910     }\r
7911     sizeX = newSizeX;\r
7912     sizeY = newSizeY;\r
7913     break;\r
7914 \r
7915   case WM_GETMINMAXINFO:\r
7916     /* Prevent resizing window too small */\r
7917     mmi = (MINMAXINFO *) lParam;\r
7918     mmi->ptMinTrackSize.x = 100;\r
7919     mmi->ptMinTrackSize.y = 100;\r
7920     break;\r
7921 \r
7922   /* [AS] Snapping */\r
7923   case WM_ENTERSIZEMOVE:\r
7924     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7925 \r
7926   case WM_SIZING:\r
7927     return OnSizing( &sd, hDlg, wParam, lParam );\r
7928 \r
7929   case WM_MOVING:\r
7930     return OnMoving( &sd, hDlg, wParam, lParam );\r
7931 \r
7932   case WM_EXITSIZEMOVE:\r
7933         UpdateICSWidth(hText);\r
7934     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7935   }\r
7936 \r
7937   return DefWindowProc(hDlg, message, wParam, lParam);\r
7938 }\r
7939 \r
7940 \r
7941 VOID\r
7942 ConsoleCreate()\r
7943 {\r
7944   HWND hCons;\r
7945   if (hwndConsole) return;\r
7946   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7947   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7948 }\r
7949 \r
7950 \r
7951 VOID\r
7952 ConsoleOutput(char* data, int length, int forceVisible)\r
7953 {\r
7954   HWND hText;\r
7955   int trim, exlen;\r
7956   char *p, *q;\r
7957   char buf[CO_MAX+1];\r
7958   POINT pEnd;\r
7959   RECT rect;\r
7960   static int delayLF = 0;\r
7961   CHARRANGE savesel, sel;\r
7962 \r
7963   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7964   p = data;\r
7965   q = buf;\r
7966   if (delayLF) {\r
7967     *q++ = '\r';\r
7968     *q++ = '\n';\r
7969     delayLF = 0;\r
7970   }\r
7971   while (length--) {\r
7972     if (*p == '\n') {\r
7973       if (*++p) {\r
7974         *q++ = '\r';\r
7975         *q++ = '\n';\r
7976       } else {\r
7977         delayLF = 1;\r
7978       }\r
7979     } else if (*p == '\007') {\r
7980        MyPlaySound(&sounds[(int)SoundBell]);\r
7981        p++;\r
7982     } else {\r
7983       *q++ = *p++;\r
7984     }\r
7985   }\r
7986   *q = NULLCHAR;\r
7987   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7988   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7989   /* Save current selection */\r
7990   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7991   exlen = GetWindowTextLength(hText);\r
7992   /* Find out whether current end of text is visible */\r
7993   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7994   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7995   /* Trim existing text if it's too long */\r
7996   if (exlen + (q - buf) > CO_MAX) {\r
7997     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7998     sel.cpMin = 0;\r
7999     sel.cpMax = trim;\r
8000     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8001     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8002     exlen -= trim;\r
8003     savesel.cpMin -= trim;\r
8004     savesel.cpMax -= trim;\r
8005     if (exlen < 0) exlen = 0;\r
8006     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8007     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8008   }\r
8009   /* Append the new text */\r
8010   sel.cpMin = exlen;\r
8011   sel.cpMax = exlen;\r
8012   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8013   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8014   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8015   if (forceVisible || exlen == 0 ||\r
8016       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8017        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8018     /* Scroll to make new end of text visible if old end of text\r
8019        was visible or new text is an echo of user typein */\r
8020     sel.cpMin = 9999999;\r
8021     sel.cpMax = 9999999;\r
8022     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8023     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8024     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8025     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8026   }\r
8027   if (savesel.cpMax == exlen || forceVisible) {\r
8028     /* Move insert point to new end of text if it was at the old\r
8029        end of text or if the new text is an echo of user typein */\r
8030     sel.cpMin = 9999999;\r
8031     sel.cpMax = 9999999;\r
8032     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8033   } else {\r
8034     /* Restore previous selection */\r
8035     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8036   }\r
8037   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8038 }\r
8039 \r
8040 /*---------*/\r
8041 \r
8042 \r
8043 void\r
8044 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8045 {\r
8046   char buf[100];\r
8047   char *str;\r
8048   COLORREF oldFg, oldBg;\r
8049   HFONT oldFont;\r
8050   RECT rect;\r
8051 \r
8052   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8053 \r
8054   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8055   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8056   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8057 \r
8058   rect.left = x;\r
8059   rect.right = x + squareSize;\r
8060   rect.top  = y;\r
8061   rect.bottom = y + squareSize;\r
8062   str = buf;\r
8063 \r
8064   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8065                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8066              y, ETO_CLIPPED|ETO_OPAQUE,\r
8067              &rect, str, strlen(str), NULL);\r
8068 \r
8069   (void) SetTextColor(hdc, oldFg);\r
8070   (void) SetBkColor(hdc, oldBg);\r
8071   (void) SelectObject(hdc, oldFont);\r
8072 }\r
8073 \r
8074 void\r
8075 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8076               RECT *rect, char *color, char *flagFell)\r
8077 {\r
8078   char buf[100];\r
8079   char *str;\r
8080   COLORREF oldFg, oldBg;\r
8081   HFONT oldFont;\r
8082 \r
8083   if (appData.clockMode) {\r
8084     if (tinyLayout)\r
8085       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8086     else\r
8087       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8088     str = buf;\r
8089   } else {\r
8090     str = color;\r
8091   }\r
8092 \r
8093   if (highlight) {\r
8094     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8095     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8096   } else {\r
8097     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8098     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8099   }\r
8100   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8101 \r
8102   JAWS_SILENCE\r
8103 \r
8104   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8105              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8106              rect, str, strlen(str), NULL);\r
8107   if(logoHeight > 0 && appData.clockMode) {\r
8108       RECT r;\r
8109       sprintf(buf, "%s %s", buf+7, flagFell);\r
8110       r.top = rect->top + logoHeight/2;\r
8111       r.left = rect->left;\r
8112       r.right = rect->right;\r
8113       r.bottom = rect->bottom;\r
8114       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8115                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8116                  &r, str, strlen(str), NULL);\r
8117   }\r
8118   (void) SetTextColor(hdc, oldFg);\r
8119   (void) SetBkColor(hdc, oldBg);\r
8120   (void) SelectObject(hdc, oldFont);\r
8121 }\r
8122 \r
8123 \r
8124 int\r
8125 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8126            OVERLAPPED *ovl)\r
8127 {\r
8128   int ok, err;\r
8129 \r
8130   /* [AS]  */\r
8131   if( count <= 0 ) {\r
8132     if (appData.debugMode) {\r
8133       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8134     }\r
8135 \r
8136     return ERROR_INVALID_USER_BUFFER;\r
8137   }\r
8138 \r
8139   ResetEvent(ovl->hEvent);\r
8140   ovl->Offset = ovl->OffsetHigh = 0;\r
8141   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8142   if (ok) {\r
8143     err = NO_ERROR;\r
8144   } else {\r
8145     err = GetLastError();\r
8146     if (err == ERROR_IO_PENDING) {\r
8147       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8148       if (ok)\r
8149         err = NO_ERROR;\r
8150       else\r
8151         err = GetLastError();\r
8152     }\r
8153   }\r
8154   return err;\r
8155 }\r
8156 \r
8157 int\r
8158 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8159             OVERLAPPED *ovl)\r
8160 {\r
8161   int ok, err;\r
8162 \r
8163   ResetEvent(ovl->hEvent);\r
8164   ovl->Offset = ovl->OffsetHigh = 0;\r
8165   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8166   if (ok) {\r
8167     err = NO_ERROR;\r
8168   } else {\r
8169     err = GetLastError();\r
8170     if (err == ERROR_IO_PENDING) {\r
8171       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8172       if (ok)\r
8173         err = NO_ERROR;\r
8174       else\r
8175         err = GetLastError();\r
8176     }\r
8177   }\r
8178   return err;\r
8179 }\r
8180 \r
8181 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8182 void CheckForInputBufferFull( InputSource * is )\r
8183 {\r
8184     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8185         /* Look for end of line */\r
8186         char * p = is->buf;\r
8187         \r
8188         while( p < is->next && *p != '\n' ) {\r
8189             p++;\r
8190         }\r
8191 \r
8192         if( p >= is->next ) {\r
8193             if (appData.debugMode) {\r
8194                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8195             }\r
8196 \r
8197             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8198             is->count = (DWORD) -1;\r
8199             is->next = is->buf;\r
8200         }\r
8201     }\r
8202 }\r
8203 \r
8204 DWORD\r
8205 InputThread(LPVOID arg)\r
8206 {\r
8207   InputSource *is;\r
8208   OVERLAPPED ovl;\r
8209 \r
8210   is = (InputSource *) arg;\r
8211   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8212   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8213   while (is->hThread != NULL) {\r
8214     is->error = DoReadFile(is->hFile, is->next,\r
8215                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8216                            &is->count, &ovl);\r
8217     if (is->error == NO_ERROR) {\r
8218       is->next += is->count;\r
8219     } else {\r
8220       if (is->error == ERROR_BROKEN_PIPE) {\r
8221         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8222         is->count = 0;\r
8223       } else {\r
8224         is->count = (DWORD) -1;\r
8225         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8226         break; \r
8227       }\r
8228     }\r
8229 \r
8230     CheckForInputBufferFull( is );\r
8231 \r
8232     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8233 \r
8234     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8235 \r
8236     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8237   }\r
8238 \r
8239   CloseHandle(ovl.hEvent);\r
8240   CloseHandle(is->hFile);\r
8241 \r
8242   if (appData.debugMode) {\r
8243     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8244   }\r
8245 \r
8246   return 0;\r
8247 }\r
8248 \r
8249 \r
8250 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8251 DWORD\r
8252 NonOvlInputThread(LPVOID arg)\r
8253 {\r
8254   InputSource *is;\r
8255   char *p, *q;\r
8256   int i;\r
8257   char prev;\r
8258 \r
8259   is = (InputSource *) arg;\r
8260   while (is->hThread != NULL) {\r
8261     is->error = ReadFile(is->hFile, is->next,\r
8262                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8263                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8264     if (is->error == NO_ERROR) {\r
8265       /* Change CRLF to LF */\r
8266       if (is->next > is->buf) {\r
8267         p = is->next - 1;\r
8268         i = is->count + 1;\r
8269       } else {\r
8270         p = is->next;\r
8271         i = is->count;\r
8272       }\r
8273       q = p;\r
8274       prev = NULLCHAR;\r
8275       while (i > 0) {\r
8276         if (prev == '\r' && *p == '\n') {\r
8277           *(q-1) = '\n';\r
8278           is->count--;\r
8279         } else { \r
8280           *q++ = *p;\r
8281         }\r
8282         prev = *p++;\r
8283         i--;\r
8284       }\r
8285       *q = NULLCHAR;\r
8286       is->next = q;\r
8287     } else {\r
8288       if (is->error == ERROR_BROKEN_PIPE) {\r
8289         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8290         is->count = 0; \r
8291       } else {\r
8292         is->count = (DWORD) -1;\r
8293       }\r
8294     }\r
8295 \r
8296     CheckForInputBufferFull( is );\r
8297 \r
8298     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8299 \r
8300     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8301 \r
8302     if (is->count < 0) break;  /* Quit on error */\r
8303   }\r
8304   CloseHandle(is->hFile);\r
8305   return 0;\r
8306 }\r
8307 \r
8308 DWORD\r
8309 SocketInputThread(LPVOID arg)\r
8310 {\r
8311   InputSource *is;\r
8312 \r
8313   is = (InputSource *) arg;\r
8314   while (is->hThread != NULL) {\r
8315     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8316     if ((int)is->count == SOCKET_ERROR) {\r
8317       is->count = (DWORD) -1;\r
8318       is->error = WSAGetLastError();\r
8319     } else {\r
8320       is->error = NO_ERROR;\r
8321       is->next += is->count;\r
8322       if (is->count == 0 && is->second == is) {\r
8323         /* End of file on stderr; quit with no message */\r
8324         break;\r
8325       }\r
8326     }\r
8327     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8328 \r
8329     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8330 \r
8331     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8332   }\r
8333   return 0;\r
8334 }\r
8335 \r
8336 VOID\r
8337 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8338 {\r
8339   InputSource *is;\r
8340 \r
8341   is = (InputSource *) lParam;\r
8342   if (is->lineByLine) {\r
8343     /* Feed in lines one by one */\r
8344     char *p = is->buf;\r
8345     char *q = p;\r
8346     while (q < is->next) {\r
8347       if (*q++ == '\n') {\r
8348         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8349         p = q;\r
8350       }\r
8351     }\r
8352     \r
8353     /* Move any partial line to the start of the buffer */\r
8354     q = is->buf;\r
8355     while (p < is->next) {\r
8356       *q++ = *p++;\r
8357     }\r
8358     is->next = q;\r
8359 \r
8360     if (is->error != NO_ERROR || is->count == 0) {\r
8361       /* Notify backend of the error.  Note: If there was a partial\r
8362          line at the end, it is not flushed through. */\r
8363       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8364     }\r
8365   } else {\r
8366     /* Feed in the whole chunk of input at once */\r
8367     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8368     is->next = is->buf;\r
8369   }\r
8370 }\r
8371 \r
8372 /*---------------------------------------------------------------------------*\\r
8373  *\r
8374  *  Menu enables. Used when setting various modes.\r
8375  *\r
8376 \*---------------------------------------------------------------------------*/\r
8377 \r
8378 typedef struct {\r
8379   int item;\r
8380   int flags;\r
8381 } Enables;\r
8382 \r
8383 VOID\r
8384 GreyRevert(Boolean grey)\r
8385 { // [HGM] vari: for retracting variations in local mode\r
8386   HMENU hmenu = GetMenu(hwndMain);\r
8387   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8388 }\r
8389 \r
8390 VOID\r
8391 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8392 {\r
8393   while (enab->item > 0) {\r
8394     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8395     enab++;\r
8396   }\r
8397 }\r
8398 \r
8399 Enables gnuEnables[] = {\r
8400   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8401   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8402   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8403   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8404   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8405   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8406   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8407   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8408   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8409   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8410   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8411   { -1, -1 }\r
8412 };\r
8413 \r
8414 Enables icsEnables[] = {\r
8415   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8416   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8417   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8418   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8419   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8420   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8421   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8422   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8423   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8424   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8425   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8426   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8427   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8428   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8429   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8430   { -1, -1 }\r
8431 };\r
8432 \r
8433 #ifdef ZIPPY\r
8434 Enables zippyEnables[] = {\r
8435   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8436   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8437   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8438   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8439   { -1, -1 }\r
8440 };\r
8441 #endif\r
8442 \r
8443 Enables ncpEnables[] = {\r
8444   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8445   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8446   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8447   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8448   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8449   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8450   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8451   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8452   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8453   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8454   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8455   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8456   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8457   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8458   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8459   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8460   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8461   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8462   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8463   { -1, -1 }\r
8464 };\r
8465 \r
8466 Enables trainingOnEnables[] = {\r
8467   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8468   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8469   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8470   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8471   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8472   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8473   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8474   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8475   { -1, -1 }\r
8476 };\r
8477 \r
8478 Enables trainingOffEnables[] = {\r
8479   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8480   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8481   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8482   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8483   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8484   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8485   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8486   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8487   { -1, -1 }\r
8488 };\r
8489 \r
8490 /* These modify either ncpEnables or gnuEnables */\r
8491 Enables cmailEnables[] = {\r
8492   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8493   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8494   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8495   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8496   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8497   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8498   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8499   { -1, -1 }\r
8500 };\r
8501 \r
8502 Enables machineThinkingEnables[] = {\r
8503   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8504   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8505   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8506   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8507   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8510   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8511   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8513   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8515   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8516   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8517   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8518   { -1, -1 }\r
8519 };\r
8520 \r
8521 Enables userThinkingEnables[] = {\r
8522   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8523   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8524   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8525   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8526   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8527   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8528   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8529   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8530   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8531   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8532   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8533   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8534   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8535   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8536   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8537   { -1, -1 }\r
8538 };\r
8539 \r
8540 /*---------------------------------------------------------------------------*\\r
8541  *\r
8542  *  Front-end interface functions exported by XBoard.\r
8543  *  Functions appear in same order as prototypes in frontend.h.\r
8544  * \r
8545 \*---------------------------------------------------------------------------*/\r
8546 VOID\r
8547 ModeHighlight()\r
8548 {\r
8549   static UINT prevChecked = 0;\r
8550   static int prevPausing = 0;\r
8551   UINT nowChecked;\r
8552 \r
8553   if (pausing != prevPausing) {\r
8554     prevPausing = pausing;\r
8555     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8556                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8557     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8558   }\r
8559 \r
8560   switch (gameMode) {\r
8561   case BeginningOfGame:\r
8562     if (appData.icsActive)\r
8563       nowChecked = IDM_IcsClient;\r
8564     else if (appData.noChessProgram)\r
8565       nowChecked = IDM_EditGame;\r
8566     else\r
8567       nowChecked = IDM_MachineBlack;\r
8568     break;\r
8569   case MachinePlaysBlack:\r
8570     nowChecked = IDM_MachineBlack;\r
8571     break;\r
8572   case MachinePlaysWhite:\r
8573     nowChecked = IDM_MachineWhite;\r
8574     break;\r
8575   case TwoMachinesPlay:\r
8576     nowChecked = IDM_TwoMachines;\r
8577     break;\r
8578   case AnalyzeMode:\r
8579     nowChecked = IDM_AnalysisMode;\r
8580     break;\r
8581   case AnalyzeFile:\r
8582     nowChecked = IDM_AnalyzeFile;\r
8583     break;\r
8584   case EditGame:\r
8585     nowChecked = IDM_EditGame;\r
8586     break;\r
8587   case PlayFromGameFile:\r
8588     nowChecked = IDM_LoadGame;\r
8589     break;\r
8590   case EditPosition:\r
8591     nowChecked = IDM_EditPosition;\r
8592     break;\r
8593   case Training:\r
8594     nowChecked = IDM_Training;\r
8595     break;\r
8596   case IcsPlayingWhite:\r
8597   case IcsPlayingBlack:\r
8598   case IcsObserving:\r
8599   case IcsIdle:\r
8600     nowChecked = IDM_IcsClient;\r
8601     break;\r
8602   default:\r
8603   case EndOfGame:\r
8604     nowChecked = 0;\r
8605     break;\r
8606   }\r
8607   if (prevChecked != 0)\r
8608     (void) CheckMenuItem(GetMenu(hwndMain),\r
8609                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8610   if (nowChecked != 0)\r
8611     (void) CheckMenuItem(GetMenu(hwndMain),\r
8612                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8613 \r
8614   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8615     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8616                           MF_BYCOMMAND|MF_ENABLED);\r
8617   } else {\r
8618     (void) EnableMenuItem(GetMenu(hwndMain), \r
8619                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8620   }\r
8621 \r
8622   prevChecked = nowChecked;\r
8623 \r
8624   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8625   if (appData.icsActive) {\r
8626        if (appData.icsEngineAnalyze) {\r
8627                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8628                        MF_BYCOMMAND|MF_CHECKED);\r
8629        } else {\r
8630                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8631                        MF_BYCOMMAND|MF_UNCHECKED);\r
8632        }\r
8633   }\r
8634 }\r
8635 \r
8636 VOID\r
8637 SetICSMode()\r
8638 {\r
8639   HMENU hmenu = GetMenu(hwndMain);\r
8640   SetMenuEnables(hmenu, icsEnables);\r
8641   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8642     MF_BYPOSITION|MF_ENABLED);\r
8643 #ifdef ZIPPY\r
8644   if (appData.zippyPlay) {\r
8645     SetMenuEnables(hmenu, zippyEnables);\r
8646     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8647          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8648           MF_BYCOMMAND|MF_ENABLED);\r
8649   }\r
8650 #endif\r
8651 }\r
8652 \r
8653 VOID\r
8654 SetGNUMode()\r
8655 {\r
8656   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8657 }\r
8658 \r
8659 VOID\r
8660 SetNCPMode()\r
8661 {\r
8662   HMENU hmenu = GetMenu(hwndMain);\r
8663   SetMenuEnables(hmenu, ncpEnables);\r
8664   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8665     MF_BYPOSITION|MF_GRAYED);\r
8666     DrawMenuBar(hwndMain);\r
8667 }\r
8668 \r
8669 VOID\r
8670 SetCmailMode()\r
8671 {\r
8672   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8673 }\r
8674 \r
8675 VOID \r
8676 SetTrainingModeOn()\r
8677 {\r
8678   int i;\r
8679   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8680   for (i = 0; i < N_BUTTONS; i++) {\r
8681     if (buttonDesc[i].hwnd != NULL)\r
8682       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8683   }\r
8684   CommentPopDown();\r
8685 }\r
8686 \r
8687 VOID SetTrainingModeOff()\r
8688 {\r
8689   int i;\r
8690   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8691   for (i = 0; i < N_BUTTONS; i++) {\r
8692     if (buttonDesc[i].hwnd != NULL)\r
8693       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8694   }\r
8695 }\r
8696 \r
8697 \r
8698 VOID\r
8699 SetUserThinkingEnables()\r
8700 {\r
8701   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8702 }\r
8703 \r
8704 VOID\r
8705 SetMachineThinkingEnables()\r
8706 {\r
8707   HMENU hMenu = GetMenu(hwndMain);\r
8708   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8709 \r
8710   SetMenuEnables(hMenu, machineThinkingEnables);\r
8711 \r
8712   if (gameMode == MachinePlaysBlack) {\r
8713     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8714   } else if (gameMode == MachinePlaysWhite) {\r
8715     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8716   } else if (gameMode == TwoMachinesPlay) {\r
8717     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8718   }\r
8719 }\r
8720 \r
8721 \r
8722 VOID\r
8723 DisplayTitle(char *str)\r
8724 {\r
8725   char title[MSG_SIZ], *host;\r
8726   if (str[0] != NULLCHAR) {\r
8727     strcpy(title, str);\r
8728   } else if (appData.icsActive) {\r
8729     if (appData.icsCommPort[0] != NULLCHAR)\r
8730       host = "ICS";\r
8731     else \r
8732       host = appData.icsHost;\r
8733     sprintf(title, "%s: %s", szTitle, host);\r
8734   } else if (appData.noChessProgram) {\r
8735     strcpy(title, szTitle);\r
8736   } else {\r
8737     strcpy(title, szTitle);\r
8738     strcat(title, ": ");\r
8739     strcat(title, first.tidy);\r
8740   }\r
8741   SetWindowText(hwndMain, title);\r
8742 }\r
8743 \r
8744 \r
8745 VOID\r
8746 DisplayMessage(char *str1, char *str2)\r
8747 {\r
8748   HDC hdc;\r
8749   HFONT oldFont;\r
8750   int remain = MESSAGE_TEXT_MAX - 1;\r
8751   int len;\r
8752 \r
8753   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8754   messageText[0] = NULLCHAR;\r
8755   if (*str1) {\r
8756     len = strlen(str1);\r
8757     if (len > remain) len = remain;\r
8758     strncpy(messageText, str1, len);\r
8759     messageText[len] = NULLCHAR;\r
8760     remain -= len;\r
8761   }\r
8762   if (*str2 && remain >= 2) {\r
8763     if (*str1) {\r
8764       strcat(messageText, "  ");\r
8765       remain -= 2;\r
8766     }\r
8767     len = strlen(str2);\r
8768     if (len > remain) len = remain;\r
8769     strncat(messageText, str2, len);\r
8770   }\r
8771   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8772 \r
8773   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8774 \r
8775   SAYMACHINEMOVE();\r
8776 \r
8777   hdc = GetDC(hwndMain);\r
8778   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8779   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8780              &messageRect, messageText, strlen(messageText), NULL);\r
8781   (void) SelectObject(hdc, oldFont);\r
8782   (void) ReleaseDC(hwndMain, hdc);\r
8783 }\r
8784 \r
8785 VOID\r
8786 DisplayError(char *str, int error)\r
8787 {\r
8788   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8789   int len;\r
8790 \r
8791   if (error == 0) {\r
8792     strcpy(buf, str);\r
8793   } else {\r
8794     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8795                         NULL, error, LANG_NEUTRAL,\r
8796                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8797     if (len > 0) {\r
8798       sprintf(buf, "%s:\n%s", str, buf2);\r
8799     } else {\r
8800       ErrorMap *em = errmap;\r
8801       while (em->err != 0 && em->err != error) em++;\r
8802       if (em->err != 0) {\r
8803         sprintf(buf, "%s:\n%s", str, em->msg);\r
8804       } else {\r
8805         sprintf(buf, "%s:\nError code %d", str, error);\r
8806       }\r
8807     }\r
8808   }\r
8809   \r
8810   ErrorPopUp("Error", buf);\r
8811 }\r
8812 \r
8813 \r
8814 VOID\r
8815 DisplayMoveError(char *str)\r
8816 {\r
8817   fromX = fromY = -1;\r
8818   ClearHighlights();\r
8819   DrawPosition(FALSE, NULL);\r
8820   if (appData.popupMoveErrors) {\r
8821     ErrorPopUp("Error", str);\r
8822   } else {\r
8823     DisplayMessage(str, "");\r
8824     moveErrorMessageUp = TRUE;\r
8825   }\r
8826 }\r
8827 \r
8828 VOID\r
8829 DisplayFatalError(char *str, int error, int exitStatus)\r
8830 {\r
8831   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8832   int len;\r
8833   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8834 \r
8835   if (error != 0) {\r
8836     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8837                         NULL, error, LANG_NEUTRAL,\r
8838                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8839     if (len > 0) {\r
8840       sprintf(buf, "%s:\n%s", str, buf2);\r
8841     } else {\r
8842       ErrorMap *em = errmap;\r
8843       while (em->err != 0 && em->err != error) em++;\r
8844       if (em->err != 0) {\r
8845         sprintf(buf, "%s:\n%s", str, em->msg);\r
8846       } else {\r
8847         sprintf(buf, "%s:\nError code %d", str, error);\r
8848       }\r
8849     }\r
8850     str = buf;\r
8851   }\r
8852   if (appData.debugMode) {\r
8853     fprintf(debugFP, "%s: %s\n", label, str);\r
8854   }\r
8855   if (appData.popupExitMessage) {\r
8856     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8857                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8858   }\r
8859   ExitEvent(exitStatus);\r
8860 }\r
8861 \r
8862 \r
8863 VOID\r
8864 DisplayInformation(char *str)\r
8865 {\r
8866   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8867 }\r
8868 \r
8869 \r
8870 VOID\r
8871 DisplayNote(char *str)\r
8872 {\r
8873   ErrorPopUp("Note", str);\r
8874 }\r
8875 \r
8876 \r
8877 typedef struct {\r
8878   char *title, *question, *replyPrefix;\r
8879   ProcRef pr;\r
8880 } QuestionParams;\r
8881 \r
8882 LRESULT CALLBACK\r
8883 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8884 {\r
8885   static QuestionParams *qp;\r
8886   char reply[MSG_SIZ];\r
8887   int len, err;\r
8888 \r
8889   switch (message) {\r
8890   case WM_INITDIALOG:\r
8891     qp = (QuestionParams *) lParam;\r
8892     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8893     SetWindowText(hDlg, qp->title);\r
8894     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8895     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8896     return FALSE;\r
8897 \r
8898   case WM_COMMAND:\r
8899     switch (LOWORD(wParam)) {\r
8900     case IDOK:\r
8901       strcpy(reply, qp->replyPrefix);\r
8902       if (*reply) strcat(reply, " ");\r
8903       len = strlen(reply);\r
8904       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8905       strcat(reply, "\n");\r
8906       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8907       EndDialog(hDlg, TRUE);\r
8908       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8909       return TRUE;\r
8910     case IDCANCEL:\r
8911       EndDialog(hDlg, FALSE);\r
8912       return TRUE;\r
8913     default:\r
8914       break;\r
8915     }\r
8916     break;\r
8917   }\r
8918   return FALSE;\r
8919 }\r
8920 \r
8921 VOID\r
8922 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8923 {\r
8924     QuestionParams qp;\r
8925     FARPROC lpProc;\r
8926     \r
8927     qp.title = title;\r
8928     qp.question = question;\r
8929     qp.replyPrefix = replyPrefix;\r
8930     qp.pr = pr;\r
8931     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8932     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8933       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8934     FreeProcInstance(lpProc);\r
8935 }\r
8936 \r
8937 /* [AS] Pick FRC position */\r
8938 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8939 {\r
8940     static int * lpIndexFRC;\r
8941     BOOL index_is_ok;\r
8942     char buf[16];\r
8943 \r
8944     switch( message )\r
8945     {\r
8946     case WM_INITDIALOG:\r
8947         lpIndexFRC = (int *) lParam;\r
8948 \r
8949         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8950 \r
8951         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8952         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8953         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8954         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8955 \r
8956         break;\r
8957 \r
8958     case WM_COMMAND:\r
8959         switch( LOWORD(wParam) ) {\r
8960         case IDOK:\r
8961             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8962             EndDialog( hDlg, 0 );\r
8963             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8964             return TRUE;\r
8965         case IDCANCEL:\r
8966             EndDialog( hDlg, 1 );   \r
8967             return TRUE;\r
8968         case IDC_NFG_Edit:\r
8969             if( HIWORD(wParam) == EN_CHANGE ) {\r
8970                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8971 \r
8972                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8973             }\r
8974             return TRUE;\r
8975         case IDC_NFG_Random:\r
8976             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8977             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8978             return TRUE;\r
8979         }\r
8980 \r
8981         break;\r
8982     }\r
8983 \r
8984     return FALSE;\r
8985 }\r
8986 \r
8987 int NewGameFRC()\r
8988 {\r
8989     int result;\r
8990     int index = appData.defaultFrcPosition;\r
8991     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8992 \r
8993     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8994 \r
8995     if( result == 0 ) {\r
8996         appData.defaultFrcPosition = index;\r
8997     }\r
8998 \r
8999     return result;\r
9000 }\r
9001 \r
9002 /* [AS] Game list options */\r
9003 typedef struct {\r
9004     char id;\r
9005     char * name;\r
9006 } GLT_Item;\r
9007 \r
9008 static GLT_Item GLT_ItemInfo[] = {\r
9009     { GLT_EVENT,      "Event" },\r
9010     { GLT_SITE,       "Site" },\r
9011     { GLT_DATE,       "Date" },\r
9012     { GLT_ROUND,      "Round" },\r
9013     { GLT_PLAYERS,    "Players" },\r
9014     { GLT_RESULT,     "Result" },\r
9015     { GLT_WHITE_ELO,  "White Rating" },\r
9016     { GLT_BLACK_ELO,  "Black Rating" },\r
9017     { GLT_TIME_CONTROL,"Time Control" },\r
9018     { GLT_VARIANT,    "Variant" },\r
9019     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9020     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9021     { 0, 0 }\r
9022 };\r
9023 \r
9024 const char * GLT_FindItem( char id )\r
9025 {\r
9026     const char * result = 0;\r
9027 \r
9028     GLT_Item * list = GLT_ItemInfo;\r
9029 \r
9030     while( list->id != 0 ) {\r
9031         if( list->id == id ) {\r
9032             result = list->name;\r
9033             break;\r
9034         }\r
9035 \r
9036         list++;\r
9037     }\r
9038 \r
9039     return result;\r
9040 }\r
9041 \r
9042 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9043 {\r
9044     const char * name = GLT_FindItem( id );\r
9045 \r
9046     if( name != 0 ) {\r
9047         if( index >= 0 ) {\r
9048             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9049         }\r
9050         else {\r
9051             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9052         }\r
9053     }\r
9054 }\r
9055 \r
9056 void GLT_TagsToList( HWND hDlg, char * tags )\r
9057 {\r
9058     char * pc = tags;\r
9059 \r
9060     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9061 \r
9062     while( *pc ) {\r
9063         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9064         pc++;\r
9065     }\r
9066 \r
9067     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9068 \r
9069     pc = GLT_ALL_TAGS;\r
9070 \r
9071     while( *pc ) {\r
9072         if( strchr( tags, *pc ) == 0 ) {\r
9073             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9074         }\r
9075         pc++;\r
9076     }\r
9077 \r
9078     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9079 }\r
9080 \r
9081 char GLT_ListItemToTag( HWND hDlg, int index )\r
9082 {\r
9083     char result = '\0';\r
9084     char name[128];\r
9085 \r
9086     GLT_Item * list = GLT_ItemInfo;\r
9087 \r
9088     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9089         while( list->id != 0 ) {\r
9090             if( strcmp( list->name, name ) == 0 ) {\r
9091                 result = list->id;\r
9092                 break;\r
9093             }\r
9094 \r
9095             list++;\r
9096         }\r
9097     }\r
9098 \r
9099     return result;\r
9100 }\r
9101 \r
9102 void GLT_MoveSelection( HWND hDlg, int delta )\r
9103 {\r
9104     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9105     int idx2 = idx1 + delta;\r
9106     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9107 \r
9108     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9109         char buf[128];\r
9110 \r
9111         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9112         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9113         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9114         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9115     }\r
9116 }\r
9117 \r
9118 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9119 {\r
9120     static char glt[64];\r
9121     static char * lpUserGLT;\r
9122 \r
9123     switch( message )\r
9124     {\r
9125     case WM_INITDIALOG:\r
9126         lpUserGLT = (char *) lParam;\r
9127         \r
9128         strcpy( glt, lpUserGLT );\r
9129 \r
9130         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9131 \r
9132         /* Initialize list */\r
9133         GLT_TagsToList( hDlg, glt );\r
9134 \r
9135         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9136 \r
9137         break;\r
9138 \r
9139     case WM_COMMAND:\r
9140         switch( LOWORD(wParam) ) {\r
9141         case IDOK:\r
9142             {\r
9143                 char * pc = lpUserGLT;\r
9144                 int idx = 0;\r
9145 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9146                 char id;\r
9147 \r
9148                 do {\r
9149                     id = GLT_ListItemToTag( hDlg, idx );\r
9150 \r
9151                     *pc++ = id;\r
9152                     idx++;\r
9153                 } while( id != '\0' );\r
9154             }\r
9155             EndDialog( hDlg, 0 );\r
9156             return TRUE;\r
9157         case IDCANCEL:\r
9158             EndDialog( hDlg, 1 );\r
9159             return TRUE;\r
9160 \r
9161         case IDC_GLT_Default:\r
9162             strcpy( glt, GLT_DEFAULT_TAGS );\r
9163             GLT_TagsToList( hDlg, glt );\r
9164             return TRUE;\r
9165 \r
9166         case IDC_GLT_Restore:\r
9167             strcpy( glt, lpUserGLT );\r
9168             GLT_TagsToList( hDlg, glt );\r
9169             return TRUE;\r
9170 \r
9171         case IDC_GLT_Up:\r
9172             GLT_MoveSelection( hDlg, -1 );\r
9173             return TRUE;\r
9174 \r
9175         case IDC_GLT_Down:\r
9176             GLT_MoveSelection( hDlg, +1 );\r
9177             return TRUE;\r
9178         }\r
9179 \r
9180         break;\r
9181     }\r
9182 \r
9183     return FALSE;\r
9184 }\r
9185 \r
9186 int GameListOptions()\r
9187 {\r
9188     char glt[64];\r
9189     int result;\r
9190     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9191 \r
9192     strcpy( glt, appData.gameListTags );\r
9193 \r
9194     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9195 \r
9196     if( result == 0 ) {\r
9197         /* [AS] Memory leak here! */\r
9198         appData.gameListTags = strdup( glt ); \r
9199     }\r
9200 \r
9201     return result;\r
9202 }\r
9203 \r
9204 \r
9205 VOID\r
9206 DisplayIcsInteractionTitle(char *str)\r
9207 {\r
9208   char consoleTitle[MSG_SIZ];\r
9209 \r
9210   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9211   SetWindowText(hwndConsole, consoleTitle);\r
9212 }\r
9213 \r
9214 void\r
9215 DrawPosition(int fullRedraw, Board board)\r
9216 {\r
9217   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9218 }\r
9219 \r
9220 void NotifyFrontendLogin()\r
9221 {\r
9222         if (hwndConsole)\r
9223                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9224 }\r
9225 \r
9226 VOID\r
9227 ResetFrontEnd()\r
9228 {\r
9229   fromX = fromY = -1;\r
9230   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9231     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9232     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9233     dragInfo.lastpos = dragInfo.pos;\r
9234     dragInfo.start.x = dragInfo.start.y = -1;\r
9235     dragInfo.from = dragInfo.start;\r
9236     ReleaseCapture();\r
9237     DrawPosition(TRUE, NULL);\r
9238   }\r
9239 }\r
9240 \r
9241 \r
9242 VOID\r
9243 CommentPopUp(char *title, char *str)\r
9244 {\r
9245   HWND hwnd = GetActiveWindow();\r
9246   EitherCommentPopUp(0, title, str, FALSE);\r
9247   SAY(str);\r
9248   SetActiveWindow(hwnd);\r
9249 }\r
9250 \r
9251 VOID\r
9252 CommentPopDown(void)\r
9253 {\r
9254   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9255   if (commentDialog) {\r
9256     ShowWindow(commentDialog, SW_HIDE);\r
9257   }\r
9258   commentUp = FALSE;\r
9259 }\r
9260 \r
9261 VOID\r
9262 EditCommentPopUp(int index, char *title, char *str)\r
9263 {\r
9264   EitherCommentPopUp(index, title, str, TRUE);\r
9265 }\r
9266 \r
9267 \r
9268 VOID\r
9269 RingBell()\r
9270 {\r
9271   MyPlaySound(&sounds[(int)SoundMove]);\r
9272 }\r
9273 \r
9274 VOID PlayIcsWinSound()\r
9275 {\r
9276   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9277 }\r
9278 \r
9279 VOID PlayIcsLossSound()\r
9280 {\r
9281   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9282 }\r
9283 \r
9284 VOID PlayIcsDrawSound()\r
9285 {\r
9286   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9287 }\r
9288 \r
9289 VOID PlayIcsUnfinishedSound()\r
9290 {\r
9291   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9292 }\r
9293 \r
9294 VOID\r
9295 PlayAlarmSound()\r
9296 {\r
9297   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9298 }\r
9299 \r
9300 \r
9301 VOID\r
9302 EchoOn()\r
9303 {\r
9304   HWND hInput;\r
9305   consoleEcho = TRUE;\r
9306   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9307   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9308   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9309 }\r
9310 \r
9311 \r
9312 VOID\r
9313 EchoOff()\r
9314 {\r
9315   CHARFORMAT cf;\r
9316   HWND hInput;\r
9317   consoleEcho = FALSE;\r
9318   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9319   /* This works OK: set text and background both to the same color */\r
9320   cf = consoleCF;\r
9321   cf.crTextColor = COLOR_ECHOOFF;\r
9322   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9323   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9324 }\r
9325 \r
9326 /* No Raw()...? */\r
9327 \r
9328 void Colorize(ColorClass cc, int continuation)\r
9329 {\r
9330   currentColorClass = cc;\r
9331   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9332   consoleCF.crTextColor = textAttribs[cc].color;\r
9333   consoleCF.dwEffects = textAttribs[cc].effects;\r
9334   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9335 }\r
9336 \r
9337 char *\r
9338 UserName()\r
9339 {\r
9340   static char buf[MSG_SIZ];\r
9341   DWORD bufsiz = MSG_SIZ;\r
9342 \r
9343   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9344         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9345   }\r
9346   if (!GetUserName(buf, &bufsiz)) {\r
9347     /*DisplayError("Error getting user name", GetLastError());*/\r
9348     strcpy(buf, "User");\r
9349   }\r
9350   return buf;\r
9351 }\r
9352 \r
9353 char *\r
9354 HostName()\r
9355 {\r
9356   static char buf[MSG_SIZ];\r
9357   DWORD bufsiz = MSG_SIZ;\r
9358 \r
9359   if (!GetComputerName(buf, &bufsiz)) {\r
9360     /*DisplayError("Error getting host name", GetLastError());*/\r
9361     strcpy(buf, "Unknown");\r
9362   }\r
9363   return buf;\r
9364 }\r
9365 \r
9366 \r
9367 int\r
9368 ClockTimerRunning()\r
9369 {\r
9370   return clockTimerEvent != 0;\r
9371 }\r
9372 \r
9373 int\r
9374 StopClockTimer()\r
9375 {\r
9376   if (clockTimerEvent == 0) return FALSE;\r
9377   KillTimer(hwndMain, clockTimerEvent);\r
9378   clockTimerEvent = 0;\r
9379   return TRUE;\r
9380 }\r
9381 \r
9382 void\r
9383 StartClockTimer(long millisec)\r
9384 {\r
9385   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9386                              (UINT) millisec, NULL);\r
9387 }\r
9388 \r
9389 void\r
9390 DisplayWhiteClock(long timeRemaining, int highlight)\r
9391 {\r
9392   HDC hdc;\r
9393   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9394 \r
9395   if(appData.noGUI) return;\r
9396   hdc = GetDC(hwndMain);\r
9397   if (!IsIconic(hwndMain)) {\r
9398     DisplayAClock(hdc, timeRemaining, highlight, \r
9399                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9400   }\r
9401   if (highlight && iconCurrent == iconBlack) {\r
9402     iconCurrent = iconWhite;\r
9403     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9404     if (IsIconic(hwndMain)) {\r
9405       DrawIcon(hdc, 2, 2, iconCurrent);\r
9406     }\r
9407   }\r
9408   (void) ReleaseDC(hwndMain, hdc);\r
9409   if (hwndConsole)\r
9410     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9411 }\r
9412 \r
9413 void\r
9414 DisplayBlackClock(long timeRemaining, int highlight)\r
9415 {\r
9416   HDC hdc;\r
9417   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9418 \r
9419   if(appData.noGUI) return;\r
9420   hdc = GetDC(hwndMain);\r
9421   if (!IsIconic(hwndMain)) {\r
9422     DisplayAClock(hdc, timeRemaining, highlight, \r
9423                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9424   }\r
9425   if (highlight && iconCurrent == iconWhite) {\r
9426     iconCurrent = iconBlack;\r
9427     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9428     if (IsIconic(hwndMain)) {\r
9429       DrawIcon(hdc, 2, 2, iconCurrent);\r
9430     }\r
9431   }\r
9432   (void) ReleaseDC(hwndMain, hdc);\r
9433   if (hwndConsole)\r
9434     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9435 }\r
9436 \r
9437 \r
9438 int\r
9439 LoadGameTimerRunning()\r
9440 {\r
9441   return loadGameTimerEvent != 0;\r
9442 }\r
9443 \r
9444 int\r
9445 StopLoadGameTimer()\r
9446 {\r
9447   if (loadGameTimerEvent == 0) return FALSE;\r
9448   KillTimer(hwndMain, loadGameTimerEvent);\r
9449   loadGameTimerEvent = 0;\r
9450   return TRUE;\r
9451 }\r
9452 \r
9453 void\r
9454 StartLoadGameTimer(long millisec)\r
9455 {\r
9456   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9457                                 (UINT) millisec, NULL);\r
9458 }\r
9459 \r
9460 void\r
9461 AutoSaveGame()\r
9462 {\r
9463   char *defName;\r
9464   FILE *f;\r
9465   char fileTitle[MSG_SIZ];\r
9466 \r
9467   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9468   f = OpenFileDialog(hwndMain, "a", defName,\r
9469                      appData.oldSaveStyle ? "gam" : "pgn",\r
9470                      GAME_FILT, \r
9471                      "Save Game to File", NULL, fileTitle, NULL);\r
9472   if (f != NULL) {\r
9473     SaveGame(f, 0, "");\r
9474     fclose(f);\r
9475   }\r
9476 }\r
9477 \r
9478 \r
9479 void\r
9480 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9481 {\r
9482   if (delayedTimerEvent != 0) {\r
9483     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9484       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9485     }\r
9486     KillTimer(hwndMain, delayedTimerEvent);\r
9487     delayedTimerEvent = 0;\r
9488     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9489     delayedTimerCallback();\r
9490   }\r
9491   delayedTimerCallback = cb;\r
9492   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9493                                 (UINT) millisec, NULL);\r
9494 }\r
9495 \r
9496 DelayedEventCallback\r
9497 GetDelayedEvent()\r
9498 {\r
9499   if (delayedTimerEvent) {\r
9500     return delayedTimerCallback;\r
9501   } else {\r
9502     return NULL;\r
9503   }\r
9504 }\r
9505 \r
9506 void\r
9507 CancelDelayedEvent()\r
9508 {\r
9509   if (delayedTimerEvent) {\r
9510     KillTimer(hwndMain, delayedTimerEvent);\r
9511     delayedTimerEvent = 0;\r
9512   }\r
9513 }\r
9514 \r
9515 DWORD GetWin32Priority(int nice)\r
9516 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9517 /*\r
9518 REALTIME_PRIORITY_CLASS     0x00000100\r
9519 HIGH_PRIORITY_CLASS         0x00000080\r
9520 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9521 NORMAL_PRIORITY_CLASS       0x00000020\r
9522 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9523 IDLE_PRIORITY_CLASS         0x00000040\r
9524 */\r
9525         if (nice < -15) return 0x00000080;\r
9526         if (nice < 0)   return 0x00008000;\r
9527         if (nice == 0)  return 0x00000020;\r
9528         if (nice < 15)  return 0x00004000;\r
9529         return 0x00000040;\r
9530 }\r
9531 \r
9532 /* Start a child process running the given program.\r
9533    The process's standard output can be read from "from", and its\r
9534    standard input can be written to "to".\r
9535    Exit with fatal error if anything goes wrong.\r
9536    Returns an opaque pointer that can be used to destroy the process\r
9537    later.\r
9538 */\r
9539 int\r
9540 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9541 {\r
9542 #define BUFSIZE 4096\r
9543 \r
9544   HANDLE hChildStdinRd, hChildStdinWr,\r
9545     hChildStdoutRd, hChildStdoutWr;\r
9546   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9547   SECURITY_ATTRIBUTES saAttr;\r
9548   BOOL fSuccess;\r
9549   PROCESS_INFORMATION piProcInfo;\r
9550   STARTUPINFO siStartInfo;\r
9551   ChildProc *cp;\r
9552   char buf[MSG_SIZ];\r
9553   DWORD err;\r
9554 \r
9555   if (appData.debugMode) {\r
9556     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9557   }\r
9558 \r
9559   *pr = NoProc;\r
9560 \r
9561   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9562   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9563   saAttr.bInheritHandle = TRUE;\r
9564   saAttr.lpSecurityDescriptor = NULL;\r
9565 \r
9566   /*\r
9567    * The steps for redirecting child's STDOUT:\r
9568    *     1. Create anonymous pipe to be STDOUT for child.\r
9569    *     2. Create a noninheritable duplicate of read handle,\r
9570    *         and close the inheritable read handle.\r
9571    */\r
9572 \r
9573   /* Create a pipe for the child's STDOUT. */\r
9574   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9575     return GetLastError();\r
9576   }\r
9577 \r
9578   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9579   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9580                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9581                              FALSE,     /* not inherited */\r
9582                              DUPLICATE_SAME_ACCESS);\r
9583   if (! fSuccess) {\r
9584     return GetLastError();\r
9585   }\r
9586   CloseHandle(hChildStdoutRd);\r
9587 \r
9588   /*\r
9589    * The steps for redirecting child's STDIN:\r
9590    *     1. Create anonymous pipe to be STDIN for child.\r
9591    *     2. Create a noninheritable duplicate of write handle,\r
9592    *         and close the inheritable write handle.\r
9593    */\r
9594 \r
9595   /* Create a pipe for the child's STDIN. */\r
9596   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9597     return GetLastError();\r
9598   }\r
9599 \r
9600   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9601   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9602                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9603                              FALSE,     /* not inherited */\r
9604                              DUPLICATE_SAME_ACCESS);\r
9605   if (! fSuccess) {\r
9606     return GetLastError();\r
9607   }\r
9608   CloseHandle(hChildStdinWr);\r
9609 \r
9610   /* Arrange to (1) look in dir for the child .exe file, and\r
9611    * (2) have dir be the child's working directory.  Interpret\r
9612    * dir relative to the directory WinBoard loaded from. */\r
9613   GetCurrentDirectory(MSG_SIZ, buf);\r
9614   SetCurrentDirectory(installDir);\r
9615   SetCurrentDirectory(dir);\r
9616 \r
9617   /* Now create the child process. */\r
9618 \r
9619   siStartInfo.cb = sizeof(STARTUPINFO);\r
9620   siStartInfo.lpReserved = NULL;\r
9621   siStartInfo.lpDesktop = NULL;\r
9622   siStartInfo.lpTitle = NULL;\r
9623   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9624   siStartInfo.cbReserved2 = 0;\r
9625   siStartInfo.lpReserved2 = NULL;\r
9626   siStartInfo.hStdInput = hChildStdinRd;\r
9627   siStartInfo.hStdOutput = hChildStdoutWr;\r
9628   siStartInfo.hStdError = hChildStdoutWr;\r
9629 \r
9630   fSuccess = CreateProcess(NULL,\r
9631                            cmdLine,        /* command line */\r
9632                            NULL,           /* process security attributes */\r
9633                            NULL,           /* primary thread security attrs */\r
9634                            TRUE,           /* handles are inherited */\r
9635                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9636                            NULL,           /* use parent's environment */\r
9637                            NULL,\r
9638                            &siStartInfo, /* STARTUPINFO pointer */\r
9639                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9640 \r
9641   err = GetLastError();\r
9642   SetCurrentDirectory(buf); /* return to prev directory */\r
9643   if (! fSuccess) {\r
9644     return err;\r
9645   }\r
9646 \r
9647   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9648     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9649     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9650   }\r
9651 \r
9652   /* Close the handles we don't need in the parent */\r
9653   CloseHandle(piProcInfo.hThread);\r
9654   CloseHandle(hChildStdinRd);\r
9655   CloseHandle(hChildStdoutWr);\r
9656 \r
9657   /* Prepare return value */\r
9658   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9659   cp->kind = CPReal;\r
9660   cp->hProcess = piProcInfo.hProcess;\r
9661   cp->pid = piProcInfo.dwProcessId;\r
9662   cp->hFrom = hChildStdoutRdDup;\r
9663   cp->hTo = hChildStdinWrDup;\r
9664 \r
9665   *pr = (void *) cp;\r
9666 \r
9667   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9668      2000 where engines sometimes don't see the initial command(s)\r
9669      from WinBoard and hang.  I don't understand how that can happen,\r
9670      but the Sleep is harmless, so I've put it in.  Others have also\r
9671      reported what may be the same problem, so hopefully this will fix\r
9672      it for them too.  */\r
9673   Sleep(500);\r
9674 \r
9675   return NO_ERROR;\r
9676 }\r
9677 \r
9678 \r
9679 void\r
9680 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9681 {\r
9682   ChildProc *cp; int result;\r
9683 \r
9684   cp = (ChildProc *) pr;\r
9685   if (cp == NULL) return;\r
9686 \r
9687   switch (cp->kind) {\r
9688   case CPReal:\r
9689     /* TerminateProcess is considered harmful, so... */\r
9690     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9691     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9692     /* The following doesn't work because the chess program\r
9693        doesn't "have the same console" as WinBoard.  Maybe\r
9694        we could arrange for this even though neither WinBoard\r
9695        nor the chess program uses a console for stdio? */\r
9696     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9697 \r
9698     /* [AS] Special termination modes for misbehaving programs... */\r
9699     if( signal == 9 ) { \r
9700         result = TerminateProcess( cp->hProcess, 0 );\r
9701 \r
9702         if ( appData.debugMode) {\r
9703             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9704         }\r
9705     }\r
9706     else if( signal == 10 ) {\r
9707         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9708 \r
9709         if( dw != WAIT_OBJECT_0 ) {\r
9710             result = TerminateProcess( cp->hProcess, 0 );\r
9711 \r
9712             if ( appData.debugMode) {\r
9713                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9714             }\r
9715 \r
9716         }\r
9717     }\r
9718 \r
9719     CloseHandle(cp->hProcess);\r
9720     break;\r
9721 \r
9722   case CPComm:\r
9723     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9724     break;\r
9725 \r
9726   case CPSock:\r
9727     closesocket(cp->sock);\r
9728     WSACleanup();\r
9729     break;\r
9730 \r
9731   case CPRcmd:\r
9732     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9733     closesocket(cp->sock);\r
9734     closesocket(cp->sock2);\r
9735     WSACleanup();\r
9736     break;\r
9737   }\r
9738   free(cp);\r
9739 }\r
9740 \r
9741 void\r
9742 InterruptChildProcess(ProcRef pr)\r
9743 {\r
9744   ChildProc *cp;\r
9745 \r
9746   cp = (ChildProc *) pr;\r
9747   if (cp == NULL) return;\r
9748   switch (cp->kind) {\r
9749   case CPReal:\r
9750     /* The following doesn't work because the chess program\r
9751        doesn't "have the same console" as WinBoard.  Maybe\r
9752        we could arrange for this even though neither WinBoard\r
9753        nor the chess program uses a console for stdio */\r
9754     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9755     break;\r
9756 \r
9757   case CPComm:\r
9758   case CPSock:\r
9759     /* Can't interrupt */\r
9760     break;\r
9761 \r
9762   case CPRcmd:\r
9763     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9764     break;\r
9765   }\r
9766 }\r
9767 \r
9768 \r
9769 int\r
9770 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9771 {\r
9772   char cmdLine[MSG_SIZ];\r
9773 \r
9774   if (port[0] == NULLCHAR) {\r
9775     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9776   } else {\r
9777     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9778   }\r
9779   return StartChildProcess(cmdLine, "", pr);\r
9780 }\r
9781 \r
9782 \r
9783 /* Code to open TCP sockets */\r
9784 \r
9785 int\r
9786 OpenTCP(char *host, char *port, ProcRef *pr)\r
9787 {\r
9788   ChildProc *cp;\r
9789   int err;\r
9790   SOCKET s;\r
9791   struct sockaddr_in sa, mysa;\r
9792   struct hostent FAR *hp;\r
9793   unsigned short uport;\r
9794   WORD wVersionRequested;\r
9795   WSADATA wsaData;\r
9796 \r
9797   /* Initialize socket DLL */\r
9798   wVersionRequested = MAKEWORD(1, 1);\r
9799   err = WSAStartup(wVersionRequested, &wsaData);\r
9800   if (err != 0) return err;\r
9801 \r
9802   /* Make socket */\r
9803   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9804     err = WSAGetLastError();\r
9805     WSACleanup();\r
9806     return err;\r
9807   }\r
9808 \r
9809   /* Bind local address using (mostly) don't-care values.\r
9810    */\r
9811   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9812   mysa.sin_family = AF_INET;\r
9813   mysa.sin_addr.s_addr = INADDR_ANY;\r
9814   uport = (unsigned short) 0;\r
9815   mysa.sin_port = htons(uport);\r
9816   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9817       == SOCKET_ERROR) {\r
9818     err = WSAGetLastError();\r
9819     WSACleanup();\r
9820     return err;\r
9821   }\r
9822 \r
9823   /* Resolve remote host name */\r
9824   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9825   if (!(hp = gethostbyname(host))) {\r
9826     unsigned int b0, b1, b2, b3;\r
9827 \r
9828     err = WSAGetLastError();\r
9829 \r
9830     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9831       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9832       hp->h_addrtype = AF_INET;\r
9833       hp->h_length = 4;\r
9834       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9835       hp->h_addr_list[0] = (char *) malloc(4);\r
9836       hp->h_addr_list[0][0] = (char) b0;\r
9837       hp->h_addr_list[0][1] = (char) b1;\r
9838       hp->h_addr_list[0][2] = (char) b2;\r
9839       hp->h_addr_list[0][3] = (char) b3;\r
9840     } else {\r
9841       WSACleanup();\r
9842       return err;\r
9843     }\r
9844   }\r
9845   sa.sin_family = hp->h_addrtype;\r
9846   uport = (unsigned short) atoi(port);\r
9847   sa.sin_port = htons(uport);\r
9848   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9849 \r
9850   /* Make connection */\r
9851   if (connect(s, (struct sockaddr *) &sa,\r
9852               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9853     err = WSAGetLastError();\r
9854     WSACleanup();\r
9855     return err;\r
9856   }\r
9857 \r
9858   /* Prepare return value */\r
9859   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9860   cp->kind = CPSock;\r
9861   cp->sock = s;\r
9862   *pr = (ProcRef *) cp;\r
9863 \r
9864   return NO_ERROR;\r
9865 }\r
9866 \r
9867 int\r
9868 OpenCommPort(char *name, ProcRef *pr)\r
9869 {\r
9870   HANDLE h;\r
9871   COMMTIMEOUTS ct;\r
9872   ChildProc *cp;\r
9873   char fullname[MSG_SIZ];\r
9874 \r
9875   if (*name != '\\')\r
9876     sprintf(fullname, "\\\\.\\%s", name);\r
9877   else\r
9878     strcpy(fullname, name);\r
9879 \r
9880   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9881                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9882   if (h == (HANDLE) -1) {\r
9883     return GetLastError();\r
9884   }\r
9885   hCommPort = h;\r
9886 \r
9887   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9888 \r
9889   /* Accumulate characters until a 100ms pause, then parse */\r
9890   ct.ReadIntervalTimeout = 100;\r
9891   ct.ReadTotalTimeoutMultiplier = 0;\r
9892   ct.ReadTotalTimeoutConstant = 0;\r
9893   ct.WriteTotalTimeoutMultiplier = 0;\r
9894   ct.WriteTotalTimeoutConstant = 0;\r
9895   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9896 \r
9897   /* Prepare return value */\r
9898   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9899   cp->kind = CPComm;\r
9900   cp->hFrom = h;\r
9901   cp->hTo = h;\r
9902   *pr = (ProcRef *) cp;\r
9903 \r
9904   return NO_ERROR;\r
9905 }\r
9906 \r
9907 int\r
9908 OpenLoopback(ProcRef *pr)\r
9909 {\r
9910   DisplayFatalError("Not implemented", 0, 1);\r
9911   return NO_ERROR;\r
9912 }\r
9913 \r
9914 \r
9915 int\r
9916 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9917 {\r
9918   ChildProc *cp;\r
9919   int err;\r
9920   SOCKET s, s2, s3;\r
9921   struct sockaddr_in sa, mysa;\r
9922   struct hostent FAR *hp;\r
9923   unsigned short uport;\r
9924   WORD wVersionRequested;\r
9925   WSADATA wsaData;\r
9926   int fromPort;\r
9927   char stderrPortStr[MSG_SIZ];\r
9928 \r
9929   /* Initialize socket DLL */\r
9930   wVersionRequested = MAKEWORD(1, 1);\r
9931   err = WSAStartup(wVersionRequested, &wsaData);\r
9932   if (err != 0) return err;\r
9933 \r
9934   /* Resolve remote host name */\r
9935   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9936   if (!(hp = gethostbyname(host))) {\r
9937     unsigned int b0, b1, b2, b3;\r
9938 \r
9939     err = WSAGetLastError();\r
9940 \r
9941     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9942       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9943       hp->h_addrtype = AF_INET;\r
9944       hp->h_length = 4;\r
9945       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9946       hp->h_addr_list[0] = (char *) malloc(4);\r
9947       hp->h_addr_list[0][0] = (char) b0;\r
9948       hp->h_addr_list[0][1] = (char) b1;\r
9949       hp->h_addr_list[0][2] = (char) b2;\r
9950       hp->h_addr_list[0][3] = (char) b3;\r
9951     } else {\r
9952       WSACleanup();\r
9953       return err;\r
9954     }\r
9955   }\r
9956   sa.sin_family = hp->h_addrtype;\r
9957   uport = (unsigned short) 514;\r
9958   sa.sin_port = htons(uport);\r
9959   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9960 \r
9961   /* Bind local socket to unused "privileged" port address\r
9962    */\r
9963   s = INVALID_SOCKET;\r
9964   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9965   mysa.sin_family = AF_INET;\r
9966   mysa.sin_addr.s_addr = INADDR_ANY;\r
9967   for (fromPort = 1023;; fromPort--) {\r
9968     if (fromPort < 0) {\r
9969       WSACleanup();\r
9970       return WSAEADDRINUSE;\r
9971     }\r
9972     if (s == INVALID_SOCKET) {\r
9973       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9974         err = WSAGetLastError();\r
9975         WSACleanup();\r
9976         return err;\r
9977       }\r
9978     }\r
9979     uport = (unsigned short) fromPort;\r
9980     mysa.sin_port = htons(uport);\r
9981     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9982         == SOCKET_ERROR) {\r
9983       err = WSAGetLastError();\r
9984       if (err == WSAEADDRINUSE) continue;\r
9985       WSACleanup();\r
9986       return err;\r
9987     }\r
9988     if (connect(s, (struct sockaddr *) &sa,\r
9989       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9990       err = WSAGetLastError();\r
9991       if (err == WSAEADDRINUSE) {\r
9992         closesocket(s);\r
9993         s = -1;\r
9994         continue;\r
9995       }\r
9996       WSACleanup();\r
9997       return err;\r
9998     }\r
9999     break;\r
10000   }\r
10001 \r
10002   /* Bind stderr local socket to unused "privileged" port address\r
10003    */\r
10004   s2 = INVALID_SOCKET;\r
10005   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10006   mysa.sin_family = AF_INET;\r
10007   mysa.sin_addr.s_addr = INADDR_ANY;\r
10008   for (fromPort = 1023;; fromPort--) {\r
10009     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10010     if (fromPort < 0) {\r
10011       (void) closesocket(s);\r
10012       WSACleanup();\r
10013       return WSAEADDRINUSE;\r
10014     }\r
10015     if (s2 == INVALID_SOCKET) {\r
10016       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10017         err = WSAGetLastError();\r
10018         closesocket(s);\r
10019         WSACleanup();\r
10020         return err;\r
10021       }\r
10022     }\r
10023     uport = (unsigned short) fromPort;\r
10024     mysa.sin_port = htons(uport);\r
10025     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10026         == SOCKET_ERROR) {\r
10027       err = WSAGetLastError();\r
10028       if (err == WSAEADDRINUSE) continue;\r
10029       (void) closesocket(s);\r
10030       WSACleanup();\r
10031       return err;\r
10032     }\r
10033     if (listen(s2, 1) == SOCKET_ERROR) {\r
10034       err = WSAGetLastError();\r
10035       if (err == WSAEADDRINUSE) {\r
10036         closesocket(s2);\r
10037         s2 = INVALID_SOCKET;\r
10038         continue;\r
10039       }\r
10040       (void) closesocket(s);\r
10041       (void) closesocket(s2);\r
10042       WSACleanup();\r
10043       return err;\r
10044     }\r
10045     break;\r
10046   }\r
10047   prevStderrPort = fromPort; // remember port used\r
10048   sprintf(stderrPortStr, "%d", fromPort);\r
10049 \r
10050   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\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 \r
10058   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10059     err = WSAGetLastError();\r
10060     (void) closesocket(s);\r
10061     (void) closesocket(s2);\r
10062     WSACleanup();\r
10063     return err;\r
10064   }\r
10065   if (*user == NULLCHAR) user = UserName();\r
10066   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10067     err = WSAGetLastError();\r
10068     (void) closesocket(s);\r
10069     (void) closesocket(s2);\r
10070     WSACleanup();\r
10071     return err;\r
10072   }\r
10073   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10074     err = WSAGetLastError();\r
10075     (void) closesocket(s);\r
10076     (void) closesocket(s2);\r
10077     WSACleanup();\r
10078     return err;\r
10079   }\r
10080 \r
10081   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10082     err = WSAGetLastError();\r
10083     (void) closesocket(s);\r
10084     (void) closesocket(s2);\r
10085     WSACleanup();\r
10086     return err;\r
10087   }\r
10088   (void) closesocket(s2);  /* Stop listening */\r
10089 \r
10090   /* Prepare return value */\r
10091   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10092   cp->kind = CPRcmd;\r
10093   cp->sock = s;\r
10094   cp->sock2 = s3;\r
10095   *pr = (ProcRef *) cp;\r
10096 \r
10097   return NO_ERROR;\r
10098 }\r
10099 \r
10100 \r
10101 InputSourceRef\r
10102 AddInputSource(ProcRef pr, int lineByLine,\r
10103                InputCallback func, VOIDSTAR closure)\r
10104 {\r
10105   InputSource *is, *is2 = NULL;\r
10106   ChildProc *cp = (ChildProc *) pr;\r
10107 \r
10108   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10109   is->lineByLine = lineByLine;\r
10110   is->func = func;\r
10111   is->closure = closure;\r
10112   is->second = NULL;\r
10113   is->next = is->buf;\r
10114   if (pr == NoProc) {\r
10115     is->kind = CPReal;\r
10116     consoleInputSource = is;\r
10117   } else {\r
10118     is->kind = cp->kind;\r
10119     /* \r
10120         [AS] Try to avoid a race condition if the thread is given control too early:\r
10121         we create all threads suspended so that the is->hThread variable can be\r
10122         safely assigned, then let the threads start with ResumeThread.\r
10123     */\r
10124     switch (cp->kind) {\r
10125     case CPReal:\r
10126       is->hFile = cp->hFrom;\r
10127       cp->hFrom = NULL; /* now owned by InputThread */\r
10128       is->hThread =\r
10129         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10130                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10131       break;\r
10132 \r
10133     case CPComm:\r
10134       is->hFile = cp->hFrom;\r
10135       cp->hFrom = NULL; /* now owned by InputThread */\r
10136       is->hThread =\r
10137         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10138                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10139       break;\r
10140 \r
10141     case CPSock:\r
10142       is->sock = cp->sock;\r
10143       is->hThread =\r
10144         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10145                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10146       break;\r
10147 \r
10148     case CPRcmd:\r
10149       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10150       *is2 = *is;\r
10151       is->sock = cp->sock;\r
10152       is->second = is2;\r
10153       is2->sock = cp->sock2;\r
10154       is2->second = is2;\r
10155       is->hThread =\r
10156         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10157                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10158       is2->hThread =\r
10159         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10160                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10161       break;\r
10162     }\r
10163 \r
10164     if( is->hThread != NULL ) {\r
10165         ResumeThread( is->hThread );\r
10166     }\r
10167 \r
10168     if( is2 != NULL && is2->hThread != NULL ) {\r
10169         ResumeThread( is2->hThread );\r
10170     }\r
10171   }\r
10172 \r
10173   return (InputSourceRef) is;\r
10174 }\r
10175 \r
10176 void\r
10177 RemoveInputSource(InputSourceRef isr)\r
10178 {\r
10179   InputSource *is;\r
10180 \r
10181   is = (InputSource *) isr;\r
10182   is->hThread = NULL;  /* tell thread to stop */\r
10183   CloseHandle(is->hThread);\r
10184   if (is->second != NULL) {\r
10185     is->second->hThread = NULL;\r
10186     CloseHandle(is->second->hThread);\r
10187   }\r
10188 }\r
10189 \r
10190 int no_wrap(char *message, int count)\r
10191 {\r
10192     ConsoleOutput(message, count, FALSE);\r
10193     return count;\r
10194 }\r
10195 \r
10196 int\r
10197 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10198 {\r
10199   DWORD dOutCount;\r
10200   int outCount = SOCKET_ERROR;\r
10201   ChildProc *cp = (ChildProc *) pr;\r
10202   static OVERLAPPED ovl;\r
10203   static int line = 0;\r
10204 \r
10205   if (pr == NoProc)\r
10206   {\r
10207     if (appData.noJoin || !appData.useInternalWrap)\r
10208       return no_wrap(message, count);\r
10209     else\r
10210     {\r
10211       int width = get_term_width();\r
10212       int len = wrap(NULL, message, count, width, &line);\r
10213       char *msg = malloc(len);\r
10214       int dbgchk;\r
10215 \r
10216       if (!msg)\r
10217         return no_wrap(message, count);\r
10218       else\r
10219       {\r
10220         dbgchk = wrap(msg, message, count, width, &line);\r
10221         if (dbgchk != len && appData.debugMode)\r
10222             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
10223         ConsoleOutput(msg, len, FALSE);\r
10224         free(msg);\r
10225         return len;\r
10226       }\r
10227     }\r
10228   }\r
10229 \r
10230   if (ovl.hEvent == NULL) {\r
10231     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10232   }\r
10233   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10234 \r
10235   switch (cp->kind) {\r
10236   case CPSock:\r
10237   case CPRcmd:\r
10238     outCount = send(cp->sock, message, count, 0);\r
10239     if (outCount == SOCKET_ERROR) {\r
10240       *outError = WSAGetLastError();\r
10241     } else {\r
10242       *outError = NO_ERROR;\r
10243     }\r
10244     break;\r
10245 \r
10246   case CPReal:\r
10247     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10248                   &dOutCount, NULL)) {\r
10249       *outError = NO_ERROR;\r
10250       outCount = (int) dOutCount;\r
10251     } else {\r
10252       *outError = GetLastError();\r
10253     }\r
10254     break;\r
10255 \r
10256   case CPComm:\r
10257     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10258                             &dOutCount, &ovl);\r
10259     if (*outError == NO_ERROR) {\r
10260       outCount = (int) dOutCount;\r
10261     }\r
10262     break;\r
10263   }\r
10264   return outCount;\r
10265 }\r
10266 \r
10267 int\r
10268 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10269                        long msdelay)\r
10270 {\r
10271   /* Ignore delay, not implemented for WinBoard */\r
10272   return OutputToProcess(pr, message, count, outError);\r
10273 }\r
10274 \r
10275 \r
10276 void\r
10277 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10278                         char *buf, int count, int error)\r
10279 {\r
10280   DisplayFatalError("Not implemented", 0, 1);\r
10281 }\r
10282 \r
10283 /* see wgamelist.c for Game List functions */\r
10284 /* see wedittags.c for Edit Tags functions */\r
10285 \r
10286 \r
10287 VOID\r
10288 ICSInitScript()\r
10289 {\r
10290   FILE *f;\r
10291   char buf[MSG_SIZ];\r
10292   char *dummy;\r
10293 \r
10294   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10295     f = fopen(buf, "r");\r
10296     if (f != NULL) {\r
10297       ProcessICSInitScript(f);\r
10298       fclose(f);\r
10299     }\r
10300   }\r
10301 }\r
10302 \r
10303 \r
10304 VOID\r
10305 StartAnalysisClock()\r
10306 {\r
10307   if (analysisTimerEvent) return;\r
10308   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10309                                         (UINT) 2000, NULL);\r
10310 }\r
10311 \r
10312 VOID\r
10313 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10314 {\r
10315   highlightInfo.sq[0].x = fromX;\r
10316   highlightInfo.sq[0].y = fromY;\r
10317   highlightInfo.sq[1].x = toX;\r
10318   highlightInfo.sq[1].y = toY;\r
10319 }\r
10320 \r
10321 VOID\r
10322 ClearHighlights()\r
10323 {\r
10324   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10325     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10326 }\r
10327 \r
10328 VOID\r
10329 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10330 {\r
10331   premoveHighlightInfo.sq[0].x = fromX;\r
10332   premoveHighlightInfo.sq[0].y = fromY;\r
10333   premoveHighlightInfo.sq[1].x = toX;\r
10334   premoveHighlightInfo.sq[1].y = toY;\r
10335 }\r
10336 \r
10337 VOID\r
10338 ClearPremoveHighlights()\r
10339 {\r
10340   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10341     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10342 }\r
10343 \r
10344 VOID\r
10345 ShutDownFrontEnd()\r
10346 {\r
10347   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10348   DeleteClipboardTempFiles();\r
10349 }\r
10350 \r
10351 void\r
10352 BoardToTop()\r
10353 {\r
10354     if (IsIconic(hwndMain))\r
10355       ShowWindow(hwndMain, SW_RESTORE);\r
10356 \r
10357     SetActiveWindow(hwndMain);\r
10358 }\r
10359 \r
10360 /*\r
10361  * Prototypes for animation support routines\r
10362  */\r
10363 static void ScreenSquare(int column, int row, POINT * pt);\r
10364 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10365      POINT frames[], int * nFrames);\r
10366 \r
10367 \r
10368 void\r
10369 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10370 {       // [HGM] atomic: animate blast wave\r
10371         int i;\r
10372 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10373         explodeInfo.fromX = fromX;\r
10374         explodeInfo.fromY = fromY;\r
10375         explodeInfo.toX = toX;\r
10376         explodeInfo.toY = toY;\r
10377         for(i=1; i<nFrames; i++) {\r
10378             explodeInfo.radius = (i*180)/(nFrames-1);\r
10379             DrawPosition(FALSE, NULL);\r
10380             Sleep(appData.animSpeed);\r
10381         }\r
10382         explodeInfo.radius = 0;\r
10383         DrawPosition(TRUE, NULL);\r
10384 }\r
10385 \r
10386 #define kFactor 4\r
10387 \r
10388 void\r
10389 AnimateMove(board, fromX, fromY, toX, toY)\r
10390      Board board;\r
10391      int fromX;\r
10392      int fromY;\r
10393      int toX;\r
10394      int toY;\r
10395 {\r
10396   ChessSquare piece;\r
10397   POINT start, finish, mid;\r
10398   POINT frames[kFactor * 2 + 1];\r
10399   int nFrames, n;\r
10400 \r
10401   if (!appData.animate) return;\r
10402   if (doingSizing) return;\r
10403   if (fromY < 0 || fromX < 0) return;\r
10404   piece = board[fromY][fromX];\r
10405   if (piece >= EmptySquare) return;\r
10406 \r
10407   ScreenSquare(fromX, fromY, &start);\r
10408   ScreenSquare(toX, toY, &finish);\r
10409 \r
10410   /* All pieces except knights move in straight line */\r
10411   if (piece != WhiteKnight && piece != BlackKnight) {\r
10412     mid.x = start.x + (finish.x - start.x) / 2;\r
10413     mid.y = start.y + (finish.y - start.y) / 2;\r
10414   } else {\r
10415     /* Knight: make diagonal movement then straight */\r
10416     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10417        mid.x = start.x + (finish.x - start.x) / 2;\r
10418        mid.y = finish.y;\r
10419      } else {\r
10420        mid.x = finish.x;\r
10421        mid.y = start.y + (finish.y - start.y) / 2;\r
10422      }\r
10423   }\r
10424   \r
10425   /* Don't use as many frames for very short moves */\r
10426   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10427     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10428   else\r
10429     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10430 \r
10431   animInfo.from.x = fromX;\r
10432   animInfo.from.y = fromY;\r
10433   animInfo.to.x = toX;\r
10434   animInfo.to.y = toY;\r
10435   animInfo.lastpos = start;\r
10436   animInfo.piece = piece;\r
10437   for (n = 0; n < nFrames; n++) {\r
10438     animInfo.pos = frames[n];\r
10439     DrawPosition(FALSE, NULL);\r
10440     animInfo.lastpos = animInfo.pos;\r
10441     Sleep(appData.animSpeed);\r
10442   }\r
10443   animInfo.pos = finish;\r
10444   DrawPosition(FALSE, NULL);\r
10445   animInfo.piece = EmptySquare;\r
10446   if(gameInfo.variant == VariantAtomic && \r
10447      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10448         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10449 }\r
10450 \r
10451 /*      Convert board position to corner of screen rect and color       */\r
10452 \r
10453 static void\r
10454 ScreenSquare(column, row, pt)\r
10455      int column; int row; POINT * pt;\r
10456 {\r
10457   if (flipView) {\r
10458     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10459     pt->y = lineGap + row * (squareSize + lineGap);\r
10460   } else {\r
10461     pt->x = lineGap + column * (squareSize + lineGap);\r
10462     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10463   }\r
10464 }\r
10465 \r
10466 /*      Generate a series of frame coords from start->mid->finish.\r
10467         The movement rate doubles until the half way point is\r
10468         reached, then halves back down to the final destination,\r
10469         which gives a nice slow in/out effect. The algorithmn\r
10470         may seem to generate too many intermediates for short\r
10471         moves, but remember that the purpose is to attract the\r
10472         viewers attention to the piece about to be moved and\r
10473         then to where it ends up. Too few frames would be less\r
10474         noticeable.                                             */\r
10475 \r
10476 static void\r
10477 Tween(start, mid, finish, factor, frames, nFrames)\r
10478      POINT * start; POINT * mid;\r
10479      POINT * finish; int factor;\r
10480      POINT frames[]; int * nFrames;\r
10481 {\r
10482   int n, fraction = 1, count = 0;\r
10483 \r
10484   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10485   for (n = 0; n < factor; n++)\r
10486     fraction *= 2;\r
10487   for (n = 0; n < factor; n++) {\r
10488     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10489     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10490     count ++;\r
10491     fraction = fraction / 2;\r
10492   }\r
10493   \r
10494   /* Midpoint */\r
10495   frames[count] = *mid;\r
10496   count ++;\r
10497   \r
10498   /* Slow out, stepping 1/2, then 1/4, ... */\r
10499   fraction = 2;\r
10500   for (n = 0; n < factor; n++) {\r
10501     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10502     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10503     count ++;\r
10504     fraction = fraction * 2;\r
10505   }\r
10506   *nFrames = count;\r
10507 }\r
10508 \r
10509 void\r
10510 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10511 {\r
10512     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10513 \r
10514     EvalGraphSet( first, last, current, pvInfoList );\r
10515 }\r