refactoring of engineoutput
[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 struct {\r
879   char *argName;\r
880   ArgType argType;\r
881   /***\r
882   union {\r
883     String *pString;       // ArgString\r
884     int *pInt;             // ArgInt\r
885     float *pFloat;         // ArgFloat\r
886     Boolean *pBoolean;     // ArgBoolean\r
887     COLORREF *pColor;      // ArgColor\r
888     ColorClass cc;         // ArgAttribs\r
889     String *pFilename;     // ArgFilename\r
890     BoardSize *pBoardSize; // ArgBoardSize\r
891     int whichFont;         // ArgFont\r
892     DCB *pDCB;             // ArgCommSettings\r
893     String *pFilename;     // ArgSettingsFilename\r
894   } argLoc;\r
895   ***/\r
896   LPVOID argLoc;\r
897   BOOL save;\r
898 } ArgDescriptor;\r
899 \r
900 int junk;\r
901 ArgDescriptor argDescriptors[] = {\r
902   /* positional arguments */\r
903   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
904   { "", ArgNone, NULL },\r
905   /* keyword arguments */\r
906   JAWS_ARGS\r
907   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
908   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
909   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
910   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
911   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
912   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
913   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
914   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
915   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
916   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
917   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
918   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
919   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
920   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
921   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
922   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
923   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
924   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
925     FALSE },\r
926   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
927     FALSE },\r
928   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
929     FALSE },\r
930   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
931   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
932     FALSE },\r
933   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
934   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
935   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
936   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
937   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
938   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
939   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
940   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
941   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
942   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
943   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
944   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
945   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
946   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
947   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
948   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
949   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
950   /*!!bitmapDirectory?*/\r
951   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
952   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
953   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
954   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
955   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
956   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
957   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
958   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
959   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
960   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
961   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
962   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
963   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
964   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
965   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
966   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
967   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
968   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
969   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
970   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
971   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
972   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
973   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
974   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
975   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
976   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
977   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
978   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
979   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
980   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
981   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
982   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
983   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
984   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
985   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
986   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
987   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
988   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
989   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
990   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
991   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
992   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
993   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
994   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
995   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
996   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
997   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
998   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
999   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1000   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1001   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1002   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1003   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1004   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1005   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1006   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1007   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1008   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1009   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1010   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1011   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1012   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1013   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1014   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1015   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1016   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1017   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1018   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1019   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1020   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1021   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1022   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1023   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1024   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1025   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1026   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1027   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1028   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1029   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1030   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1031   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1032   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1033   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1034   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1035   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1036   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1037   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1038   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1039   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1040   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1041   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1042   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1043   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1044   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1045     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1046   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1047   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1048   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1049   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1050   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1051   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1052   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1053   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1054     TRUE }, /* must come after all fonts */\r
1055   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1056   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1057     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1058   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1059   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1060   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1061   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1062   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1063   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1064   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1065   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1066   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1067   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1068   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1069   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1070   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1071   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1072   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1073   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1074   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1075   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1076   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1077   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1078   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1079   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1080   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1081   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1082   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1083   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1084   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1085   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1086   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1087   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1088   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1089   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1090   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1091   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1092   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1093   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1094   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1095   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1096   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1097   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1098   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1099   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1100   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1101   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1102   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1103   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1104   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1105   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1106   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1107   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1108   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1109   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1110   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1111   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1112   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1113   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1114   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1115   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1116   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1117   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1118   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1119   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1120   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1121   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1122   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1123   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1124   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1125   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1126   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1127   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1128   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1129   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1130   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1131   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1132   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1133   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1134   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1135   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1136   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1137   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1138   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1139   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1140   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1141   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1142   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1143   { "highlightLastMove", ArgBoolean,\r
1144     (LPVOID) &appData.highlightLastMove, TRUE },\r
1145   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1146   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1147   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1148   { "highlightDragging", ArgBoolean,\r
1149     (LPVOID) &appData.highlightDragging, TRUE },\r
1150   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1151   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1152   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1153   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1154   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1155   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1156   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1157   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1158   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1159   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1160   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1161   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1162   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1163   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1164   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1165   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1166   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1167   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1168   { "soundShout", ArgFilename,\r
1169     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1170   { "soundSShout", ArgFilename,\r
1171     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1172   { "soundChannel1", ArgFilename,\r
1173     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1174   { "soundChannel", ArgFilename,\r
1175     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1176   { "soundKibitz", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1178   { "soundTell", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1180   { "soundChallenge", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1182   { "soundRequest", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1184   { "soundSeek", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1186   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1187   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1188   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1189   { "soundIcsLoss", ArgFilename, \r
1190     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1191   { "soundIcsDraw", ArgFilename, \r
1192     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1193   { "soundIcsUnfinished", ArgFilename, \r
1194     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1195   { "soundIcsAlarm", ArgFilename, \r
1196     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1197   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1198   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1199   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1200   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1201   { "reuseChessPrograms", ArgBoolean,\r
1202     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1203   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1204   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1205   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1206   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1207   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1208   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1209   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1210   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1211   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1212   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1213   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1214   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1215   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1216   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1217   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1218     TRUE },\r
1219   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1220     TRUE },\r
1221   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1222   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1223   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1224   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1225   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1226   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1227   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1228   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1229   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1230   /* [AS] New features */\r
1231   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1232   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1233   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1234   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1235   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1236   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1237   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1238   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1239   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1240   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1241   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1242   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1243   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1244   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1245   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1246   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1247   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1248   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1249   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1250   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1251   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1252   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1253   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1254   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1255   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1256   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1257   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1258   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1259   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1260   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1261   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1262   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1263   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1264   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1265   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1266   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1267   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1268   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1269   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1270   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1271   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1272   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1273   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1274   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1275   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1276   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1277   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1278   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1279   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1280   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1281 \r
1282   /* [HGM] board-size, adjudication and misc. options */\r
1283   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1284   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1285   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1286   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1287   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1288   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1289   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1290   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1291   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1292   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1293   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1294   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1295   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1296   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1297   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1298   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1299   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1300   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1301   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1302   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1303   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1304   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1305   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1306   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1307   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1308   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1309   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1310   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1311   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1312   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1313   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1314   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1315   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1316   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1317 \r
1318 #ifdef ZIPPY\r
1319   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1320   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1321   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1322   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1323   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1324   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1325   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1326   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1327   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1328   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1329   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1330   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1331   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1332     FALSE },\r
1333   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1334   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1335   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1336   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1337   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1338   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1339   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1340     FALSE },\r
1341   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1342   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1343   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1344   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1345   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1346   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1347   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1348   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1349   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1350   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1351   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1352   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1353   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1354   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1355   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1356   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1357   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1358   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1359   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1360 #endif\r
1361   /* [HGM] options for broadcasting and time odds */\r
1362   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1363   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1364   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1365   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1366   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1367   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1368   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1369   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1370   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1371   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1372   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1373   { "keepLineBreaksICS", ArgBoolean, (LPVOID) &appData.noJoin, TRUE },\r
1374   { "wrapContinuationSequence", ArgString, (LPVOID) &appData.wrapContSeq, FALSE },\r
1375   { "useInternalWrap", ArgTrue, (LPVOID) &appData.useInternalWrap, FALSE }, /* noJoin usurps this if set */\r
1376   \r
1377   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1378   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1379   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1380   { "winWidth",  ArgInt, (LPVOID) &wpMain.width,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1381   { "winHeight", ArgInt, (LPVOID) &wpMain.height, TRUE }, //       for attaching auxiliary windows to them\r
1382   { "x", ArgInt, (LPVOID) &wpMain.x, TRUE },\r
1383   { "y", ArgInt, (LPVOID) &wpMain.y, TRUE },\r
1384   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1385   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1386   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1387   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1388   { "analysisX", ArgX,   (LPVOID) &dummy, FALSE }, // [HGM] placement: analysis window no longer exists\r
1389   { "analysisY", ArgY,   (LPVOID) &dummy, FALSE }, //       provided for compatibility with old ini files\r
1390   { "analysisW", ArgInt, (LPVOID) &dummy, FALSE },\r
1391   { "analysisH", ArgInt, (LPVOID) &dummy, FALSE },\r
1392   { "commentX", ArgX,   (LPVOID) &wpComment.x, TRUE },\r
1393   { "commentY", ArgY,   (LPVOID) &wpComment.y, TRUE },\r
1394   { "commentW", ArgInt, (LPVOID) &wpComment.width, TRUE },\r
1395   { "commentH", ArgInt, (LPVOID) &wpComment.height, TRUE },\r
1396   { "tagsX", ArgX,   (LPVOID) &wpTags.x, TRUE },\r
1397   { "tagsY", ArgY,   (LPVOID) &wpTags.y, TRUE },\r
1398   { "tagsW", ArgInt, (LPVOID) &wpTags.width, TRUE },\r
1399   { "tagsH", ArgInt, (LPVOID) &wpTags.height, TRUE },\r
1400   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1401   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1402   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1403   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1404   /* [AS] Layout stuff */\r
1405   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1406   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1407   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1408   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1409   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1410 \r
1411   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1412   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1413   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1414   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1415   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1416 \r
1417   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1418   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1419   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1420   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1421   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1422 \r
1423   { NULL, ArgNone, NULL, FALSE }\r
1424 };\r
1425 \r
1426 \r
1427 /* Kludge for indirection files on command line */\r
1428 char* lastIndirectionFilename;\r
1429 ArgDescriptor argDescriptorIndirection =\r
1430 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1431 \r
1432 \r
1433 VOID\r
1434 ExitArgError(char *msg, char *badArg)\r
1435 {\r
1436   char buf[MSG_SIZ];\r
1437 \r
1438   sprintf(buf, "%s %s", msg, badArg);\r
1439   DisplayFatalError(buf, 0, 2);\r
1440   exit(2);\r
1441 }\r
1442 \r
1443 /* Command line font name parser.  NULL name means do nothing.\r
1444    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1445    For backward compatibility, syntax without the colon is also\r
1446    accepted, but font names with digits in them won't work in that case.\r
1447 */\r
1448 VOID\r
1449 ParseFontName(char *name, MyFontParams *mfp)\r
1450 {\r
1451   char *p, *q;\r
1452   if (name == NULL) return;\r
1453   p = name;\r
1454   q = strchr(p, ':');\r
1455   if (q) {\r
1456     if (q - p >= sizeof(mfp->faceName))\r
1457       ExitArgError("Font name too long:", name);\r
1458     memcpy(mfp->faceName, p, q - p);\r
1459     mfp->faceName[q - p] = NULLCHAR;\r
1460     p = q + 1;\r
1461   } else {\r
1462     q = mfp->faceName;\r
1463     while (*p && !isdigit(*p)) {\r
1464       *q++ = *p++;\r
1465       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1466         ExitArgError("Font name too long:", name);\r
1467     }\r
1468     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1469     *q = NULLCHAR;\r
1470   }\r
1471   if (!*p) ExitArgError("Font point size missing:", name);\r
1472   mfp->pointSize = (float) atof(p);\r
1473   mfp->bold = (strchr(p, 'b') != NULL);\r
1474   mfp->italic = (strchr(p, 'i') != NULL);\r
1475   mfp->underline = (strchr(p, 'u') != NULL);\r
1476   mfp->strikeout = (strchr(p, 's') != NULL);\r
1477   mfp->charset = DEFAULT_CHARSET;\r
1478   q = strchr(p, 'c');\r
1479   if (q)\r
1480     mfp->charset = (BYTE) atoi(q+1);\r
1481 }\r
1482 \r
1483 /* Color name parser.\r
1484    X version accepts X color names, but this one\r
1485    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1486 COLORREF\r
1487 ParseColorName(char *name)\r
1488 {\r
1489   int red, green, blue, count;\r
1490   char buf[MSG_SIZ];\r
1491 \r
1492   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1493   if (count != 3) {\r
1494     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1495       &red, &green, &blue);\r
1496   }\r
1497   if (count != 3) {\r
1498     sprintf(buf, "Can't parse color name %s", name);\r
1499     DisplayError(buf, 0);\r
1500     return RGB(0, 0, 0);\r
1501   }\r
1502   return PALETTERGB(red, green, blue);\r
1503 }\r
1504 \r
1505 \r
1506 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1507 {\r
1508   char *e = argValue;\r
1509   int eff = 0;\r
1510 \r
1511   while (*e) {\r
1512     if (*e == 'b')      eff |= CFE_BOLD;\r
1513     else if (*e == 'i') eff |= CFE_ITALIC;\r
1514     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1515     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1516     else if (*e == '#' || isdigit(*e)) break;\r
1517     e++;\r
1518   }\r
1519   *effects = eff;\r
1520   *color   = ParseColorName(e);\r
1521 }\r
1522 \r
1523 \r
1524 BoardSize\r
1525 ParseBoardSize(char *name)\r
1526 {\r
1527   BoardSize bs = SizeTiny;\r
1528   while (sizeInfo[bs].name != NULL) {\r
1529     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1530     bs++;\r
1531   }\r
1532   ExitArgError("Unrecognized board size value", name);\r
1533   return bs; /* not reached */\r
1534 }\r
1535 \r
1536 \r
1537 char\r
1538 StringGet(void *getClosure)\r
1539 {\r
1540   char **p = (char **) getClosure;\r
1541   return *((*p)++);\r
1542 }\r
1543 \r
1544 char\r
1545 FileGet(void *getClosure)\r
1546 {\r
1547   int c;\r
1548   FILE* f = (FILE*) getClosure;\r
1549 \r
1550   c = getc(f);\r
1551   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1552   if (c == EOF)\r
1553     return NULLCHAR;\r
1554   else\r
1555     return (char) c;\r
1556 }\r
1557 \r
1558 /* Parse settings file named "name". If file found, return the\r
1559    full name in fullname and return TRUE; else return FALSE */\r
1560 BOOLEAN\r
1561 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1562 {\r
1563   char *dummy;\r
1564   FILE *f;\r
1565   int ok; char buf[MSG_SIZ];\r
1566 \r
1567   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1568   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1569     sprintf(buf, "%s.ini", name);\r
1570     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1571   }\r
1572   if (ok) {\r
1573     f = fopen(fullname, "r");\r
1574     if (f != NULL) {\r
1575       ParseArgs(FileGet, f);\r
1576       fclose(f);\r
1577       return TRUE;\r
1578     }\r
1579   }\r
1580   return FALSE;\r
1581 }\r
1582 \r
1583 VOID\r
1584 ParseArgs(GetFunc get, void *cl)\r
1585 {\r
1586   char argName[ARG_MAX];\r
1587   char argValue[ARG_MAX];\r
1588   ArgDescriptor *ad;\r
1589   char start;\r
1590   char *q;\r
1591   int i, octval;\r
1592   char ch;\r
1593   int posarg = 0;\r
1594 \r
1595   ch = get(cl);\r
1596   for (;;) {\r
1597     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1598     if (ch == NULLCHAR) break;\r
1599     if (ch == ';') {\r
1600       /* Comment to end of line */\r
1601       ch = get(cl);\r
1602       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1603       continue;\r
1604     } else if (ch == '/' || ch == '-') {\r
1605       /* Switch */\r
1606       q = argName;\r
1607       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1608              ch != '\n' && ch != '\t') {\r
1609         *q++ = ch;\r
1610         ch = get(cl);\r
1611       }\r
1612       *q = NULLCHAR;\r
1613 \r
1614       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1615         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1616 \r
1617       if (ad->argName == NULL)\r
1618         ExitArgError("Unrecognized argument", argName);\r
1619 \r
1620     } else if (ch == '@') {\r
1621       /* Indirection file */\r
1622       ad = &argDescriptorIndirection;\r
1623       ch = get(cl);\r
1624     } else {\r
1625       /* Positional argument */\r
1626       ad = &argDescriptors[posarg++];\r
1627       strcpy(argName, ad->argName);\r
1628     }\r
1629 \r
1630     if (ad->argType == ArgTrue) {\r
1631       *(Boolean *) ad->argLoc = TRUE;\r
1632       continue;\r
1633     }\r
1634     if (ad->argType == ArgFalse) {\r
1635       *(Boolean *) ad->argLoc = FALSE;\r
1636       continue;\r
1637     }\r
1638 \r
1639     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1640     if (ch == NULLCHAR || ch == '\n') {\r
1641       ExitArgError("No value provided for argument", argName);\r
1642     }\r
1643     q = argValue;\r
1644     if (ch == '{') {\r
1645       // Quoting with { }.  No characters have to (or can) be escaped.\r
1646       // Thus the string cannot contain a '}' character.\r
1647       start = ch;\r
1648       ch = get(cl);\r
1649       while (start) {\r
1650         switch (ch) {\r
1651         case NULLCHAR:\r
1652           start = NULLCHAR;\r
1653           break;\r
1654           \r
1655         case '}':\r
1656           ch = get(cl);\r
1657           start = NULLCHAR;\r
1658           break;\r
1659 \r
1660         default:\r
1661           *q++ = ch;\r
1662           ch = get(cl);\r
1663           break;\r
1664         }\r
1665       }   \r
1666     } else if (ch == '\'' || ch == '"') {\r
1667       // Quoting with ' ' or " ", with \ as escape character.\r
1668       // Inconvenient for long strings that may contain Windows filenames.\r
1669       start = ch;\r
1670       ch = get(cl);\r
1671       while (start) {\r
1672         switch (ch) {\r
1673         case NULLCHAR:\r
1674           start = NULLCHAR;\r
1675           break;\r
1676 \r
1677         default:\r
1678         not_special:\r
1679           *q++ = ch;\r
1680           ch = get(cl);\r
1681           break;\r
1682 \r
1683         case '\'':\r
1684         case '\"':\r
1685           if (ch == start) {\r
1686             ch = get(cl);\r
1687             start = NULLCHAR;\r
1688             break;\r
1689           } else {\r
1690             goto not_special;\r
1691           }\r
1692 \r
1693         case '\\':\r
1694           if (ad->argType == ArgFilename\r
1695               || ad->argType == ArgSettingsFilename) {\r
1696               goto not_special;\r
1697           }\r
1698           ch = get(cl);\r
1699           switch (ch) {\r
1700           case NULLCHAR:\r
1701             ExitArgError("Incomplete \\ escape in value for", argName);\r
1702             break;\r
1703           case 'n':\r
1704             *q++ = '\n';\r
1705             ch = get(cl);\r
1706             break;\r
1707           case 'r':\r
1708             *q++ = '\r';\r
1709             ch = get(cl);\r
1710             break;\r
1711           case 't':\r
1712             *q++ = '\t';\r
1713             ch = get(cl);\r
1714             break;\r
1715           case 'b':\r
1716             *q++ = '\b';\r
1717             ch = get(cl);\r
1718             break;\r
1719           case 'f':\r
1720             *q++ = '\f';\r
1721             ch = get(cl);\r
1722             break;\r
1723           default:\r
1724             octval = 0;\r
1725             for (i = 0; i < 3; i++) {\r
1726               if (ch >= '0' && ch <= '7') {\r
1727                 octval = octval*8 + (ch - '0');\r
1728                 ch = get(cl);\r
1729               } else {\r
1730                 break;\r
1731               }\r
1732             }\r
1733             if (i > 0) {\r
1734               *q++ = (char) octval;\r
1735             } else {\r
1736               *q++ = ch;\r
1737               ch = get(cl);\r
1738             }\r
1739             break;\r
1740           }\r
1741           break;\r
1742         }\r
1743       }\r
1744     } else {\r
1745       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1746         *q++ = ch;\r
1747         ch = get(cl);\r
1748       }\r
1749     }\r
1750     *q = NULLCHAR;\r
1751 \r
1752     switch (ad->argType) {\r
1753     case ArgInt:\r
1754       *(int *) ad->argLoc = atoi(argValue);\r
1755       break;\r
1756 \r
1757     case ArgX:\r
1758       *(int *) ad->argLoc = atoi(argValue) + wpMain.x; // [HGM] placement: translate stored relative to absolute \r
1759       break;\r
1760 \r
1761     case ArgY:\r
1762       *(int *) ad->argLoc = atoi(argValue) + wpMain.y; // (this is really kludgey, it should be done where used...)\r
1763       break;\r
1764 \r
1765     case ArgZ:\r
1766       *(int *) ad->argLoc = atoi(argValue);\r
1767       EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY); \r
1768       break;\r
1769 \r
1770     case ArgFloat:\r
1771       *(float *) ad->argLoc = (float) atof(argValue);\r
1772       break;\r
1773 \r
1774     case ArgString:\r
1775     case ArgFilename:\r
1776       *(char **) ad->argLoc = strdup(argValue);\r
1777       break;\r
1778 \r
1779     case ArgSettingsFilename:\r
1780       {\r
1781         char fullname[MSG_SIZ];\r
1782         if (ParseSettingsFile(argValue, fullname)) {\r
1783           if (ad->argLoc != NULL) {\r
1784             *(char **) ad->argLoc = strdup(fullname);\r
1785           }\r
1786         } else {\r
1787           if (ad->argLoc != NULL) {\r
1788           } else {\r
1789             ExitArgError("Failed to open indirection file", argValue);\r
1790           }\r
1791         }\r
1792       }\r
1793       break;\r
1794 \r
1795     case ArgBoolean:\r
1796       switch (argValue[0]) {\r
1797       case 't':\r
1798       case 'T':\r
1799         *(Boolean *) ad->argLoc = TRUE;\r
1800         break;\r
1801       case 'f':\r
1802       case 'F':\r
1803         *(Boolean *) ad->argLoc = FALSE;\r
1804         break;\r
1805       default:\r
1806         ExitArgError("Unrecognized boolean argument value", argValue);\r
1807         break;\r
1808       }\r
1809       break;\r
1810 \r
1811     case ArgColor:\r
1812       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1813       break;\r
1814 \r
1815     case ArgAttribs: {\r
1816       ColorClass cc = (ColorClass)ad->argLoc;\r
1817       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1818       }\r
1819       break;\r
1820       \r
1821     case ArgBoardSize:\r
1822       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1823       break;\r
1824 \r
1825     case ArgFont:\r
1826       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1827       break;\r
1828 \r
1829     case ArgCommSettings:\r
1830       ParseCommSettings(argValue, &dcb);\r
1831       break;\r
1832 \r
1833     case ArgNone:\r
1834       ExitArgError("Unrecognized argument", argValue);\r
1835       break;\r
1836     case ArgTrue:\r
1837     case ArgFalse: ;\r
1838     }\r
1839   }\r
1840 }\r
1841 \r
1842 VOID\r
1843 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1844 {\r
1845   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1846   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1847   DeleteDC(hdc);\r
1848   lf->lfWidth = 0;\r
1849   lf->lfEscapement = 0;\r
1850   lf->lfOrientation = 0;\r
1851   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1852   lf->lfItalic = mfp->italic;\r
1853   lf->lfUnderline = mfp->underline;\r
1854   lf->lfStrikeOut = mfp->strikeout;\r
1855   lf->lfCharSet = mfp->charset;\r
1856   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1857   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1858   lf->lfQuality = DEFAULT_QUALITY;\r
1859   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1860   strcpy(lf->lfFaceName, mfp->faceName);\r
1861 }\r
1862 \r
1863 VOID\r
1864 CreateFontInMF(MyFont *mf)\r
1865 {\r
1866   LFfromMFP(&mf->lf, &mf->mfp);\r
1867   if (mf->hf) DeleteObject(mf->hf);\r
1868   mf->hf = CreateFontIndirect(&mf->lf);\r
1869 }\r
1870 \r
1871 VOID\r
1872 SetDefaultTextAttribs()\r
1873 {\r
1874   ColorClass cc;\r
1875   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1876     ParseAttribs(&textAttribs[cc].color, \r
1877                  &textAttribs[cc].effects, \r
1878                  defaultTextAttribs[cc]);\r
1879   }\r
1880 }\r
1881 \r
1882 VOID\r
1883 SetDefaultSounds()\r
1884 {\r
1885   ColorClass cc;\r
1886   SoundClass sc;\r
1887   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1888     textAttribs[cc].sound.name = strdup("");\r
1889     textAttribs[cc].sound.data = NULL;\r
1890   }\r
1891   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1892     sounds[sc].name = strdup("");\r
1893     sounds[sc].data = NULL;\r
1894   }\r
1895   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1896 }\r
1897 \r
1898 VOID\r
1899 LoadAllSounds()\r
1900 {\r
1901   ColorClass cc;\r
1902   SoundClass sc;\r
1903   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1904     MyLoadSound(&textAttribs[cc].sound);\r
1905   }\r
1906   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1907     MyLoadSound(&sounds[sc]);\r
1908   }\r
1909 }\r
1910 \r
1911 VOID\r
1912 InitAppData(LPSTR lpCmdLine)\r
1913 {\r
1914   int i, j;\r
1915   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1916   char *dummy, *p;\r
1917 \r
1918   programName = szAppName;\r
1919 \r
1920   /* Initialize to defaults */\r
1921   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1922   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1923   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1924   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1925   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1926   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1927   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1928   SetDefaultTextAttribs();\r
1929   SetDefaultSounds();\r
1930   appData.movesPerSession = MOVES_PER_SESSION;\r
1931   appData.initString = INIT_STRING;\r
1932   appData.secondInitString = INIT_STRING;\r
1933   appData.firstComputerString = COMPUTER_STRING;\r
1934   appData.secondComputerString = COMPUTER_STRING;\r
1935   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1936   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1937   appData.firstPlaysBlack = FALSE;\r
1938   appData.noChessProgram = FALSE;\r
1939   chessProgram = FALSE;\r
1940   appData.firstHost = FIRST_HOST;\r
1941   appData.secondHost = SECOND_HOST;\r
1942   appData.firstDirectory = FIRST_DIRECTORY;\r
1943   appData.secondDirectory = SECOND_DIRECTORY;\r
1944   appData.bitmapDirectory = "";\r
1945   appData.remoteShell = REMOTE_SHELL;\r
1946   appData.remoteUser = "";\r
1947   appData.timeDelay = TIME_DELAY;\r
1948   appData.timeControl = TIME_CONTROL;\r
1949   appData.timeIncrement = TIME_INCREMENT;\r
1950   appData.icsActive = FALSE;\r
1951   appData.icsHost = "";\r
1952   appData.icsPort = ICS_PORT;\r
1953   appData.icsCommPort = ICS_COMM_PORT;\r
1954   appData.icsLogon = ICS_LOGON;\r
1955   appData.icsHelper = "";\r
1956   appData.useTelnet = FALSE;\r
1957   appData.telnetProgram = TELNET_PROGRAM;\r
1958   appData.gateway = "";\r
1959   appData.loadGameFile = "";\r
1960   appData.loadGameIndex = 0;\r
1961   appData.saveGameFile = "";\r
1962   appData.autoSaveGames = FALSE;\r
1963   appData.loadPositionFile = "";\r
1964   appData.loadPositionIndex = 1;\r
1965   appData.savePositionFile = "";\r
1966   appData.matchMode = FALSE;\r
1967   appData.matchGames = 0;\r
1968   appData.monoMode = FALSE;\r
1969   appData.debugMode = FALSE;\r
1970   appData.clockMode = TRUE;\r
1971   boardSize = (BoardSize) -1; /* determine by screen size */\r
1972   appData.Iconic = FALSE; /*unused*/\r
1973   appData.searchTime = "";\r
1974   appData.searchDepth = 0;\r
1975   appData.showCoords = FALSE;\r
1976   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1977   appData.autoCallFlag = FALSE;\r
1978   appData.flipView = FALSE;\r
1979   appData.autoFlipView = TRUE;\r
1980   appData.cmailGameName = "";\r
1981   appData.alwaysPromoteToQueen = FALSE;\r
1982   appData.oldSaveStyle = FALSE;\r
1983   appData.quietPlay = FALSE;\r
1984   appData.showThinking = FALSE;\r
1985   appData.ponderNextMove = TRUE;\r
1986   appData.periodicUpdates = TRUE;\r
1987   appData.popupExitMessage = TRUE;\r
1988   appData.popupMoveErrors = FALSE;\r
1989   appData.autoObserve = FALSE;\r
1990   appData.autoComment = FALSE;\r
1991   appData.animate = TRUE;\r
1992   appData.animSpeed = 10;\r
1993   appData.animateDragging = TRUE;\r
1994   appData.highlightLastMove = TRUE;\r
1995   appData.getMoveList = TRUE;\r
1996   appData.testLegality = TRUE;\r
1997   appData.premove = TRUE;\r
1998   appData.premoveWhite = FALSE;\r
1999   appData.premoveWhiteText = "";\r
2000   appData.premoveBlack = FALSE;\r
2001   appData.premoveBlackText = "";\r
2002   appData.icsAlarm = TRUE;\r
2003   appData.icsAlarmTime = 5000;\r
2004   appData.autoRaiseBoard = TRUE;\r
2005   appData.localLineEditing = TRUE;\r
2006   appData.colorize = TRUE;\r
2007   appData.reuseFirst = TRUE;\r
2008   appData.reuseSecond = TRUE;\r
2009   appData.blindfold = FALSE;\r
2010   appData.icsEngineAnalyze = FALSE;\r
2011   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2012   dcb.DCBlength = sizeof(DCB);\r
2013   dcb.BaudRate = 9600;\r
2014   dcb.fBinary = TRUE;\r
2015   dcb.fParity = FALSE;\r
2016   dcb.fOutxCtsFlow = FALSE;\r
2017   dcb.fOutxDsrFlow = FALSE;\r
2018   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2019   dcb.fDsrSensitivity = FALSE;\r
2020   dcb.fTXContinueOnXoff = TRUE;\r
2021   dcb.fOutX = FALSE;\r
2022   dcb.fInX = FALSE;\r
2023   dcb.fNull = FALSE;\r
2024   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2025   dcb.fAbortOnError = FALSE;\r
2026   dcb.ByteSize = 7;\r
2027   dcb.Parity = SPACEPARITY;\r
2028   dcb.StopBits = ONESTOPBIT;\r
2029   settingsFileName = SETTINGS_FILE;\r
2030   saveSettingsOnExit = TRUE;\r
2031   wpMain.x = CW_USEDEFAULT;\r
2032   wpMain.y = CW_USEDEFAULT;\r
2033   wpComment.x = CW_USEDEFAULT; \r
2034   wpComment.y = CW_USEDEFAULT; \r
2035   wpComment.width = CW_USEDEFAULT;\r
2036   wpComment.height = CW_USEDEFAULT;\r
2037   wpTags.x = CW_USEDEFAULT; \r
2038   wpTags.y = CW_USEDEFAULT; \r
2039   wpTags.width = CW_USEDEFAULT;\r
2040   wpTags.height = CW_USEDEFAULT;\r
2041   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2042   icsNames = ICS_NAMES;\r
2043   firstChessProgramNames = FCP_NAMES;\r
2044   secondChessProgramNames = SCP_NAMES;\r
2045   appData.initialMode = "";\r
2046   appData.variant = "normal";\r
2047   appData.firstProtocolVersion = PROTOVER;\r
2048   appData.secondProtocolVersion = PROTOVER;\r
2049   appData.showButtonBar = TRUE;\r
2050 \r
2051    /* [AS] New properties (see comments in header file) */\r
2052   appData.firstScoreIsAbsolute = FALSE;\r
2053   appData.secondScoreIsAbsolute = FALSE;\r
2054   appData.saveExtendedInfoInPGN = FALSE;\r
2055   appData.hideThinkingFromHuman = FALSE;\r
2056   appData.liteBackTextureFile = "";\r
2057   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2058   appData.darkBackTextureFile = "";\r
2059   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2060   appData.renderPiecesWithFont = "";\r
2061   appData.fontToPieceTable = "";\r
2062   appData.fontBackColorWhite = 0;\r
2063   appData.fontForeColorWhite = 0;\r
2064   appData.fontBackColorBlack = 0;\r
2065   appData.fontForeColorBlack = 0;\r
2066   appData.fontPieceSize = 80;\r
2067   appData.overrideLineGap = 1;\r
2068   appData.adjudicateLossThreshold = 0;\r
2069   appData.delayBeforeQuit = 0;\r
2070   appData.delayAfterQuit = 0;\r
2071   appData.nameOfDebugFile = "winboard.debug";\r
2072   appData.pgnEventHeader = "Computer Chess Game";\r
2073   appData.defaultFrcPosition = -1;\r
2074   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2075   appData.saveOutOfBookInfo = TRUE;\r
2076   appData.showEvalInMoveHistory = TRUE;\r
2077   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2078   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2079   appData.highlightMoveWithArrow = FALSE;\r
2080   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2081   appData.useStickyWindows = TRUE;\r
2082   appData.adjudicateDrawMoves = 0;\r
2083   appData.autoDisplayComment = TRUE;\r
2084   appData.autoDisplayTags = TRUE;\r
2085   appData.firstIsUCI = FALSE;\r
2086   appData.secondIsUCI = FALSE;\r
2087   appData.firstHasOwnBookUCI = TRUE;\r
2088   appData.secondHasOwnBookUCI = TRUE;\r
2089   appData.polyglotDir = "";\r
2090   appData.usePolyglotBook = FALSE;\r
2091   appData.polyglotBook = "";\r
2092   appData.defaultHashSize = 64;\r
2093   appData.defaultCacheSizeEGTB = 4;\r
2094   appData.defaultPathEGTB = "c:\\egtb";\r
2095   appData.firstOptions = "";\r
2096   appData.secondOptions = "";\r
2097 \r
2098   InitWindowPlacement( &wpGameList );\r
2099   InitWindowPlacement( &wpMoveHistory );\r
2100   InitWindowPlacement( &wpEvalGraph );\r
2101   InitWindowPlacement( &wpEngineOutput );\r
2102   InitWindowPlacement( &wpConsole );\r
2103 \r
2104   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2105   appData.NrFiles      = -1;\r
2106   appData.NrRanks      = -1;\r
2107   appData.holdingsSize = -1;\r
2108   appData.testClaims   = FALSE;\r
2109   appData.checkMates   = FALSE;\r
2110   appData.materialDraws= FALSE;\r
2111   appData.trivialDraws = FALSE;\r
2112   appData.ruleMoves    = 51;\r
2113   appData.drawRepeats  = 6;\r
2114   appData.matchPause   = 10000;\r
2115   appData.alphaRank    = FALSE;\r
2116   appData.allWhite     = FALSE;\r
2117   appData.upsideDown   = FALSE;\r
2118   appData.serverPause  = 15;\r
2119   appData.serverMovesName   = NULL;\r
2120   appData.suppressLoadMoves = FALSE;\r
2121   appData.firstTimeOdds  = 1;\r
2122   appData.secondTimeOdds = 1;\r
2123   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2124   appData.secondAccumulateTC = 1;\r
2125   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2126   appData.secondNPS = -1;\r
2127   appData.engineComments = 1;\r
2128   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2129   appData.egtFormats = "";\r
2130 \r
2131 #ifdef ZIPPY\r
2132   appData.zippyTalk = ZIPPY_TALK;\r
2133   appData.zippyPlay = ZIPPY_PLAY;\r
2134   appData.zippyLines = ZIPPY_LINES;\r
2135   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2136   appData.zippyPassword = ZIPPY_PASSWORD;\r
2137   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2138   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2139   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2140   appData.zippyUseI = ZIPPY_USE_I;\r
2141   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2142   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2143   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2144   appData.zippyGameStart = ZIPPY_GAME_START;\r
2145   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2146   appData.zippyAbort = ZIPPY_ABORT;\r
2147   appData.zippyVariants = ZIPPY_VARIANTS;\r
2148   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2149   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2150 #endif\r
2151 \r
2152   /* Point font array elements to structures and\r
2153      parse default font names */\r
2154   for (i=0; i<NUM_FONTS; i++) {\r
2155     for (j=0; j<NUM_SIZES; j++) {\r
2156       font[j][i] = &fontRec[j][i];\r
2157       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2158     }\r
2159   }\r
2160   \r
2161   /* Parse default settings file if any */\r
2162   if (ParseSettingsFile(settingsFileName, buf)) {\r
2163     settingsFileName = strdup(buf);\r
2164   }\r
2165 \r
2166   /* Parse command line */\r
2167   ParseArgs(StringGet, &lpCmdLine);\r
2168 \r
2169   /* [HGM] make sure board size is acceptable */\r
2170   if(appData.NrFiles > BOARD_FILES ||\r
2171      appData.NrRanks > BOARD_RANKS   )\r
2172       DisplayFatalError("Recompile with BOARD_RANKS or BOARD_FILES, to support this size", 0, 2);\r
2173 \r
2174   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2175    * with options from the command line, we now make an even higher priority\r
2176    * overrule by WB options attached to the engine command line. This so that\r
2177    * tournament managers can use WB options (such as /timeOdds) that follow\r
2178    * the engines.\r
2179    */\r
2180   if(appData.firstChessProgram != NULL) {\r
2181       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2182       static char *f = "first";\r
2183       char buf[MSG_SIZ], *q = buf;\r
2184       if(p != NULL) { // engine command line contains WinBoard options\r
2185           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2186           ParseArgs(StringGet, &q);\r
2187           p[-1] = 0; // cut them offengine command line\r
2188       }\r
2189   }\r
2190   // now do same for second chess program\r
2191   if(appData.secondChessProgram != NULL) {\r
2192       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2193       static char *s = "second";\r
2194       char buf[MSG_SIZ], *q = buf;\r
2195       if(p != NULL) { // engine command line contains WinBoard options\r
2196           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2197           ParseArgs(StringGet, &q);\r
2198           p[-1] = 0; // cut them offengine command line\r
2199       }\r
2200   }\r
2201 \r
2202 \r
2203   /* Propagate options that affect others */\r
2204   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2205   if (appData.icsActive || appData.noChessProgram) {\r
2206      chessProgram = FALSE;  /* not local chess program mode */\r
2207   }\r
2208 \r
2209   /* Open startup dialog if needed */\r
2210   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2211       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2212       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2213                         *appData.secondChessProgram == NULLCHAR))) {\r
2214     FARPROC lpProc;\r
2215     \r
2216     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2217     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2218     FreeProcInstance(lpProc);\r
2219   }\r
2220 \r
2221   /* Make sure save files land in the right (?) directory */\r
2222   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2223     appData.saveGameFile = strdup(buf);\r
2224   }\r
2225   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2226     appData.savePositionFile = strdup(buf);\r
2227   }\r
2228 \r
2229   /* Finish initialization for fonts and sounds */\r
2230   for (i=0; i<NUM_FONTS; i++) {\r
2231     for (j=0; j<NUM_SIZES; j++) {\r
2232       CreateFontInMF(font[j][i]);\r
2233     }\r
2234   }\r
2235   /* xboard, and older WinBoards, controlled the move sound with the\r
2236      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2237      always turn the option on (so that the backend will call us),\r
2238      then let the user turn the sound off by setting it to silence if\r
2239      desired.  To accommodate old winboard.ini files saved by old\r
2240      versions of WinBoard, we also turn off the sound if the option\r
2241      was initially set to false. */\r
2242   if (!appData.ringBellAfterMoves) {\r
2243     sounds[(int)SoundMove].name = strdup("");\r
2244     appData.ringBellAfterMoves = TRUE;\r
2245   }\r
2246   GetCurrentDirectory(MSG_SIZ, currDir);\r
2247   SetCurrentDirectory(installDir);\r
2248   LoadAllSounds();\r
2249   SetCurrentDirectory(currDir);\r
2250 \r
2251   p = icsTextMenuString;\r
2252   if (p[0] == '@') {\r
2253     FILE* f = fopen(p + 1, "r");\r
2254     if (f == NULL) {\r
2255       DisplayFatalError(p + 1, errno, 2);\r
2256       return;\r
2257     }\r
2258     i = fread(buf, 1, sizeof(buf)-1, f);\r
2259     fclose(f);\r
2260     buf[i] = NULLCHAR;\r
2261     p = buf;\r
2262   }\r
2263   ParseIcsTextMenu(strdup(p));\r
2264 }\r
2265 \r
2266 \r
2267 VOID\r
2268 InitMenuChecks()\r
2269 {\r
2270   HMENU hmenu = GetMenu(hwndMain);\r
2271 \r
2272   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2273                         MF_BYCOMMAND|((appData.icsActive &&\r
2274                                        *appData.icsCommPort != NULLCHAR) ?\r
2275                                       MF_ENABLED : MF_GRAYED));\r
2276   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2277                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2278                                      MF_CHECKED : MF_UNCHECKED));\r
2279 }\r
2280 \r
2281 // [HGM] args: these two cases taken out to stay in front-end\r
2282       void SaveFontArg(FILE *f, ArgDescriptor *ad)\r
2283       {\r
2284         int bs;\r
2285         for (bs=0; bs<NUM_SIZES; bs++) {\r
2286           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2287           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2288           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
2289             ad->argName, mfp->faceName, mfp->pointSize,\r
2290             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2291             mfp->bold ? "b" : "",\r
2292             mfp->italic ? "i" : "",\r
2293             mfp->underline ? "u" : "",\r
2294             mfp->strikeout ? "s" : "",\r
2295             (int)mfp->charset);\r
2296         }\r
2297       }\r
2298 \r
2299       void SaveAttribsArg(FILE *f, ArgDescriptor *ad)\r
2300       {\r
2301         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2302         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2303           (ta->effects & CFE_BOLD) ? "b" : "",\r
2304           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2305           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2306           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2307           (ta->effects) ? " " : "",\r
2308           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2309       }\r
2310 \r
2311 int MainWindowUp()\r
2312 { // [HGM] args: allows testing if main window is realized from back-end\r
2313   return hwndMain != NULL;\r
2314 }\r
2315 \r
2316 VOID\r
2317 SaveSettings(char* name)\r
2318 {\r
2319   FILE *f;\r
2320   ArgDescriptor *ad;\r
2321   char dir[MSG_SIZ];\r
2322 \r
2323   if (!MainWindowUp()) return;\r
2324 \r
2325   GetCurrentDirectory(MSG_SIZ, dir);\r
2326   SetCurrentDirectory(installDir);\r
2327   f = fopen(name, "w");\r
2328   SetCurrentDirectory(dir);\r
2329   if (f == NULL) {\r
2330     DisplayError(name, errno);\r
2331     return;\r
2332   }\r
2333   fprintf(f, ";\n");\r
2334   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2335   fprintf(f, ";\n");\r
2336   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2337   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2338   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2339   fprintf(f, ";\n");\r
2340 \r
2341   GetActualPlacement(hwndMain, &wpMain);\r
2342   GetActualPlacement(hwndConsole, &wpConsole);\r
2343   GetActualPlacement(commentDialog, &wpComment);\r
2344   GetActualPlacement(editTagsDialog, &wpTags);\r
2345   GetActualPlacement(gameListDialog, &wpGameList);\r
2346 \r
2347   /* [AS] Move history */\r
2348   wpMoveHistory.visible = MoveHistoryIsUp();\r
2349   GetActualPlacement(moveHistoryDialog, &wpMoveHistory);\r
2350 \r
2351   /* [AS] Eval graph */\r
2352   wpEvalGraph.visible = EvalGraphIsUp();\r
2353   GetActualPlacement(evalGraphDialog, &wpEvalGraph);\r
2354 \r
2355   /* [AS] Engine output */\r
2356   wpEngineOutput.visible = EngineOutputIsUp();\r
2357   GetActualPlacement(engineOutputDialog, &wpEngineOutput);\r
2358 \r
2359   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2360     if (!ad->save) continue;\r
2361     switch (ad->argType) {\r
2362     case ArgString:\r
2363       {\r
2364         char *p = *(char **)ad->argLoc;\r
2365         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2366           /* Quote multiline values or \-containing values\r
2367              with { } if possible */\r
2368           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2369         } else {\r
2370           /* Else quote with " " */\r
2371           fprintf(f, "/%s=\"", ad->argName);\r
2372           while (*p) {\r
2373             if (*p == '\n') fprintf(f, "\n");\r
2374             else if (*p == '\r') fprintf(f, "\\r");\r
2375             else if (*p == '\t') fprintf(f, "\\t");\r
2376             else if (*p == '\b') fprintf(f, "\\b");\r
2377             else if (*p == '\f') fprintf(f, "\\f");\r
2378             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2379             else if (*p == '\"') fprintf(f, "\\\"");\r
2380             else if (*p == '\\') fprintf(f, "\\\\");\r
2381             else putc(*p, f);\r
2382             p++;\r
2383           }\r
2384           fprintf(f, "\"\n");\r
2385         }\r
2386       }\r
2387       break;\r
2388     case ArgInt:\r
2389     case ArgZ:\r
2390       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2391       break;\r
2392     case ArgX:\r
2393       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - wpMain.x); // [HGM] placement: stor relative value\r
2394       break;\r
2395     case ArgY:\r
2396       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - wpMain.y);\r
2397       break;\r
2398     case ArgFloat:\r
2399       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2400       break;\r
2401     case ArgBoolean:\r
2402       fprintf(f, "/%s=%s\n", ad->argName, \r
2403         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2404       break;\r
2405     case ArgTrue:\r
2406       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2407       break;\r
2408     case ArgFalse:\r
2409       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2410       break;\r
2411     case ArgColor:\r
2412       {\r
2413         COLORREF color = *(COLORREF *)ad->argLoc;\r
2414         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2415           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2416       }\r
2417       break;\r
2418     case ArgAttribs:\r
2419       SaveAttribsArg(f, ad);\r
2420       break;\r
2421     case ArgFilename:\r
2422       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2423         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2424       } else {\r
2425         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2426       }\r
2427       break;\r
2428     case ArgBoardSize:\r
2429       fprintf(f, "/%s=%s\n", ad->argName,\r
2430               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2431       break;\r
2432     case ArgFont:\r
2433       SaveFontArg(f, ad);\r
2434       break;\r
2435     case ArgCommSettings:\r
2436       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2437     case ArgNone:\r
2438     case ArgSettingsFilename: ;\r
2439     }\r
2440   }\r
2441   fclose(f);\r
2442 }\r
2443 \r
2444 \r
2445 \r
2446 /*---------------------------------------------------------------------------*\\r
2447  *\r
2448  * GDI board drawing routines\r
2449  *\r
2450 \*---------------------------------------------------------------------------*/\r
2451 \r
2452 /* [AS] Draw square using background texture */\r
2453 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2454 {\r
2455     XFORM   x;\r
2456 \r
2457     if( mode == 0 ) {\r
2458         return; /* Should never happen! */\r
2459     }\r
2460 \r
2461     SetGraphicsMode( dst, GM_ADVANCED );\r
2462 \r
2463     switch( mode ) {\r
2464     case 1:\r
2465         /* Identity */\r
2466         break;\r
2467     case 2:\r
2468         /* X reflection */\r
2469         x.eM11 = -1.0;\r
2470         x.eM12 = 0;\r
2471         x.eM21 = 0;\r
2472         x.eM22 = 1.0;\r
2473         x.eDx = (FLOAT) dw + dx - 1;\r
2474         x.eDy = 0;\r
2475         dx = 0;\r
2476         SetWorldTransform( dst, &x );\r
2477         break;\r
2478     case 3:\r
2479         /* Y reflection */\r
2480         x.eM11 = 1.0;\r
2481         x.eM12 = 0;\r
2482         x.eM21 = 0;\r
2483         x.eM22 = -1.0;\r
2484         x.eDx = 0;\r
2485         x.eDy = (FLOAT) dh + dy - 1;\r
2486         dy = 0;\r
2487         SetWorldTransform( dst, &x );\r
2488         break;\r
2489     case 4:\r
2490         /* X/Y flip */\r
2491         x.eM11 = 0;\r
2492         x.eM12 = 1.0;\r
2493         x.eM21 = 1.0;\r
2494         x.eM22 = 0;\r
2495         x.eDx = (FLOAT) dx;\r
2496         x.eDy = (FLOAT) dy;\r
2497         dx = 0;\r
2498         dy = 0;\r
2499         SetWorldTransform( dst, &x );\r
2500         break;\r
2501     }\r
2502 \r
2503     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2504 \r
2505     x.eM11 = 1.0;\r
2506     x.eM12 = 0;\r
2507     x.eM21 = 0;\r
2508     x.eM22 = 1.0;\r
2509     x.eDx = 0;\r
2510     x.eDy = 0;\r
2511     SetWorldTransform( dst, &x );\r
2512 \r
2513     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2514 }\r
2515 \r
2516 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2517 enum {\r
2518     PM_WP = (int) WhitePawn, \r
2519     PM_WN = (int) WhiteKnight, \r
2520     PM_WB = (int) WhiteBishop, \r
2521     PM_WR = (int) WhiteRook, \r
2522     PM_WQ = (int) WhiteQueen, \r
2523     PM_WF = (int) WhiteFerz, \r
2524     PM_WW = (int) WhiteWazir, \r
2525     PM_WE = (int) WhiteAlfil, \r
2526     PM_WM = (int) WhiteMan, \r
2527     PM_WO = (int) WhiteCannon, \r
2528     PM_WU = (int) WhiteUnicorn, \r
2529     PM_WH = (int) WhiteNightrider, \r
2530     PM_WA = (int) WhiteAngel, \r
2531     PM_WC = (int) WhiteMarshall, \r
2532     PM_WAB = (int) WhiteCardinal, \r
2533     PM_WD = (int) WhiteDragon, \r
2534     PM_WL = (int) WhiteLance, \r
2535     PM_WS = (int) WhiteCobra, \r
2536     PM_WV = (int) WhiteFalcon, \r
2537     PM_WSG = (int) WhiteSilver, \r
2538     PM_WG = (int) WhiteGrasshopper, \r
2539     PM_WK = (int) WhiteKing,\r
2540     PM_BP = (int) BlackPawn, \r
2541     PM_BN = (int) BlackKnight, \r
2542     PM_BB = (int) BlackBishop, \r
2543     PM_BR = (int) BlackRook, \r
2544     PM_BQ = (int) BlackQueen, \r
2545     PM_BF = (int) BlackFerz, \r
2546     PM_BW = (int) BlackWazir, \r
2547     PM_BE = (int) BlackAlfil, \r
2548     PM_BM = (int) BlackMan,\r
2549     PM_BO = (int) BlackCannon, \r
2550     PM_BU = (int) BlackUnicorn, \r
2551     PM_BH = (int) BlackNightrider, \r
2552     PM_BA = (int) BlackAngel, \r
2553     PM_BC = (int) BlackMarshall, \r
2554     PM_BG = (int) BlackGrasshopper, \r
2555     PM_BAB = (int) BlackCardinal,\r
2556     PM_BD = (int) BlackDragon,\r
2557     PM_BL = (int) BlackLance,\r
2558     PM_BS = (int) BlackCobra,\r
2559     PM_BV = (int) BlackFalcon,\r
2560     PM_BSG = (int) BlackSilver,\r
2561     PM_BK = (int) BlackKing\r
2562 };\r
2563 \r
2564 static HFONT hPieceFont = NULL;\r
2565 static HBITMAP hPieceMask[(int) EmptySquare];\r
2566 static HBITMAP hPieceFace[(int) EmptySquare];\r
2567 static int fontBitmapSquareSize = 0;\r
2568 static char pieceToFontChar[(int) EmptySquare] =\r
2569                               { 'p', 'n', 'b', 'r', 'q', \r
2570                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2571                       'k', 'o', 'm', 'v', 't', 'w', \r
2572                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2573                                                               'l' };\r
2574 \r
2575 extern BOOL SetCharTable( char *table, const char * map );\r
2576 /* [HGM] moved to backend.c */\r
2577 \r
2578 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2579 {\r
2580     HBRUSH hbrush;\r
2581     BYTE r1 = GetRValue( color );\r
2582     BYTE g1 = GetGValue( color );\r
2583     BYTE b1 = GetBValue( color );\r
2584     BYTE r2 = r1 / 2;\r
2585     BYTE g2 = g1 / 2;\r
2586     BYTE b2 = b1 / 2;\r
2587     RECT rc;\r
2588 \r
2589     /* Create a uniform background first */\r
2590     hbrush = CreateSolidBrush( color );\r
2591     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2592     FillRect( hdc, &rc, hbrush );\r
2593     DeleteObject( hbrush );\r
2594     \r
2595     if( mode == 1 ) {\r
2596         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2597         int steps = squareSize / 2;\r
2598         int i;\r
2599 \r
2600         for( i=0; i<steps; i++ ) {\r
2601             BYTE r = r1 - (r1-r2) * i / steps;\r
2602             BYTE g = g1 - (g1-g2) * i / steps;\r
2603             BYTE b = b1 - (b1-b2) * i / steps;\r
2604 \r
2605             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2606             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2607             FillRect( hdc, &rc, hbrush );\r
2608             DeleteObject(hbrush);\r
2609         }\r
2610     }\r
2611     else if( mode == 2 ) {\r
2612         /* Diagonal gradient, good more or less for every piece */\r
2613         POINT triangle[3];\r
2614         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2615         HBRUSH hbrush_old;\r
2616         int steps = squareSize;\r
2617         int i;\r
2618 \r
2619         triangle[0].x = squareSize - steps;\r
2620         triangle[0].y = squareSize;\r
2621         triangle[1].x = squareSize;\r
2622         triangle[1].y = squareSize;\r
2623         triangle[2].x = squareSize;\r
2624         triangle[2].y = squareSize - steps;\r
2625 \r
2626         for( i=0; i<steps; i++ ) {\r
2627             BYTE r = r1 - (r1-r2) * i / steps;\r
2628             BYTE g = g1 - (g1-g2) * i / steps;\r
2629             BYTE b = b1 - (b1-b2) * i / steps;\r
2630 \r
2631             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2632             hbrush_old = SelectObject( hdc, hbrush );\r
2633             Polygon( hdc, triangle, 3 );\r
2634             SelectObject( hdc, hbrush_old );\r
2635             DeleteObject(hbrush);\r
2636             triangle[0].x++;\r
2637             triangle[2].y++;\r
2638         }\r
2639 \r
2640         SelectObject( hdc, hpen );\r
2641     }\r
2642 }\r
2643 \r
2644 /*\r
2645     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2646     seems to work ok. The main problem here is to find the "inside" of a chess\r
2647     piece: follow the steps as explained below.\r
2648 */\r
2649 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2650 {\r
2651     HBITMAP hbm;\r
2652     HBITMAP hbm_old;\r
2653     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2654     RECT rc;\r
2655     SIZE sz;\r
2656     POINT pt;\r
2657     int backColor = whitePieceColor; \r
2658     int foreColor = blackPieceColor;\r
2659     \r
2660     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2661         backColor = appData.fontBackColorWhite;\r
2662         foreColor = appData.fontForeColorWhite;\r
2663     }\r
2664     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2665         backColor = appData.fontBackColorBlack;\r
2666         foreColor = appData.fontForeColorBlack;\r
2667     }\r
2668 \r
2669     /* Mask */\r
2670     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2671 \r
2672     hbm_old = SelectObject( hdc, hbm );\r
2673 \r
2674     rc.left = 0;\r
2675     rc.top = 0;\r
2676     rc.right = squareSize;\r
2677     rc.bottom = squareSize;\r
2678 \r
2679     /* Step 1: background is now black */\r
2680     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2681 \r
2682     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2683 \r
2684     pt.x = (squareSize - sz.cx) / 2;\r
2685     pt.y = (squareSize - sz.cy) / 2;\r
2686 \r
2687     SetBkMode( hdc, TRANSPARENT );\r
2688     SetTextColor( hdc, chroma );\r
2689     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2690     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2691 \r
2692     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2693     /* Step 3: the area outside the piece is filled with white */\r
2694 //    FloodFill( hdc, 0, 0, chroma );\r
2695     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2696     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2697     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2698     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2699     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2700     /* \r
2701         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2702         but if the start point is not inside the piece we're lost!\r
2703         There should be a better way to do this... if we could create a region or path\r
2704         from the fill operation we would be fine for example.\r
2705     */\r
2706 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2707     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2708 \r
2709     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2710         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2711         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2712 \r
2713         SelectObject( dc2, bm2 );\r
2714         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2715         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2716         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2717         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2718         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2719 \r
2720         DeleteDC( dc2 );\r
2721         DeleteObject( bm2 );\r
2722     }\r
2723 \r
2724     SetTextColor( hdc, 0 );\r
2725     /* \r
2726         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2727         draw the piece again in black for safety.\r
2728     */\r
2729     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2730 \r
2731     SelectObject( hdc, hbm_old );\r
2732 \r
2733     if( hPieceMask[index] != NULL ) {\r
2734         DeleteObject( hPieceMask[index] );\r
2735     }\r
2736 \r
2737     hPieceMask[index] = hbm;\r
2738 \r
2739     /* Face */\r
2740     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2741 \r
2742     SelectObject( hdc, hbm );\r
2743 \r
2744     {\r
2745         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2746         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2747         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2748 \r
2749         SelectObject( dc1, hPieceMask[index] );\r
2750         SelectObject( dc2, bm2 );\r
2751         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2752         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2753         \r
2754         /* \r
2755             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2756             the piece background and deletes (makes transparent) the rest.\r
2757             Thanks to that mask, we are free to paint the background with the greates\r
2758             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2759             We use this, to make gradients and give the pieces a "roundish" look.\r
2760         */\r
2761         SetPieceBackground( hdc, backColor, 2 );\r
2762         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2763 \r
2764         DeleteDC( dc2 );\r
2765         DeleteDC( dc1 );\r
2766         DeleteObject( bm2 );\r
2767     }\r
2768 \r
2769     SetTextColor( hdc, foreColor );\r
2770     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2771 \r
2772     SelectObject( hdc, hbm_old );\r
2773 \r
2774     if( hPieceFace[index] != NULL ) {\r
2775         DeleteObject( hPieceFace[index] );\r
2776     }\r
2777 \r
2778     hPieceFace[index] = hbm;\r
2779 }\r
2780 \r
2781 static int TranslatePieceToFontPiece( int piece )\r
2782 {\r
2783     switch( piece ) {\r
2784     case BlackPawn:\r
2785         return PM_BP;\r
2786     case BlackKnight:\r
2787         return PM_BN;\r
2788     case BlackBishop:\r
2789         return PM_BB;\r
2790     case BlackRook:\r
2791         return PM_BR;\r
2792     case BlackQueen:\r
2793         return PM_BQ;\r
2794     case BlackKing:\r
2795         return PM_BK;\r
2796     case WhitePawn:\r
2797         return PM_WP;\r
2798     case WhiteKnight:\r
2799         return PM_WN;\r
2800     case WhiteBishop:\r
2801         return PM_WB;\r
2802     case WhiteRook:\r
2803         return PM_WR;\r
2804     case WhiteQueen:\r
2805         return PM_WQ;\r
2806     case WhiteKing:\r
2807         return PM_WK;\r
2808 \r
2809     case BlackAngel:\r
2810         return PM_BA;\r
2811     case BlackMarshall:\r
2812         return PM_BC;\r
2813     case BlackFerz:\r
2814         return PM_BF;\r
2815     case BlackNightrider:\r
2816         return PM_BH;\r
2817     case BlackAlfil:\r
2818         return PM_BE;\r
2819     case BlackWazir:\r
2820         return PM_BW;\r
2821     case BlackUnicorn:\r
2822         return PM_BU;\r
2823     case BlackCannon:\r
2824         return PM_BO;\r
2825     case BlackGrasshopper:\r
2826         return PM_BG;\r
2827     case BlackMan:\r
2828         return PM_BM;\r
2829     case BlackSilver:\r
2830         return PM_BSG;\r
2831     case BlackLance:\r
2832         return PM_BL;\r
2833     case BlackFalcon:\r
2834         return PM_BV;\r
2835     case BlackCobra:\r
2836         return PM_BS;\r
2837     case BlackCardinal:\r
2838         return PM_BAB;\r
2839     case BlackDragon:\r
2840         return PM_BD;\r
2841 \r
2842     case WhiteAngel:\r
2843         return PM_WA;\r
2844     case WhiteMarshall:\r
2845         return PM_WC;\r
2846     case WhiteFerz:\r
2847         return PM_WF;\r
2848     case WhiteNightrider:\r
2849         return PM_WH;\r
2850     case WhiteAlfil:\r
2851         return PM_WE;\r
2852     case WhiteWazir:\r
2853         return PM_WW;\r
2854     case WhiteUnicorn:\r
2855         return PM_WU;\r
2856     case WhiteCannon:\r
2857         return PM_WO;\r
2858     case WhiteGrasshopper:\r
2859         return PM_WG;\r
2860     case WhiteMan:\r
2861         return PM_WM;\r
2862     case WhiteSilver:\r
2863         return PM_WSG;\r
2864     case WhiteLance:\r
2865         return PM_WL;\r
2866     case WhiteFalcon:\r
2867         return PM_WV;\r
2868     case WhiteCobra:\r
2869         return PM_WS;\r
2870     case WhiteCardinal:\r
2871         return PM_WAB;\r
2872     case WhiteDragon:\r
2873         return PM_WD;\r
2874     }\r
2875 \r
2876     return 0;\r
2877 }\r
2878 \r
2879 void CreatePiecesFromFont()\r
2880 {\r
2881     LOGFONT lf;\r
2882     HDC hdc_window = NULL;\r
2883     HDC hdc = NULL;\r
2884     HFONT hfont_old;\r
2885     int fontHeight;\r
2886     int i;\r
2887 \r
2888     if( fontBitmapSquareSize < 0 ) {\r
2889         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2890         return;\r
2891     }\r
2892 \r
2893     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2894         fontBitmapSquareSize = -1;\r
2895         return;\r
2896     }\r
2897 \r
2898     if( fontBitmapSquareSize != squareSize ) {\r
2899         hdc_window = GetDC( hwndMain );\r
2900         hdc = CreateCompatibleDC( hdc_window );\r
2901 \r
2902         if( hPieceFont != NULL ) {\r
2903             DeleteObject( hPieceFont );\r
2904         }\r
2905         else {\r
2906             for( i=0; i<=(int)BlackKing; i++ ) {\r
2907                 hPieceMask[i] = NULL;\r
2908                 hPieceFace[i] = NULL;\r
2909             }\r
2910         }\r
2911 \r
2912         fontHeight = 75;\r
2913 \r
2914         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2915             fontHeight = appData.fontPieceSize;\r
2916         }\r
2917 \r
2918         fontHeight = (fontHeight * squareSize) / 100;\r
2919 \r
2920         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2921         lf.lfWidth = 0;\r
2922         lf.lfEscapement = 0;\r
2923         lf.lfOrientation = 0;\r
2924         lf.lfWeight = FW_NORMAL;\r
2925         lf.lfItalic = 0;\r
2926         lf.lfUnderline = 0;\r
2927         lf.lfStrikeOut = 0;\r
2928         lf.lfCharSet = DEFAULT_CHARSET;\r
2929         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2930         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2931         lf.lfQuality = PROOF_QUALITY;\r
2932         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2933         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2934         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2935 \r
2936         hPieceFont = CreateFontIndirect( &lf );\r
2937 \r
2938         if( hPieceFont == NULL ) {\r
2939             fontBitmapSquareSize = -2;\r
2940         }\r
2941         else {\r
2942             /* Setup font-to-piece character table */\r
2943             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2944                 /* No (or wrong) global settings, try to detect the font */\r
2945                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2946                     /* Alpha */\r
2947                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2948                 }\r
2949                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2950                     /* DiagramTT* family */\r
2951                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2952                 }\r
2953                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2954                     /* Fairy symbols */\r
2955                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2956                 }\r
2957                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2958                     /* Good Companion (Some characters get warped as literal :-( */\r
2959                     char s[] = "1cmWG0??S??oYI23wgQU";\r
2960                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2961                     SetCharTable(pieceToFontChar, s);\r
2962                 }\r
2963                 else {\r
2964                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2965                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2966                 }\r
2967             }\r
2968 \r
2969             /* Create bitmaps */\r
2970             hfont_old = SelectObject( hdc, hPieceFont );\r
2971             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2972                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2973                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2974 \r
2975             SelectObject( hdc, hfont_old );\r
2976 \r
2977             fontBitmapSquareSize = squareSize;\r
2978         }\r
2979     }\r
2980 \r
2981     if( hdc != NULL ) {\r
2982         DeleteDC( hdc );\r
2983     }\r
2984 \r
2985     if( hdc_window != NULL ) {\r
2986         ReleaseDC( hwndMain, hdc_window );\r
2987     }\r
2988 }\r
2989 \r
2990 HBITMAP\r
2991 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2992 {\r
2993   char name[128];\r
2994 \r
2995   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2996   if (gameInfo.event &&\r
2997       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2998       strcmp(name, "k80s") == 0) {\r
2999     strcpy(name, "tim");\r
3000   }\r
3001   return LoadBitmap(hinst, name);\r
3002 }\r
3003 \r
3004 \r
3005 /* Insert a color into the program's logical palette\r
3006    structure.  This code assumes the given color is\r
3007    the result of the RGB or PALETTERGB macro, and it\r
3008    knows how those macros work (which is documented).\r
3009 */\r
3010 VOID\r
3011 InsertInPalette(COLORREF color)\r
3012 {\r
3013   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3014 \r
3015   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3016     DisplayFatalError("Too many colors", 0, 1);\r
3017     pLogPal->palNumEntries--;\r
3018     return;\r
3019   }\r
3020 \r
3021   pe->peFlags = (char) 0;\r
3022   pe->peRed = (char) (0xFF & color);\r
3023   pe->peGreen = (char) (0xFF & (color >> 8));\r
3024   pe->peBlue = (char) (0xFF & (color >> 16));\r
3025   return;\r
3026 }\r
3027 \r
3028 \r
3029 VOID\r
3030 InitDrawingColors()\r
3031 {\r
3032   if (pLogPal == NULL) {\r
3033     /* Allocate enough memory for a logical palette with\r
3034      * PALETTESIZE entries and set the size and version fields\r
3035      * of the logical palette structure.\r
3036      */\r
3037     pLogPal = (NPLOGPALETTE)\r
3038       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3039                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3040     pLogPal->palVersion    = 0x300;\r
3041   }\r
3042   pLogPal->palNumEntries = 0;\r
3043 \r
3044   InsertInPalette(lightSquareColor);\r
3045   InsertInPalette(darkSquareColor);\r
3046   InsertInPalette(whitePieceColor);\r
3047   InsertInPalette(blackPieceColor);\r
3048   InsertInPalette(highlightSquareColor);\r
3049   InsertInPalette(premoveHighlightColor);\r
3050 \r
3051   /*  create a logical color palette according the information\r
3052    *  in the LOGPALETTE structure.\r
3053    */\r
3054   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3055 \r
3056   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3057   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3058   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3059   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3060   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3061   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3062   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3063   /* [AS] Force rendering of the font-based pieces */\r
3064   if( fontBitmapSquareSize > 0 ) {\r
3065     fontBitmapSquareSize = 0;\r
3066   }\r
3067 }\r
3068 \r
3069 \r
3070 int\r
3071 BoardWidth(int boardSize, int n)\r
3072 { /* [HGM] argument n added to allow different width and height */\r
3073   int lineGap = sizeInfo[boardSize].lineGap;\r
3074 \r
3075   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3076       lineGap = appData.overrideLineGap;\r
3077   }\r
3078 \r
3079   return (n + 1) * lineGap +\r
3080           n * sizeInfo[boardSize].squareSize;\r
3081 }\r
3082 \r
3083 /* Respond to board resize by dragging edge */\r
3084 VOID\r
3085 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3086 {\r
3087   BoardSize newSize = NUM_SIZES - 1;\r
3088   static int recurse = 0;\r
3089   if (IsIconic(hwndMain)) return;\r
3090   if (recurse > 0) return;\r
3091   recurse++;\r
3092   while (newSize > 0) {\r
3093         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3094         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3095            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3096     newSize--;\r
3097   } \r
3098   boardSize = newSize;\r
3099   InitDrawingSizes(boardSize, flags);\r
3100   recurse--;\r
3101 }\r
3102 \r
3103 \r
3104 \r
3105 VOID\r
3106 InitDrawingSizes(BoardSize boardSize, int flags)\r
3107 {\r
3108   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3109   ChessSquare piece;\r
3110   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3111   HDC hdc;\r
3112   SIZE clockSize, messageSize;\r
3113   HFONT oldFont;\r
3114   char buf[MSG_SIZ];\r
3115   char *str;\r
3116   HMENU hmenu = GetMenu(hwndMain);\r
3117   RECT crect, wrect, oldRect;\r
3118   int offby;\r
3119   LOGBRUSH logbrush;\r
3120 \r
3121   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3122   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3123 \r
3124   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3125   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3126 \r
3127   oldRect.left = wpMain.x; //[HGM] placement: remember previous window params\r
3128   oldRect.top = wpMain.y;\r
3129   oldRect.right = wpMain.x + wpMain.width;\r
3130   oldRect.bottom = wpMain.y + wpMain.height;\r
3131 \r
3132   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3133   smallLayout = sizeInfo[boardSize].smallLayout;\r
3134   squareSize = sizeInfo[boardSize].squareSize;\r
3135   lineGap = sizeInfo[boardSize].lineGap;\r
3136   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3137 \r
3138   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3139       lineGap = appData.overrideLineGap;\r
3140   }\r
3141 \r
3142   if (tinyLayout != oldTinyLayout) {\r
3143     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3144     if (tinyLayout) {\r
3145       style &= ~WS_SYSMENU;\r
3146       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3147                  "&Minimize\tCtrl+F4");\r
3148     } else {\r
3149       style |= WS_SYSMENU;\r
3150       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3151     }\r
3152     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3153 \r
3154     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3155       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3156         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3157     }\r
3158     DrawMenuBar(hwndMain);\r
3159   }\r
3160 \r
3161   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3162   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3163 \r
3164   /* Get text area sizes */\r
3165   hdc = GetDC(hwndMain);\r
3166   if (appData.clockMode) {\r
3167     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3168   } else {\r
3169     sprintf(buf, "White");\r
3170   }\r
3171   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3172   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3173   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3174   str = "We only care about the height here";\r
3175   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3176   SelectObject(hdc, oldFont);\r
3177   ReleaseDC(hwndMain, hdc);\r
3178 \r
3179   /* Compute where everything goes */\r
3180   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3181         /* [HGM] logo: if either logo is on, reserve space for it */\r
3182         logoHeight =  2*clockSize.cy;\r
3183         leftLogoRect.left   = OUTER_MARGIN;\r
3184         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3185         leftLogoRect.top    = OUTER_MARGIN;\r
3186         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3187 \r
3188         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3189         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3190         rightLogoRect.top    = OUTER_MARGIN;\r
3191         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3192 \r
3193 \r
3194     whiteRect.left = leftLogoRect.right;\r
3195     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3196     whiteRect.top = OUTER_MARGIN;\r
3197     whiteRect.bottom = whiteRect.top + logoHeight;\r
3198 \r
3199     blackRect.right = rightLogoRect.left;\r
3200     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3201     blackRect.top = whiteRect.top;\r
3202     blackRect.bottom = whiteRect.bottom;\r
3203   } else {\r
3204     whiteRect.left = OUTER_MARGIN;\r
3205     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3206     whiteRect.top = OUTER_MARGIN;\r
3207     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3208 \r
3209     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3210     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3211     blackRect.top = whiteRect.top;\r
3212     blackRect.bottom = whiteRect.bottom;\r
3213   }\r
3214 \r
3215   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3216   if (appData.showButtonBar) {\r
3217     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3218       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3219   } else {\r
3220     messageRect.right = OUTER_MARGIN + boardWidth;\r
3221   }\r
3222   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3223   messageRect.bottom = messageRect.top + messageSize.cy;\r
3224 \r
3225   boardRect.left = OUTER_MARGIN;\r
3226   boardRect.right = boardRect.left + boardWidth;\r
3227   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3228   boardRect.bottom = boardRect.top + boardHeight;\r
3229 \r
3230   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3231   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3232   oldBoardSize = boardSize;\r
3233   oldTinyLayout = tinyLayout;\r
3234   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3235   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3236     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3237   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3238   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3239   wpMain.height = winH; //       without disturbing window attachments\r
3240   GetWindowRect(hwndMain, &wrect);\r
3241   SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
3242                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3243 \r
3244   // [HGM] placement: let attached windows follow size change.\r
3245   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );\r
3246   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );\r
3247   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );\r
3248   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );\r
3249   ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );\r
3250 \r
3251   /* compensate if menu bar wrapped */\r
3252   GetClientRect(hwndMain, &crect);\r
3253   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3254   wpMain.height += offby;\r
3255   switch (flags) {\r
3256   case WMSZ_TOPLEFT:\r
3257     SetWindowPos(hwndMain, NULL, \r
3258                  wrect.right - wpMain.width, wrect.bottom - wpMain.height, \r
3259                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3260     break;\r
3261 \r
3262   case WMSZ_TOPRIGHT:\r
3263   case WMSZ_TOP:\r
3264     SetWindowPos(hwndMain, NULL, \r
3265                  wrect.left, wrect.bottom - wpMain.height, \r
3266                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3267     break;\r
3268 \r
3269   case WMSZ_BOTTOMLEFT:\r
3270   case WMSZ_LEFT:\r
3271     SetWindowPos(hwndMain, NULL, \r
3272                  wrect.right - wpMain.width, wrect.top, \r
3273                  wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3274     break;\r
3275 \r
3276   case WMSZ_BOTTOMRIGHT:\r
3277   case WMSZ_BOTTOM:\r
3278   case WMSZ_RIGHT:\r
3279   default:\r
3280     SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,\r
3281                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3282     break;\r
3283   }\r
3284 \r
3285   hwndPause = NULL;\r
3286   for (i = 0; i < N_BUTTONS; i++) {\r
3287     if (buttonDesc[i].hwnd != NULL) {\r
3288       DestroyWindow(buttonDesc[i].hwnd);\r
3289       buttonDesc[i].hwnd = NULL;\r
3290     }\r
3291     if (appData.showButtonBar) {\r
3292       buttonDesc[i].hwnd =\r
3293         CreateWindow("BUTTON", buttonDesc[i].label,\r
3294                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3295                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3296                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3297                      (HMENU) buttonDesc[i].id,\r
3298                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3299       if (tinyLayout) {\r
3300         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3301                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3302                     MAKELPARAM(FALSE, 0));\r
3303       }\r
3304       if (buttonDesc[i].id == IDM_Pause)\r
3305         hwndPause = buttonDesc[i].hwnd;\r
3306       buttonDesc[i].wndproc = (WNDPROC)\r
3307         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3308     }\r
3309   }\r
3310   if (gridPen != NULL) DeleteObject(gridPen);\r
3311   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3312   if (premovePen != NULL) DeleteObject(premovePen);\r
3313   if (lineGap != 0) {\r
3314     logbrush.lbStyle = BS_SOLID;\r
3315     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3316     gridPen =\r
3317       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3318                    lineGap, &logbrush, 0, NULL);\r
3319     logbrush.lbColor = highlightSquareColor;\r
3320     highlightPen =\r
3321       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3322                    lineGap, &logbrush, 0, NULL);\r
3323 \r
3324     logbrush.lbColor = premoveHighlightColor; \r
3325     premovePen =\r
3326       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3327                    lineGap, &logbrush, 0, NULL);\r
3328 \r
3329     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3330     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3331       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3332       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3333         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3334       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3335         BOARD_WIDTH * (squareSize + lineGap);\r
3336       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3337     }\r
3338     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3339       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3340       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3341         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3342         lineGap / 2 + (i * (squareSize + lineGap));\r
3343       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3344         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3345       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3346     }\r
3347   }\r
3348 \r
3349   /* [HGM] Licensing requirement */\r
3350 #ifdef GOTHIC\r
3351   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3352 #endif\r
3353 #ifdef FALCON\r
3354   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3355 #endif\r
3356   GothicPopUp( "", VariantNormal);\r
3357 \r
3358 \r
3359 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3360 \r
3361   /* Load piece bitmaps for this board size */\r
3362   for (i=0; i<=2; i++) {\r
3363     for (piece = WhitePawn;\r
3364          (int) piece < (int) BlackPawn;\r
3365          piece = (ChessSquare) ((int) piece + 1)) {\r
3366       if (pieceBitmap[i][piece] != NULL)\r
3367         DeleteObject(pieceBitmap[i][piece]);\r
3368     }\r
3369   }\r
3370 \r
3371   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3372   // Orthodox Chess pieces\r
3373   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3374   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3375   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3376   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3377   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3378   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3379   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3380   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3381   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3382   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3383   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3384   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3385   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3386   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3387   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3388   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3389     // in Shogi, Hijack the unused Queen for Lance\r
3390     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3391     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3392     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3393   } else {\r
3394     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3395     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3396     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3397   }\r
3398 \r
3399   if(squareSize <= 72 && squareSize >= 33) { \r
3400     /* A & C are available in most sizes now */\r
3401     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3402       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3403       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3404       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3405       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3406       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3407       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3408       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3409       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3410       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3411       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3412       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3413       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3414     } else { // Smirf-like\r
3415       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3416       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3417       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3418     }\r
3419     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3420       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3421       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3422       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3423     } else { // WinBoard standard\r
3424       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3425       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3426       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3427     }\r
3428   }\r
3429 \r
3430 \r
3431   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3432     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3433     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3434     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3435     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3436     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3437     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3438     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3439     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3440     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3441     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3442     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3443     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3444     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3445     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3446     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3447     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3448     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3449     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3450     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3451     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3452     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3453     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3454     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3455     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3456     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3457     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3458     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3459     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3460     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3461     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3462 \r
3463     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3464       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3465       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3466       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3467       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3468       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3469       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3470       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3471       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3472       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3473       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3474       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3475       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3476     } else {\r
3477       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3478       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3479       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3480       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3481       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3482       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3483       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3484       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3485       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3486       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3487       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3488       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3489     }\r
3490 \r
3491   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3492     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3493     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3494     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3495     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3496     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3497     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3498     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3499     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3500     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3501     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3502     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3503     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3504     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3505     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3506   }\r
3507 \r
3508 \r
3509   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3510   /* special Shogi support in this size */\r
3511   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3512       for (piece = WhitePawn;\r
3513            (int) piece < (int) BlackPawn;\r
3514            piece = (ChessSquare) ((int) piece + 1)) {\r
3515         if (pieceBitmap[i][piece] != NULL)\r
3516           DeleteObject(pieceBitmap[i][piece]);\r
3517       }\r
3518     }\r
3519   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3520   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3521   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3522   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3523   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3524   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3525   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3526   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3527   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3528   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3529   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3530   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3531   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3532   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3533   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3534   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3535   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3536   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3537   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3538   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3539   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3540   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3541   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3542   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3543   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3544   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3545   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3546   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3547   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3548   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3549   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3550   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3551   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3552   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3553   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3554   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3555   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3556   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3557   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3558   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3559   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3560   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3561   minorSize = 0;\r
3562   }\r
3563 }\r
3564 \r
3565 HBITMAP\r
3566 PieceBitmap(ChessSquare p, int kind)\r
3567 {\r
3568   if ((int) p >= (int) BlackPawn)\r
3569     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3570 \r
3571   return pieceBitmap[kind][(int) p];\r
3572 }\r
3573 \r
3574 /***************************************************************/\r
3575 \r
3576 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3577 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3578 /*\r
3579 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3580 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3581 */\r
3582 \r
3583 VOID\r
3584 SquareToPos(int row, int column, int * x, int * y)\r
3585 {\r
3586   if (flipView) {\r
3587     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3588     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3589   } else {\r
3590     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3591     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3592   }\r
3593 }\r
3594 \r
3595 VOID\r
3596 DrawCoordsOnDC(HDC hdc)\r
3597 {\r
3598   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
3599   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
3600   char str[2] = { NULLCHAR, NULLCHAR };\r
3601   int oldMode, oldAlign, x, y, start, i;\r
3602   HFONT oldFont;\r
3603   HBRUSH oldBrush;\r
3604 \r
3605   if (!appData.showCoords)\r
3606     return;\r
3607 \r
3608   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3609 \r
3610   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3611   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3612   oldAlign = GetTextAlign(hdc);\r
3613   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3614 \r
3615   y = boardRect.top + lineGap;\r
3616   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3617 \r
3618   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3619   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3620     str[0] = files[start + i];\r
3621     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3622     y += squareSize + lineGap;\r
3623   }\r
3624 \r
3625   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3626 \r
3627   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3628   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3629     str[0] = ranks[start + i];\r
3630     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3631     x += squareSize + lineGap;\r
3632   }    \r
3633 \r
3634   SelectObject(hdc, oldBrush);\r
3635   SetBkMode(hdc, oldMode);\r
3636   SetTextAlign(hdc, oldAlign);\r
3637   SelectObject(hdc, oldFont);\r
3638 }\r
3639 \r
3640 VOID\r
3641 DrawGridOnDC(HDC hdc)\r
3642 {\r
3643   HPEN oldPen;\r
3644  \r
3645   if (lineGap != 0) {\r
3646     oldPen = SelectObject(hdc, gridPen);\r
3647     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3648     SelectObject(hdc, oldPen);\r
3649   }\r
3650 }\r
3651 \r
3652 #define HIGHLIGHT_PEN 0\r
3653 #define PREMOVE_PEN   1\r
3654 \r
3655 VOID\r
3656 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3657 {\r
3658   int x1, y1;\r
3659   HPEN oldPen, hPen;\r
3660   if (lineGap == 0) return;\r
3661   if (flipView) {\r
3662     x1 = boardRect.left +\r
3663       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3664     y1 = boardRect.top +\r
3665       lineGap/2 + y * (squareSize + lineGap);\r
3666   } else {\r
3667     x1 = boardRect.left +\r
3668       lineGap/2 + x * (squareSize + lineGap);\r
3669     y1 = boardRect.top +\r
3670       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3671   }\r
3672   hPen = pen ? premovePen : highlightPen;\r
3673   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3674   MoveToEx(hdc, x1, y1, NULL);\r
3675   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3676   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3677   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3678   LineTo(hdc, x1, y1);\r
3679   SelectObject(hdc, oldPen);\r
3680 }\r
3681 \r
3682 VOID\r
3683 DrawHighlightsOnDC(HDC hdc)\r
3684 {\r
3685   int i;\r
3686   for (i=0; i<2; i++) {\r
3687     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3688       DrawHighlightOnDC(hdc, TRUE,\r
3689                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3690                         HIGHLIGHT_PEN);\r
3691   }\r
3692   for (i=0; i<2; i++) {\r
3693     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3694         premoveHighlightInfo.sq[i].y >= 0) {\r
3695         DrawHighlightOnDC(hdc, TRUE,\r
3696                           premoveHighlightInfo.sq[i].x, \r
3697                           premoveHighlightInfo.sq[i].y,\r
3698                           PREMOVE_PEN);\r
3699     }\r
3700   }\r
3701 }\r
3702 \r
3703 /* Note: sqcolor is used only in monoMode */\r
3704 /* Note that this code is largely duplicated in woptions.c,\r
3705    function DrawSampleSquare, so that needs to be updated too */\r
3706 VOID\r
3707 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3708 {\r
3709   HBITMAP oldBitmap;\r
3710   HBRUSH oldBrush;\r
3711   int tmpSize;\r
3712 \r
3713   if (appData.blindfold) return;\r
3714 \r
3715   /* [AS] Use font-based pieces if needed */\r
3716   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3717     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3718     CreatePiecesFromFont();\r
3719 \r
3720     if( fontBitmapSquareSize == squareSize ) {\r
3721         int index = TranslatePieceToFontPiece(piece);\r
3722 \r
3723         SelectObject( tmphdc, hPieceMask[ index ] );\r
3724 \r
3725         BitBlt( hdc,\r
3726             x, y,\r
3727             squareSize, squareSize,\r
3728             tmphdc,\r
3729             0, 0,\r
3730             SRCAND );\r
3731 \r
3732         SelectObject( tmphdc, hPieceFace[ index ] );\r
3733 \r
3734         BitBlt( hdc,\r
3735             x, y,\r
3736             squareSize, squareSize,\r
3737             tmphdc,\r
3738             0, 0,\r
3739             SRCPAINT );\r
3740 \r
3741         return;\r
3742     }\r
3743   }\r
3744 \r
3745   if (appData.monoMode) {\r
3746     SelectObject(tmphdc, PieceBitmap(piece, \r
3747       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3748     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3749            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3750   } else {\r
3751     tmpSize = squareSize;\r
3752     if(minorSize &&\r
3753         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3754          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3755       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3756       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3757       x += (squareSize - minorSize)>>1;\r
3758       y += squareSize - minorSize - 2;\r
3759       tmpSize = minorSize;\r
3760     }\r
3761     if (color || appData.allWhite ) {\r
3762       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3763       if( color )\r
3764               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3765       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3766       if(appData.upsideDown && color==flipView)\r
3767         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3768       else\r
3769         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3770       /* Use black for outline of white pieces */\r
3771       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3772       if(appData.upsideDown && color==flipView)\r
3773         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3774       else\r
3775         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3776     } else {\r
3777       /* Use square color for details of black pieces */\r
3778       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3779       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3780       if(appData.upsideDown && !flipView)\r
3781         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3782       else\r
3783         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3784     }\r
3785     SelectObject(hdc, oldBrush);\r
3786     SelectObject(tmphdc, oldBitmap);\r
3787   }\r
3788 }\r
3789 \r
3790 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3791 int GetBackTextureMode( int algo )\r
3792 {\r
3793     int result = BACK_TEXTURE_MODE_DISABLED;\r
3794 \r
3795     switch( algo ) \r
3796     {\r
3797         case BACK_TEXTURE_MODE_PLAIN:\r
3798             result = 1; /* Always use identity map */\r
3799             break;\r
3800         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3801             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3802             break;\r
3803     }\r
3804 \r
3805     return result;\r
3806 }\r
3807 \r
3808 /* \r
3809     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3810     to handle redraws cleanly (as random numbers would always be different).\r
3811 */\r
3812 VOID RebuildTextureSquareInfo()\r
3813 {\r
3814     BITMAP bi;\r
3815     int lite_w = 0;\r
3816     int lite_h = 0;\r
3817     int dark_w = 0;\r
3818     int dark_h = 0;\r
3819     int row;\r
3820     int col;\r
3821 \r
3822     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3823 \r
3824     if( liteBackTexture != NULL ) {\r
3825         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3826             lite_w = bi.bmWidth;\r
3827             lite_h = bi.bmHeight;\r
3828         }\r
3829     }\r
3830 \r
3831     if( darkBackTexture != NULL ) {\r
3832         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3833             dark_w = bi.bmWidth;\r
3834             dark_h = bi.bmHeight;\r
3835         }\r
3836     }\r
3837 \r
3838     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3839         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3840             if( (col + row) & 1 ) {\r
3841                 /* Lite square */\r
3842                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3843                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3844                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3845                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3846                 }\r
3847             }\r
3848             else {\r
3849                 /* Dark square */\r
3850                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3851                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3852                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3853                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3854                 }\r
3855             }\r
3856         }\r
3857     }\r
3858 }\r
3859 \r
3860 /* [AS] Arrow highlighting support */\r
3861 \r
3862 static int A_WIDTH = 5; /* Width of arrow body */\r
3863 \r
3864 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3865 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3866 \r
3867 static double Sqr( double x )\r
3868 {\r
3869     return x*x;\r
3870 }\r
3871 \r
3872 static int Round( double x )\r
3873 {\r
3874     return (int) (x + 0.5);\r
3875 }\r
3876 \r
3877 /* Draw an arrow between two points using current settings */\r
3878 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3879 {\r
3880     POINT arrow[7];\r
3881     double dx, dy, j, k, x, y;\r
3882 \r
3883     if( d_x == s_x ) {\r
3884         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3885 \r
3886         arrow[0].x = s_x + A_WIDTH;\r
3887         arrow[0].y = s_y;\r
3888 \r
3889         arrow[1].x = s_x + A_WIDTH;\r
3890         arrow[1].y = d_y - h;\r
3891 \r
3892         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3893         arrow[2].y = d_y - h;\r
3894 \r
3895         arrow[3].x = d_x;\r
3896         arrow[3].y = d_y;\r
3897 \r
3898         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3899         arrow[4].y = d_y - h;\r
3900 \r
3901         arrow[5].x = s_x - A_WIDTH;\r
3902         arrow[5].y = d_y - h;\r
3903 \r
3904         arrow[6].x = s_x - A_WIDTH;\r
3905         arrow[6].y = s_y;\r
3906     }\r
3907     else if( d_y == s_y ) {\r
3908         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3909 \r
3910         arrow[0].x = s_x;\r
3911         arrow[0].y = s_y + A_WIDTH;\r
3912 \r
3913         arrow[1].x = d_x - w;\r
3914         arrow[1].y = s_y + A_WIDTH;\r
3915 \r
3916         arrow[2].x = d_x - w;\r
3917         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3918 \r
3919         arrow[3].x = d_x;\r
3920         arrow[3].y = d_y;\r
3921 \r
3922         arrow[4].x = d_x - w;\r
3923         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3924 \r
3925         arrow[5].x = d_x - w;\r
3926         arrow[5].y = s_y - A_WIDTH;\r
3927 \r
3928         arrow[6].x = s_x;\r
3929         arrow[6].y = s_y - A_WIDTH;\r
3930     }\r
3931     else {\r
3932         /* [AS] Needed a lot of paper for this! :-) */\r
3933         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3934         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3935   \r
3936         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3937 \r
3938         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3939 \r
3940         x = s_x;\r
3941         y = s_y;\r
3942 \r
3943         arrow[0].x = Round(x - j);\r
3944         arrow[0].y = Round(y + j*dx);\r
3945 \r
3946         arrow[1].x = Round(x + j);\r
3947         arrow[1].y = Round(y - j*dx);\r
3948 \r
3949         if( d_x > s_x ) {\r
3950             x = (double) d_x - k;\r
3951             y = (double) d_y - k*dy;\r
3952         }\r
3953         else {\r
3954             x = (double) d_x + k;\r
3955             y = (double) d_y + k*dy;\r
3956         }\r
3957 \r
3958         arrow[2].x = Round(x + j);\r
3959         arrow[2].y = Round(y - j*dx);\r
3960 \r
3961         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3962         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3963 \r
3964         arrow[4].x = d_x;\r
3965         arrow[4].y = d_y;\r
3966 \r
3967         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3968         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3969 \r
3970         arrow[6].x = Round(x - j);\r
3971         arrow[6].y = Round(y + j*dx);\r
3972     }\r
3973 \r
3974     Polygon( hdc, arrow, 7 );\r
3975 }\r
3976 \r
3977 /* [AS] Draw an arrow between two squares */\r
3978 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3979 {\r
3980     int s_x, s_y, d_x, d_y;\r
3981     HPEN hpen;\r
3982     HPEN holdpen;\r
3983     HBRUSH hbrush;\r
3984     HBRUSH holdbrush;\r
3985     LOGBRUSH stLB;\r
3986 \r
3987     if( s_col == d_col && s_row == d_row ) {\r
3988         return;\r
3989     }\r
3990 \r
3991     /* Get source and destination points */\r
3992     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3993     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3994 \r
3995     if( d_y > s_y ) {\r
3996         d_y += squareSize / 4;\r
3997     }\r
3998     else if( d_y < s_y ) {\r
3999         d_y += 3 * squareSize / 4;\r
4000     }\r
4001     else {\r
4002         d_y += squareSize / 2;\r
4003     }\r
4004 \r
4005     if( d_x > s_x ) {\r
4006         d_x += squareSize / 4;\r
4007     }\r
4008     else if( d_x < s_x ) {\r
4009         d_x += 3 * squareSize / 4;\r
4010     }\r
4011     else {\r
4012         d_x += squareSize / 2;\r
4013     }\r
4014 \r
4015     s_x += squareSize / 2;\r
4016     s_y += squareSize / 2;\r
4017 \r
4018     /* Adjust width */\r
4019     A_WIDTH = squareSize / 14;\r
4020 \r
4021     /* Draw */\r
4022     stLB.lbStyle = BS_SOLID;\r
4023     stLB.lbColor = appData.highlightArrowColor;\r
4024     stLB.lbHatch = 0;\r
4025 \r
4026     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4027     holdpen = SelectObject( hdc, hpen );\r
4028     hbrush = CreateBrushIndirect( &stLB );\r
4029     holdbrush = SelectObject( hdc, hbrush );\r
4030 \r
4031     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4032 \r
4033     SelectObject( hdc, holdpen );\r
4034     SelectObject( hdc, holdbrush );\r
4035     DeleteObject( hpen );\r
4036     DeleteObject( hbrush );\r
4037 }\r
4038 \r
4039 BOOL HasHighlightInfo()\r
4040 {\r
4041     BOOL result = FALSE;\r
4042 \r
4043     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4044         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4045     {\r
4046         result = TRUE;\r
4047     }\r
4048 \r
4049     return result;\r
4050 }\r
4051 \r
4052 BOOL IsDrawArrowEnabled()\r
4053 {\r
4054     BOOL result = FALSE;\r
4055 \r
4056     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4057         result = TRUE;\r
4058     }\r
4059 \r
4060     return result;\r
4061 }\r
4062 \r
4063 VOID DrawArrowHighlight( HDC hdc )\r
4064 {\r
4065     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4066         DrawArrowBetweenSquares( hdc,\r
4067             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4068             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4069     }\r
4070 }\r
4071 \r
4072 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4073 {\r
4074     HRGN result = NULL;\r
4075 \r
4076     if( HasHighlightInfo() ) {\r
4077         int x1, y1, x2, y2;\r
4078         int sx, sy, dx, dy;\r
4079 \r
4080         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4081         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4082 \r
4083         sx = MIN( x1, x2 );\r
4084         sy = MIN( y1, y2 );\r
4085         dx = MAX( x1, x2 ) + squareSize;\r
4086         dy = MAX( y1, y2 ) + squareSize;\r
4087 \r
4088         result = CreateRectRgn( sx, sy, dx, dy );\r
4089     }\r
4090 \r
4091     return result;\r
4092 }\r
4093 \r
4094 /*\r
4095     Warning: this function modifies the behavior of several other functions. \r
4096     \r
4097     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4098     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4099     repaint is scattered all over the place, which is not good for features such as\r
4100     "arrow highlighting" that require a full repaint of the board.\r
4101 \r
4102     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4103     user interaction, when speed is not so important) but especially to avoid errors\r
4104     in the displayed graphics.\r
4105 \r
4106     In such patched places, I always try refer to this function so there is a single\r
4107     place to maintain knowledge.\r
4108     \r
4109     To restore the original behavior, just return FALSE unconditionally.\r
4110 */\r
4111 BOOL IsFullRepaintPreferrable()\r
4112 {\r
4113     BOOL result = FALSE;\r
4114 \r
4115     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4116         /* Arrow may appear on the board */\r
4117         result = TRUE;\r
4118     }\r
4119 \r
4120     return result;\r
4121 }\r
4122 \r
4123 /* \r
4124     This function is called by DrawPosition to know whether a full repaint must\r
4125     be forced or not.\r
4126 \r
4127     Only DrawPosition may directly call this function, which makes use of \r
4128     some state information. Other function should call DrawPosition specifying \r
4129     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4130 */\r
4131 BOOL DrawPositionNeedsFullRepaint()\r
4132 {\r
4133     BOOL result = FALSE;\r
4134 \r
4135     /* \r
4136         Probably a slightly better policy would be to trigger a full repaint\r
4137         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4138         but animation is fast enough that it's difficult to notice.\r
4139     */\r
4140     if( animInfo.piece == EmptySquare ) {\r
4141         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {\r
4142             result = TRUE;\r
4143         }\r
4144     }\r
4145 \r
4146     return result;\r
4147 }\r
4148 \r
4149 VOID\r
4150 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4151 {\r
4152   int row, column, x, y, square_color, piece_color;\r
4153   ChessSquare piece;\r
4154   HBRUSH oldBrush;\r
4155   HDC texture_hdc = NULL;\r
4156 \r
4157   /* [AS] Initialize background textures if needed */\r
4158   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4159       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4160       if( backTextureSquareSize != squareSize \r
4161        || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {\r
4162           backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;\r
4163           backTextureSquareSize = squareSize;\r
4164           RebuildTextureSquareInfo();\r
4165       }\r
4166 \r
4167       texture_hdc = CreateCompatibleDC( hdc );\r
4168   }\r
4169 \r
4170   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4171     for (column = 0; column < BOARD_WIDTH; column++) {\r
4172   \r
4173       SquareToPos(row, column, &x, &y);\r
4174 \r
4175       piece = board[row][column];\r
4176 \r
4177       square_color = ((column + row) % 2) == 1;\r
4178       if( gameInfo.variant == VariantXiangqi ) {\r
4179           square_color = !InPalace(row, column);\r
4180           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4181           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4182       }\r
4183       piece_color = (int) piece < (int) BlackPawn;\r
4184 \r
4185 \r
4186       /* [HGM] holdings file: light square or black */\r
4187       if(column == BOARD_LEFT-2) {\r
4188             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4189                 square_color = 1;\r
4190             else {\r
4191                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4192                 continue;\r
4193             }\r
4194       } else\r
4195       if(column == BOARD_RGHT + 1 ) {\r
4196             if( row < gameInfo.holdingsSize )\r
4197                 square_color = 1;\r
4198             else {\r
4199                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4200                 continue;\r
4201             }\r
4202       }\r
4203       if(column == BOARD_LEFT-1 ) /* left align */\r
4204             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4205       else if( column == BOARD_RGHT) /* right align */\r
4206             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4207       else\r
4208       if (appData.monoMode) {\r
4209         if (piece == EmptySquare) {\r
4210           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4211                  square_color ? WHITENESS : BLACKNESS);\r
4212         } else {\r
4213           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4214         }\r
4215       } \r
4216       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4217           /* [AS] Draw the square using a texture bitmap */\r
4218           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4219           int r = row, c = column; // [HGM] do not flip board in flipView\r
4220           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4221 \r
4222           DrawTile( x, y, \r
4223               squareSize, squareSize, \r
4224               hdc, \r
4225               texture_hdc,\r
4226               backTextureSquareInfo[r][c].mode,\r
4227               backTextureSquareInfo[r][c].x,\r
4228               backTextureSquareInfo[r][c].y );\r
4229 \r
4230           SelectObject( texture_hdc, hbm );\r
4231 \r
4232           if (piece != EmptySquare) {\r
4233               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4234           }\r
4235       }\r
4236       else {\r
4237         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4238 \r
4239         oldBrush = SelectObject(hdc, brush );\r
4240         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4241         SelectObject(hdc, oldBrush);\r
4242         if (piece != EmptySquare)\r
4243           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4244       }\r
4245     }\r
4246   }\r
4247 \r
4248   if( texture_hdc != NULL ) {\r
4249     DeleteDC( texture_hdc );\r
4250   }\r
4251 }\r
4252 \r
4253 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4254 void fputDW(FILE *f, int x)\r
4255 {\r
4256         fputc(x     & 255, f);\r
4257         fputc(x>>8  & 255, f);\r
4258         fputc(x>>16 & 255, f);\r
4259         fputc(x>>24 & 255, f);\r
4260 }\r
4261 \r
4262 #define MAX_CLIPS 200   /* more than enough */\r
4263 \r
4264 VOID\r
4265 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4266 {\r
4267 //  HBITMAP bufferBitmap;\r
4268   BITMAP bi;\r
4269 //  RECT Rect;\r
4270   HDC tmphdc;\r
4271   HBITMAP hbm;\r
4272   int w = 100, h = 50;\r
4273 \r
4274   if(logo == NULL) return;\r
4275 //  GetClientRect(hwndMain, &Rect);\r
4276 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4277 //                                      Rect.bottom-Rect.top+1);\r
4278   tmphdc = CreateCompatibleDC(hdc);\r
4279   hbm = SelectObject(tmphdc, logo);\r
4280   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4281             w = bi.bmWidth;\r
4282             h = bi.bmHeight;\r
4283   }\r
4284   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4285                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4286   SelectObject(tmphdc, hbm);\r
4287   DeleteDC(tmphdc);\r
4288 }\r
4289 \r
4290 VOID\r
4291 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4292 {\r
4293   static Board lastReq, lastDrawn;\r
4294   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4295   static int lastDrawnFlipView = 0;\r
4296   static int lastReqValid = 0, lastDrawnValid = 0;\r
4297   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4298   HDC tmphdc;\r
4299   HDC hdcmem;\r
4300   HBITMAP bufferBitmap;\r
4301   HBITMAP oldBitmap;\r
4302   RECT Rect;\r
4303   HRGN clips[MAX_CLIPS];\r
4304   ChessSquare dragged_piece = EmptySquare;\r
4305 \r
4306   /* I'm undecided on this - this function figures out whether a full\r
4307    * repaint is necessary on its own, so there's no real reason to have the\r
4308    * caller tell it that.  I think this can safely be set to FALSE - but\r
4309    * if we trust the callers not to request full repaints unnessesarily, then\r
4310    * we could skip some clipping work.  In other words, only request a full\r
4311    * redraw when the majority of pieces have changed positions (ie. flip, \r
4312    * gamestart and similar)  --Hawk\r
4313    */\r
4314   Boolean fullrepaint = repaint;\r
4315 \r
4316   if( DrawPositionNeedsFullRepaint() ) {\r
4317       fullrepaint = TRUE;\r
4318   }\r
4319 \r
4320   if (board == NULL) {\r
4321     if (!lastReqValid) {\r
4322       return;\r
4323     }\r
4324     board = lastReq;\r
4325   } else {\r
4326     CopyBoard(lastReq, board);\r
4327     lastReqValid = 1;\r
4328   }\r
4329 \r
4330   if (doingSizing) {\r
4331     return;\r
4332   }\r
4333 \r
4334   if (IsIconic(hwndMain)) {\r
4335     return;\r
4336   }\r
4337 \r
4338   if (hdc == NULL) {\r
4339     hdc = GetDC(hwndMain);\r
4340     if (!appData.monoMode) {\r
4341       SelectPalette(hdc, hPal, FALSE);\r
4342       RealizePalette(hdc);\r
4343     }\r
4344     releaseDC = TRUE;\r
4345   } else {\r
4346     releaseDC = FALSE;\r
4347   }\r
4348 \r
4349   /* Create some work-DCs */\r
4350   hdcmem = CreateCompatibleDC(hdc);\r
4351   tmphdc = CreateCompatibleDC(hdc);\r
4352 \r
4353   /* If dragging is in progress, we temporarely remove the piece */\r
4354   /* [HGM] or temporarily decrease count if stacked              */\r
4355   /*       !! Moved to before board compare !!                   */\r
4356   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4357     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4358     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4359             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4360         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4361     } else \r
4362     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4363             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4364         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4365     } else \r
4366         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4367   }\r
4368 \r
4369   /* Figure out which squares need updating by comparing the \r
4370    * newest board with the last drawn board and checking if\r
4371    * flipping has changed.\r
4372    */\r
4373   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4374     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4375       for (column = 0; column < BOARD_WIDTH; column++) {\r
4376         if (lastDrawn[row][column] != board[row][column]) {\r
4377           SquareToPos(row, column, &x, &y);\r
4378           clips[num_clips++] =\r
4379             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4380         }\r
4381       }\r
4382     }\r
4383     for (i=0; i<2; i++) {\r
4384       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4385           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4386         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4387             lastDrawnHighlight.sq[i].y >= 0) {\r
4388           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4389                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4390           clips[num_clips++] =\r
4391             CreateRectRgn(x - lineGap, y - lineGap, \r
4392                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4393         }\r
4394         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4395           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4396           clips[num_clips++] =\r
4397             CreateRectRgn(x - lineGap, y - lineGap, \r
4398                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4399         }\r
4400       }\r
4401     }\r
4402     for (i=0; i<2; i++) {\r
4403       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4404           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4405         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4406             lastDrawnPremove.sq[i].y >= 0) {\r
4407           SquareToPos(lastDrawnPremove.sq[i].y,\r
4408                       lastDrawnPremove.sq[i].x, &x, &y);\r
4409           clips[num_clips++] =\r
4410             CreateRectRgn(x - lineGap, y - lineGap, \r
4411                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4412         }\r
4413         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4414             premoveHighlightInfo.sq[i].y >= 0) {\r
4415           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4416                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4417           clips[num_clips++] =\r
4418             CreateRectRgn(x - lineGap, y - lineGap, \r
4419                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4420         }\r
4421       }\r
4422     }\r
4423   } else {\r
4424     fullrepaint = TRUE;\r
4425   }\r
4426 \r
4427   /* Create a buffer bitmap - this is the actual bitmap\r
4428    * being written to.  When all the work is done, we can\r
4429    * copy it to the real DC (the screen).  This avoids\r
4430    * the problems with flickering.\r
4431    */\r
4432   GetClientRect(hwndMain, &Rect);\r
4433   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4434                                         Rect.bottom-Rect.top+1);\r
4435   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4436   if (!appData.monoMode) {\r
4437     SelectPalette(hdcmem, hPal, FALSE);\r
4438   }\r
4439 \r
4440   /* Create clips for dragging */\r
4441   if (!fullrepaint) {\r
4442     if (dragInfo.from.x >= 0) {\r
4443       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4444       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4445     }\r
4446     if (dragInfo.start.x >= 0) {\r
4447       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4448       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4449     }\r
4450     if (dragInfo.pos.x >= 0) {\r
4451       x = dragInfo.pos.x - squareSize / 2;\r
4452       y = dragInfo.pos.y - squareSize / 2;\r
4453       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4454     }\r
4455     if (dragInfo.lastpos.x >= 0) {\r
4456       x = dragInfo.lastpos.x - squareSize / 2;\r
4457       y = dragInfo.lastpos.y - squareSize / 2;\r
4458       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4459     }\r
4460   }\r
4461 \r
4462   /* Are we animating a move?  \r
4463    * If so, \r
4464    *   - remove the piece from the board (temporarely)\r
4465    *   - calculate the clipping region\r
4466    */\r
4467   if (!fullrepaint) {\r
4468     if (animInfo.piece != EmptySquare) {\r
4469       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4470       x = boardRect.left + animInfo.lastpos.x;\r
4471       y = boardRect.top + animInfo.lastpos.y;\r
4472       x2 = boardRect.left + animInfo.pos.x;\r
4473       y2 = boardRect.top + animInfo.pos.y;\r
4474       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4475       /* Slight kludge.  The real problem is that after AnimateMove is\r
4476          done, the position on the screen does not match lastDrawn.\r
4477          This currently causes trouble only on e.p. captures in\r
4478          atomic, where the piece moves to an empty square and then\r
4479          explodes.  The old and new positions both had an empty square\r
4480          at the destination, but animation has drawn a piece there and\r
4481          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4482       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4483     }\r
4484   }\r
4485 \r
4486   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4487   if (num_clips == 0)\r
4488     fullrepaint = TRUE;\r
4489 \r
4490   /* Set clipping on the memory DC */\r
4491   if (!fullrepaint) {\r
4492     SelectClipRgn(hdcmem, clips[0]);\r
4493     for (x = 1; x < num_clips; x++) {\r
4494       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4495         abort();  // this should never ever happen!\r
4496     }\r
4497   }\r
4498 \r
4499   /* Do all the drawing to the memory DC */\r
4500   if(explodeInfo.radius) { // [HGM] atomic\r
4501         HBRUSH oldBrush;\r
4502         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4503         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4504         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4505         x += squareSize/2;\r
4506         y += squareSize/2;\r
4507         if(!fullrepaint) {\r
4508           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4509           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4510         }\r
4511         DrawGridOnDC(hdcmem);\r
4512         DrawHighlightsOnDC(hdcmem);\r
4513         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4514         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4515         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4516         SelectObject(hdcmem, oldBrush);\r
4517   } else {\r
4518     DrawGridOnDC(hdcmem);\r
4519     DrawHighlightsOnDC(hdcmem);\r
4520     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4521   }\r
4522   if(logoHeight) {\r
4523         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4524         if(appData.autoLogo) {\r
4525           \r
4526           switch(gameMode) { // pick logos based on game mode\r
4527             case IcsObserving:\r
4528                 whiteLogo = second.programLogo; // ICS logo\r
4529                 blackLogo = second.programLogo;\r
4530             default:\r
4531                 break;\r
4532             case IcsPlayingWhite:\r
4533                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4534                 blackLogo = second.programLogo; // ICS logo\r
4535                 break;\r
4536             case IcsPlayingBlack:\r
4537                 whiteLogo = second.programLogo; // ICS logo\r
4538                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4539                 break;\r
4540             case TwoMachinesPlay:\r
4541                 if(first.twoMachinesColor[0] == 'b') {\r
4542                     whiteLogo = second.programLogo;\r
4543                     blackLogo = first.programLogo;\r
4544                 }\r
4545                 break;\r
4546             case MachinePlaysWhite:\r
4547                 blackLogo = userLogo;\r
4548                 break;\r
4549             case MachinePlaysBlack:\r
4550                 whiteLogo = userLogo;\r
4551                 blackLogo = first.programLogo;\r
4552           }\r
4553         }\r
4554         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4555         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4556   }\r
4557 \r
4558   if( appData.highlightMoveWithArrow ) {\r
4559     DrawArrowHighlight(hdcmem);\r
4560   }\r
4561 \r
4562   DrawCoordsOnDC(hdcmem);\r
4563 \r
4564   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4565                  /* to make sure lastDrawn contains what is actually drawn */\r
4566 \r
4567   /* Put the dragged piece back into place and draw it (out of place!) */\r
4568     if (dragged_piece != EmptySquare) {\r
4569     /* [HGM] or restack */\r
4570     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4571                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4572     else\r
4573     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4574                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4575     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4576     x = dragInfo.pos.x - squareSize / 2;\r
4577     y = dragInfo.pos.y - squareSize / 2;\r
4578     DrawPieceOnDC(hdcmem, dragged_piece,\r
4579                   ((int) dragged_piece < (int) BlackPawn), \r
4580                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4581   }   \r
4582   \r
4583   /* Put the animated piece back into place and draw it */\r
4584   if (animInfo.piece != EmptySquare) {\r
4585     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4586     x = boardRect.left + animInfo.pos.x;\r
4587     y = boardRect.top + animInfo.pos.y;\r
4588     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4589                   ((int) animInfo.piece < (int) BlackPawn),\r
4590                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4591   }\r
4592 \r
4593   /* Release the bufferBitmap by selecting in the old bitmap \r
4594    * and delete the memory DC\r
4595    */\r
4596   SelectObject(hdcmem, oldBitmap);\r
4597   DeleteDC(hdcmem);\r
4598 \r
4599   /* Set clipping on the target DC */\r
4600   if (!fullrepaint) {\r
4601     SelectClipRgn(hdc, clips[0]);\r
4602     for (x = 1; x < num_clips; x++) {\r
4603       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4604         abort();   // this should never ever happen!\r
4605     } \r
4606   }\r
4607 \r
4608   /* Copy the new bitmap onto the screen in one go.\r
4609    * This way we avoid any flickering\r
4610    */\r
4611   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4612   BitBlt(hdc, boardRect.left, boardRect.top,\r
4613          boardRect.right - boardRect.left,\r
4614          boardRect.bottom - boardRect.top,\r
4615          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4616   if(saveDiagFlag) { \r
4617     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4618     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4619 \r
4620     GetObject(bufferBitmap, sizeof(b), &b);\r
4621     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4622         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4623         bih.biWidth = b.bmWidth;\r
4624         bih.biHeight = b.bmHeight;\r
4625         bih.biPlanes = 1;\r
4626         bih.biBitCount = b.bmBitsPixel;\r
4627         bih.biCompression = 0;\r
4628         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4629         bih.biXPelsPerMeter = 0;\r
4630         bih.biYPelsPerMeter = 0;\r
4631         bih.biClrUsed = 0;\r
4632         bih.biClrImportant = 0;\r
4633 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4634 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4635         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4636 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4637 \r
4638         wb = b.bmWidthBytes;\r
4639         // count colors\r
4640         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4641                 int k = ((int*) pData)[i];\r
4642                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4643                 if(j >= 16) break;\r
4644                 color[j] = k;\r
4645                 if(j >= nrColors) nrColors = j+1;\r
4646         }\r
4647         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4648                 INT p = 0;\r
4649                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4650                     for(w=0; w<(wb>>2); w+=2) {\r
4651                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4652                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4653                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4654                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4655                         pData[p++] = m | j<<4;\r
4656                     }\r
4657                     while(p&3) pData[p++] = 0;\r
4658                 }\r
4659                 fac = 3;\r
4660                 wb = ((wb+31)>>5)<<2;\r
4661         }\r
4662         // write BITMAPFILEHEADER\r
4663         fprintf(diagFile, "BM");\r
4664         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4665         fputDW(diagFile, 0);\r
4666         fputDW(diagFile, 0x36 + (fac?64:0));\r
4667         // write BITMAPINFOHEADER\r
4668         fputDW(diagFile, 40);\r
4669         fputDW(diagFile, b.bmWidth);\r
4670         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4671         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4672         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4673         fputDW(diagFile, 0);\r
4674         fputDW(diagFile, 0);\r
4675         fputDW(diagFile, 0);\r
4676         fputDW(diagFile, 0);\r
4677         fputDW(diagFile, 0);\r
4678         fputDW(diagFile, 0);\r
4679         // write color table\r
4680         if(fac)\r
4681         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4682         // write bitmap data\r
4683         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4684                 fputc(pData[i], diagFile);\r
4685      }\r
4686   }\r
4687 \r
4688   SelectObject(tmphdc, oldBitmap);\r
4689 \r
4690   /* Massive cleanup */\r
4691   for (x = 0; x < num_clips; x++)\r
4692     DeleteObject(clips[x]);\r
4693 \r
4694   DeleteDC(tmphdc);\r
4695   DeleteObject(bufferBitmap);\r
4696 \r
4697   if (releaseDC) \r
4698     ReleaseDC(hwndMain, hdc);\r
4699   \r
4700   if (lastDrawnFlipView != flipView) {\r
4701     if (flipView)\r
4702       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4703     else\r
4704       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4705   }\r
4706 \r
4707 /*  CopyBoard(lastDrawn, board);*/\r
4708   lastDrawnHighlight = highlightInfo;\r
4709   lastDrawnPremove   = premoveHighlightInfo;\r
4710   lastDrawnFlipView = flipView;\r
4711   lastDrawnValid = 1;\r
4712 }\r
4713 \r
4714 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4715 int\r
4716 SaveDiagram(f)\r
4717      FILE *f;\r
4718 {\r
4719     saveDiagFlag = 1; diagFile = f;\r
4720     HDCDrawPosition(NULL, TRUE, NULL);\r
4721 \r
4722     saveDiagFlag = 0;\r
4723 \r
4724 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4725     \r
4726     fclose(f);\r
4727     return TRUE;\r
4728 }\r
4729 \r
4730 \r
4731 /*---------------------------------------------------------------------------*\\r
4732 | CLIENT PAINT PROCEDURE\r
4733 |   This is the main event-handler for the WM_PAINT message.\r
4734 |\r
4735 \*---------------------------------------------------------------------------*/\r
4736 VOID\r
4737 PaintProc(HWND hwnd)\r
4738 {\r
4739   HDC         hdc;\r
4740   PAINTSTRUCT ps;\r
4741   HFONT       oldFont;\r
4742 \r
4743   if((hdc = BeginPaint(hwnd, &ps))) {\r
4744     if (IsIconic(hwnd)) {\r
4745       DrawIcon(hdc, 2, 2, iconCurrent);\r
4746     } else {\r
4747       if (!appData.monoMode) {\r
4748         SelectPalette(hdc, hPal, FALSE);\r
4749         RealizePalette(hdc);\r
4750       }\r
4751       HDCDrawPosition(hdc, 1, NULL);\r
4752       oldFont =\r
4753         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4754       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4755                  ETO_CLIPPED|ETO_OPAQUE,\r
4756                  &messageRect, messageText, strlen(messageText), NULL);\r
4757       SelectObject(hdc, oldFont);\r
4758       DisplayBothClocks();\r
4759     }\r
4760     EndPaint(hwnd,&ps);\r
4761   }\r
4762 \r
4763   return;\r
4764 }\r
4765 \r
4766 \r
4767 /*\r
4768  * If the user selects on a border boundary, return -1; if off the board,\r
4769  *   return -2.  Otherwise map the event coordinate to the square.\r
4770  * The offset boardRect.left or boardRect.top must already have been\r
4771  *   subtracted from x.\r
4772  */\r
4773 int EventToSquare(x, limit)\r
4774      int x, limit;\r
4775 {\r
4776   if (x <= 0)\r
4777     return -2;\r
4778   if (x < lineGap)\r
4779     return -1;\r
4780   x -= lineGap;\r
4781   if ((x % (squareSize + lineGap)) >= squareSize)\r
4782     return -1;\r
4783   x /= (squareSize + lineGap);\r
4784     if (x >= limit)\r
4785     return -2;\r
4786   return x;\r
4787 }\r
4788 \r
4789 typedef struct {\r
4790   char piece;\r
4791   int command;\r
4792   char* name;\r
4793 } DropEnable;\r
4794 \r
4795 DropEnable dropEnables[] = {\r
4796   { 'P', DP_Pawn, "Pawn" },\r
4797   { 'N', DP_Knight, "Knight" },\r
4798   { 'B', DP_Bishop, "Bishop" },\r
4799   { 'R', DP_Rook, "Rook" },\r
4800   { 'Q', DP_Queen, "Queen" },\r
4801 };\r
4802 \r
4803 VOID\r
4804 SetupDropMenu(HMENU hmenu)\r
4805 {\r
4806   int i, count, enable;\r
4807   char *p;\r
4808   extern char white_holding[], black_holding[];\r
4809   char item[MSG_SIZ];\r
4810 \r
4811   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4812     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4813                dropEnables[i].piece);\r
4814     count = 0;\r
4815     while (p && *p++ == dropEnables[i].piece) count++;\r
4816     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4817     enable = count > 0 || !appData.testLegality\r
4818       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4819                       && !appData.icsActive);\r
4820     ModifyMenu(hmenu, dropEnables[i].command,\r
4821                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4822                dropEnables[i].command, item);\r
4823   }\r
4824 }\r
4825 \r
4826 void DragPieceBegin(int x, int y)\r
4827 {\r
4828       dragInfo.lastpos.x = boardRect.left + x;\r
4829       dragInfo.lastpos.y = boardRect.top + y;\r
4830       dragInfo.from.x = fromX;\r
4831       dragInfo.from.y = fromY;\r
4832       dragInfo.start = dragInfo.from;\r
4833       SetCapture(hwndMain);\r
4834 }\r
4835 \r
4836 void DragPieceEnd(int x, int y)\r
4837 {\r
4838     ReleaseCapture();\r
4839     dragInfo.start.x = dragInfo.start.y = -1;\r
4840     dragInfo.from = dragInfo.start;\r
4841     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4842 }\r
4843 \r
4844 /* Event handler for mouse messages */\r
4845 VOID\r
4846 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4847 {\r
4848   int x, y;\r
4849   POINT pt;\r
4850   static int recursive = 0;\r
4851   HMENU hmenu;\r
4852   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4853 \r
4854   if (recursive) {\r
4855     if (message == WM_MBUTTONUP) {\r
4856       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4857          to the middle button: we simulate pressing the left button too!\r
4858          */\r
4859       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4860       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4861     }\r
4862     return;\r
4863   }\r
4864   recursive++;\r
4865   \r
4866   pt.x = LOWORD(lParam);\r
4867   pt.y = HIWORD(lParam);\r
4868   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4869   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4870   if (!flipView && y >= 0) {\r
4871     y = BOARD_HEIGHT - 1 - y;\r
4872   }\r
4873   if (flipView && x >= 0) {\r
4874     x = BOARD_WIDTH - 1 - x;\r
4875   }\r
4876 \r
4877   switch (message) {\r
4878   case WM_LBUTTONDOWN:\r
4879       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4880         if (gameMode == EditPosition) {\r
4881           SetWhiteToPlayEvent();\r
4882         } else if (gameMode == IcsPlayingBlack ||\r
4883                    gameMode == MachinePlaysWhite) {\r
4884           CallFlagEvent();\r
4885         } else if (gameMode == EditGame) {\r
4886           AdjustClock(flipClock, -1);\r
4887         }\r
4888       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4889         if (gameMode == EditPosition) {\r
4890           SetBlackToPlayEvent();\r
4891         } else if (gameMode == IcsPlayingWhite ||\r
4892                    gameMode == MachinePlaysBlack) {\r
4893           CallFlagEvent();\r
4894         } else if (gameMode == EditGame) {\r
4895           AdjustClock(!flipClock, -1);\r
4896         }\r
4897       }\r
4898       dragInfo.start.x = dragInfo.start.y = -1;\r
4899       dragInfo.from = dragInfo.start;\r
4900     if(fromX == -1 && frozen) { // not sure where this is for\r
4901                 fromX = fromY = -1; \r
4902       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4903       break;\r
4904     }\r
4905       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4906       DrawPosition(TRUE, NULL);\r
4907     break;\r
4908 \r
4909   case WM_LBUTTONUP:\r
4910       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4911       DrawPosition(TRUE, NULL);\r
4912     break;\r
4913 \r
4914   case WM_MOUSEMOVE:\r
4915     if ((appData.animateDragging || appData.highlightDragging)\r
4916         && (wParam & MK_LBUTTON)\r
4917         && dragInfo.from.x >= 0) \r
4918     {\r
4919       BOOL full_repaint = FALSE;\r
4920 \r
4921       if (appData.animateDragging) {\r
4922         dragInfo.pos = pt;\r
4923       }\r
4924       if (appData.highlightDragging) {\r
4925         SetHighlights(fromX, fromY, x, y);\r
4926         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4927             full_repaint = TRUE;\r
4928         }\r
4929       }\r
4930       \r
4931       DrawPosition( full_repaint, NULL);\r
4932       \r
4933       dragInfo.lastpos = dragInfo.pos;\r
4934     }\r
4935     break;\r
4936 \r
4937   case WM_MOUSEWHEEL: // [DM]\r
4938     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
4939        /* Mouse Wheel is being rolled forward\r
4940         * Play moves forward\r
4941         */\r
4942        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
4943                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
4944        /* Mouse Wheel is being rolled backward\r
4945         * Play moves backward\r
4946         */\r
4947        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
4948                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
4949     }\r
4950     break;\r
4951 \r
4952   case WM_MBUTTONDOWN:\r
4953   case WM_RBUTTONDOWN:\r
4954     ErrorPopDown();\r
4955     ReleaseCapture();\r
4956     fromX = fromY = -1;\r
4957     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4958     dragInfo.start.x = dragInfo.start.y = -1;\r
4959     dragInfo.from = dragInfo.start;\r
4960     dragInfo.lastpos = dragInfo.pos;\r
4961     if (appData.highlightDragging) {\r
4962       ClearHighlights();\r
4963     }\r
4964     if(y == -2) {\r
4965       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4966       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4967           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4968       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4969           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4970       }\r
4971     }\r
4972     DrawPosition(TRUE, NULL);\r
4973 \r
4974     switch (gameMode) {\r
4975     case EditPosition:\r
4976     case IcsExamining:\r
4977       if (x < 0 || y < 0) break;\r
4978       fromX = x;\r
4979       fromY = y;\r
4980       if (message == WM_MBUTTONDOWN) {\r
4981         buttonCount = 3;  /* even if system didn't think so */\r
4982         if (wParam & MK_SHIFT) \r
4983           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4984         else\r
4985           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4986       } else { /* message == WM_RBUTTONDOWN */\r
4987         /* Just have one menu, on the right button.  Windows users don't\r
4988            think to try the middle one, and sometimes other software steals\r
4989            it, or it doesn't really exist. */\r
4990         if(gameInfo.variant != VariantShogi)\r
4991             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4992         else\r
4993             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
4994       }\r
4995       break;\r
4996     case IcsPlayingWhite:\r
4997     case IcsPlayingBlack:\r
4998     case EditGame:\r
4999     case MachinePlaysWhite:\r
5000     case MachinePlaysBlack:\r
5001       if (appData.testLegality &&\r
5002           gameInfo.variant != VariantBughouse &&\r
5003           gameInfo.variant != VariantCrazyhouse) break;\r
5004       if (x < 0 || y < 0) break;\r
5005       fromX = x;\r
5006       fromY = y;\r
5007       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5008       SetupDropMenu(hmenu);\r
5009       MenuPopup(hwnd, pt, hmenu, -1);\r
5010       break;\r
5011     default:\r
5012       break;\r
5013     }\r
5014     break;\r
5015   }\r
5016 \r
5017   recursive--;\r
5018 }\r
5019 \r
5020 /* Preprocess messages for buttons in main window */\r
5021 LRESULT CALLBACK\r
5022 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5023 {\r
5024   int id = GetWindowLong(hwnd, GWL_ID);\r
5025   int i, dir;\r
5026 \r
5027   for (i=0; i<N_BUTTONS; i++) {\r
5028     if (buttonDesc[i].id == id) break;\r
5029   }\r
5030   if (i == N_BUTTONS) return 0;\r
5031   switch (message) {\r
5032   case WM_KEYDOWN:\r
5033     switch (wParam) {\r
5034     case VK_LEFT:\r
5035     case VK_RIGHT:\r
5036       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5037       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5038       return TRUE;\r
5039     }\r
5040     break;\r
5041   case WM_CHAR:\r
5042     switch (wParam) {\r
5043     case '\r':\r
5044       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5045       return TRUE;\r
5046     default:\r
5047       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5048         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5049         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5050         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5051         SetFocus(h);\r
5052         SendMessage(h, WM_CHAR, wParam, lParam);\r
5053         return TRUE;\r
5054       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5055         PopUpMoveDialog((char)wParam);\r
5056       }\r
5057       break;\r
5058     }\r
5059     break;\r
5060   }\r
5061   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5062 }\r
5063 \r
5064 /* Process messages for Promotion dialog box */\r
5065 LRESULT CALLBACK\r
5066 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5067 {\r
5068   char promoChar;\r
5069 \r
5070   switch (message) {\r
5071   case WM_INITDIALOG: /* message: initialize dialog box */\r
5072     /* Center the dialog over the application window */\r
5073     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5074     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5075       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5076        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5077                SW_SHOW : SW_HIDE);\r
5078     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5079     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5080        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5081          PieceToChar(WhiteAngel) != '~') ||\r
5082         (PieceToChar(BlackAngel) >= 'A' &&\r
5083          PieceToChar(BlackAngel) != '~')   ) ?\r
5084                SW_SHOW : SW_HIDE);\r
5085     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5086        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5087          PieceToChar(WhiteMarshall) != '~') ||\r
5088         (PieceToChar(BlackMarshall) >= 'A' &&\r
5089          PieceToChar(BlackMarshall) != '~')   ) ?\r
5090                SW_SHOW : SW_HIDE);\r
5091     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5092     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5093        gameInfo.variant != VariantShogi ?\r
5094                SW_SHOW : SW_HIDE);\r
5095     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5096        gameInfo.variant != VariantShogi ?\r
5097                SW_SHOW : SW_HIDE);\r
5098     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5099        gameInfo.variant == VariantShogi ?\r
5100                SW_SHOW : SW_HIDE);\r
5101     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5102        gameInfo.variant == VariantShogi ?\r
5103                SW_SHOW : SW_HIDE);\r
5104     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5105        gameInfo.variant == VariantSuper ?\r
5106                SW_SHOW : SW_HIDE);\r
5107     return TRUE;\r
5108 \r
5109   case WM_COMMAND: /* message: received a command */\r
5110     switch (LOWORD(wParam)) {\r
5111     case IDCANCEL:\r
5112       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5113       ClearHighlights();\r
5114       DrawPosition(FALSE, NULL);\r
5115       return TRUE;\r
5116     case PB_King:\r
5117       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5118       break;\r
5119     case PB_Queen:\r
5120       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5121       break;\r
5122     case PB_Rook:\r
5123       promoChar = PieceToChar(BlackRook);\r
5124       break;\r
5125     case PB_Bishop:\r
5126       promoChar = PieceToChar(BlackBishop);\r
5127       break;\r
5128     case PB_Chancellor:\r
5129       promoChar = PieceToChar(BlackMarshall);\r
5130       break;\r
5131     case PB_Archbishop:\r
5132       promoChar = PieceToChar(BlackAngel);\r
5133       break;\r
5134     case PB_Knight:\r
5135       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5136       break;\r
5137     default:\r
5138       return FALSE;\r
5139     }\r
5140     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5141     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5142        only show the popup when we are already sure the move is valid or\r
5143        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5144        will figure out it is a promotion from the promoChar. */\r
5145     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
5146     fromX = fromY = -1;\r
5147     if (!appData.highlightLastMove) {\r
5148       ClearHighlights();\r
5149       DrawPosition(FALSE, NULL);\r
5150     }\r
5151     return TRUE;\r
5152   }\r
5153   return FALSE;\r
5154 }\r
5155 \r
5156 /* Pop up promotion dialog */\r
5157 VOID\r
5158 PromotionPopup(HWND hwnd)\r
5159 {\r
5160   FARPROC lpProc;\r
5161 \r
5162   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5163   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5164     hwnd, (DLGPROC)lpProc);\r
5165   FreeProcInstance(lpProc);\r
5166 }\r
5167 \r
5168 void\r
5169 PromotionPopUp()\r
5170 {\r
5171   DrawPosition(TRUE, NULL);\r
5172   PromotionPopup(hwndMain);\r
5173 }\r
5174 \r
5175 /* Toggle ShowThinking */\r
5176 VOID\r
5177 ToggleShowThinking()\r
5178 {\r
5179   appData.showThinking = !appData.showThinking;\r
5180   ShowThinkingEvent();\r
5181 }\r
5182 \r
5183 VOID\r
5184 LoadGameDialog(HWND hwnd, char* title)\r
5185 {\r
5186   UINT number = 0;\r
5187   FILE *f;\r
5188   char fileTitle[MSG_SIZ];\r
5189   f = OpenFileDialog(hwnd, "rb", "",\r
5190                      appData.oldSaveStyle ? "gam" : "pgn",\r
5191                      GAME_FILT,\r
5192                      title, &number, fileTitle, NULL);\r
5193   if (f != NULL) {\r
5194     cmailMsgLoaded = FALSE;\r
5195     if (number == 0) {\r
5196       int error = GameListBuild(f);\r
5197       if (error) {\r
5198         DisplayError("Cannot build game list", error);\r
5199       } else if (!ListEmpty(&gameList) &&\r
5200                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5201         GameListPopUp(f, fileTitle);\r
5202         return;\r
5203       }\r
5204       GameListDestroy();\r
5205       number = 1;\r
5206     }\r
5207     LoadGame(f, number, fileTitle, FALSE);\r
5208   }\r
5209 }\r
5210 \r
5211 int get_term_width()\r
5212 {\r
5213     HDC hdc;\r
5214     TEXTMETRIC tm;\r
5215     RECT rc;\r
5216     HFONT hfont, hold_font;\r
5217     LOGFONT lf;\r
5218     HWND hText;\r
5219 \r
5220     if (hwndConsole)\r
5221         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5222     else\r
5223         return 79;\r
5224 \r
5225     // get the text metrics\r
5226     hdc = GetDC(hText);\r
5227     lf = font[boardSize][CONSOLE_FONT]->lf;\r
5228     if (consoleCF.dwEffects & CFE_BOLD)\r
5229         lf.lfWeight = FW_BOLD;\r
5230     if (consoleCF.dwEffects & CFE_ITALIC)\r
5231         lf.lfItalic = TRUE;\r
5232     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
5233         lf.lfStrikeOut = TRUE;\r
5234     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
5235         lf.lfUnderline = TRUE;\r
5236     hfont = CreateFontIndirect(&lf);\r
5237     hold_font = SelectObject(hdc, hfont);\r
5238     GetTextMetrics(hdc, &tm);\r
5239     SelectObject(hdc, hold_font);\r
5240     DeleteObject(hfont);\r
5241     ReleaseDC(hText, hdc);\r
5242 \r
5243     // get the rectangle\r
5244     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5245 \r
5246     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
5247 }\r
5248 \r
5249 void UpdateICSWidth(HWND hText)\r
5250 {\r
5251     LONG old_width, new_width;\r
5252 \r
5253     new_width = get_term_width(hText, FALSE);\r
5254     old_width = GetWindowLong(hText, GWL_USERDATA);\r
5255     if (new_width != old_width)\r
5256     {\r
5257         ics_update_width(new_width);\r
5258         SetWindowLong(hText, GWL_USERDATA, new_width);\r
5259     }\r
5260 }\r
5261 \r
5262 VOID\r
5263 ChangedConsoleFont()\r
5264 {\r
5265   CHARFORMAT cfmt;\r
5266   CHARRANGE tmpsel, sel;\r
5267   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5268   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5269   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5270   PARAFORMAT paraf;\r
5271 \r
5272   cfmt.cbSize = sizeof(CHARFORMAT);\r
5273   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5274   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5275   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5276    * size.  This was undocumented in the version of MSVC++ that I had\r
5277    * when I wrote the code, but is apparently documented now.\r
5278    */\r
5279   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5280   cfmt.bCharSet = f->lf.lfCharSet;\r
5281   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5282   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5283   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5284   /* Why are the following seemingly needed too? */\r
5285   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5286   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5287   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5288   tmpsel.cpMin = 0;\r
5289   tmpsel.cpMax = -1; /*999999?*/\r
5290   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5291   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5292   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5293    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5294    */\r
5295   paraf.cbSize = sizeof(paraf);\r
5296   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5297   paraf.dxStartIndent = 0;\r
5298   paraf.dxOffset = WRAP_INDENT;\r
5299   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5300   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5301   UpdateICSWidth(hText);\r
5302 }\r
5303 \r
5304 /*---------------------------------------------------------------------------*\\r
5305  *\r
5306  * Window Proc for main window\r
5307  *\r
5308 \*---------------------------------------------------------------------------*/\r
5309 \r
5310 /* Process messages for main window, etc. */\r
5311 LRESULT CALLBACK\r
5312 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5313 {\r
5314   FARPROC lpProc;\r
5315   int wmId, wmEvent;\r
5316   char *defName;\r
5317   FILE *f;\r
5318   UINT number;\r
5319   char fileTitle[MSG_SIZ];\r
5320   char buf[MSG_SIZ];\r
5321   static SnapData sd;\r
5322 \r
5323   switch (message) {\r
5324 \r
5325   case WM_PAINT: /* message: repaint portion of window */\r
5326     PaintProc(hwnd);\r
5327     break;\r
5328 \r
5329   case WM_ERASEBKGND:\r
5330     if (IsIconic(hwnd)) {\r
5331       /* Cheat; change the message */\r
5332       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5333     } else {\r
5334       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5335     }\r
5336     break;\r
5337 \r
5338   case WM_LBUTTONDOWN:\r
5339   case WM_MBUTTONDOWN:\r
5340   case WM_RBUTTONDOWN:\r
5341   case WM_LBUTTONUP:\r
5342   case WM_MBUTTONUP:\r
5343   case WM_RBUTTONUP:\r
5344   case WM_MOUSEMOVE:\r
5345   case WM_MOUSEWHEEL:\r
5346     MouseEvent(hwnd, message, wParam, lParam);\r
5347     break;\r
5348 \r
5349   JAWS_KB_NAVIGATION\r
5350 \r
5351   case WM_CHAR:\r
5352     \r
5353     JAWS_ALT_INTERCEPT\r
5354 \r
5355     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
5356         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5357         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5358         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5359         SetFocus(h);\r
5360         SendMessage(h, message, wParam, lParam);\r
5361     } else if(lParam != KF_REPEAT) {\r
5362         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5363                 PopUpMoveDialog((char)wParam);\r
5364         } else if((char)wParam == 003) CopyGameToClipboard();\r
5365          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5366     }\r
5367 \r
5368     break;\r
5369 \r
5370   case WM_PALETTECHANGED:\r
5371     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5372       int nnew;\r
5373       HDC hdc = GetDC(hwndMain);\r
5374       SelectPalette(hdc, hPal, TRUE);\r
5375       nnew = RealizePalette(hdc);\r
5376       if (nnew > 0) {\r
5377         paletteChanged = TRUE;\r
5378         InvalidateRect(hwnd, &boardRect, FALSE);\r
5379       }\r
5380       ReleaseDC(hwnd, hdc);\r
5381     }\r
5382     break;\r
5383 \r
5384   case WM_QUERYNEWPALETTE:\r
5385     if (!appData.monoMode /*&& paletteChanged*/) {\r
5386       int nnew;\r
5387       HDC hdc = GetDC(hwndMain);\r
5388       paletteChanged = FALSE;\r
5389       SelectPalette(hdc, hPal, FALSE);\r
5390       nnew = RealizePalette(hdc);\r
5391       if (nnew > 0) {\r
5392         InvalidateRect(hwnd, &boardRect, FALSE);\r
5393       }\r
5394       ReleaseDC(hwnd, hdc);\r
5395       return TRUE;\r
5396     }\r
5397     return FALSE;\r
5398 \r
5399   case WM_COMMAND: /* message: command from application menu */\r
5400     wmId    = LOWORD(wParam);\r
5401     wmEvent = HIWORD(wParam);\r
5402 \r
5403     switch (wmId) {\r
5404     case IDM_NewGame:\r
5405       ResetGameEvent();\r
5406       SAY("new game enter a move to play against the computer with white");\r
5407       break;\r
5408 \r
5409     case IDM_NewGameFRC:\r
5410       if( NewGameFRC() == 0 ) {\r
5411         ResetGameEvent();\r
5412       }\r
5413       break;\r
5414 \r
5415     case IDM_NewVariant:\r
5416       NewVariantPopup(hwnd);\r
5417       break;\r
5418 \r
5419     case IDM_LoadGame:\r
5420       LoadGameDialog(hwnd, "Load Game from File");\r
5421       break;\r
5422 \r
5423     case IDM_LoadNextGame:\r
5424       ReloadGame(1);\r
5425       break;\r
5426 \r
5427     case IDM_LoadPrevGame:\r
5428       ReloadGame(-1);\r
5429       break;\r
5430 \r
5431     case IDM_ReloadGame:\r
5432       ReloadGame(0);\r
5433       break;\r
5434 \r
5435     case IDM_LoadPosition:\r
5436       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5437         Reset(FALSE, TRUE);\r
5438       }\r
5439       number = 1;\r
5440       f = OpenFileDialog(hwnd, "rb", "",\r
5441                          appData.oldSaveStyle ? "pos" : "fen",\r
5442                          POSITION_FILT,\r
5443                          "Load Position from File", &number, fileTitle, NULL);\r
5444       if (f != NULL) {\r
5445         LoadPosition(f, number, fileTitle);\r
5446       }\r
5447       break;\r
5448 \r
5449     case IDM_LoadNextPosition:\r
5450       ReloadPosition(1);\r
5451       break;\r
5452 \r
5453     case IDM_LoadPrevPosition:\r
5454       ReloadPosition(-1);\r
5455       break;\r
5456 \r
5457     case IDM_ReloadPosition:\r
5458       ReloadPosition(0);\r
5459       break;\r
5460 \r
5461     case IDM_SaveGame:\r
5462       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5463       f = OpenFileDialog(hwnd, "a", defName,\r
5464                          appData.oldSaveStyle ? "gam" : "pgn",\r
5465                          GAME_FILT,\r
5466                          "Save Game to File", NULL, fileTitle, NULL);\r
5467       if (f != NULL) {\r
5468         SaveGame(f, 0, "");\r
5469       }\r
5470       break;\r
5471 \r
5472     case IDM_SavePosition:\r
5473       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5474       f = OpenFileDialog(hwnd, "a", defName,\r
5475                          appData.oldSaveStyle ? "pos" : "fen",\r
5476                          POSITION_FILT,\r
5477                          "Save Position to File", NULL, fileTitle, NULL);\r
5478       if (f != NULL) {\r
5479         SavePosition(f, 0, "");\r
5480       }\r
5481       break;\r
5482 \r
5483     case IDM_SaveDiagram:\r
5484       defName = "diagram";\r
5485       f = OpenFileDialog(hwnd, "wb", defName,\r
5486                          "bmp",\r
5487                          DIAGRAM_FILT,\r
5488                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5489       if (f != NULL) {\r
5490         SaveDiagram(f);\r
5491       }\r
5492       break;\r
5493 \r
5494     case IDM_CopyGame:\r
5495       CopyGameToClipboard();\r
5496       break;\r
5497 \r
5498     case IDM_PasteGame:\r
5499       PasteGameFromClipboard();\r
5500       break;\r
5501 \r
5502     case IDM_CopyGameListToClipboard:\r
5503       CopyGameListToClipboard();\r
5504       break;\r
5505 \r
5506     /* [AS] Autodetect FEN or PGN data */\r
5507     case IDM_PasteAny:\r
5508       PasteGameOrFENFromClipboard();\r
5509       break;\r
5510 \r
5511     /* [AS] Move history */\r
5512     case IDM_ShowMoveHistory:\r
5513         if( MoveHistoryIsUp() ) {\r
5514             MoveHistoryPopDown();\r
5515         }\r
5516         else {\r
5517             MoveHistoryPopUp();\r
5518         }\r
5519         break;\r
5520 \r
5521     /* [AS] Eval graph */\r
5522     case IDM_ShowEvalGraph:\r
5523         if( EvalGraphIsUp() ) {\r
5524             EvalGraphPopDown();\r
5525         }\r
5526         else {\r
5527             EvalGraphPopUp();\r
5528             SetFocus(hwndMain);\r
5529         }\r
5530         break;\r
5531 \r
5532     /* [AS] Engine output */\r
5533     case IDM_ShowEngineOutput:\r
5534         if( EngineOutputIsUp() ) {\r
5535             EngineOutputPopDown();\r
5536         }\r
5537         else {\r
5538             EngineOutputPopUp();\r
5539         }\r
5540         break;\r
5541 \r
5542     /* [AS] User adjudication */\r
5543     case IDM_UserAdjudication_White:\r
5544         UserAdjudicationEvent( +1 );\r
5545         break;\r
5546 \r
5547     case IDM_UserAdjudication_Black:\r
5548         UserAdjudicationEvent( -1 );\r
5549         break;\r
5550 \r
5551     case IDM_UserAdjudication_Draw:\r
5552         UserAdjudicationEvent( 0 );\r
5553         break;\r
5554 \r
5555     /* [AS] Game list options dialog */\r
5556     case IDM_GameListOptions:\r
5557       GameListOptions();\r
5558       break;\r
5559 \r
5560     case IDM_NewChat:\r
5561       ChatPopUp();\r
5562       break;\r
5563 \r
5564     case IDM_CopyPosition:\r
5565       CopyFENToClipboard();\r
5566       break;\r
5567 \r
5568     case IDM_PastePosition:\r
5569       PasteFENFromClipboard();\r
5570       break;\r
5571 \r
5572     case IDM_MailMove:\r
5573       MailMoveEvent();\r
5574       break;\r
5575 \r
5576     case IDM_ReloadCMailMsg:\r
5577       Reset(TRUE, TRUE);\r
5578       ReloadCmailMsgEvent(FALSE);\r
5579       break;\r
5580 \r
5581     case IDM_Minimize:\r
5582       ShowWindow(hwnd, SW_MINIMIZE);\r
5583       break;\r
5584 \r
5585     case IDM_Exit:\r
5586       ExitEvent(0);\r
5587       break;\r
5588 \r
5589     case IDM_MachineWhite:\r
5590       MachineWhiteEvent();\r
5591       /*\r
5592        * refresh the tags dialog only if it's visible\r
5593        */\r
5594       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5595           char *tags;\r
5596           tags = PGNTags(&gameInfo);\r
5597           TagsPopUp(tags, CmailMsg());\r
5598           free(tags);\r
5599       }\r
5600       SAY("computer starts playing white");\r
5601       break;\r
5602 \r
5603     case IDM_MachineBlack:\r
5604       MachineBlackEvent();\r
5605       /*\r
5606        * refresh the tags dialog only if it's visible\r
5607        */\r
5608       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5609           char *tags;\r
5610           tags = PGNTags(&gameInfo);\r
5611           TagsPopUp(tags, CmailMsg());\r
5612           free(tags);\r
5613       }\r
5614       SAY("computer starts playing black");\r
5615       break;\r
5616 \r
5617     case IDM_TwoMachines:\r
5618       TwoMachinesEvent();\r
5619       /*\r
5620        * refresh the tags dialog only if it's visible\r
5621        */\r
5622       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5623           char *tags;\r
5624           tags = PGNTags(&gameInfo);\r
5625           TagsPopUp(tags, CmailMsg());\r
5626           free(tags);\r
5627       }\r
5628       SAY("programs start playing each other");\r
5629       break;\r
5630 \r
5631     case IDM_AnalysisMode:\r
5632       if (!first.analysisSupport) {\r
5633         sprintf(buf, "%s does not support analysis", first.tidy);\r
5634         DisplayError(buf, 0);\r
5635       } else {\r
5636         SAY("analyzing current position");\r
5637         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5638         if (appData.icsActive) {\r
5639                if (gameMode != IcsObserving) {\r
5640                        sprintf(buf, "You are not observing a game");\r
5641                        DisplayError(buf, 0);\r
5642                        /* secure check */\r
5643                        if (appData.icsEngineAnalyze) {\r
5644                                if (appData.debugMode) \r
5645                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5646                                ExitAnalyzeMode();\r
5647                                ModeHighlight();\r
5648                                break;\r
5649                        }\r
5650                        break;\r
5651                } else {\r
5652                        /* if enable, user want disable icsEngineAnalyze */\r
5653                        if (appData.icsEngineAnalyze) {\r
5654                                ExitAnalyzeMode();\r
5655                                ModeHighlight();\r
5656                                break;\r
5657                        }\r
5658                        appData.icsEngineAnalyze = TRUE;\r
5659                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5660                }\r
5661         } \r
5662         if (!appData.showThinking) ToggleShowThinking();\r
5663         AnalyzeModeEvent();\r
5664       }\r
5665       break;\r
5666 \r
5667     case IDM_AnalyzeFile:\r
5668       if (!first.analysisSupport) {\r
5669         char buf[MSG_SIZ];\r
5670         sprintf(buf, "%s does not support analysis", first.tidy);\r
5671         DisplayError(buf, 0);\r
5672       } else {\r
5673         if (!appData.showThinking) ToggleShowThinking();\r
5674         AnalyzeFileEvent();\r
5675         LoadGameDialog(hwnd, "Analyze Game from File");\r
5676         AnalysisPeriodicEvent(1);\r
5677       }\r
5678       break;\r
5679 \r
5680     case IDM_IcsClient:\r
5681       IcsClientEvent();\r
5682       break;\r
5683 \r
5684     case IDM_EditGame:\r
5685       EditGameEvent();\r
5686       SAY("edit game");\r
5687       break;\r
5688 \r
5689     case IDM_EditPosition:\r
5690       EditPositionEvent();\r
5691       SAY("to set up a position type a FEN");\r
5692       break;\r
5693 \r
5694     case IDM_Training:\r
5695       TrainingEvent();\r
5696       break;\r
5697 \r
5698     case IDM_ShowGameList:\r
5699       ShowGameListProc();\r
5700       break;\r
5701 \r
5702     case IDM_EditTags:\r
5703       EditTagsProc();\r
5704       break;\r
5705 \r
5706     case IDM_EditComment:\r
5707       if (commentUp && editComment) {\r
5708         CommentPopDown();\r
5709       } else {\r
5710         EditCommentEvent();\r
5711       }\r
5712       break;\r
5713 \r
5714     case IDM_Pause:\r
5715       PauseEvent();\r
5716       break;\r
5717 \r
5718     case IDM_Accept:\r
5719       AcceptEvent();\r
5720       break;\r
5721 \r
5722     case IDM_Decline:\r
5723       DeclineEvent();\r
5724       break;\r
5725 \r
5726     case IDM_Rematch:\r
5727       RematchEvent();\r
5728       break;\r
5729 \r
5730     case IDM_CallFlag:\r
5731       CallFlagEvent();\r
5732       break;\r
5733 \r
5734     case IDM_Draw:\r
5735       DrawEvent();\r
5736       break;\r
5737 \r
5738     case IDM_Adjourn:\r
5739       AdjournEvent();\r
5740       break;\r
5741 \r
5742     case IDM_Abort:\r
5743       AbortEvent();\r
5744       break;\r
5745 \r
5746     case IDM_Resign:\r
5747       ResignEvent();\r
5748       break;\r
5749 \r
5750     case IDM_StopObserving:\r
5751       StopObservingEvent();\r
5752       break;\r
5753 \r
5754     case IDM_StopExamining:\r
5755       StopExaminingEvent();\r
5756       break;\r
5757 \r
5758     case IDM_TypeInMove:\r
5759       PopUpMoveDialog('\000');\r
5760       break;\r
5761 \r
5762     case IDM_TypeInName:\r
5763       PopUpNameDialog('\000');\r
5764       break;\r
5765 \r
5766     case IDM_Backward:\r
5767       BackwardEvent();\r
5768       SetFocus(hwndMain);\r
5769       break;\r
5770 \r
5771     JAWS_MENU_ITEMS\r
5772 \r
5773     case IDM_Forward:\r
5774       ForwardEvent();\r
5775       SetFocus(hwndMain);\r
5776       break;\r
5777 \r
5778     case IDM_ToStart:\r
5779       ToStartEvent();\r
5780       SetFocus(hwndMain);\r
5781       break;\r
5782 \r
5783     case IDM_ToEnd:\r
5784       ToEndEvent();\r
5785       SetFocus(hwndMain);\r
5786       break;\r
5787 \r
5788     case IDM_Revert:\r
5789       RevertEvent();\r
5790       break;\r
5791 \r
5792     case IDM_TruncateGame:\r
5793       TruncateGameEvent();\r
5794       break;\r
5795 \r
5796     case IDM_MoveNow:\r
5797       MoveNowEvent();\r
5798       break;\r
5799 \r
5800     case IDM_RetractMove:\r
5801       RetractMoveEvent();\r
5802       break;\r
5803 \r
5804     case IDM_FlipView:\r
5805       flipView = !flipView;\r
5806       DrawPosition(FALSE, NULL);\r
5807       break;\r
5808 \r
5809     case IDM_FlipClock:\r
5810       flipClock = !flipClock;\r
5811       DisplayBothClocks();\r
5812       DrawPosition(FALSE, NULL);\r
5813       break;\r
5814 \r
5815     case IDM_MuteSounds:\r
5816       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5817       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5818                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5819       break;\r
5820 \r
5821     case IDM_GeneralOptions:\r
5822       GeneralOptionsPopup(hwnd);\r
5823       DrawPosition(TRUE, NULL);\r
5824       break;\r
5825 \r
5826     case IDM_BoardOptions:\r
5827       BoardOptionsPopup(hwnd);\r
5828       break;\r
5829 \r
5830     case IDM_EnginePlayOptions:\r
5831       EnginePlayOptionsPopup(hwnd);\r
5832       break;\r
5833 \r
5834     case IDM_Engine1Options:\r
5835       EngineOptionsPopup(hwnd, &first);\r
5836       break;\r
5837 \r
5838     case IDM_Engine2Options:\r
5839       EngineOptionsPopup(hwnd, &second);\r
5840       break;\r
5841 \r
5842     case IDM_OptionsUCI:\r
5843       UciOptionsPopup(hwnd);\r
5844       break;\r
5845 \r
5846     case IDM_IcsOptions:\r
5847       IcsOptionsPopup(hwnd);\r
5848       break;\r
5849 \r
5850     case IDM_Fonts:\r
5851       FontsOptionsPopup(hwnd);\r
5852       break;\r
5853 \r
5854     case IDM_Sounds:\r
5855       SoundOptionsPopup(hwnd);\r
5856       break;\r
5857 \r
5858     case IDM_CommPort:\r
5859       CommPortOptionsPopup(hwnd);\r
5860       break;\r
5861 \r
5862     case IDM_LoadOptions:\r
5863       LoadOptionsPopup(hwnd);\r
5864       break;\r
5865 \r
5866     case IDM_SaveOptions:\r
5867       SaveOptionsPopup(hwnd);\r
5868       break;\r
5869 \r
5870     case IDM_TimeControl:\r
5871       TimeControlOptionsPopup(hwnd);\r
5872       break;\r
5873 \r
5874     case IDM_SaveSettings:\r
5875       SaveSettings(settingsFileName);\r
5876       break;\r
5877 \r
5878     case IDM_SaveSettingsOnExit:\r
5879       saveSettingsOnExit = !saveSettingsOnExit;\r
5880       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5881                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5882                                          MF_CHECKED : MF_UNCHECKED));\r
5883       break;\r
5884 \r
5885     case IDM_Hint:\r
5886       HintEvent();\r
5887       break;\r
5888 \r
5889     case IDM_Book:\r
5890       BookEvent();\r
5891       break;\r
5892 \r
5893     case IDM_AboutGame:\r
5894       AboutGameEvent();\r
5895       break;\r
5896 \r
5897     case IDM_Debug:\r
5898       appData.debugMode = !appData.debugMode;\r
5899       if (appData.debugMode) {\r
5900         char dir[MSG_SIZ];\r
5901         GetCurrentDirectory(MSG_SIZ, dir);\r
5902         SetCurrentDirectory(installDir);\r
5903         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5904         SetCurrentDirectory(dir);\r
5905         setbuf(debugFP, NULL);\r
5906       } else {\r
5907         fclose(debugFP);\r
5908         debugFP = NULL;\r
5909       }\r
5910       break;\r
5911 \r
5912     case IDM_HELPCONTENTS:\r
5913       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5914           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5915           MessageBox (GetFocus(),\r
5916                     "Unable to activate help",\r
5917                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5918       }\r
5919       break;\r
5920 \r
5921     case IDM_HELPSEARCH:\r
5922         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5923             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5924         MessageBox (GetFocus(),\r
5925                     "Unable to activate help",\r
5926                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5927       }\r
5928       break;\r
5929 \r
5930     case IDM_HELPHELP:\r
5931       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5932         MessageBox (GetFocus(),\r
5933                     "Unable to activate help",\r
5934                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5935       }\r
5936       break;\r
5937 \r
5938     case IDM_ABOUT:\r
5939       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5940       DialogBox(hInst, \r
5941         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5942         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5943       FreeProcInstance(lpProc);\r
5944       break;\r
5945 \r
5946     case IDM_DirectCommand1:\r
5947       AskQuestionEvent("Direct Command",\r
5948                        "Send to chess program:", "", "1");\r
5949       break;\r
5950     case IDM_DirectCommand2:\r
5951       AskQuestionEvent("Direct Command",\r
5952                        "Send to second chess program:", "", "2");\r
5953       break;\r
5954 \r
5955     case EP_WhitePawn:\r
5956       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5957       fromX = fromY = -1;\r
5958       break;\r
5959 \r
5960     case EP_WhiteKnight:\r
5961       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5962       fromX = fromY = -1;\r
5963       break;\r
5964 \r
5965     case EP_WhiteBishop:\r
5966       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5967       fromX = fromY = -1;\r
5968       break;\r
5969 \r
5970     case EP_WhiteRook:\r
5971       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5972       fromX = fromY = -1;\r
5973       break;\r
5974 \r
5975     case EP_WhiteQueen:\r
5976       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5977       fromX = fromY = -1;\r
5978       break;\r
5979 \r
5980     case EP_WhiteFerz:\r
5981       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5982       fromX = fromY = -1;\r
5983       break;\r
5984 \r
5985     case EP_WhiteWazir:\r
5986       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5987       fromX = fromY = -1;\r
5988       break;\r
5989 \r
5990     case EP_WhiteAlfil:\r
5991       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5992       fromX = fromY = -1;\r
5993       break;\r
5994 \r
5995     case EP_WhiteCannon:\r
5996       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5997       fromX = fromY = -1;\r
5998       break;\r
5999 \r
6000     case EP_WhiteCardinal:\r
6001       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6002       fromX = fromY = -1;\r
6003       break;\r
6004 \r
6005     case EP_WhiteMarshall:\r
6006       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6007       fromX = fromY = -1;\r
6008       break;\r
6009 \r
6010     case EP_WhiteKing:\r
6011       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6012       fromX = fromY = -1;\r
6013       break;\r
6014 \r
6015     case EP_BlackPawn:\r
6016       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6017       fromX = fromY = -1;\r
6018       break;\r
6019 \r
6020     case EP_BlackKnight:\r
6021       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6022       fromX = fromY = -1;\r
6023       break;\r
6024 \r
6025     case EP_BlackBishop:\r
6026       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6027       fromX = fromY = -1;\r
6028       break;\r
6029 \r
6030     case EP_BlackRook:\r
6031       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6032       fromX = fromY = -1;\r
6033       break;\r
6034 \r
6035     case EP_BlackQueen:\r
6036       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6037       fromX = fromY = -1;\r
6038       break;\r
6039 \r
6040     case EP_BlackFerz:\r
6041       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6042       fromX = fromY = -1;\r
6043       break;\r
6044 \r
6045     case EP_BlackWazir:\r
6046       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6047       fromX = fromY = -1;\r
6048       break;\r
6049 \r
6050     case EP_BlackAlfil:\r
6051       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6052       fromX = fromY = -1;\r
6053       break;\r
6054 \r
6055     case EP_BlackCannon:\r
6056       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6057       fromX = fromY = -1;\r
6058       break;\r
6059 \r
6060     case EP_BlackCardinal:\r
6061       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6062       fromX = fromY = -1;\r
6063       break;\r
6064 \r
6065     case EP_BlackMarshall:\r
6066       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6067       fromX = fromY = -1;\r
6068       break;\r
6069 \r
6070     case EP_BlackKing:\r
6071       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6072       fromX = fromY = -1;\r
6073       break;\r
6074 \r
6075     case EP_EmptySquare:\r
6076       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6077       fromX = fromY = -1;\r
6078       break;\r
6079 \r
6080     case EP_ClearBoard:\r
6081       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6082       fromX = fromY = -1;\r
6083       break;\r
6084 \r
6085     case EP_White:\r
6086       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6087       fromX = fromY = -1;\r
6088       break;\r
6089 \r
6090     case EP_Black:\r
6091       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6092       fromX = fromY = -1;\r
6093       break;\r
6094 \r
6095     case EP_Promote:\r
6096       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6097       fromX = fromY = -1;\r
6098       break;\r
6099 \r
6100     case EP_Demote:\r
6101       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6102       fromX = fromY = -1;\r
6103       break;\r
6104 \r
6105     case DP_Pawn:\r
6106       DropMenuEvent(WhitePawn, fromX, fromY);\r
6107       fromX = fromY = -1;\r
6108       break;\r
6109 \r
6110     case DP_Knight:\r
6111       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6112       fromX = fromY = -1;\r
6113       break;\r
6114 \r
6115     case DP_Bishop:\r
6116       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6117       fromX = fromY = -1;\r
6118       break;\r
6119 \r
6120     case DP_Rook:\r
6121       DropMenuEvent(WhiteRook, fromX, fromY);\r
6122       fromX = fromY = -1;\r
6123       break;\r
6124 \r
6125     case DP_Queen:\r
6126       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6127       fromX = fromY = -1;\r
6128       break;\r
6129 \r
6130     default:\r
6131       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6132     }\r
6133     break;\r
6134 \r
6135   case WM_TIMER:\r
6136     switch (wParam) {\r
6137     case CLOCK_TIMER_ID:\r
6138       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6139       clockTimerEvent = 0;\r
6140       DecrementClocks(); /* call into back end */\r
6141       break;\r
6142     case LOAD_GAME_TIMER_ID:\r
6143       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6144       loadGameTimerEvent = 0;\r
6145       AutoPlayGameLoop(); /* call into back end */\r
6146       break;\r
6147     case ANALYSIS_TIMER_ID:\r
6148       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6149                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6150         AnalysisPeriodicEvent(0);\r
6151       } else {\r
6152         KillTimer(hwnd, analysisTimerEvent);\r
6153         analysisTimerEvent = 0;\r
6154       }\r
6155       break;\r
6156     case DELAYED_TIMER_ID:\r
6157       KillTimer(hwnd, delayedTimerEvent);\r
6158       delayedTimerEvent = 0;\r
6159       delayedTimerCallback();\r
6160       break;\r
6161     }\r
6162     break;\r
6163 \r
6164   case WM_USER_Input:\r
6165     InputEvent(hwnd, message, wParam, lParam);\r
6166     break;\r
6167 \r
6168   /* [AS] Also move "attached" child windows */\r
6169   case WM_WINDOWPOSCHANGING:\r
6170 \r
6171     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6172         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6173 \r
6174         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6175             /* Window is moving */\r
6176             RECT rcMain;\r
6177 \r
6178 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6179             rcMain.left   = wpMain.x;           //              replace by these 4 lines to reconstruct old rect\r
6180             rcMain.right  = wpMain.x + wpMain.width;\r
6181             rcMain.top    = wpMain.y;\r
6182             rcMain.bottom = wpMain.y + wpMain.height;\r
6183             \r
6184             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6185             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6186             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6187             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6188             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6189             wpMain.x = lpwp->x;\r
6190             wpMain.y = lpwp->y;\r
6191         }\r
6192     }\r
6193     break;\r
6194 \r
6195   /* [AS] Snapping */\r
6196   case WM_ENTERSIZEMOVE:\r
6197     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6198     if (hwnd == hwndMain) {\r
6199       doingSizing = TRUE;\r
6200       lastSizing = 0;\r
6201     }\r
6202     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6203     break;\r
6204 \r
6205   case WM_SIZING:\r
6206     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6207     if (hwnd == hwndMain) {\r
6208       lastSizing = wParam;\r
6209     }\r
6210     break;\r
6211 \r
6212   case WM_MOVING:\r
6213     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6214       return OnMoving( &sd, hwnd, wParam, lParam );\r
6215 \r
6216   case WM_EXITSIZEMOVE:\r
6217     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6218     if (hwnd == hwndMain) {\r
6219       RECT client;\r
6220       doingSizing = FALSE;\r
6221       InvalidateRect(hwnd, &boardRect, FALSE);\r
6222       GetClientRect(hwnd, &client);\r
6223       ResizeBoard(client.right, client.bottom, lastSizing);\r
6224       lastSizing = 0;\r
6225       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6226     }\r
6227     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6228     break;\r
6229 \r
6230   case WM_DESTROY: /* message: window being destroyed */\r
6231     PostQuitMessage(0);\r
6232     break;\r
6233 \r
6234   case WM_CLOSE:\r
6235     if (hwnd == hwndMain) {\r
6236       ExitEvent(0);\r
6237     }\r
6238     break;\r
6239 \r
6240   default:      /* Passes it on if unprocessed */\r
6241     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6242   }\r
6243   return 0;\r
6244 }\r
6245 \r
6246 /*---------------------------------------------------------------------------*\\r
6247  *\r
6248  * Misc utility routines\r
6249  *\r
6250 \*---------------------------------------------------------------------------*/\r
6251 \r
6252 /*\r
6253  * Decent random number generator, at least not as bad as Windows\r
6254  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6255  */\r
6256 unsigned int randstate;\r
6257 \r
6258 int\r
6259 myrandom(void)\r
6260 {\r
6261   randstate = randstate * 1664525 + 1013904223;\r
6262   return (int) randstate & 0x7fffffff;\r
6263 }\r
6264 \r
6265 void\r
6266 mysrandom(unsigned int seed)\r
6267 {\r
6268   randstate = seed;\r
6269 }\r
6270 \r
6271 \r
6272 /* \r
6273  * returns TRUE if user selects a different color, FALSE otherwise \r
6274  */\r
6275 \r
6276 BOOL\r
6277 ChangeColor(HWND hwnd, COLORREF *which)\r
6278 {\r
6279   static BOOL firstTime = TRUE;\r
6280   static DWORD customColors[16];\r
6281   CHOOSECOLOR cc;\r
6282   COLORREF newcolor;\r
6283   int i;\r
6284   ColorClass ccl;\r
6285 \r
6286   if (firstTime) {\r
6287     /* Make initial colors in use available as custom colors */\r
6288     /* Should we put the compiled-in defaults here instead? */\r
6289     i = 0;\r
6290     customColors[i++] = lightSquareColor & 0xffffff;\r
6291     customColors[i++] = darkSquareColor & 0xffffff;\r
6292     customColors[i++] = whitePieceColor & 0xffffff;\r
6293     customColors[i++] = blackPieceColor & 0xffffff;\r
6294     customColors[i++] = highlightSquareColor & 0xffffff;\r
6295     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6296 \r
6297     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6298       customColors[i++] = textAttribs[ccl].color;\r
6299     }\r
6300     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6301     firstTime = FALSE;\r
6302   }\r
6303 \r
6304   cc.lStructSize = sizeof(cc);\r
6305   cc.hwndOwner = hwnd;\r
6306   cc.hInstance = NULL;\r
6307   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6308   cc.lpCustColors = (LPDWORD) customColors;\r
6309   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6310 \r
6311   if (!ChooseColor(&cc)) return FALSE;\r
6312 \r
6313   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6314   if (newcolor == *which) return FALSE;\r
6315   *which = newcolor;\r
6316   return TRUE;\r
6317 \r
6318   /*\r
6319   InitDrawingColors();\r
6320   InvalidateRect(hwnd, &boardRect, FALSE);\r
6321   */\r
6322 }\r
6323 \r
6324 BOOLEAN\r
6325 MyLoadSound(MySound *ms)\r
6326 {\r
6327   BOOL ok = FALSE;\r
6328   struct stat st;\r
6329   FILE *f;\r
6330 \r
6331   if (ms->data) free(ms->data);\r
6332   ms->data = NULL;\r
6333 \r
6334   switch (ms->name[0]) {\r
6335   case NULLCHAR:\r
6336     /* Silence */\r
6337     ok = TRUE;\r
6338     break;\r
6339   case '$':\r
6340     /* System sound from Control Panel.  Don't preload here. */\r
6341     ok = TRUE;\r
6342     break;\r
6343   case '!':\r
6344     if (ms->name[1] == NULLCHAR) {\r
6345       /* "!" alone = silence */\r
6346       ok = TRUE;\r
6347     } else {\r
6348       /* Builtin wave resource.  Error if not found. */\r
6349       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6350       if (h == NULL) break;\r
6351       ms->data = (void *)LoadResource(hInst, h);\r
6352       if (h == NULL) break;\r
6353       ok = TRUE;\r
6354     }\r
6355     break;\r
6356   default:\r
6357     /* .wav file.  Error if not found. */\r
6358     f = fopen(ms->name, "rb");\r
6359     if (f == NULL) break;\r
6360     if (fstat(fileno(f), &st) < 0) break;\r
6361     ms->data = malloc(st.st_size);\r
6362     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6363     fclose(f);\r
6364     ok = TRUE;\r
6365     break;\r
6366   }\r
6367   if (!ok) {\r
6368     char buf[MSG_SIZ];\r
6369     sprintf(buf, "Error loading sound %s", ms->name);\r
6370     DisplayError(buf, GetLastError());\r
6371   }\r
6372   return ok;\r
6373 }\r
6374 \r
6375 BOOLEAN\r
6376 MyPlaySound(MySound *ms)\r
6377 {\r
6378   BOOLEAN ok = FALSE;\r
6379 \r
6380   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6381   switch (ms->name[0]) {\r
6382   case NULLCHAR:\r
6383         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6384     /* Silence */\r
6385     ok = TRUE;\r
6386     break;\r
6387   case '$':\r
6388     /* System sound from Control Panel (deprecated feature).\r
6389        "$" alone or an unset sound name gets default beep (still in use). */\r
6390     if (ms->name[1]) {\r
6391       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6392     }\r
6393     if (!ok) ok = MessageBeep(MB_OK);\r
6394     break; \r
6395   case '!':\r
6396     /* Builtin wave resource, or "!" alone for silence */\r
6397     if (ms->name[1]) {\r
6398       if (ms->data == NULL) return FALSE;\r
6399       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6400     } else {\r
6401       ok = TRUE;\r
6402     }\r
6403     break;\r
6404   default:\r
6405     /* .wav file.  Error if not found. */\r
6406     if (ms->data == NULL) return FALSE;\r
6407     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6408     break;\r
6409   }\r
6410   /* Don't print an error: this can happen innocently if the sound driver\r
6411      is busy; for instance, if another instance of WinBoard is playing\r
6412      a sound at about the same time. */\r
6413   return ok;\r
6414 }\r
6415 \r
6416 \r
6417 LRESULT CALLBACK\r
6418 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6419 {\r
6420   BOOL ok;\r
6421   OPENFILENAME *ofn;\r
6422   static UINT *number; /* gross that this is static */\r
6423 \r
6424   switch (message) {\r
6425   case WM_INITDIALOG: /* message: initialize dialog box */\r
6426     /* Center the dialog over the application window */\r
6427     ofn = (OPENFILENAME *) lParam;\r
6428     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6429       number = (UINT *) ofn->lCustData;\r
6430       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6431     } else {\r
6432       number = NULL;\r
6433     }\r
6434     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6435     return FALSE;  /* Allow for further processing */\r
6436 \r
6437   case WM_COMMAND:\r
6438     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6439       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6440     }\r
6441     return FALSE;  /* Allow for further processing */\r
6442   }\r
6443   return FALSE;\r
6444 }\r
6445 \r
6446 UINT APIENTRY\r
6447 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6448 {\r
6449   static UINT *number;\r
6450   OPENFILENAME *ofname;\r
6451   OFNOTIFY *ofnot;\r
6452   switch (uiMsg) {\r
6453   case WM_INITDIALOG:\r
6454     ofname = (OPENFILENAME *)lParam;\r
6455     number = (UINT *)(ofname->lCustData);\r
6456     break;\r
6457   case WM_NOTIFY:\r
6458     ofnot = (OFNOTIFY *)lParam;\r
6459     if (ofnot->hdr.code == CDN_FILEOK) {\r
6460       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6461     }\r
6462     break;\r
6463   }\r
6464   return 0;\r
6465 }\r
6466 \r
6467 \r
6468 FILE *\r
6469 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6470                char *nameFilt, char *dlgTitle, UINT *number,\r
6471                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6472 {\r
6473   OPENFILENAME openFileName;\r
6474   char buf1[MSG_SIZ];\r
6475   FILE *f;\r
6476 \r
6477   if (fileName == NULL) fileName = buf1;\r
6478   if (defName == NULL) {\r
6479     strcpy(fileName, "*.");\r
6480     strcat(fileName, defExt);\r
6481   } else {\r
6482     strcpy(fileName, defName);\r
6483   }\r
6484   if (fileTitle) strcpy(fileTitle, "");\r
6485   if (number) *number = 0;\r
6486 \r
6487   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6488   openFileName.hwndOwner         = hwnd;\r
6489   openFileName.hInstance         = (HANDLE) hInst;\r
6490   openFileName.lpstrFilter       = nameFilt;\r
6491   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6492   openFileName.nMaxCustFilter    = 0L;\r
6493   openFileName.nFilterIndex      = 1L;\r
6494   openFileName.lpstrFile         = fileName;\r
6495   openFileName.nMaxFile          = MSG_SIZ;\r
6496   openFileName.lpstrFileTitle    = fileTitle;\r
6497   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6498   openFileName.lpstrInitialDir   = NULL;\r
6499   openFileName.lpstrTitle        = dlgTitle;\r
6500   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6501     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6502     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6503     | (oldDialog ? 0 : OFN_EXPLORER);\r
6504   openFileName.nFileOffset       = 0;\r
6505   openFileName.nFileExtension    = 0;\r
6506   openFileName.lpstrDefExt       = defExt;\r
6507   openFileName.lCustData         = (LONG) number;\r
6508   openFileName.lpfnHook          = oldDialog ?\r
6509     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6510   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6511 \r
6512   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6513                         GetOpenFileName(&openFileName)) {\r
6514     /* open the file */\r
6515     f = fopen(openFileName.lpstrFile, write);\r
6516     if (f == NULL) {\r
6517       MessageBox(hwnd, "File open failed", NULL,\r
6518                  MB_OK|MB_ICONEXCLAMATION);\r
6519       return NULL;\r
6520     }\r
6521   } else {\r
6522     int err = CommDlgExtendedError();\r
6523     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6524     return FALSE;\r
6525   }\r
6526   return f;\r
6527 }\r
6528 \r
6529 \r
6530 \r
6531 VOID APIENTRY\r
6532 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6533 {\r
6534   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6535 \r
6536   /*\r
6537    * Get the first pop-up menu in the menu template. This is the\r
6538    * menu that TrackPopupMenu displays.\r
6539    */\r
6540   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6541 \r
6542   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6543 \r
6544   /*\r
6545    * TrackPopup uses screen coordinates, so convert the\r
6546    * coordinates of the mouse click to screen coordinates.\r
6547    */\r
6548   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6549 \r
6550   /* Draw and track the floating pop-up menu. */\r
6551   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6552                  pt.x, pt.y, 0, hwnd, NULL);\r
6553 \r
6554   /* Destroy the menu.*/\r
6555   DestroyMenu(hmenu);\r
6556 }\r
6557    \r
6558 typedef struct {\r
6559   HWND hDlg, hText;\r
6560   int sizeX, sizeY, newSizeX, newSizeY;\r
6561   HDWP hdwp;\r
6562 } ResizeEditPlusButtonsClosure;\r
6563 \r
6564 BOOL CALLBACK\r
6565 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6566 {\r
6567   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6568   RECT rect;\r
6569   POINT pt;\r
6570 \r
6571   if (hChild == cl->hText) return TRUE;\r
6572   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6573   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6574   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6575   ScreenToClient(cl->hDlg, &pt);\r
6576   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6577     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6578   return TRUE;\r
6579 }\r
6580 \r
6581 /* Resize a dialog that has a (rich) edit field filling most of\r
6582    the top, with a row of buttons below */\r
6583 VOID\r
6584 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6585 {\r
6586   RECT rectText;\r
6587   int newTextHeight, newTextWidth;\r
6588   ResizeEditPlusButtonsClosure cl;\r
6589   \r
6590   /*if (IsIconic(hDlg)) return;*/\r
6591   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6592   \r
6593   cl.hdwp = BeginDeferWindowPos(8);\r
6594 \r
6595   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6596   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6597   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6598   if (newTextHeight < 0) {\r
6599     newSizeY += -newTextHeight;\r
6600     newTextHeight = 0;\r
6601   }\r
6602   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6603     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6604 \r
6605   cl.hDlg = hDlg;\r
6606   cl.hText = hText;\r
6607   cl.sizeX = sizeX;\r
6608   cl.sizeY = sizeY;\r
6609   cl.newSizeX = newSizeX;\r
6610   cl.newSizeY = newSizeY;\r
6611   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6612 \r
6613   EndDeferWindowPos(cl.hdwp);\r
6614 }\r
6615 \r
6616 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6617 {\r
6618     RECT    rChild, rParent;\r
6619     int     wChild, hChild, wParent, hParent;\r
6620     int     wScreen, hScreen, xNew, yNew;\r
6621     HDC     hdc;\r
6622 \r
6623     /* Get the Height and Width of the child window */\r
6624     GetWindowRect (hwndChild, &rChild);\r
6625     wChild = rChild.right - rChild.left;\r
6626     hChild = rChild.bottom - rChild.top;\r
6627 \r
6628     /* Get the Height and Width of the parent window */\r
6629     GetWindowRect (hwndParent, &rParent);\r
6630     wParent = rParent.right - rParent.left;\r
6631     hParent = rParent.bottom - rParent.top;\r
6632 \r
6633     /* Get the display limits */\r
6634     hdc = GetDC (hwndChild);\r
6635     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6636     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6637     ReleaseDC(hwndChild, hdc);\r
6638 \r
6639     /* Calculate new X position, then adjust for screen */\r
6640     xNew = rParent.left + ((wParent - wChild) /2);\r
6641     if (xNew < 0) {\r
6642         xNew = 0;\r
6643     } else if ((xNew+wChild) > wScreen) {\r
6644         xNew = wScreen - wChild;\r
6645     }\r
6646 \r
6647     /* Calculate new Y position, then adjust for screen */\r
6648     if( mode == 0 ) {\r
6649         yNew = rParent.top  + ((hParent - hChild) /2);\r
6650     }\r
6651     else {\r
6652         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6653     }\r
6654 \r
6655     if (yNew < 0) {\r
6656         yNew = 0;\r
6657     } else if ((yNew+hChild) > hScreen) {\r
6658         yNew = hScreen - hChild;\r
6659     }\r
6660 \r
6661     /* Set it, and return */\r
6662     return SetWindowPos (hwndChild, NULL,\r
6663                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6664 }\r
6665 \r
6666 /* Center one window over another */\r
6667 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6668 {\r
6669     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6670 }\r
6671 \r
6672 /*---------------------------------------------------------------------------*\\r
6673  *\r
6674  * Startup Dialog functions\r
6675  *\r
6676 \*---------------------------------------------------------------------------*/\r
6677 void\r
6678 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6679 {\r
6680   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6681 \r
6682   while (*cd != NULL) {\r
6683     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6684     cd++;\r
6685   }\r
6686 }\r
6687 \r
6688 void\r
6689 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6690 {\r
6691   char buf1[ARG_MAX];\r
6692   int len;\r
6693 \r
6694   if (str[0] == '@') {\r
6695     FILE* f = fopen(str + 1, "r");\r
6696     if (f == NULL) {\r
6697       DisplayFatalError(str + 1, errno, 2);\r
6698       return;\r
6699     }\r
6700     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6701     fclose(f);\r
6702     buf1[len] = NULLCHAR;\r
6703     str = buf1;\r
6704   }\r
6705 \r
6706   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6707 \r
6708   for (;;) {\r
6709     char buf[MSG_SIZ];\r
6710     char *end = strchr(str, '\n');\r
6711     if (end == NULL) return;\r
6712     memcpy(buf, str, end - str);\r
6713     buf[end - str] = NULLCHAR;\r
6714     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6715     str = end + 1;\r
6716   }\r
6717 }\r
6718 \r
6719 void\r
6720 SetStartupDialogEnables(HWND hDlg)\r
6721 {\r
6722   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6723     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6724     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6725   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6726     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6727   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6728     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6729   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6730     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6731   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6732     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6733     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6734     IsDlgButtonChecked(hDlg, OPT_View));\r
6735 }\r
6736 \r
6737 char *\r
6738 QuoteForFilename(char *filename)\r
6739 {\r
6740   int dquote, space;\r
6741   dquote = strchr(filename, '"') != NULL;\r
6742   space = strchr(filename, ' ') != NULL;\r
6743   if (dquote || space) {\r
6744     if (dquote) {\r
6745       return "'";\r
6746     } else {\r
6747       return "\"";\r
6748     }\r
6749   } else {\r
6750     return "";\r
6751   }\r
6752 }\r
6753 \r
6754 VOID\r
6755 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6756 {\r
6757   char buf[MSG_SIZ];\r
6758   char *q;\r
6759 \r
6760   InitComboStringsFromOption(hwndCombo, nthnames);\r
6761   q = QuoteForFilename(nthcp);\r
6762   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6763   if (*nthdir != NULLCHAR) {\r
6764     q = QuoteForFilename(nthdir);\r
6765     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6766   }\r
6767   if (*nthcp == NULLCHAR) {\r
6768     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6769   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6770     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6771     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6772   }\r
6773 }\r
6774 \r
6775 LRESULT CALLBACK\r
6776 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6777 {\r
6778   char buf[MSG_SIZ];\r
6779   HANDLE hwndCombo;\r
6780   char *p;\r
6781 \r
6782   switch (message) {\r
6783   case WM_INITDIALOG:\r
6784     /* Center the dialog */\r
6785     CenterWindow (hDlg, GetDesktopWindow());\r
6786     /* Initialize the dialog items */\r
6787     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6788                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6789                   firstChessProgramNames);\r
6790     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6791                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6792                   secondChessProgramNames);\r
6793     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6794     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6795     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6796     if (*appData.icsHelper != NULLCHAR) {\r
6797       char *q = QuoteForFilename(appData.icsHelper);\r
6798       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6799     }\r
6800     if (*appData.icsHost == NULLCHAR) {\r
6801       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6802       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6803     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6804       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6805       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6806     }\r
6807 \r
6808     if (appData.icsActive) {\r
6809       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6810     }\r
6811     else if (appData.noChessProgram) {\r
6812       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6813     }\r
6814     else {\r
6815       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6816     }\r
6817 \r
6818     SetStartupDialogEnables(hDlg);\r
6819     return TRUE;\r
6820 \r
6821   case WM_COMMAND:\r
6822     switch (LOWORD(wParam)) {\r
6823     case IDOK:\r
6824       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6825         strcpy(buf, "/fcp=");\r
6826         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6827         p = buf;\r
6828         ParseArgs(StringGet, &p);\r
6829         strcpy(buf, "/scp=");\r
6830         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6831         p = buf;\r
6832         ParseArgs(StringGet, &p);\r
6833         appData.noChessProgram = FALSE;\r
6834         appData.icsActive = FALSE;\r
6835       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6836         strcpy(buf, "/ics /icshost=");\r
6837         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6838         p = buf;\r
6839         ParseArgs(StringGet, &p);\r
6840         if (appData.zippyPlay) {\r
6841           strcpy(buf, "/fcp=");\r
6842           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6843           p = buf;\r
6844           ParseArgs(StringGet, &p);\r
6845         }\r
6846       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6847         appData.noChessProgram = TRUE;\r
6848         appData.icsActive = FALSE;\r
6849       } else {\r
6850         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6851                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6852         return TRUE;\r
6853       }\r
6854       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6855         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6856         p = buf;\r
6857         ParseArgs(StringGet, &p);\r
6858       }\r
6859       EndDialog(hDlg, TRUE);\r
6860       return TRUE;\r
6861 \r
6862     case IDCANCEL:\r
6863       ExitEvent(0);\r
6864       return TRUE;\r
6865 \r
6866     case IDM_HELPCONTENTS:\r
6867       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6868         MessageBox (GetFocus(),\r
6869                     "Unable to activate help",\r
6870                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6871       }\r
6872       break;\r
6873 \r
6874     default:\r
6875       SetStartupDialogEnables(hDlg);\r
6876       break;\r
6877     }\r
6878     break;\r
6879   }\r
6880   return FALSE;\r
6881 }\r
6882 \r
6883 /*---------------------------------------------------------------------------*\\r
6884  *\r
6885  * About box dialog functions\r
6886  *\r
6887 \*---------------------------------------------------------------------------*/\r
6888 \r
6889 /* Process messages for "About" dialog box */\r
6890 LRESULT CALLBACK\r
6891 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6892 {\r
6893   switch (message) {\r
6894   case WM_INITDIALOG: /* message: initialize dialog box */\r
6895     /* Center the dialog over the application window */\r
6896     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6897     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6898     JAWS_COPYRIGHT\r
6899     return (TRUE);\r
6900 \r
6901   case WM_COMMAND: /* message: received a command */\r
6902     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6903         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6904       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6905       return (TRUE);\r
6906     }\r
6907     break;\r
6908   }\r
6909   return (FALSE);\r
6910 }\r
6911 \r
6912 /*---------------------------------------------------------------------------*\\r
6913  *\r
6914  * Comment Dialog functions\r
6915  *\r
6916 \*---------------------------------------------------------------------------*/\r
6917 \r
6918 LRESULT CALLBACK\r
6919 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6920 {\r
6921   static HANDLE hwndText = NULL;\r
6922   int len, newSizeX, newSizeY, flags;\r
6923   static int sizeX, sizeY;\r
6924   char *str;\r
6925   RECT rect;\r
6926   MINMAXINFO *mmi;\r
6927 \r
6928   switch (message) {\r
6929   case WM_INITDIALOG: /* message: initialize dialog box */\r
6930     /* Initialize the dialog items */\r
6931     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6932     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6933     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6934     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6935     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6936     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6937     SetWindowText(hDlg, commentTitle);\r
6938     if (editComment) {\r
6939       SetFocus(hwndText);\r
6940     } else {\r
6941       SetFocus(GetDlgItem(hDlg, IDOK));\r
6942     }\r
6943     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6944                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6945                 MAKELPARAM(FALSE, 0));\r
6946     /* Size and position the dialog */\r
6947     if (!commentDialog) {\r
6948       commentDialog = hDlg;\r
6949       flags = SWP_NOZORDER;\r
6950       GetClientRect(hDlg, &rect);\r
6951       sizeX = rect.right;\r
6952       sizeY = rect.bottom;\r
6953       if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&\r
6954           wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {\r
6955         WINDOWPLACEMENT wp;\r
6956         EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);\r
6957         wp.length = sizeof(WINDOWPLACEMENT);\r
6958         wp.flags = 0;\r
6959         wp.showCmd = SW_SHOW;\r
6960         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6961         wp.rcNormalPosition.left = wpComment.x;\r
6962         wp.rcNormalPosition.right = wpComment.x + wpComment.width;\r
6963         wp.rcNormalPosition.top = wpComment.y;\r
6964         wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;\r
6965         SetWindowPlacement(hDlg, &wp);\r
6966 \r
6967         GetClientRect(hDlg, &rect);\r
6968         newSizeX = rect.right;\r
6969         newSizeY = rect.bottom;\r
6970         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6971                               newSizeX, newSizeY);\r
6972         sizeX = newSizeX;\r
6973         sizeY = newSizeY;\r
6974       }\r
6975     }\r
6976     return FALSE;\r
6977 \r
6978   case WM_COMMAND: /* message: received a command */\r
6979     switch (LOWORD(wParam)) {\r
6980     case IDOK:\r
6981       if (editComment) {\r
6982         char *p, *q;\r
6983         /* Read changed options from the dialog box */\r
6984         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6985         len = GetWindowTextLength(hwndText);\r
6986         str = (char *) malloc(len + 1);\r
6987         GetWindowText(hwndText, str, len + 1);\r
6988         p = q = str;\r
6989         while (*q) {\r
6990           if (*q == '\r')\r
6991             q++;\r
6992           else\r
6993             *p++ = *q++;\r
6994         }\r
6995         *p = NULLCHAR;\r
6996         ReplaceComment(commentIndex, str);\r
6997         free(str);\r
6998       }\r
6999       CommentPopDown();\r
7000       return TRUE;\r
7001 \r
7002     case IDCANCEL:\r
7003     case OPT_CancelComment:\r
7004       CommentPopDown();\r
7005       return TRUE;\r
7006 \r
7007     case OPT_ClearComment:\r
7008       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7009       break;\r
7010 \r
7011     case OPT_EditComment:\r
7012       EditCommentEvent();\r
7013       return TRUE;\r
7014 \r
7015     default:\r
7016       break;\r
7017     }\r
7018     break;\r
7019 \r
7020   case WM_SIZE:\r
7021     newSizeX = LOWORD(lParam);\r
7022     newSizeY = HIWORD(lParam);\r
7023     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7024     sizeX = newSizeX;\r
7025     sizeY = newSizeY;\r
7026     break;\r
7027 \r
7028   case WM_GETMINMAXINFO:\r
7029     /* Prevent resizing window too small */\r
7030     mmi = (MINMAXINFO *) lParam;\r
7031     mmi->ptMinTrackSize.x = 100;\r
7032     mmi->ptMinTrackSize.y = 100;\r
7033     break;\r
7034   }\r
7035   return FALSE;\r
7036 }\r
7037 \r
7038 VOID\r
7039 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7040 {\r
7041   FARPROC lpProc;\r
7042   char *p, *q;\r
7043 \r
7044   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7045 \r
7046   if (str == NULL) str = "";\r
7047   p = (char *) malloc(2 * strlen(str) + 2);\r
7048   q = p;\r
7049   while (*str) {\r
7050     if (*str == '\n') *q++ = '\r';\r
7051     *q++ = *str++;\r
7052   }\r
7053   *q = NULLCHAR;\r
7054   if (commentText != NULL) free(commentText);\r
7055 \r
7056   commentIndex = index;\r
7057   commentTitle = title;\r
7058   commentText = p;\r
7059   editComment = edit;\r
7060 \r
7061   if (commentDialog) {\r
7062     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7063     if (!commentUp) ShowWindow(commentDialog, SW_SHOW);\r
7064   } else {\r
7065     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7066     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7067                  hwndMain, (DLGPROC)lpProc);\r
7068     FreeProcInstance(lpProc);\r
7069   }\r
7070   commentUp = TRUE;\r
7071 }\r
7072 \r
7073 \r
7074 /*---------------------------------------------------------------------------*\\r
7075  *\r
7076  * Type-in move dialog functions\r
7077  * \r
7078 \*---------------------------------------------------------------------------*/\r
7079 \r
7080 LRESULT CALLBACK\r
7081 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7082 {\r
7083   char move[MSG_SIZ];\r
7084   HWND hInput;\r
7085   ChessMove moveType;\r
7086   int fromX, fromY, toX, toY;\r
7087   char promoChar;\r
7088 \r
7089   switch (message) {\r
7090   case WM_INITDIALOG:\r
7091     move[0] = (char) lParam;\r
7092     move[1] = NULLCHAR;\r
7093     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7094     hInput = GetDlgItem(hDlg, OPT_Move);\r
7095     SetWindowText(hInput, move);\r
7096     SetFocus(hInput);\r
7097     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7098     return FALSE;\r
7099 \r
7100   case WM_COMMAND:\r
7101     switch (LOWORD(wParam)) {\r
7102     case IDOK:\r
7103       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7104       { int n; Board board;\r
7105         // [HGM] FENedit\r
7106         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7107                 EditPositionPasteFEN(move);\r
7108                 EndDialog(hDlg, TRUE);\r
7109                 return TRUE;\r
7110         }\r
7111         // [HGM] movenum: allow move number to be typed in any mode\r
7112         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7113           ToNrEvent(2*n-1);\r
7114           EndDialog(hDlg, TRUE);\r
7115           return TRUE;\r
7116         }\r
7117       }\r
7118       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7119         gameMode != Training) {\r
7120         DisplayMoveError("Displayed move is not current");\r
7121       } else {\r
7122 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7123         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7124           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7125         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7126         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7127           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7128           if (gameMode != Training)\r
7129               forwardMostMove = currentMove;\r
7130           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7131         } else {\r
7132           DisplayMoveError("Could not parse move");\r
7133         }\r
7134       }\r
7135       EndDialog(hDlg, TRUE);\r
7136       return TRUE;\r
7137     case IDCANCEL:\r
7138       EndDialog(hDlg, FALSE);\r
7139       return TRUE;\r
7140     default:\r
7141       break;\r
7142     }\r
7143     break;\r
7144   }\r
7145   return FALSE;\r
7146 }\r
7147 \r
7148 VOID\r
7149 PopUpMoveDialog(char firstchar)\r
7150 {\r
7151     FARPROC lpProc;\r
7152     \r
7153     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7154         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7155         gameMode == AnalyzeMode || gameMode == EditGame || \r
7156         gameMode == EditPosition || gameMode == IcsExamining ||\r
7157         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7158         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7159                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7160                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7161         gameMode == Training) {\r
7162       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7163       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7164         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7165       FreeProcInstance(lpProc);\r
7166     }\r
7167 }\r
7168 \r
7169 /*---------------------------------------------------------------------------*\\r
7170  *\r
7171  * Type-in name dialog functions\r
7172  * \r
7173 \*---------------------------------------------------------------------------*/\r
7174 \r
7175 LRESULT CALLBACK\r
7176 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7177 {\r
7178   char move[MSG_SIZ];\r
7179   HWND hInput;\r
7180 \r
7181   switch (message) {\r
7182   case WM_INITDIALOG:\r
7183     move[0] = (char) lParam;\r
7184     move[1] = NULLCHAR;\r
7185     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7186     hInput = GetDlgItem(hDlg, OPT_Name);\r
7187     SetWindowText(hInput, move);\r
7188     SetFocus(hInput);\r
7189     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7190     return FALSE;\r
7191 \r
7192   case WM_COMMAND:\r
7193     switch (LOWORD(wParam)) {\r
7194     case IDOK:\r
7195       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7196       appData.userName = strdup(move);\r
7197       SetUserLogo();\r
7198 \r
7199       EndDialog(hDlg, TRUE);\r
7200       return TRUE;\r
7201     case IDCANCEL:\r
7202       EndDialog(hDlg, FALSE);\r
7203       return TRUE;\r
7204     default:\r
7205       break;\r
7206     }\r
7207     break;\r
7208   }\r
7209   return FALSE;\r
7210 }\r
7211 \r
7212 VOID\r
7213 PopUpNameDialog(char firstchar)\r
7214 {\r
7215     FARPROC lpProc;\r
7216     \r
7217       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7218       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7219         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7220       FreeProcInstance(lpProc);\r
7221 }\r
7222 \r
7223 /*---------------------------------------------------------------------------*\\r
7224  *\r
7225  *  Error dialogs\r
7226  * \r
7227 \*---------------------------------------------------------------------------*/\r
7228 \r
7229 /* Nonmodal error box */\r
7230 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7231                              WPARAM wParam, LPARAM lParam);\r
7232 \r
7233 VOID\r
7234 ErrorPopUp(char *title, char *content)\r
7235 {\r
7236   FARPROC lpProc;\r
7237   char *p, *q;\r
7238   BOOLEAN modal = hwndMain == NULL;\r
7239 \r
7240   p = content;\r
7241   q = errorMessage;\r
7242   while (*p) {\r
7243     if (*p == '\n') {\r
7244       if (modal) {\r
7245         *q++ = ' ';\r
7246         p++;\r
7247       } else {\r
7248         *q++ = '\r';\r
7249         *q++ = *p++;\r
7250       }\r
7251     } else {\r
7252       *q++ = *p++;\r
7253     }\r
7254   }\r
7255   *q = NULLCHAR;\r
7256   strncpy(errorTitle, title, sizeof(errorTitle));\r
7257   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7258   \r
7259   if (modal) {\r
7260     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7261   } else {\r
7262     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7263     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7264                  hwndMain, (DLGPROC)lpProc);\r
7265     FreeProcInstance(lpProc);\r
7266   }\r
7267 }\r
7268 \r
7269 VOID\r
7270 ErrorPopDown()\r
7271 {\r
7272   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7273   if (errorDialog == NULL) return;\r
7274   DestroyWindow(errorDialog);\r
7275   errorDialog = NULL;\r
7276 }\r
7277 \r
7278 LRESULT CALLBACK\r
7279 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7280 {\r
7281   HANDLE hwndText;\r
7282   RECT rChild;\r
7283 \r
7284   switch (message) {\r
7285   case WM_INITDIALOG:\r
7286     GetWindowRect(hDlg, &rChild);\r
7287 \r
7288     /*\r
7289     SetWindowPos(hDlg, NULL, rChild.left,\r
7290       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7291       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7292     */\r
7293 \r
7294     /* \r
7295         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7296         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7297         and it doesn't work when you resize the dialog.\r
7298         For now, just give it a default position.\r
7299     */\r
7300     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7301 \r
7302     errorDialog = hDlg;\r
7303     SetWindowText(hDlg, errorTitle);\r
7304     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7305     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7306     return FALSE;\r
7307 \r
7308   case WM_COMMAND:\r
7309     switch (LOWORD(wParam)) {\r
7310     case IDOK:\r
7311     case IDCANCEL:\r
7312       if (errorDialog == hDlg) errorDialog = NULL;\r
7313       DestroyWindow(hDlg);\r
7314       return TRUE;\r
7315 \r
7316     default:\r
7317       break;\r
7318     }\r
7319     break;\r
7320   }\r
7321   return FALSE;\r
7322 }\r
7323 \r
7324 #ifdef GOTHIC\r
7325 HWND gothicDialog = NULL;\r
7326 \r
7327 LRESULT CALLBACK\r
7328 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7329 {\r
7330   HANDLE hwndText;\r
7331   RECT rChild;\r
7332   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7333 \r
7334   switch (message) {\r
7335   case WM_INITDIALOG:\r
7336     GetWindowRect(hDlg, &rChild);\r
7337 \r
7338     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
7339                                                              SWP_NOZORDER);\r
7340 \r
7341     /* \r
7342         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7343         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7344         and it doesn't work when you resize the dialog.\r
7345         For now, just give it a default position.\r
7346     */\r
7347     gothicDialog = hDlg;\r
7348     SetWindowText(hDlg, errorTitle);\r
7349     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7350     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7351     return FALSE;\r
7352 \r
7353   case WM_COMMAND:\r
7354     switch (LOWORD(wParam)) {\r
7355     case IDOK:\r
7356     case IDCANCEL:\r
7357       if (errorDialog == hDlg) errorDialog = NULL;\r
7358       DestroyWindow(hDlg);\r
7359       return TRUE;\r
7360 \r
7361     default:\r
7362       break;\r
7363     }\r
7364     break;\r
7365   }\r
7366   return FALSE;\r
7367 }\r
7368 \r
7369 VOID\r
7370 GothicPopUp(char *title, VariantClass variant)\r
7371 {\r
7372   FARPROC lpProc;\r
7373   static char *lastTitle;\r
7374 \r
7375   strncpy(errorTitle, title, sizeof(errorTitle));\r
7376   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7377 \r
7378   if(lastTitle != title && gothicDialog != NULL) {\r
7379     DestroyWindow(gothicDialog);\r
7380     gothicDialog = NULL;\r
7381   }\r
7382   if(variant != VariantNormal && gothicDialog == NULL) {\r
7383     title = lastTitle;\r
7384     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7385     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7386                  hwndMain, (DLGPROC)lpProc);\r
7387     FreeProcInstance(lpProc);\r
7388   }\r
7389 }\r
7390 #endif\r
7391 \r
7392 /*---------------------------------------------------------------------------*\\r
7393  *\r
7394  *  Ics Interaction console functions\r
7395  *\r
7396 \*---------------------------------------------------------------------------*/\r
7397 \r
7398 #define HISTORY_SIZE 64\r
7399 static char *history[HISTORY_SIZE];\r
7400 int histIn = 0, histP = 0;\r
7401 \r
7402 VOID\r
7403 SaveInHistory(char *cmd)\r
7404 {\r
7405   if (history[histIn] != NULL) {\r
7406     free(history[histIn]);\r
7407     history[histIn] = NULL;\r
7408   }\r
7409   if (*cmd == NULLCHAR) return;\r
7410   history[histIn] = StrSave(cmd);\r
7411   histIn = (histIn + 1) % HISTORY_SIZE;\r
7412   if (history[histIn] != NULL) {\r
7413     free(history[histIn]);\r
7414     history[histIn] = NULL;\r
7415   }\r
7416   histP = histIn;\r
7417 }\r
7418 \r
7419 char *\r
7420 PrevInHistory(char *cmd)\r
7421 {\r
7422   int newhp;\r
7423   if (histP == histIn) {\r
7424     if (history[histIn] != NULL) free(history[histIn]);\r
7425     history[histIn] = StrSave(cmd);\r
7426   }\r
7427   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7428   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7429   histP = newhp;\r
7430   return history[histP];\r
7431 }\r
7432 \r
7433 char *\r
7434 NextInHistory()\r
7435 {\r
7436   if (histP == histIn) return NULL;\r
7437   histP = (histP + 1) % HISTORY_SIZE;\r
7438   return history[histP];\r
7439 }\r
7440 \r
7441 typedef struct {\r
7442   char *item;\r
7443   char *command;\r
7444   BOOLEAN getname;\r
7445   BOOLEAN immediate;\r
7446 } IcsTextMenuEntry;\r
7447 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7448 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7449 \r
7450 void\r
7451 ParseIcsTextMenu(char *icsTextMenuString)\r
7452 {\r
7453 //  int flags = 0;\r
7454   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7455   char *p = icsTextMenuString;\r
7456   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7457     free(e->item);\r
7458     e->item = NULL;\r
7459     if (e->command != NULL) {\r
7460       free(e->command);\r
7461       e->command = NULL;\r
7462     }\r
7463     e++;\r
7464   }\r
7465   e = icsTextMenuEntry;\r
7466   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7467     if (*p == ';' || *p == '\n') {\r
7468       e->item = strdup("-");\r
7469       e->command = NULL;\r
7470       p++;\r
7471     } else if (*p == '-') {\r
7472       e->item = strdup("-");\r
7473       e->command = NULL;\r
7474       p++;\r
7475       if (*p) p++;\r
7476     } else {\r
7477       char *q, *r, *s, *t;\r
7478       char c;\r
7479       q = strchr(p, ',');\r
7480       if (q == NULL) break;\r
7481       *q = NULLCHAR;\r
7482       r = strchr(q + 1, ',');\r
7483       if (r == NULL) break;\r
7484       *r = NULLCHAR;\r
7485       s = strchr(r + 1, ',');\r
7486       if (s == NULL) break;\r
7487       *s = NULLCHAR;\r
7488       c = ';';\r
7489       t = strchr(s + 1, c);\r
7490       if (t == NULL) {\r
7491         c = '\n';\r
7492         t = strchr(s + 1, c);\r
7493       }\r
7494       if (t != NULL) *t = NULLCHAR;\r
7495       e->item = strdup(p);\r
7496       e->command = strdup(q + 1);\r
7497       e->getname = *(r + 1) != '0';\r
7498       e->immediate = *(s + 1) != '0';\r
7499       *q = ',';\r
7500       *r = ',';\r
7501       *s = ',';\r
7502       if (t == NULL) break;\r
7503       *t = c;\r
7504       p = t + 1;\r
7505     }\r
7506     e++;\r
7507   } \r
7508 }\r
7509 \r
7510 HMENU\r
7511 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7512 {\r
7513   HMENU hmenu, h;\r
7514   int i = 0;\r
7515   hmenu = LoadMenu(hInst, "TextMenu");\r
7516   h = GetSubMenu(hmenu, 0);\r
7517   while (e->item) {\r
7518     if (strcmp(e->item, "-") == 0) {\r
7519       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7520     } else {\r
7521       if (e->item[0] == '|') {\r
7522         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7523                    IDM_CommandX + i, &e->item[1]);\r
7524       } else {\r
7525         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7526       }\r
7527     }\r
7528     e++;\r
7529     i++;\r
7530   } \r
7531   return hmenu;\r
7532 }\r
7533 \r
7534 WNDPROC consoleTextWindowProc;\r
7535 \r
7536 void\r
7537 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7538 {\r
7539   char buf[MSG_SIZ], name[MSG_SIZ];\r
7540   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7541   CHARRANGE sel;\r
7542 \r
7543   if (!getname) {\r
7544     SetWindowText(hInput, command);\r
7545     if (immediate) {\r
7546       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7547     } else {\r
7548       sel.cpMin = 999999;\r
7549       sel.cpMax = 999999;\r
7550       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7551       SetFocus(hInput);\r
7552     }\r
7553     return;\r
7554   }    \r
7555   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7556   if (sel.cpMin == sel.cpMax) {\r
7557     /* Expand to surrounding word */\r
7558     TEXTRANGE tr;\r
7559     do {\r
7560       tr.chrg.cpMax = sel.cpMin;\r
7561       tr.chrg.cpMin = --sel.cpMin;\r
7562       if (sel.cpMin < 0) break;\r
7563       tr.lpstrText = name;\r
7564       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7565     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7566     sel.cpMin++;\r
7567 \r
7568     do {\r
7569       tr.chrg.cpMin = sel.cpMax;\r
7570       tr.chrg.cpMax = ++sel.cpMax;\r
7571       tr.lpstrText = name;\r
7572       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7573     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7574     sel.cpMax--;\r
7575 \r
7576     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7577       MessageBeep(MB_ICONEXCLAMATION);\r
7578       return;\r
7579     }\r
7580     tr.chrg = sel;\r
7581     tr.lpstrText = name;\r
7582     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7583   } else {\r
7584     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7585       MessageBeep(MB_ICONEXCLAMATION);\r
7586       return;\r
7587     }\r
7588     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7589   }\r
7590   if (immediate) {\r
7591     sprintf(buf, "%s %s", command, name);\r
7592     SetWindowText(hInput, buf);\r
7593     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7594   } else {\r
7595     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7596     SetWindowText(hInput, buf);\r
7597     sel.cpMin = 999999;\r
7598     sel.cpMax = 999999;\r
7599     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7600     SetFocus(hInput);\r
7601   }\r
7602 }\r
7603 \r
7604 LRESULT CALLBACK \r
7605 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7606 {\r
7607   HWND hInput;\r
7608   CHARRANGE sel;\r
7609 \r
7610   switch (message) {\r
7611   case WM_KEYDOWN:\r
7612     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7613     switch (wParam) {\r
7614     case VK_PRIOR:\r
7615       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7616       return 0;\r
7617     case VK_NEXT:\r
7618       sel.cpMin = 999999;\r
7619       sel.cpMax = 999999;\r
7620       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7621       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7622       return 0;\r
7623     }\r
7624     break;\r
7625   case WM_CHAR:\r
7626    if(wParam != '\022') {\r
7627     if (wParam == '\t') {\r
7628       if (GetKeyState(VK_SHIFT) < 0) {\r
7629         /* shifted */\r
7630         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7631         if (buttonDesc[0].hwnd) {\r
7632           SetFocus(buttonDesc[0].hwnd);\r
7633         } else {\r
7634           SetFocus(hwndMain);\r
7635         }\r
7636       } else {\r
7637         /* unshifted */\r
7638         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7639       }\r
7640     } else {\r
7641       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7642       JAWS_DELETE( SetFocus(hInput); )\r
7643       SendMessage(hInput, message, wParam, lParam);\r
7644     }\r
7645     return 0;\r
7646    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7647   case WM_RBUTTONUP:\r
7648     if (GetKeyState(VK_SHIFT) & ~1) {\r
7649       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7650         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7651     } else {\r
7652       POINT pt;\r
7653       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7654       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7655       if (sel.cpMin == sel.cpMax) {\r
7656         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7657         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7658       }\r
7659       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7660         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7661       }\r
7662       pt.x = LOWORD(lParam);\r
7663       pt.y = HIWORD(lParam);\r
7664       MenuPopup(hwnd, pt, hmenu, -1);\r
7665     }\r
7666     return 0;\r
7667   case WM_PASTE:\r
7668     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7669     SetFocus(hInput);\r
7670     return SendMessage(hInput, message, wParam, lParam);\r
7671   case WM_MBUTTONDOWN:\r
7672     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7673   case WM_RBUTTONDOWN:\r
7674     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7675       /* Move selection here if it was empty */\r
7676       POINT pt;\r
7677       pt.x = LOWORD(lParam);\r
7678       pt.y = HIWORD(lParam);\r
7679       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7680       if (sel.cpMin == sel.cpMax) {\r
7681         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7682         sel.cpMax = sel.cpMin;\r
7683         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7684       }\r
7685       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7686     }\r
7687     return 0;\r
7688   case WM_COMMAND:\r
7689     switch (LOWORD(wParam)) {\r
7690     case IDM_QuickPaste:\r
7691       {\r
7692         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7693         if (sel.cpMin == sel.cpMax) {\r
7694           MessageBeep(MB_ICONEXCLAMATION);\r
7695           return 0;\r
7696         }\r
7697         SendMessage(hwnd, WM_COPY, 0, 0);\r
7698         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7699         SendMessage(hInput, WM_PASTE, 0, 0);\r
7700         SetFocus(hInput);\r
7701         return 0;\r
7702       }\r
7703     case IDM_Cut:\r
7704       SendMessage(hwnd, WM_CUT, 0, 0);\r
7705       return 0;\r
7706     case IDM_Paste:\r
7707       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7708       return 0;\r
7709     case IDM_Copy:\r
7710       SendMessage(hwnd, WM_COPY, 0, 0);\r
7711       return 0;\r
7712     default:\r
7713       {\r
7714         int i = LOWORD(wParam) - IDM_CommandX;\r
7715         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7716             icsTextMenuEntry[i].command != NULL) {\r
7717           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7718                    icsTextMenuEntry[i].getname,\r
7719                    icsTextMenuEntry[i].immediate);\r
7720           return 0;\r
7721         }\r
7722       }\r
7723       break;\r
7724     }\r
7725     break;\r
7726   }\r
7727   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7728 }\r
7729 \r
7730 WNDPROC consoleInputWindowProc;\r
7731 \r
7732 LRESULT CALLBACK\r
7733 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7734 {\r
7735   char buf[MSG_SIZ];\r
7736   char *p;\r
7737   static BOOL sendNextChar = FALSE;\r
7738   static BOOL quoteNextChar = FALSE;\r
7739   InputSource *is = consoleInputSource;\r
7740   CHARFORMAT cf;\r
7741   CHARRANGE sel;\r
7742 \r
7743   switch (message) {\r
7744   case WM_CHAR:\r
7745     if (!appData.localLineEditing || sendNextChar) {\r
7746       is->buf[0] = (CHAR) wParam;\r
7747       is->count = 1;\r
7748       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7749       sendNextChar = FALSE;\r
7750       return 0;\r
7751     }\r
7752     if (quoteNextChar) {\r
7753       buf[0] = (char) wParam;\r
7754       buf[1] = NULLCHAR;\r
7755       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7756       quoteNextChar = FALSE;\r
7757       return 0;\r
7758     }\r
7759     switch (wParam) {\r
7760     case '\r':   /* Enter key */\r
7761       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7762       if (consoleEcho) SaveInHistory(is->buf);\r
7763       is->buf[is->count++] = '\n';\r
7764       is->buf[is->count] = NULLCHAR;\r
7765       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7766       if (consoleEcho) {\r
7767         ConsoleOutput(is->buf, is->count, TRUE);\r
7768       } else if (appData.localLineEditing) {\r
7769         ConsoleOutput("\n", 1, TRUE);\r
7770       }\r
7771       /* fall thru */\r
7772     case '\033': /* Escape key */\r
7773       SetWindowText(hwnd, "");\r
7774       cf.cbSize = sizeof(CHARFORMAT);\r
7775       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7776       if (consoleEcho) {\r
7777         cf.crTextColor = textAttribs[ColorNormal].color;\r
7778       } else {\r
7779         cf.crTextColor = COLOR_ECHOOFF;\r
7780       }\r
7781       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7782       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7783       return 0;\r
7784     case '\t':   /* Tab key */\r
7785       if (GetKeyState(VK_SHIFT) < 0) {\r
7786         /* shifted */\r
7787         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7788       } else {\r
7789         /* unshifted */\r
7790         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7791         if (buttonDesc[0].hwnd) {\r
7792           SetFocus(buttonDesc[0].hwnd);\r
7793         } else {\r
7794           SetFocus(hwndMain);\r
7795         }\r
7796       }\r
7797       return 0;\r
7798     case '\023': /* Ctrl+S */\r
7799       sendNextChar = TRUE;\r
7800       return 0;\r
7801     case '\021': /* Ctrl+Q */\r
7802       quoteNextChar = TRUE;\r
7803       return 0;\r
7804     JAWS_REPLAY\r
7805     default:\r
7806       break;\r
7807     }\r
7808     break;\r
7809   case WM_KEYDOWN:\r
7810     switch (wParam) {\r
7811     case VK_UP:\r
7812       GetWindowText(hwnd, buf, MSG_SIZ);\r
7813       p = PrevInHistory(buf);\r
7814       if (p != NULL) {\r
7815         SetWindowText(hwnd, p);\r
7816         sel.cpMin = 999999;\r
7817         sel.cpMax = 999999;\r
7818         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7819         return 0;\r
7820       }\r
7821       break;\r
7822     case VK_DOWN:\r
7823       p = NextInHistory();\r
7824       if (p != NULL) {\r
7825         SetWindowText(hwnd, p);\r
7826         sel.cpMin = 999999;\r
7827         sel.cpMax = 999999;\r
7828         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7829         return 0;\r
7830       }\r
7831       break;\r
7832     case VK_HOME:\r
7833     case VK_END:\r
7834       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7835       /* fall thru */\r
7836     case VK_PRIOR:\r
7837     case VK_NEXT:\r
7838       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7839       return 0;\r
7840     }\r
7841     break;\r
7842   case WM_MBUTTONDOWN:\r
7843     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7844       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7845     break;\r
7846   case WM_RBUTTONUP:\r
7847     if (GetKeyState(VK_SHIFT) & ~1) {\r
7848       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7849         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7850     } else {\r
7851       POINT pt;\r
7852       HMENU hmenu;\r
7853       hmenu = LoadMenu(hInst, "InputMenu");\r
7854       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7855       if (sel.cpMin == sel.cpMax) {\r
7856         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7857         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7858       }\r
7859       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7860         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7861       }\r
7862       pt.x = LOWORD(lParam);\r
7863       pt.y = HIWORD(lParam);\r
7864       MenuPopup(hwnd, pt, hmenu, -1);\r
7865     }\r
7866     return 0;\r
7867   case WM_COMMAND:\r
7868     switch (LOWORD(wParam)) { \r
7869     case IDM_Undo:\r
7870       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7871       return 0;\r
7872     case IDM_SelectAll:\r
7873       sel.cpMin = 0;\r
7874       sel.cpMax = -1; /*999999?*/\r
7875       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7876       return 0;\r
7877     case IDM_Cut:\r
7878       SendMessage(hwnd, WM_CUT, 0, 0);\r
7879       return 0;\r
7880     case IDM_Paste:\r
7881       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7882       return 0;\r
7883     case IDM_Copy:\r
7884       SendMessage(hwnd, WM_COPY, 0, 0);\r
7885       return 0;\r
7886     }\r
7887     break;\r
7888   }\r
7889   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7890 }\r
7891 \r
7892 #define CO_MAX  100000\r
7893 #define CO_TRIM   1000\r
7894 \r
7895 LRESULT CALLBACK\r
7896 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7897 {\r
7898   static SnapData sd;\r
7899   HWND hText, hInput;\r
7900   RECT rect;\r
7901   static int sizeX, sizeY;\r
7902   int newSizeX, newSizeY;\r
7903   MINMAXINFO *mmi;\r
7904   WORD wMask;\r
7905 \r
7906   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7907   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7908 \r
7909   switch (message) {\r
7910   case WM_NOTIFY:\r
7911     if (((NMHDR*)lParam)->code == EN_LINK)\r
7912     {\r
7913       ENLINK *pLink = (ENLINK*)lParam;\r
7914       if (pLink->msg == WM_LBUTTONUP)\r
7915       {\r
7916         TEXTRANGE tr;\r
7917 \r
7918         tr.chrg = pLink->chrg;\r
7919         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7920         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7921         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7922         free(tr.lpstrText);\r
7923       }\r
7924     }\r
7925     break;\r
7926   case WM_INITDIALOG: /* message: initialize dialog box */\r
7927     hwndConsole = hDlg;\r
7928     SetFocus(hInput);\r
7929     consoleTextWindowProc = (WNDPROC)\r
7930       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7931     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7932     consoleInputWindowProc = (WNDPROC)\r
7933       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7934     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7935     Colorize(ColorNormal, TRUE);\r
7936     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7937     ChangedConsoleFont();\r
7938     GetClientRect(hDlg, &rect);\r
7939     sizeX = rect.right;\r
7940     sizeY = rect.bottom;\r
7941     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7942         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7943       WINDOWPLACEMENT wp;\r
7944       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7945       wp.length = sizeof(WINDOWPLACEMENT);\r
7946       wp.flags = 0;\r
7947       wp.showCmd = SW_SHOW;\r
7948       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7949       wp.rcNormalPosition.left = wpConsole.x;\r
7950       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7951       wp.rcNormalPosition.top = wpConsole.y;\r
7952       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7953       SetWindowPlacement(hDlg, &wp);\r
7954     }\r
7955 \r
7956    // [HGM] Chessknight's change 2004-07-13\r
7957    else { /* Determine Defaults */\r
7958        WINDOWPLACEMENT wp;\r
7959        wpConsole.x = wpMain.width + 1;\r
7960        wpConsole.y = wpMain.y;\r
7961        wpConsole.width = screenWidth -  wpMain.width;\r
7962        wpConsole.height = wpMain.height;\r
7963        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7964        wp.length = sizeof(WINDOWPLACEMENT);\r
7965        wp.flags = 0;\r
7966        wp.showCmd = SW_SHOW;\r
7967        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7968        wp.rcNormalPosition.left = wpConsole.x;\r
7969        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7970        wp.rcNormalPosition.top = wpConsole.y;\r
7971        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7972        SetWindowPlacement(hDlg, &wp);\r
7973     }\r
7974 \r
7975    // Allow hText to highlight URLs and send notifications on them\r
7976    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7977    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7978    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7979    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7980 \r
7981     return FALSE;\r
7982 \r
7983   case WM_SETFOCUS:\r
7984     SetFocus(hInput);\r
7985     return 0;\r
7986 \r
7987   case WM_CLOSE:\r
7988     ExitEvent(0);\r
7989     /* not reached */\r
7990     break;\r
7991 \r
7992   case WM_SIZE:\r
7993     if (IsIconic(hDlg)) break;\r
7994     newSizeX = LOWORD(lParam);\r
7995     newSizeY = HIWORD(lParam);\r
7996     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7997       RECT rectText, rectInput;\r
7998       POINT pt;\r
7999       int newTextHeight, newTextWidth;\r
8000       GetWindowRect(hText, &rectText);\r
8001       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8002       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8003       if (newTextHeight < 0) {\r
8004         newSizeY += -newTextHeight;\r
8005         newTextHeight = 0;\r
8006       }\r
8007       SetWindowPos(hText, NULL, 0, 0,\r
8008         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8009       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8010       pt.x = rectInput.left;\r
8011       pt.y = rectInput.top + newSizeY - sizeY;\r
8012       ScreenToClient(hDlg, &pt);\r
8013       SetWindowPos(hInput, NULL, \r
8014         pt.x, pt.y, /* needs client coords */   \r
8015         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8016         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8017     }\r
8018     sizeX = newSizeX;\r
8019     sizeY = newSizeY;\r
8020     break;\r
8021 \r
8022   case WM_GETMINMAXINFO:\r
8023     /* Prevent resizing window too small */\r
8024     mmi = (MINMAXINFO *) lParam;\r
8025     mmi->ptMinTrackSize.x = 100;\r
8026     mmi->ptMinTrackSize.y = 100;\r
8027     break;\r
8028 \r
8029   /* [AS] Snapping */\r
8030   case WM_ENTERSIZEMOVE:\r
8031     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8032 \r
8033   case WM_SIZING:\r
8034     return OnSizing( &sd, hDlg, wParam, lParam );\r
8035 \r
8036   case WM_MOVING:\r
8037     return OnMoving( &sd, hDlg, wParam, lParam );\r
8038 \r
8039   case WM_EXITSIZEMOVE:\r
8040         UpdateICSWidth(hText);\r
8041     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8042   }\r
8043 \r
8044   return DefWindowProc(hDlg, message, wParam, lParam);\r
8045 }\r
8046 \r
8047 \r
8048 VOID\r
8049 ConsoleCreate()\r
8050 {\r
8051   HWND hCons;\r
8052   if (hwndConsole) return;\r
8053   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8054   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8055 }\r
8056 \r
8057 \r
8058 VOID\r
8059 ConsoleOutput(char* data, int length, int forceVisible)\r
8060 {\r
8061   HWND hText;\r
8062   int trim, exlen;\r
8063   char *p, *q;\r
8064   char buf[CO_MAX+1];\r
8065   POINT pEnd;\r
8066   RECT rect;\r
8067   static int delayLF = 0;\r
8068   CHARRANGE savesel, sel;\r
8069 \r
8070   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8071   p = data;\r
8072   q = buf;\r
8073   if (delayLF) {\r
8074     *q++ = '\r';\r
8075     *q++ = '\n';\r
8076     delayLF = 0;\r
8077   }\r
8078   while (length--) {\r
8079     if (*p == '\n') {\r
8080       if (*++p) {\r
8081         *q++ = '\r';\r
8082         *q++ = '\n';\r
8083       } else {\r
8084         delayLF = 1;\r
8085       }\r
8086     } else if (*p == '\007') {\r
8087        MyPlaySound(&sounds[(int)SoundBell]);\r
8088        p++;\r
8089     } else {\r
8090       *q++ = *p++;\r
8091     }\r
8092   }\r
8093   *q = NULLCHAR;\r
8094   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8095   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8096   /* Save current selection */\r
8097   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8098   exlen = GetWindowTextLength(hText);\r
8099   /* Find out whether current end of text is visible */\r
8100   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8101   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8102   /* Trim existing text if it's too long */\r
8103   if (exlen + (q - buf) > CO_MAX) {\r
8104     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8105     sel.cpMin = 0;\r
8106     sel.cpMax = trim;\r
8107     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8108     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8109     exlen -= trim;\r
8110     savesel.cpMin -= trim;\r
8111     savesel.cpMax -= trim;\r
8112     if (exlen < 0) exlen = 0;\r
8113     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8114     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8115   }\r
8116   /* Append the new text */\r
8117   sel.cpMin = exlen;\r
8118   sel.cpMax = exlen;\r
8119   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8120   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8121   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8122   if (forceVisible || exlen == 0 ||\r
8123       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8124        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8125     /* Scroll to make new end of text visible if old end of text\r
8126        was visible or new text is an echo of user typein */\r
8127     sel.cpMin = 9999999;\r
8128     sel.cpMax = 9999999;\r
8129     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8130     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8131     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8132     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8133   }\r
8134   if (savesel.cpMax == exlen || forceVisible) {\r
8135     /* Move insert point to new end of text if it was at the old\r
8136        end of text or if the new text is an echo of user typein */\r
8137     sel.cpMin = 9999999;\r
8138     sel.cpMax = 9999999;\r
8139     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8140   } else {\r
8141     /* Restore previous selection */\r
8142     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8143   }\r
8144   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8145 }\r
8146 \r
8147 /*---------*/\r
8148 \r
8149 \r
8150 void\r
8151 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8152 {\r
8153   char buf[100];\r
8154   char *str;\r
8155   COLORREF oldFg, oldBg;\r
8156   HFONT oldFont;\r
8157   RECT rect;\r
8158 \r
8159   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8160 \r
8161   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8162   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8163   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8164 \r
8165   rect.left = x;\r
8166   rect.right = x + squareSize;\r
8167   rect.top  = y;\r
8168   rect.bottom = y + squareSize;\r
8169   str = buf;\r
8170 \r
8171   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8172                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8173              y, ETO_CLIPPED|ETO_OPAQUE,\r
8174              &rect, str, strlen(str), NULL);\r
8175 \r
8176   (void) SetTextColor(hdc, oldFg);\r
8177   (void) SetBkColor(hdc, oldBg);\r
8178   (void) SelectObject(hdc, oldFont);\r
8179 }\r
8180 \r
8181 void\r
8182 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8183               RECT *rect, char *color, char *flagFell)\r
8184 {\r
8185   char buf[100];\r
8186   char *str;\r
8187   COLORREF oldFg, oldBg;\r
8188   HFONT oldFont;\r
8189 \r
8190   if (appData.clockMode) {\r
8191     if (tinyLayout)\r
8192       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8193     else\r
8194       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8195     str = buf;\r
8196   } else {\r
8197     str = color;\r
8198   }\r
8199 \r
8200   if (highlight) {\r
8201     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8202     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8203   } else {\r
8204     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8205     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8206   }\r
8207   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8208 \r
8209   JAWS_SILENCE\r
8210 \r
8211   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8212              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8213              rect, str, strlen(str), NULL);\r
8214   if(logoHeight > 0 && appData.clockMode) {\r
8215       RECT r;\r
8216       sprintf(buf, "%s %s", buf+7, flagFell);\r
8217       r.top = rect->top + logoHeight/2;\r
8218       r.left = rect->left;\r
8219       r.right = rect->right;\r
8220       r.bottom = rect->bottom;\r
8221       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8222                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8223                  &r, str, strlen(str), NULL);\r
8224   }\r
8225   (void) SetTextColor(hdc, oldFg);\r
8226   (void) SetBkColor(hdc, oldBg);\r
8227   (void) SelectObject(hdc, oldFont);\r
8228 }\r
8229 \r
8230 \r
8231 int\r
8232 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8233            OVERLAPPED *ovl)\r
8234 {\r
8235   int ok, err;\r
8236 \r
8237   /* [AS]  */\r
8238   if( count <= 0 ) {\r
8239     if (appData.debugMode) {\r
8240       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8241     }\r
8242 \r
8243     return ERROR_INVALID_USER_BUFFER;\r
8244   }\r
8245 \r
8246   ResetEvent(ovl->hEvent);\r
8247   ovl->Offset = ovl->OffsetHigh = 0;\r
8248   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8249   if (ok) {\r
8250     err = NO_ERROR;\r
8251   } else {\r
8252     err = GetLastError();\r
8253     if (err == ERROR_IO_PENDING) {\r
8254       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8255       if (ok)\r
8256         err = NO_ERROR;\r
8257       else\r
8258         err = GetLastError();\r
8259     }\r
8260   }\r
8261   return err;\r
8262 }\r
8263 \r
8264 int\r
8265 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8266             OVERLAPPED *ovl)\r
8267 {\r
8268   int ok, err;\r
8269 \r
8270   ResetEvent(ovl->hEvent);\r
8271   ovl->Offset = ovl->OffsetHigh = 0;\r
8272   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8273   if (ok) {\r
8274     err = NO_ERROR;\r
8275   } else {\r
8276     err = GetLastError();\r
8277     if (err == ERROR_IO_PENDING) {\r
8278       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8279       if (ok)\r
8280         err = NO_ERROR;\r
8281       else\r
8282         err = GetLastError();\r
8283     }\r
8284   }\r
8285   return err;\r
8286 }\r
8287 \r
8288 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8289 void CheckForInputBufferFull( InputSource * is )\r
8290 {\r
8291     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8292         /* Look for end of line */\r
8293         char * p = is->buf;\r
8294         \r
8295         while( p < is->next && *p != '\n' ) {\r
8296             p++;\r
8297         }\r
8298 \r
8299         if( p >= is->next ) {\r
8300             if (appData.debugMode) {\r
8301                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8302             }\r
8303 \r
8304             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8305             is->count = (DWORD) -1;\r
8306             is->next = is->buf;\r
8307         }\r
8308     }\r
8309 }\r
8310 \r
8311 DWORD\r
8312 InputThread(LPVOID arg)\r
8313 {\r
8314   InputSource *is;\r
8315   OVERLAPPED ovl;\r
8316 \r
8317   is = (InputSource *) arg;\r
8318   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8319   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8320   while (is->hThread != NULL) {\r
8321     is->error = DoReadFile(is->hFile, is->next,\r
8322                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8323                            &is->count, &ovl);\r
8324     if (is->error == NO_ERROR) {\r
8325       is->next += is->count;\r
8326     } else {\r
8327       if (is->error == ERROR_BROKEN_PIPE) {\r
8328         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8329         is->count = 0;\r
8330       } else {\r
8331         is->count = (DWORD) -1;\r
8332         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8333         break; \r
8334       }\r
8335     }\r
8336 \r
8337     CheckForInputBufferFull( is );\r
8338 \r
8339     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8340 \r
8341     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8342 \r
8343     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8344   }\r
8345 \r
8346   CloseHandle(ovl.hEvent);\r
8347   CloseHandle(is->hFile);\r
8348 \r
8349   if (appData.debugMode) {\r
8350     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8351   }\r
8352 \r
8353   return 0;\r
8354 }\r
8355 \r
8356 \r
8357 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8358 DWORD\r
8359 NonOvlInputThread(LPVOID arg)\r
8360 {\r
8361   InputSource *is;\r
8362   char *p, *q;\r
8363   int i;\r
8364   char prev;\r
8365 \r
8366   is = (InputSource *) arg;\r
8367   while (is->hThread != NULL) {\r
8368     is->error = ReadFile(is->hFile, is->next,\r
8369                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8370                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8371     if (is->error == NO_ERROR) {\r
8372       /* Change CRLF to LF */\r
8373       if (is->next > is->buf) {\r
8374         p = is->next - 1;\r
8375         i = is->count + 1;\r
8376       } else {\r
8377         p = is->next;\r
8378         i = is->count;\r
8379       }\r
8380       q = p;\r
8381       prev = NULLCHAR;\r
8382       while (i > 0) {\r
8383         if (prev == '\r' && *p == '\n') {\r
8384           *(q-1) = '\n';\r
8385           is->count--;\r
8386         } else { \r
8387           *q++ = *p;\r
8388         }\r
8389         prev = *p++;\r
8390         i--;\r
8391       }\r
8392       *q = NULLCHAR;\r
8393       is->next = q;\r
8394     } else {\r
8395       if (is->error == ERROR_BROKEN_PIPE) {\r
8396         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8397         is->count = 0; \r
8398       } else {\r
8399         is->count = (DWORD) -1;\r
8400       }\r
8401     }\r
8402 \r
8403     CheckForInputBufferFull( is );\r
8404 \r
8405     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8406 \r
8407     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8408 \r
8409     if (is->count < 0) break;  /* Quit on error */\r
8410   }\r
8411   CloseHandle(is->hFile);\r
8412   return 0;\r
8413 }\r
8414 \r
8415 DWORD\r
8416 SocketInputThread(LPVOID arg)\r
8417 {\r
8418   InputSource *is;\r
8419 \r
8420   is = (InputSource *) arg;\r
8421   while (is->hThread != NULL) {\r
8422     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8423     if ((int)is->count == SOCKET_ERROR) {\r
8424       is->count = (DWORD) -1;\r
8425       is->error = WSAGetLastError();\r
8426     } else {\r
8427       is->error = NO_ERROR;\r
8428       is->next += is->count;\r
8429       if (is->count == 0 && is->second == is) {\r
8430         /* End of file on stderr; quit with no message */\r
8431         break;\r
8432       }\r
8433     }\r
8434     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8435 \r
8436     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8437 \r
8438     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8439   }\r
8440   return 0;\r
8441 }\r
8442 \r
8443 VOID\r
8444 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8445 {\r
8446   InputSource *is;\r
8447 \r
8448   is = (InputSource *) lParam;\r
8449   if (is->lineByLine) {\r
8450     /* Feed in lines one by one */\r
8451     char *p = is->buf;\r
8452     char *q = p;\r
8453     while (q < is->next) {\r
8454       if (*q++ == '\n') {\r
8455         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8456         p = q;\r
8457       }\r
8458     }\r
8459     \r
8460     /* Move any partial line to the start of the buffer */\r
8461     q = is->buf;\r
8462     while (p < is->next) {\r
8463       *q++ = *p++;\r
8464     }\r
8465     is->next = q;\r
8466 \r
8467     if (is->error != NO_ERROR || is->count == 0) {\r
8468       /* Notify backend of the error.  Note: If there was a partial\r
8469          line at the end, it is not flushed through. */\r
8470       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8471     }\r
8472   } else {\r
8473     /* Feed in the whole chunk of input at once */\r
8474     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8475     is->next = is->buf;\r
8476   }\r
8477 }\r
8478 \r
8479 /*---------------------------------------------------------------------------*\\r
8480  *\r
8481  *  Menu enables. Used when setting various modes.\r
8482  *\r
8483 \*---------------------------------------------------------------------------*/\r
8484 \r
8485 typedef struct {\r
8486   int item;\r
8487   int flags;\r
8488 } Enables;\r
8489 \r
8490 VOID\r
8491 GreyRevert(Boolean grey)\r
8492 { // [HGM] vari: for retracting variations in local mode\r
8493   HMENU hmenu = GetMenu(hwndMain);\r
8494   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
8495 }\r
8496 \r
8497 VOID\r
8498 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8499 {\r
8500   while (enab->item > 0) {\r
8501     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8502     enab++;\r
8503   }\r
8504 }\r
8505 \r
8506 Enables gnuEnables[] = {\r
8507   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8510   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8511   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8513   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8515   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8516   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8517   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8518   { -1, -1 }\r
8519 };\r
8520 \r
8521 Enables icsEnables[] = {\r
8522   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8523   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8524   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8525   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8526   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8527   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8528   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8529   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8530   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8531   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8532   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8533   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8534   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8535   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8536   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8537   { -1, -1 }\r
8538 };\r
8539 \r
8540 #ifdef ZIPPY\r
8541 Enables zippyEnables[] = {\r
8542   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8543   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8544   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8545   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8546   { -1, -1 }\r
8547 };\r
8548 #endif\r
8549 \r
8550 Enables ncpEnables[] = {\r
8551   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8559   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8560   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8561   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8568   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8570   { -1, -1 }\r
8571 };\r
8572 \r
8573 Enables trainingOnEnables[] = {\r
8574   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8575   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8576   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8578   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8580   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8581   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8582   { -1, -1 }\r
8583 };\r
8584 \r
8585 Enables trainingOffEnables[] = {\r
8586   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8587   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8588   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8589   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8590   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8591   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8592   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8593   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8594   { -1, -1 }\r
8595 };\r
8596 \r
8597 /* These modify either ncpEnables or gnuEnables */\r
8598 Enables cmailEnables[] = {\r
8599   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8600   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8601   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8602   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8604   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8606   { -1, -1 }\r
8607 };\r
8608 \r
8609 Enables machineThinkingEnables[] = {\r
8610   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8616   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8617   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8618   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8619   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8620   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8621   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8622   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8623   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8624   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8625   { -1, -1 }\r
8626 };\r
8627 \r
8628 Enables userThinkingEnables[] = {\r
8629   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8631   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8632   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8633   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8634   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8635   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8636   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8637   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8638   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8639   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8640   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8641   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8642   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8643   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8644   { -1, -1 }\r
8645 };\r
8646 \r
8647 /*---------------------------------------------------------------------------*\\r
8648  *\r
8649  *  Front-end interface functions exported by XBoard.\r
8650  *  Functions appear in same order as prototypes in frontend.h.\r
8651  * \r
8652 \*---------------------------------------------------------------------------*/\r
8653 VOID\r
8654 ModeHighlight()\r
8655 {\r
8656   static UINT prevChecked = 0;\r
8657   static int prevPausing = 0;\r
8658   UINT nowChecked;\r
8659 \r
8660   if (pausing != prevPausing) {\r
8661     prevPausing = pausing;\r
8662     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8663                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8664     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8665   }\r
8666 \r
8667   switch (gameMode) {\r
8668   case BeginningOfGame:\r
8669     if (appData.icsActive)\r
8670       nowChecked = IDM_IcsClient;\r
8671     else if (appData.noChessProgram)\r
8672       nowChecked = IDM_EditGame;\r
8673     else\r
8674       nowChecked = IDM_MachineBlack;\r
8675     break;\r
8676   case MachinePlaysBlack:\r
8677     nowChecked = IDM_MachineBlack;\r
8678     break;\r
8679   case MachinePlaysWhite:\r
8680     nowChecked = IDM_MachineWhite;\r
8681     break;\r
8682   case TwoMachinesPlay:\r
8683     nowChecked = IDM_TwoMachines;\r
8684     break;\r
8685   case AnalyzeMode:\r
8686     nowChecked = IDM_AnalysisMode;\r
8687     break;\r
8688   case AnalyzeFile:\r
8689     nowChecked = IDM_AnalyzeFile;\r
8690     break;\r
8691   case EditGame:\r
8692     nowChecked = IDM_EditGame;\r
8693     break;\r
8694   case PlayFromGameFile:\r
8695     nowChecked = IDM_LoadGame;\r
8696     break;\r
8697   case EditPosition:\r
8698     nowChecked = IDM_EditPosition;\r
8699     break;\r
8700   case Training:\r
8701     nowChecked = IDM_Training;\r
8702     break;\r
8703   case IcsPlayingWhite:\r
8704   case IcsPlayingBlack:\r
8705   case IcsObserving:\r
8706   case IcsIdle:\r
8707     nowChecked = IDM_IcsClient;\r
8708     break;\r
8709   default:\r
8710   case EndOfGame:\r
8711     nowChecked = 0;\r
8712     break;\r
8713   }\r
8714   if (prevChecked != 0)\r
8715     (void) CheckMenuItem(GetMenu(hwndMain),\r
8716                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8717   if (nowChecked != 0)\r
8718     (void) CheckMenuItem(GetMenu(hwndMain),\r
8719                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8720 \r
8721   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8722     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8723                           MF_BYCOMMAND|MF_ENABLED);\r
8724   } else {\r
8725     (void) EnableMenuItem(GetMenu(hwndMain), \r
8726                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8727   }\r
8728 \r
8729   prevChecked = nowChecked;\r
8730 \r
8731   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8732   if (appData.icsActive) {\r
8733        if (appData.icsEngineAnalyze) {\r
8734                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8735                        MF_BYCOMMAND|MF_CHECKED);\r
8736        } else {\r
8737                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8738                        MF_BYCOMMAND|MF_UNCHECKED);\r
8739        }\r
8740   }\r
8741 }\r
8742 \r
8743 VOID\r
8744 SetICSMode()\r
8745 {\r
8746   HMENU hmenu = GetMenu(hwndMain);\r
8747   SetMenuEnables(hmenu, icsEnables);\r
8748   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8749     MF_BYPOSITION|MF_ENABLED);\r
8750 #ifdef ZIPPY\r
8751   if (appData.zippyPlay) {\r
8752     SetMenuEnables(hmenu, zippyEnables);\r
8753     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8754          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8755           MF_BYCOMMAND|MF_ENABLED);\r
8756   }\r
8757 #endif\r
8758 }\r
8759 \r
8760 VOID\r
8761 SetGNUMode()\r
8762 {\r
8763   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8764 }\r
8765 \r
8766 VOID\r
8767 SetNCPMode()\r
8768 {\r
8769   HMENU hmenu = GetMenu(hwndMain);\r
8770   SetMenuEnables(hmenu, ncpEnables);\r
8771   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8772     MF_BYPOSITION|MF_GRAYED);\r
8773     DrawMenuBar(hwndMain);\r
8774 }\r
8775 \r
8776 VOID\r
8777 SetCmailMode()\r
8778 {\r
8779   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8780 }\r
8781 \r
8782 VOID \r
8783 SetTrainingModeOn()\r
8784 {\r
8785   int i;\r
8786   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8787   for (i = 0; i < N_BUTTONS; i++) {\r
8788     if (buttonDesc[i].hwnd != NULL)\r
8789       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8790   }\r
8791   CommentPopDown();\r
8792 }\r
8793 \r
8794 VOID SetTrainingModeOff()\r
8795 {\r
8796   int i;\r
8797   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8798   for (i = 0; i < N_BUTTONS; i++) {\r
8799     if (buttonDesc[i].hwnd != NULL)\r
8800       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8801   }\r
8802 }\r
8803 \r
8804 \r
8805 VOID\r
8806 SetUserThinkingEnables()\r
8807 {\r
8808   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8809 }\r
8810 \r
8811 VOID\r
8812 SetMachineThinkingEnables()\r
8813 {\r
8814   HMENU hMenu = GetMenu(hwndMain);\r
8815   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8816 \r
8817   SetMenuEnables(hMenu, machineThinkingEnables);\r
8818 \r
8819   if (gameMode == MachinePlaysBlack) {\r
8820     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8821   } else if (gameMode == MachinePlaysWhite) {\r
8822     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8823   } else if (gameMode == TwoMachinesPlay) {\r
8824     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8825   }\r
8826 }\r
8827 \r
8828 \r
8829 VOID\r
8830 DisplayTitle(char *str)\r
8831 {\r
8832   char title[MSG_SIZ], *host;\r
8833   if (str[0] != NULLCHAR) {\r
8834     strcpy(title, str);\r
8835   } else if (appData.icsActive) {\r
8836     if (appData.icsCommPort[0] != NULLCHAR)\r
8837       host = "ICS";\r
8838     else \r
8839       host = appData.icsHost;\r
8840     sprintf(title, "%s: %s", szTitle, host);\r
8841   } else if (appData.noChessProgram) {\r
8842     strcpy(title, szTitle);\r
8843   } else {\r
8844     strcpy(title, szTitle);\r
8845     strcat(title, ": ");\r
8846     strcat(title, first.tidy);\r
8847   }\r
8848   SetWindowText(hwndMain, title);\r
8849 }\r
8850 \r
8851 \r
8852 VOID\r
8853 DisplayMessage(char *str1, char *str2)\r
8854 {\r
8855   HDC hdc;\r
8856   HFONT oldFont;\r
8857   int remain = MESSAGE_TEXT_MAX - 1;\r
8858   int len;\r
8859 \r
8860   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8861   messageText[0] = NULLCHAR;\r
8862   if (*str1) {\r
8863     len = strlen(str1);\r
8864     if (len > remain) len = remain;\r
8865     strncpy(messageText, str1, len);\r
8866     messageText[len] = NULLCHAR;\r
8867     remain -= len;\r
8868   }\r
8869   if (*str2 && remain >= 2) {\r
8870     if (*str1) {\r
8871       strcat(messageText, "  ");\r
8872       remain -= 2;\r
8873     }\r
8874     len = strlen(str2);\r
8875     if (len > remain) len = remain;\r
8876     strncat(messageText, str2, len);\r
8877   }\r
8878   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8879 \r
8880   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8881 \r
8882   SAYMACHINEMOVE();\r
8883 \r
8884   hdc = GetDC(hwndMain);\r
8885   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8886   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8887              &messageRect, messageText, strlen(messageText), NULL);\r
8888   (void) SelectObject(hdc, oldFont);\r
8889   (void) ReleaseDC(hwndMain, hdc);\r
8890 }\r
8891 \r
8892 VOID\r
8893 DisplayError(char *str, int error)\r
8894 {\r
8895   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8896   int len;\r
8897 \r
8898   if (error == 0) {\r
8899     strcpy(buf, str);\r
8900   } else {\r
8901     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8902                         NULL, error, LANG_NEUTRAL,\r
8903                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8904     if (len > 0) {\r
8905       sprintf(buf, "%s:\n%s", str, buf2);\r
8906     } else {\r
8907       ErrorMap *em = errmap;\r
8908       while (em->err != 0 && em->err != error) em++;\r
8909       if (em->err != 0) {\r
8910         sprintf(buf, "%s:\n%s", str, em->msg);\r
8911       } else {\r
8912         sprintf(buf, "%s:\nError code %d", str, error);\r
8913       }\r
8914     }\r
8915   }\r
8916   \r
8917   ErrorPopUp("Error", buf);\r
8918 }\r
8919 \r
8920 \r
8921 VOID\r
8922 DisplayMoveError(char *str)\r
8923 {\r
8924   fromX = fromY = -1;\r
8925   ClearHighlights();\r
8926   DrawPosition(FALSE, NULL);\r
8927   if (appData.popupMoveErrors) {\r
8928     ErrorPopUp("Error", str);\r
8929   } else {\r
8930     DisplayMessage(str, "");\r
8931     moveErrorMessageUp = TRUE;\r
8932   }\r
8933 }\r
8934 \r
8935 VOID\r
8936 DisplayFatalError(char *str, int error, int exitStatus)\r
8937 {\r
8938   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8939   int len;\r
8940   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8941 \r
8942   if (error != 0) {\r
8943     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8944                         NULL, error, LANG_NEUTRAL,\r
8945                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8946     if (len > 0) {\r
8947       sprintf(buf, "%s:\n%s", str, buf2);\r
8948     } else {\r
8949       ErrorMap *em = errmap;\r
8950       while (em->err != 0 && em->err != error) em++;\r
8951       if (em->err != 0) {\r
8952         sprintf(buf, "%s:\n%s", str, em->msg);\r
8953       } else {\r
8954         sprintf(buf, "%s:\nError code %d", str, error);\r
8955       }\r
8956     }\r
8957     str = buf;\r
8958   }\r
8959   if (appData.debugMode) {\r
8960     fprintf(debugFP, "%s: %s\n", label, str);\r
8961   }\r
8962   if (appData.popupExitMessage) {\r
8963     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8964                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8965   }\r
8966   ExitEvent(exitStatus);\r
8967 }\r
8968 \r
8969 \r
8970 VOID\r
8971 DisplayInformation(char *str)\r
8972 {\r
8973   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8974 }\r
8975 \r
8976 \r
8977 VOID\r
8978 DisplayNote(char *str)\r
8979 {\r
8980   ErrorPopUp("Note", str);\r
8981 }\r
8982 \r
8983 \r
8984 typedef struct {\r
8985   char *title, *question, *replyPrefix;\r
8986   ProcRef pr;\r
8987 } QuestionParams;\r
8988 \r
8989 LRESULT CALLBACK\r
8990 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8991 {\r
8992   static QuestionParams *qp;\r
8993   char reply[MSG_SIZ];\r
8994   int len, err;\r
8995 \r
8996   switch (message) {\r
8997   case WM_INITDIALOG:\r
8998     qp = (QuestionParams *) lParam;\r
8999     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9000     SetWindowText(hDlg, qp->title);\r
9001     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9002     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9003     return FALSE;\r
9004 \r
9005   case WM_COMMAND:\r
9006     switch (LOWORD(wParam)) {\r
9007     case IDOK:\r
9008       strcpy(reply, qp->replyPrefix);\r
9009       if (*reply) strcat(reply, " ");\r
9010       len = strlen(reply);\r
9011       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9012       strcat(reply, "\n");\r
9013       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9014       EndDialog(hDlg, TRUE);\r
9015       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9016       return TRUE;\r
9017     case IDCANCEL:\r
9018       EndDialog(hDlg, FALSE);\r
9019       return TRUE;\r
9020     default:\r
9021       break;\r
9022     }\r
9023     break;\r
9024   }\r
9025   return FALSE;\r
9026 }\r
9027 \r
9028 VOID\r
9029 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9030 {\r
9031     QuestionParams qp;\r
9032     FARPROC lpProc;\r
9033     \r
9034     qp.title = title;\r
9035     qp.question = question;\r
9036     qp.replyPrefix = replyPrefix;\r
9037     qp.pr = pr;\r
9038     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9039     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9040       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9041     FreeProcInstance(lpProc);\r
9042 }\r
9043 \r
9044 /* [AS] Pick FRC position */\r
9045 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9046 {\r
9047     static int * lpIndexFRC;\r
9048     BOOL index_is_ok;\r
9049     char buf[16];\r
9050 \r
9051     switch( message )\r
9052     {\r
9053     case WM_INITDIALOG:\r
9054         lpIndexFRC = (int *) lParam;\r
9055 \r
9056         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9057 \r
9058         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9059         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9060         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9061         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9062 \r
9063         break;\r
9064 \r
9065     case WM_COMMAND:\r
9066         switch( LOWORD(wParam) ) {\r
9067         case IDOK:\r
9068             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9069             EndDialog( hDlg, 0 );\r
9070             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9071             return TRUE;\r
9072         case IDCANCEL:\r
9073             EndDialog( hDlg, 1 );   \r
9074             return TRUE;\r
9075         case IDC_NFG_Edit:\r
9076             if( HIWORD(wParam) == EN_CHANGE ) {\r
9077                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9078 \r
9079                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9080             }\r
9081             return TRUE;\r
9082         case IDC_NFG_Random:\r
9083             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9084             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9085             return TRUE;\r
9086         }\r
9087 \r
9088         break;\r
9089     }\r
9090 \r
9091     return FALSE;\r
9092 }\r
9093 \r
9094 int NewGameFRC()\r
9095 {\r
9096     int result;\r
9097     int index = appData.defaultFrcPosition;\r
9098     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9099 \r
9100     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9101 \r
9102     if( result == 0 ) {\r
9103         appData.defaultFrcPosition = index;\r
9104     }\r
9105 \r
9106     return result;\r
9107 }\r
9108 \r
9109 /* [AS] Game list options */\r
9110 typedef struct {\r
9111     char id;\r
9112     char * name;\r
9113 } GLT_Item;\r
9114 \r
9115 static GLT_Item GLT_ItemInfo[] = {\r
9116     { GLT_EVENT,      "Event" },\r
9117     { GLT_SITE,       "Site" },\r
9118     { GLT_DATE,       "Date" },\r
9119     { GLT_ROUND,      "Round" },\r
9120     { GLT_PLAYERS,    "Players" },\r
9121     { GLT_RESULT,     "Result" },\r
9122     { GLT_WHITE_ELO,  "White Rating" },\r
9123     { GLT_BLACK_ELO,  "Black Rating" },\r
9124     { GLT_TIME_CONTROL,"Time Control" },\r
9125     { GLT_VARIANT,    "Variant" },\r
9126     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9127     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9128     { 0, 0 }\r
9129 };\r
9130 \r
9131 const char * GLT_FindItem( char id )\r
9132 {\r
9133     const char * result = 0;\r
9134 \r
9135     GLT_Item * list = GLT_ItemInfo;\r
9136 \r
9137     while( list->id != 0 ) {\r
9138         if( list->id == id ) {\r
9139             result = list->name;\r
9140             break;\r
9141         }\r
9142 \r
9143         list++;\r
9144     }\r
9145 \r
9146     return result;\r
9147 }\r
9148 \r
9149 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9150 {\r
9151     const char * name = GLT_FindItem( id );\r
9152 \r
9153     if( name != 0 ) {\r
9154         if( index >= 0 ) {\r
9155             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9156         }\r
9157         else {\r
9158             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9159         }\r
9160     }\r
9161 }\r
9162 \r
9163 void GLT_TagsToList( HWND hDlg, char * tags )\r
9164 {\r
9165     char * pc = tags;\r
9166 \r
9167     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9168 \r
9169     while( *pc ) {\r
9170         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9171         pc++;\r
9172     }\r
9173 \r
9174     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9175 \r
9176     pc = GLT_ALL_TAGS;\r
9177 \r
9178     while( *pc ) {\r
9179         if( strchr( tags, *pc ) == 0 ) {\r
9180             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9181         }\r
9182         pc++;\r
9183     }\r
9184 \r
9185     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9186 }\r
9187 \r
9188 char GLT_ListItemToTag( HWND hDlg, int index )\r
9189 {\r
9190     char result = '\0';\r
9191     char name[128];\r
9192 \r
9193     GLT_Item * list = GLT_ItemInfo;\r
9194 \r
9195     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9196         while( list->id != 0 ) {\r
9197             if( strcmp( list->name, name ) == 0 ) {\r
9198                 result = list->id;\r
9199                 break;\r
9200             }\r
9201 \r
9202             list++;\r
9203         }\r
9204     }\r
9205 \r
9206     return result;\r
9207 }\r
9208 \r
9209 void GLT_MoveSelection( HWND hDlg, int delta )\r
9210 {\r
9211     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9212     int idx2 = idx1 + delta;\r
9213     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9214 \r
9215     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9216         char buf[128];\r
9217 \r
9218         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9219         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9220         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9221         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9222     }\r
9223 }\r
9224 \r
9225 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9226 {\r
9227     static char glt[64];\r
9228     static char * lpUserGLT;\r
9229 \r
9230     switch( message )\r
9231     {\r
9232     case WM_INITDIALOG:\r
9233         lpUserGLT = (char *) lParam;\r
9234         \r
9235         strcpy( glt, lpUserGLT );\r
9236 \r
9237         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9238 \r
9239         /* Initialize list */\r
9240         GLT_TagsToList( hDlg, glt );\r
9241 \r
9242         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9243 \r
9244         break;\r
9245 \r
9246     case WM_COMMAND:\r
9247         switch( LOWORD(wParam) ) {\r
9248         case IDOK:\r
9249             {\r
9250                 char * pc = lpUserGLT;\r
9251                 int idx = 0;\r
9252 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9253                 char id;\r
9254 \r
9255                 do {\r
9256                     id = GLT_ListItemToTag( hDlg, idx );\r
9257 \r
9258                     *pc++ = id;\r
9259                     idx++;\r
9260                 } while( id != '\0' );\r
9261             }\r
9262             EndDialog( hDlg, 0 );\r
9263             return TRUE;\r
9264         case IDCANCEL:\r
9265             EndDialog( hDlg, 1 );\r
9266             return TRUE;\r
9267 \r
9268         case IDC_GLT_Default:\r
9269             strcpy( glt, GLT_DEFAULT_TAGS );\r
9270             GLT_TagsToList( hDlg, glt );\r
9271             return TRUE;\r
9272 \r
9273         case IDC_GLT_Restore:\r
9274             strcpy( glt, lpUserGLT );\r
9275             GLT_TagsToList( hDlg, glt );\r
9276             return TRUE;\r
9277 \r
9278         case IDC_GLT_Up:\r
9279             GLT_MoveSelection( hDlg, -1 );\r
9280             return TRUE;\r
9281 \r
9282         case IDC_GLT_Down:\r
9283             GLT_MoveSelection( hDlg, +1 );\r
9284             return TRUE;\r
9285         }\r
9286 \r
9287         break;\r
9288     }\r
9289 \r
9290     return FALSE;\r
9291 }\r
9292 \r
9293 int GameListOptions()\r
9294 {\r
9295     char glt[64];\r
9296     int result;\r
9297     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9298 \r
9299     strcpy( glt, appData.gameListTags );\r
9300 \r
9301     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9302 \r
9303     if( result == 0 ) {\r
9304         /* [AS] Memory leak here! */\r
9305         appData.gameListTags = strdup( glt ); \r
9306     }\r
9307 \r
9308     return result;\r
9309 }\r
9310 \r
9311 \r
9312 VOID\r
9313 DisplayIcsInteractionTitle(char *str)\r
9314 {\r
9315   char consoleTitle[MSG_SIZ];\r
9316 \r
9317   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9318   SetWindowText(hwndConsole, consoleTitle);\r
9319 }\r
9320 \r
9321 void\r
9322 DrawPosition(int fullRedraw, Board board)\r
9323 {\r
9324   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9325 }\r
9326 \r
9327 void NotifyFrontendLogin()\r
9328 {\r
9329         if (hwndConsole)\r
9330                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9331 }\r
9332 \r
9333 VOID\r
9334 ResetFrontEnd()\r
9335 {\r
9336   fromX = fromY = -1;\r
9337   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9338     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9339     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9340     dragInfo.lastpos = dragInfo.pos;\r
9341     dragInfo.start.x = dragInfo.start.y = -1;\r
9342     dragInfo.from = dragInfo.start;\r
9343     ReleaseCapture();\r
9344     DrawPosition(TRUE, NULL);\r
9345   }\r
9346 }\r
9347 \r
9348 \r
9349 VOID\r
9350 CommentPopUp(char *title, char *str)\r
9351 {\r
9352   HWND hwnd = GetActiveWindow();\r
9353   EitherCommentPopUp(0, title, str, FALSE);\r
9354   SAY(str);\r
9355   SetActiveWindow(hwnd);\r
9356 }\r
9357 \r
9358 VOID\r
9359 CommentPopDown(void)\r
9360 {\r
9361   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9362   if (commentDialog) {\r
9363     ShowWindow(commentDialog, SW_HIDE);\r
9364   }\r
9365   commentUp = FALSE;\r
9366 }\r
9367 \r
9368 VOID\r
9369 EditCommentPopUp(int index, char *title, char *str)\r
9370 {\r
9371   EitherCommentPopUp(index, title, str, TRUE);\r
9372 }\r
9373 \r
9374 \r
9375 VOID\r
9376 RingBell()\r
9377 {\r
9378   MyPlaySound(&sounds[(int)SoundMove]);\r
9379 }\r
9380 \r
9381 VOID PlayIcsWinSound()\r
9382 {\r
9383   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9384 }\r
9385 \r
9386 VOID PlayIcsLossSound()\r
9387 {\r
9388   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9389 }\r
9390 \r
9391 VOID PlayIcsDrawSound()\r
9392 {\r
9393   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9394 }\r
9395 \r
9396 VOID PlayIcsUnfinishedSound()\r
9397 {\r
9398   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9399 }\r
9400 \r
9401 VOID\r
9402 PlayAlarmSound()\r
9403 {\r
9404   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9405 }\r
9406 \r
9407 \r
9408 VOID\r
9409 EchoOn()\r
9410 {\r
9411   HWND hInput;\r
9412   consoleEcho = TRUE;\r
9413   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9414   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9415   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9416 }\r
9417 \r
9418 \r
9419 VOID\r
9420 EchoOff()\r
9421 {\r
9422   CHARFORMAT cf;\r
9423   HWND hInput;\r
9424   consoleEcho = FALSE;\r
9425   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9426   /* This works OK: set text and background both to the same color */\r
9427   cf = consoleCF;\r
9428   cf.crTextColor = COLOR_ECHOOFF;\r
9429   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9430   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9431 }\r
9432 \r
9433 /* No Raw()...? */\r
9434 \r
9435 void Colorize(ColorClass cc, int continuation)\r
9436 {\r
9437   currentColorClass = cc;\r
9438   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9439   consoleCF.crTextColor = textAttribs[cc].color;\r
9440   consoleCF.dwEffects = textAttribs[cc].effects;\r
9441   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9442 }\r
9443 \r
9444 char *\r
9445 UserName()\r
9446 {\r
9447   static char buf[MSG_SIZ];\r
9448   DWORD bufsiz = MSG_SIZ;\r
9449 \r
9450   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9451         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9452   }\r
9453   if (!GetUserName(buf, &bufsiz)) {\r
9454     /*DisplayError("Error getting user name", GetLastError());*/\r
9455     strcpy(buf, "User");\r
9456   }\r
9457   return buf;\r
9458 }\r
9459 \r
9460 char *\r
9461 HostName()\r
9462 {\r
9463   static char buf[MSG_SIZ];\r
9464   DWORD bufsiz = MSG_SIZ;\r
9465 \r
9466   if (!GetComputerName(buf, &bufsiz)) {\r
9467     /*DisplayError("Error getting host name", GetLastError());*/\r
9468     strcpy(buf, "Unknown");\r
9469   }\r
9470   return buf;\r
9471 }\r
9472 \r
9473 \r
9474 int\r
9475 ClockTimerRunning()\r
9476 {\r
9477   return clockTimerEvent != 0;\r
9478 }\r
9479 \r
9480 int\r
9481 StopClockTimer()\r
9482 {\r
9483   if (clockTimerEvent == 0) return FALSE;\r
9484   KillTimer(hwndMain, clockTimerEvent);\r
9485   clockTimerEvent = 0;\r
9486   return TRUE;\r
9487 }\r
9488 \r
9489 void\r
9490 StartClockTimer(long millisec)\r
9491 {\r
9492   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9493                              (UINT) millisec, NULL);\r
9494 }\r
9495 \r
9496 void\r
9497 DisplayWhiteClock(long timeRemaining, int highlight)\r
9498 {\r
9499   HDC hdc;\r
9500   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9501 \r
9502   if(appData.noGUI) return;\r
9503   hdc = GetDC(hwndMain);\r
9504   if (!IsIconic(hwndMain)) {\r
9505     DisplayAClock(hdc, timeRemaining, highlight, \r
9506                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9507   }\r
9508   if (highlight && iconCurrent == iconBlack) {\r
9509     iconCurrent = iconWhite;\r
9510     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9511     if (IsIconic(hwndMain)) {\r
9512       DrawIcon(hdc, 2, 2, iconCurrent);\r
9513     }\r
9514   }\r
9515   (void) ReleaseDC(hwndMain, hdc);\r
9516   if (hwndConsole)\r
9517     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9518 }\r
9519 \r
9520 void\r
9521 DisplayBlackClock(long timeRemaining, int highlight)\r
9522 {\r
9523   HDC hdc;\r
9524   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9525 \r
9526   if(appData.noGUI) return;\r
9527   hdc = GetDC(hwndMain);\r
9528   if (!IsIconic(hwndMain)) {\r
9529     DisplayAClock(hdc, timeRemaining, highlight, \r
9530                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9531   }\r
9532   if (highlight && iconCurrent == iconWhite) {\r
9533     iconCurrent = iconBlack;\r
9534     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9535     if (IsIconic(hwndMain)) {\r
9536       DrawIcon(hdc, 2, 2, iconCurrent);\r
9537     }\r
9538   }\r
9539   (void) ReleaseDC(hwndMain, hdc);\r
9540   if (hwndConsole)\r
9541     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9542 }\r
9543 \r
9544 \r
9545 int\r
9546 LoadGameTimerRunning()\r
9547 {\r
9548   return loadGameTimerEvent != 0;\r
9549 }\r
9550 \r
9551 int\r
9552 StopLoadGameTimer()\r
9553 {\r
9554   if (loadGameTimerEvent == 0) return FALSE;\r
9555   KillTimer(hwndMain, loadGameTimerEvent);\r
9556   loadGameTimerEvent = 0;\r
9557   return TRUE;\r
9558 }\r
9559 \r
9560 void\r
9561 StartLoadGameTimer(long millisec)\r
9562 {\r
9563   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9564                                 (UINT) millisec, NULL);\r
9565 }\r
9566 \r
9567 void\r
9568 AutoSaveGame()\r
9569 {\r
9570   char *defName;\r
9571   FILE *f;\r
9572   char fileTitle[MSG_SIZ];\r
9573 \r
9574   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9575   f = OpenFileDialog(hwndMain, "a", defName,\r
9576                      appData.oldSaveStyle ? "gam" : "pgn",\r
9577                      GAME_FILT, \r
9578                      "Save Game to File", NULL, fileTitle, NULL);\r
9579   if (f != NULL) {\r
9580     SaveGame(f, 0, "");\r
9581     fclose(f);\r
9582   }\r
9583 }\r
9584 \r
9585 \r
9586 void\r
9587 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9588 {\r
9589   if (delayedTimerEvent != 0) {\r
9590     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9591       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9592     }\r
9593     KillTimer(hwndMain, delayedTimerEvent);\r
9594     delayedTimerEvent = 0;\r
9595     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9596     delayedTimerCallback();\r
9597   }\r
9598   delayedTimerCallback = cb;\r
9599   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9600                                 (UINT) millisec, NULL);\r
9601 }\r
9602 \r
9603 DelayedEventCallback\r
9604 GetDelayedEvent()\r
9605 {\r
9606   if (delayedTimerEvent) {\r
9607     return delayedTimerCallback;\r
9608   } else {\r
9609     return NULL;\r
9610   }\r
9611 }\r
9612 \r
9613 void\r
9614 CancelDelayedEvent()\r
9615 {\r
9616   if (delayedTimerEvent) {\r
9617     KillTimer(hwndMain, delayedTimerEvent);\r
9618     delayedTimerEvent = 0;\r
9619   }\r
9620 }\r
9621 \r
9622 DWORD GetWin32Priority(int nice)\r
9623 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9624 /*\r
9625 REALTIME_PRIORITY_CLASS     0x00000100\r
9626 HIGH_PRIORITY_CLASS         0x00000080\r
9627 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9628 NORMAL_PRIORITY_CLASS       0x00000020\r
9629 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9630 IDLE_PRIORITY_CLASS         0x00000040\r
9631 */\r
9632         if (nice < -15) return 0x00000080;\r
9633         if (nice < 0)   return 0x00008000;\r
9634         if (nice == 0)  return 0x00000020;\r
9635         if (nice < 15)  return 0x00004000;\r
9636         return 0x00000040;\r
9637 }\r
9638 \r
9639 /* Start a child process running the given program.\r
9640    The process's standard output can be read from "from", and its\r
9641    standard input can be written to "to".\r
9642    Exit with fatal error if anything goes wrong.\r
9643    Returns an opaque pointer that can be used to destroy the process\r
9644    later.\r
9645 */\r
9646 int\r
9647 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9648 {\r
9649 #define BUFSIZE 4096\r
9650 \r
9651   HANDLE hChildStdinRd, hChildStdinWr,\r
9652     hChildStdoutRd, hChildStdoutWr;\r
9653   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9654   SECURITY_ATTRIBUTES saAttr;\r
9655   BOOL fSuccess;\r
9656   PROCESS_INFORMATION piProcInfo;\r
9657   STARTUPINFO siStartInfo;\r
9658   ChildProc *cp;\r
9659   char buf[MSG_SIZ];\r
9660   DWORD err;\r
9661 \r
9662   if (appData.debugMode) {\r
9663     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9664   }\r
9665 \r
9666   *pr = NoProc;\r
9667 \r
9668   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9669   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9670   saAttr.bInheritHandle = TRUE;\r
9671   saAttr.lpSecurityDescriptor = NULL;\r
9672 \r
9673   /*\r
9674    * The steps for redirecting child's STDOUT:\r
9675    *     1. Create anonymous pipe to be STDOUT for child.\r
9676    *     2. Create a noninheritable duplicate of read handle,\r
9677    *         and close the inheritable read handle.\r
9678    */\r
9679 \r
9680   /* Create a pipe for the child's STDOUT. */\r
9681   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9682     return GetLastError();\r
9683   }\r
9684 \r
9685   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9686   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9687                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9688                              FALSE,     /* not inherited */\r
9689                              DUPLICATE_SAME_ACCESS);\r
9690   if (! fSuccess) {\r
9691     return GetLastError();\r
9692   }\r
9693   CloseHandle(hChildStdoutRd);\r
9694 \r
9695   /*\r
9696    * The steps for redirecting child's STDIN:\r
9697    *     1. Create anonymous pipe to be STDIN for child.\r
9698    *     2. Create a noninheritable duplicate of write handle,\r
9699    *         and close the inheritable write handle.\r
9700    */\r
9701 \r
9702   /* Create a pipe for the child's STDIN. */\r
9703   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9704     return GetLastError();\r
9705   }\r
9706 \r
9707   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9708   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9709                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9710                              FALSE,     /* not inherited */\r
9711                              DUPLICATE_SAME_ACCESS);\r
9712   if (! fSuccess) {\r
9713     return GetLastError();\r
9714   }\r
9715   CloseHandle(hChildStdinWr);\r
9716 \r
9717   /* Arrange to (1) look in dir for the child .exe file, and\r
9718    * (2) have dir be the child's working directory.  Interpret\r
9719    * dir relative to the directory WinBoard loaded from. */\r
9720   GetCurrentDirectory(MSG_SIZ, buf);\r
9721   SetCurrentDirectory(installDir);\r
9722   SetCurrentDirectory(dir);\r
9723 \r
9724   /* Now create the child process. */\r
9725 \r
9726   siStartInfo.cb = sizeof(STARTUPINFO);\r
9727   siStartInfo.lpReserved = NULL;\r
9728   siStartInfo.lpDesktop = NULL;\r
9729   siStartInfo.lpTitle = NULL;\r
9730   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9731   siStartInfo.cbReserved2 = 0;\r
9732   siStartInfo.lpReserved2 = NULL;\r
9733   siStartInfo.hStdInput = hChildStdinRd;\r
9734   siStartInfo.hStdOutput = hChildStdoutWr;\r
9735   siStartInfo.hStdError = hChildStdoutWr;\r
9736 \r
9737   fSuccess = CreateProcess(NULL,\r
9738                            cmdLine,        /* command line */\r
9739                            NULL,           /* process security attributes */\r
9740                            NULL,           /* primary thread security attrs */\r
9741                            TRUE,           /* handles are inherited */\r
9742                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9743                            NULL,           /* use parent's environment */\r
9744                            NULL,\r
9745                            &siStartInfo, /* STARTUPINFO pointer */\r
9746                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9747 \r
9748   err = GetLastError();\r
9749   SetCurrentDirectory(buf); /* return to prev directory */\r
9750   if (! fSuccess) {\r
9751     return err;\r
9752   }\r
9753 \r
9754   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9755     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9756     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9757   }\r
9758 \r
9759   /* Close the handles we don't need in the parent */\r
9760   CloseHandle(piProcInfo.hThread);\r
9761   CloseHandle(hChildStdinRd);\r
9762   CloseHandle(hChildStdoutWr);\r
9763 \r
9764   /* Prepare return value */\r
9765   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9766   cp->kind = CPReal;\r
9767   cp->hProcess = piProcInfo.hProcess;\r
9768   cp->pid = piProcInfo.dwProcessId;\r
9769   cp->hFrom = hChildStdoutRdDup;\r
9770   cp->hTo = hChildStdinWrDup;\r
9771 \r
9772   *pr = (void *) cp;\r
9773 \r
9774   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9775      2000 where engines sometimes don't see the initial command(s)\r
9776      from WinBoard and hang.  I don't understand how that can happen,\r
9777      but the Sleep is harmless, so I've put it in.  Others have also\r
9778      reported what may be the same problem, so hopefully this will fix\r
9779      it for them too.  */\r
9780   Sleep(500);\r
9781 \r
9782   return NO_ERROR;\r
9783 }\r
9784 \r
9785 \r
9786 void\r
9787 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9788 {\r
9789   ChildProc *cp; int result;\r
9790 \r
9791   cp = (ChildProc *) pr;\r
9792   if (cp == NULL) return;\r
9793 \r
9794   switch (cp->kind) {\r
9795   case CPReal:\r
9796     /* TerminateProcess is considered harmful, so... */\r
9797     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9798     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9799     /* The following doesn't work because the chess program\r
9800        doesn't "have the same console" as WinBoard.  Maybe\r
9801        we could arrange for this even though neither WinBoard\r
9802        nor the chess program uses a console for stdio? */\r
9803     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9804 \r
9805     /* [AS] Special termination modes for misbehaving programs... */\r
9806     if( signal == 9 ) { \r
9807         result = TerminateProcess( cp->hProcess, 0 );\r
9808 \r
9809         if ( appData.debugMode) {\r
9810             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9811         }\r
9812     }\r
9813     else if( signal == 10 ) {\r
9814         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9815 \r
9816         if( dw != WAIT_OBJECT_0 ) {\r
9817             result = TerminateProcess( cp->hProcess, 0 );\r
9818 \r
9819             if ( appData.debugMode) {\r
9820                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9821             }\r
9822 \r
9823         }\r
9824     }\r
9825 \r
9826     CloseHandle(cp->hProcess);\r
9827     break;\r
9828 \r
9829   case CPComm:\r
9830     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9831     break;\r
9832 \r
9833   case CPSock:\r
9834     closesocket(cp->sock);\r
9835     WSACleanup();\r
9836     break;\r
9837 \r
9838   case CPRcmd:\r
9839     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9840     closesocket(cp->sock);\r
9841     closesocket(cp->sock2);\r
9842     WSACleanup();\r
9843     break;\r
9844   }\r
9845   free(cp);\r
9846 }\r
9847 \r
9848 void\r
9849 InterruptChildProcess(ProcRef pr)\r
9850 {\r
9851   ChildProc *cp;\r
9852 \r
9853   cp = (ChildProc *) pr;\r
9854   if (cp == NULL) return;\r
9855   switch (cp->kind) {\r
9856   case CPReal:\r
9857     /* The following doesn't work because the chess program\r
9858        doesn't "have the same console" as WinBoard.  Maybe\r
9859        we could arrange for this even though neither WinBoard\r
9860        nor the chess program uses a console for stdio */\r
9861     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9862     break;\r
9863 \r
9864   case CPComm:\r
9865   case CPSock:\r
9866     /* Can't interrupt */\r
9867     break;\r
9868 \r
9869   case CPRcmd:\r
9870     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9871     break;\r
9872   }\r
9873 }\r
9874 \r
9875 \r
9876 int\r
9877 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9878 {\r
9879   char cmdLine[MSG_SIZ];\r
9880 \r
9881   if (port[0] == NULLCHAR) {\r
9882     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9883   } else {\r
9884     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9885   }\r
9886   return StartChildProcess(cmdLine, "", pr);\r
9887 }\r
9888 \r
9889 \r
9890 /* Code to open TCP sockets */\r
9891 \r
9892 int\r
9893 OpenTCP(char *host, char *port, ProcRef *pr)\r
9894 {\r
9895   ChildProc *cp;\r
9896   int err;\r
9897   SOCKET s;\r
9898   struct sockaddr_in sa, mysa;\r
9899   struct hostent FAR *hp;\r
9900   unsigned short uport;\r
9901   WORD wVersionRequested;\r
9902   WSADATA wsaData;\r
9903 \r
9904   /* Initialize socket DLL */\r
9905   wVersionRequested = MAKEWORD(1, 1);\r
9906   err = WSAStartup(wVersionRequested, &wsaData);\r
9907   if (err != 0) return err;\r
9908 \r
9909   /* Make socket */\r
9910   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9911     err = WSAGetLastError();\r
9912     WSACleanup();\r
9913     return err;\r
9914   }\r
9915 \r
9916   /* Bind local address using (mostly) don't-care values.\r
9917    */\r
9918   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9919   mysa.sin_family = AF_INET;\r
9920   mysa.sin_addr.s_addr = INADDR_ANY;\r
9921   uport = (unsigned short) 0;\r
9922   mysa.sin_port = htons(uport);\r
9923   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9924       == SOCKET_ERROR) {\r
9925     err = WSAGetLastError();\r
9926     WSACleanup();\r
9927     return err;\r
9928   }\r
9929 \r
9930   /* Resolve remote host name */\r
9931   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9932   if (!(hp = gethostbyname(host))) {\r
9933     unsigned int b0, b1, b2, b3;\r
9934 \r
9935     err = WSAGetLastError();\r
9936 \r
9937     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9938       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9939       hp->h_addrtype = AF_INET;\r
9940       hp->h_length = 4;\r
9941       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9942       hp->h_addr_list[0] = (char *) malloc(4);\r
9943       hp->h_addr_list[0][0] = (char) b0;\r
9944       hp->h_addr_list[0][1] = (char) b1;\r
9945       hp->h_addr_list[0][2] = (char) b2;\r
9946       hp->h_addr_list[0][3] = (char) b3;\r
9947     } else {\r
9948       WSACleanup();\r
9949       return err;\r
9950     }\r
9951   }\r
9952   sa.sin_family = hp->h_addrtype;\r
9953   uport = (unsigned short) atoi(port);\r
9954   sa.sin_port = htons(uport);\r
9955   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9956 \r
9957   /* Make connection */\r
9958   if (connect(s, (struct sockaddr *) &sa,\r
9959               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9960     err = WSAGetLastError();\r
9961     WSACleanup();\r
9962     return err;\r
9963   }\r
9964 \r
9965   /* Prepare return value */\r
9966   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9967   cp->kind = CPSock;\r
9968   cp->sock = s;\r
9969   *pr = (ProcRef *) cp;\r
9970 \r
9971   return NO_ERROR;\r
9972 }\r
9973 \r
9974 int\r
9975 OpenCommPort(char *name, ProcRef *pr)\r
9976 {\r
9977   HANDLE h;\r
9978   COMMTIMEOUTS ct;\r
9979   ChildProc *cp;\r
9980   char fullname[MSG_SIZ];\r
9981 \r
9982   if (*name != '\\')\r
9983     sprintf(fullname, "\\\\.\\%s", name);\r
9984   else\r
9985     strcpy(fullname, name);\r
9986 \r
9987   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9988                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9989   if (h == (HANDLE) -1) {\r
9990     return GetLastError();\r
9991   }\r
9992   hCommPort = h;\r
9993 \r
9994   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9995 \r
9996   /* Accumulate characters until a 100ms pause, then parse */\r
9997   ct.ReadIntervalTimeout = 100;\r
9998   ct.ReadTotalTimeoutMultiplier = 0;\r
9999   ct.ReadTotalTimeoutConstant = 0;\r
10000   ct.WriteTotalTimeoutMultiplier = 0;\r
10001   ct.WriteTotalTimeoutConstant = 0;\r
10002   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10003 \r
10004   /* Prepare return value */\r
10005   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10006   cp->kind = CPComm;\r
10007   cp->hFrom = h;\r
10008   cp->hTo = h;\r
10009   *pr = (ProcRef *) cp;\r
10010 \r
10011   return NO_ERROR;\r
10012 }\r
10013 \r
10014 int\r
10015 OpenLoopback(ProcRef *pr)\r
10016 {\r
10017   DisplayFatalError("Not implemented", 0, 1);\r
10018   return NO_ERROR;\r
10019 }\r
10020 \r
10021 \r
10022 int\r
10023 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10024 {\r
10025   ChildProc *cp;\r
10026   int err;\r
10027   SOCKET s, s2, s3;\r
10028   struct sockaddr_in sa, mysa;\r
10029   struct hostent FAR *hp;\r
10030   unsigned short uport;\r
10031   WORD wVersionRequested;\r
10032   WSADATA wsaData;\r
10033   int fromPort;\r
10034   char stderrPortStr[MSG_SIZ];\r
10035 \r
10036   /* Initialize socket DLL */\r
10037   wVersionRequested = MAKEWORD(1, 1);\r
10038   err = WSAStartup(wVersionRequested, &wsaData);\r
10039   if (err != 0) return err;\r
10040 \r
10041   /* Resolve remote host name */\r
10042   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10043   if (!(hp = gethostbyname(host))) {\r
10044     unsigned int b0, b1, b2, b3;\r
10045 \r
10046     err = WSAGetLastError();\r
10047 \r
10048     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10049       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10050       hp->h_addrtype = AF_INET;\r
10051       hp->h_length = 4;\r
10052       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10053       hp->h_addr_list[0] = (char *) malloc(4);\r
10054       hp->h_addr_list[0][0] = (char) b0;\r
10055       hp->h_addr_list[0][1] = (char) b1;\r
10056       hp->h_addr_list[0][2] = (char) b2;\r
10057       hp->h_addr_list[0][3] = (char) b3;\r
10058     } else {\r
10059       WSACleanup();\r
10060       return err;\r
10061     }\r
10062   }\r
10063   sa.sin_family = hp->h_addrtype;\r
10064   uport = (unsigned short) 514;\r
10065   sa.sin_port = htons(uport);\r
10066   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10067 \r
10068   /* Bind local socket to unused "privileged" port address\r
10069    */\r
10070   s = INVALID_SOCKET;\r
10071   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10072   mysa.sin_family = AF_INET;\r
10073   mysa.sin_addr.s_addr = INADDR_ANY;\r
10074   for (fromPort = 1023;; fromPort--) {\r
10075     if (fromPort < 0) {\r
10076       WSACleanup();\r
10077       return WSAEADDRINUSE;\r
10078     }\r
10079     if (s == INVALID_SOCKET) {\r
10080       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10081         err = WSAGetLastError();\r
10082         WSACleanup();\r
10083         return err;\r
10084       }\r
10085     }\r
10086     uport = (unsigned short) fromPort;\r
10087     mysa.sin_port = htons(uport);\r
10088     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10089         == SOCKET_ERROR) {\r
10090       err = WSAGetLastError();\r
10091       if (err == WSAEADDRINUSE) continue;\r
10092       WSACleanup();\r
10093       return err;\r
10094     }\r
10095     if (connect(s, (struct sockaddr *) &sa,\r
10096       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10097       err = WSAGetLastError();\r
10098       if (err == WSAEADDRINUSE) {\r
10099         closesocket(s);\r
10100         s = -1;\r
10101         continue;\r
10102       }\r
10103       WSACleanup();\r
10104       return err;\r
10105     }\r
10106     break;\r
10107   }\r
10108 \r
10109   /* Bind stderr local socket to unused "privileged" port address\r
10110    */\r
10111   s2 = INVALID_SOCKET;\r
10112   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10113   mysa.sin_family = AF_INET;\r
10114   mysa.sin_addr.s_addr = INADDR_ANY;\r
10115   for (fromPort = 1023;; fromPort--) {\r
10116     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10117     if (fromPort < 0) {\r
10118       (void) closesocket(s);\r
10119       WSACleanup();\r
10120       return WSAEADDRINUSE;\r
10121     }\r
10122     if (s2 == INVALID_SOCKET) {\r
10123       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10124         err = WSAGetLastError();\r
10125         closesocket(s);\r
10126         WSACleanup();\r
10127         return err;\r
10128       }\r
10129     }\r
10130     uport = (unsigned short) fromPort;\r
10131     mysa.sin_port = htons(uport);\r
10132     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10133         == SOCKET_ERROR) {\r
10134       err = WSAGetLastError();\r
10135       if (err == WSAEADDRINUSE) continue;\r
10136       (void) closesocket(s);\r
10137       WSACleanup();\r
10138       return err;\r
10139     }\r
10140     if (listen(s2, 1) == SOCKET_ERROR) {\r
10141       err = WSAGetLastError();\r
10142       if (err == WSAEADDRINUSE) {\r
10143         closesocket(s2);\r
10144         s2 = INVALID_SOCKET;\r
10145         continue;\r
10146       }\r
10147       (void) closesocket(s);\r
10148       (void) closesocket(s2);\r
10149       WSACleanup();\r
10150       return err;\r
10151     }\r
10152     break;\r
10153   }\r
10154   prevStderrPort = fromPort; // remember port used\r
10155   sprintf(stderrPortStr, "%d", fromPort);\r
10156 \r
10157   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10158     err = WSAGetLastError();\r
10159     (void) closesocket(s);\r
10160     (void) closesocket(s2);\r
10161     WSACleanup();\r
10162     return err;\r
10163   }\r
10164 \r
10165   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10166     err = WSAGetLastError();\r
10167     (void) closesocket(s);\r
10168     (void) closesocket(s2);\r
10169     WSACleanup();\r
10170     return err;\r
10171   }\r
10172   if (*user == NULLCHAR) user = UserName();\r
10173   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10174     err = WSAGetLastError();\r
10175     (void) closesocket(s);\r
10176     (void) closesocket(s2);\r
10177     WSACleanup();\r
10178     return err;\r
10179   }\r
10180   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10181     err = WSAGetLastError();\r
10182     (void) closesocket(s);\r
10183     (void) closesocket(s2);\r
10184     WSACleanup();\r
10185     return err;\r
10186   }\r
10187 \r
10188   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10189     err = WSAGetLastError();\r
10190     (void) closesocket(s);\r
10191     (void) closesocket(s2);\r
10192     WSACleanup();\r
10193     return err;\r
10194   }\r
10195   (void) closesocket(s2);  /* Stop listening */\r
10196 \r
10197   /* Prepare return value */\r
10198   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10199   cp->kind = CPRcmd;\r
10200   cp->sock = s;\r
10201   cp->sock2 = s3;\r
10202   *pr = (ProcRef *) cp;\r
10203 \r
10204   return NO_ERROR;\r
10205 }\r
10206 \r
10207 \r
10208 InputSourceRef\r
10209 AddInputSource(ProcRef pr, int lineByLine,\r
10210                InputCallback func, VOIDSTAR closure)\r
10211 {\r
10212   InputSource *is, *is2 = NULL;\r
10213   ChildProc *cp = (ChildProc *) pr;\r
10214 \r
10215   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10216   is->lineByLine = lineByLine;\r
10217   is->func = func;\r
10218   is->closure = closure;\r
10219   is->second = NULL;\r
10220   is->next = is->buf;\r
10221   if (pr == NoProc) {\r
10222     is->kind = CPReal;\r
10223     consoleInputSource = is;\r
10224   } else {\r
10225     is->kind = cp->kind;\r
10226     /* \r
10227         [AS] Try to avoid a race condition if the thread is given control too early:\r
10228         we create all threads suspended so that the is->hThread variable can be\r
10229         safely assigned, then let the threads start with ResumeThread.\r
10230     */\r
10231     switch (cp->kind) {\r
10232     case CPReal:\r
10233       is->hFile = cp->hFrom;\r
10234       cp->hFrom = NULL; /* now owned by InputThread */\r
10235       is->hThread =\r
10236         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10237                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10238       break;\r
10239 \r
10240     case CPComm:\r
10241       is->hFile = cp->hFrom;\r
10242       cp->hFrom = NULL; /* now owned by InputThread */\r
10243       is->hThread =\r
10244         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10245                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10246       break;\r
10247 \r
10248     case CPSock:\r
10249       is->sock = cp->sock;\r
10250       is->hThread =\r
10251         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10252                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10253       break;\r
10254 \r
10255     case CPRcmd:\r
10256       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10257       *is2 = *is;\r
10258       is->sock = cp->sock;\r
10259       is->second = is2;\r
10260       is2->sock = cp->sock2;\r
10261       is2->second = is2;\r
10262       is->hThread =\r
10263         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10264                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10265       is2->hThread =\r
10266         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10267                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10268       break;\r
10269     }\r
10270 \r
10271     if( is->hThread != NULL ) {\r
10272         ResumeThread( is->hThread );\r
10273     }\r
10274 \r
10275     if( is2 != NULL && is2->hThread != NULL ) {\r
10276         ResumeThread( is2->hThread );\r
10277     }\r
10278   }\r
10279 \r
10280   return (InputSourceRef) is;\r
10281 }\r
10282 \r
10283 void\r
10284 RemoveInputSource(InputSourceRef isr)\r
10285 {\r
10286   InputSource *is;\r
10287 \r
10288   is = (InputSource *) isr;\r
10289   is->hThread = NULL;  /* tell thread to stop */\r
10290   CloseHandle(is->hThread);\r
10291   if (is->second != NULL) {\r
10292     is->second->hThread = NULL;\r
10293     CloseHandle(is->second->hThread);\r
10294   }\r
10295 }\r
10296 \r
10297 int no_wrap(char *message, int count)\r
10298 {\r
10299     ConsoleOutput(message, count, FALSE);\r
10300     return count;\r
10301 }\r
10302 \r
10303 int\r
10304 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10305 {\r
10306   DWORD dOutCount;\r
10307   int outCount = SOCKET_ERROR;\r
10308   ChildProc *cp = (ChildProc *) pr;\r
10309   static OVERLAPPED ovl;\r
10310   static int line = 0;\r
10311 \r
10312   if (pr == NoProc)\r
10313   {\r
10314     if (appData.noJoin || !appData.useInternalWrap)\r
10315       return no_wrap(message, count);\r
10316     else\r
10317     {\r
10318       int width = get_term_width();\r
10319       int len = wrap(NULL, message, count, width, &line);\r
10320       char *msg = malloc(len);\r
10321       int dbgchk;\r
10322 \r
10323       if (!msg)\r
10324         return no_wrap(message, count);\r
10325       else\r
10326       {\r
10327         dbgchk = wrap(msg, message, count, width, &line);\r
10328         if (dbgchk != len && appData.debugMode)\r
10329             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
10330         ConsoleOutput(msg, len, FALSE);\r
10331         free(msg);\r
10332         return len;\r
10333       }\r
10334     }\r
10335   }\r
10336 \r
10337   if (ovl.hEvent == NULL) {\r
10338     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10339   }\r
10340   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10341 \r
10342   switch (cp->kind) {\r
10343   case CPSock:\r
10344   case CPRcmd:\r
10345     outCount = send(cp->sock, message, count, 0);\r
10346     if (outCount == SOCKET_ERROR) {\r
10347       *outError = WSAGetLastError();\r
10348     } else {\r
10349       *outError = NO_ERROR;\r
10350     }\r
10351     break;\r
10352 \r
10353   case CPReal:\r
10354     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10355                   &dOutCount, NULL)) {\r
10356       *outError = NO_ERROR;\r
10357       outCount = (int) dOutCount;\r
10358     } else {\r
10359       *outError = GetLastError();\r
10360     }\r
10361     break;\r
10362 \r
10363   case CPComm:\r
10364     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10365                             &dOutCount, &ovl);\r
10366     if (*outError == NO_ERROR) {\r
10367       outCount = (int) dOutCount;\r
10368     }\r
10369     break;\r
10370   }\r
10371   return outCount;\r
10372 }\r
10373 \r
10374 int\r
10375 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10376                        long msdelay)\r
10377 {\r
10378   /* Ignore delay, not implemented for WinBoard */\r
10379   return OutputToProcess(pr, message, count, outError);\r
10380 }\r
10381 \r
10382 \r
10383 void\r
10384 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10385                         char *buf, int count, int error)\r
10386 {\r
10387   DisplayFatalError("Not implemented", 0, 1);\r
10388 }\r
10389 \r
10390 /* see wgamelist.c for Game List functions */\r
10391 /* see wedittags.c for Edit Tags functions */\r
10392 \r
10393 \r
10394 VOID\r
10395 ICSInitScript()\r
10396 {\r
10397   FILE *f;\r
10398   char buf[MSG_SIZ];\r
10399   char *dummy;\r
10400 \r
10401   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10402     f = fopen(buf, "r");\r
10403     if (f != NULL) {\r
10404       ProcessICSInitScript(f);\r
10405       fclose(f);\r
10406     }\r
10407   }\r
10408 }\r
10409 \r
10410 \r
10411 VOID\r
10412 StartAnalysisClock()\r
10413 {\r
10414   if (analysisTimerEvent) return;\r
10415   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10416                                         (UINT) 2000, NULL);\r
10417 }\r
10418 \r
10419 VOID\r
10420 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10421 {\r
10422   highlightInfo.sq[0].x = fromX;\r
10423   highlightInfo.sq[0].y = fromY;\r
10424   highlightInfo.sq[1].x = toX;\r
10425   highlightInfo.sq[1].y = toY;\r
10426 }\r
10427 \r
10428 VOID\r
10429 ClearHighlights()\r
10430 {\r
10431   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10432     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10433 }\r
10434 \r
10435 VOID\r
10436 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10437 {\r
10438   premoveHighlightInfo.sq[0].x = fromX;\r
10439   premoveHighlightInfo.sq[0].y = fromY;\r
10440   premoveHighlightInfo.sq[1].x = toX;\r
10441   premoveHighlightInfo.sq[1].y = toY;\r
10442 }\r
10443 \r
10444 VOID\r
10445 ClearPremoveHighlights()\r
10446 {\r
10447   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10448     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10449 }\r
10450 \r
10451 VOID\r
10452 ShutDownFrontEnd()\r
10453 {\r
10454   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10455   DeleteClipboardTempFiles();\r
10456 }\r
10457 \r
10458 void\r
10459 BoardToTop()\r
10460 {\r
10461     if (IsIconic(hwndMain))\r
10462       ShowWindow(hwndMain, SW_RESTORE);\r
10463 \r
10464     SetActiveWindow(hwndMain);\r
10465 }\r
10466 \r
10467 /*\r
10468  * Prototypes for animation support routines\r
10469  */\r
10470 static void ScreenSquare(int column, int row, POINT * pt);\r
10471 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10472      POINT frames[], int * nFrames);\r
10473 \r
10474 \r
10475 void\r
10476 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10477 {       // [HGM] atomic: animate blast wave\r
10478         int i;\r
10479 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10480         explodeInfo.fromX = fromX;\r
10481         explodeInfo.fromY = fromY;\r
10482         explodeInfo.toX = toX;\r
10483         explodeInfo.toY = toY;\r
10484         for(i=1; i<nFrames; i++) {\r
10485             explodeInfo.radius = (i*180)/(nFrames-1);\r
10486             DrawPosition(FALSE, NULL);\r
10487             Sleep(appData.animSpeed);\r
10488         }\r
10489         explodeInfo.radius = 0;\r
10490         DrawPosition(TRUE, NULL);\r
10491 }\r
10492 \r
10493 #define kFactor 4\r
10494 \r
10495 void\r
10496 AnimateMove(board, fromX, fromY, toX, toY)\r
10497      Board board;\r
10498      int fromX;\r
10499      int fromY;\r
10500      int toX;\r
10501      int toY;\r
10502 {\r
10503   ChessSquare piece;\r
10504   POINT start, finish, mid;\r
10505   POINT frames[kFactor * 2 + 1];\r
10506   int nFrames, n;\r
10507 \r
10508   if (!appData.animate) return;\r
10509   if (doingSizing) return;\r
10510   if (fromY < 0 || fromX < 0) return;\r
10511   piece = board[fromY][fromX];\r
10512   if (piece >= EmptySquare) return;\r
10513 \r
10514   ScreenSquare(fromX, fromY, &start);\r
10515   ScreenSquare(toX, toY, &finish);\r
10516 \r
10517   /* All pieces except knights move in straight line */\r
10518   if (piece != WhiteKnight && piece != BlackKnight) {\r
10519     mid.x = start.x + (finish.x - start.x) / 2;\r
10520     mid.y = start.y + (finish.y - start.y) / 2;\r
10521   } else {\r
10522     /* Knight: make diagonal movement then straight */\r
10523     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10524        mid.x = start.x + (finish.x - start.x) / 2;\r
10525        mid.y = finish.y;\r
10526      } else {\r
10527        mid.x = finish.x;\r
10528        mid.y = start.y + (finish.y - start.y) / 2;\r
10529      }\r
10530   }\r
10531   \r
10532   /* Don't use as many frames for very short moves */\r
10533   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10534     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10535   else\r
10536     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10537 \r
10538   animInfo.from.x = fromX;\r
10539   animInfo.from.y = fromY;\r
10540   animInfo.to.x = toX;\r
10541   animInfo.to.y = toY;\r
10542   animInfo.lastpos = start;\r
10543   animInfo.piece = piece;\r
10544   for (n = 0; n < nFrames; n++) {\r
10545     animInfo.pos = frames[n];\r
10546     DrawPosition(FALSE, NULL);\r
10547     animInfo.lastpos = animInfo.pos;\r
10548     Sleep(appData.animSpeed);\r
10549   }\r
10550   animInfo.pos = finish;\r
10551   DrawPosition(FALSE, NULL);\r
10552   animInfo.piece = EmptySquare;\r
10553   if(gameInfo.variant == VariantAtomic && \r
10554      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10555         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10556 }\r
10557 \r
10558 /*      Convert board position to corner of screen rect and color       */\r
10559 \r
10560 static void\r
10561 ScreenSquare(column, row, pt)\r
10562      int column; int row; POINT * pt;\r
10563 {\r
10564   if (flipView) {\r
10565     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10566     pt->y = lineGap + row * (squareSize + lineGap);\r
10567   } else {\r
10568     pt->x = lineGap + column * (squareSize + lineGap);\r
10569     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10570   }\r
10571 }\r
10572 \r
10573 /*      Generate a series of frame coords from start->mid->finish.\r
10574         The movement rate doubles until the half way point is\r
10575         reached, then halves back down to the final destination,\r
10576         which gives a nice slow in/out effect. The algorithmn\r
10577         may seem to generate too many intermediates for short\r
10578         moves, but remember that the purpose is to attract the\r
10579         viewers attention to the piece about to be moved and\r
10580         then to where it ends up. Too few frames would be less\r
10581         noticeable.                                             */\r
10582 \r
10583 static void\r
10584 Tween(start, mid, finish, factor, frames, nFrames)\r
10585      POINT * start; POINT * mid;\r
10586      POINT * finish; int factor;\r
10587      POINT frames[]; int * nFrames;\r
10588 {\r
10589   int n, fraction = 1, count = 0;\r
10590 \r
10591   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10592   for (n = 0; n < factor; n++)\r
10593     fraction *= 2;\r
10594   for (n = 0; n < factor; n++) {\r
10595     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10596     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10597     count ++;\r
10598     fraction = fraction / 2;\r
10599   }\r
10600   \r
10601   /* Midpoint */\r
10602   frames[count] = *mid;\r
10603   count ++;\r
10604   \r
10605   /* Slow out, stepping 1/2, then 1/4, ... */\r
10606   fraction = 2;\r
10607   for (n = 0; n < factor; n++) {\r
10608     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10609     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10610     count ++;\r
10611     fraction = fraction * 2;\r
10612   }\r
10613   *nFrames = count;\r
10614 }\r
10615 \r
10616 void\r
10617 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10618 {\r
10619     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10620 \r
10621     EvalGraphSet( first, last, current, pvInfoList );\r
10622 }\r