Some code refactoring and cleanup; one small bug fix
[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_SIZE + 1) * 4];\r
197 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 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_SIZE][BOARD_SIZE];\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_SIZE ||\r
2171      appData.NrRanks > BOARD_SIZE   )\r
2172       DisplayFatalError("Recompile with BOARD_SIZE > 12, 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_SIZE*BOARD_HEIGHT) {\r
4162           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*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           currentMove = 2*n-1;\r
7114           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7115           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7116           EndDialog(hDlg, TRUE);\r
7117           DrawPosition(TRUE, boards[currentMove]);\r
7118           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7119           else DisplayMessage("", "");\r
7120           return TRUE;\r
7121         }\r
7122       }\r
7123       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7124         gameMode != Training) {\r
7125         DisplayMoveError("Displayed move is not current");\r
7126       } else {\r
7127 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7128         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7129           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7130         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7131         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7132           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7133           if (gameMode != Training)\r
7134               forwardMostMove = currentMove;\r
7135           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7136         } else {\r
7137           DisplayMoveError("Could not parse move");\r
7138         }\r
7139       }\r
7140       EndDialog(hDlg, TRUE);\r
7141       return TRUE;\r
7142     case IDCANCEL:\r
7143       EndDialog(hDlg, FALSE);\r
7144       return TRUE;\r
7145     default:\r
7146       break;\r
7147     }\r
7148     break;\r
7149   }\r
7150   return FALSE;\r
7151 }\r
7152 \r
7153 VOID\r
7154 PopUpMoveDialog(char firstchar)\r
7155 {\r
7156     FARPROC lpProc;\r
7157     \r
7158     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7159         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7160         gameMode == AnalyzeMode || gameMode == EditGame || \r
7161         gameMode == EditPosition || gameMode == IcsExamining ||\r
7162         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7163         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7164                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7165                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7166         gameMode == Training) {\r
7167       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7168       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7169         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7170       FreeProcInstance(lpProc);\r
7171     }\r
7172 }\r
7173 \r
7174 /*---------------------------------------------------------------------------*\\r
7175  *\r
7176  * Type-in name dialog functions\r
7177  * \r
7178 \*---------------------------------------------------------------------------*/\r
7179 \r
7180 LRESULT CALLBACK\r
7181 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7182 {\r
7183   char move[MSG_SIZ];\r
7184   HWND hInput;\r
7185 \r
7186   switch (message) {\r
7187   case WM_INITDIALOG:\r
7188     move[0] = (char) lParam;\r
7189     move[1] = NULLCHAR;\r
7190     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7191     hInput = GetDlgItem(hDlg, OPT_Name);\r
7192     SetWindowText(hInput, move);\r
7193     SetFocus(hInput);\r
7194     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7195     return FALSE;\r
7196 \r
7197   case WM_COMMAND:\r
7198     switch (LOWORD(wParam)) {\r
7199     case IDOK:\r
7200       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7201       appData.userName = strdup(move);\r
7202       SetUserLogo();\r
7203 \r
7204       EndDialog(hDlg, TRUE);\r
7205       return TRUE;\r
7206     case IDCANCEL:\r
7207       EndDialog(hDlg, FALSE);\r
7208       return TRUE;\r
7209     default:\r
7210       break;\r
7211     }\r
7212     break;\r
7213   }\r
7214   return FALSE;\r
7215 }\r
7216 \r
7217 VOID\r
7218 PopUpNameDialog(char firstchar)\r
7219 {\r
7220     FARPROC lpProc;\r
7221     \r
7222       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7223       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7224         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7225       FreeProcInstance(lpProc);\r
7226 }\r
7227 \r
7228 /*---------------------------------------------------------------------------*\\r
7229  *\r
7230  *  Error dialogs\r
7231  * \r
7232 \*---------------------------------------------------------------------------*/\r
7233 \r
7234 /* Nonmodal error box */\r
7235 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7236                              WPARAM wParam, LPARAM lParam);\r
7237 \r
7238 VOID\r
7239 ErrorPopUp(char *title, char *content)\r
7240 {\r
7241   FARPROC lpProc;\r
7242   char *p, *q;\r
7243   BOOLEAN modal = hwndMain == NULL;\r
7244 \r
7245   p = content;\r
7246   q = errorMessage;\r
7247   while (*p) {\r
7248     if (*p == '\n') {\r
7249       if (modal) {\r
7250         *q++ = ' ';\r
7251         p++;\r
7252       } else {\r
7253         *q++ = '\r';\r
7254         *q++ = *p++;\r
7255       }\r
7256     } else {\r
7257       *q++ = *p++;\r
7258     }\r
7259   }\r
7260   *q = NULLCHAR;\r
7261   strncpy(errorTitle, title, sizeof(errorTitle));\r
7262   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7263   \r
7264   if (modal) {\r
7265     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7266   } else {\r
7267     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7268     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7269                  hwndMain, (DLGPROC)lpProc);\r
7270     FreeProcInstance(lpProc);\r
7271   }\r
7272 }\r
7273 \r
7274 VOID\r
7275 ErrorPopDown()\r
7276 {\r
7277   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7278   if (errorDialog == NULL) return;\r
7279   DestroyWindow(errorDialog);\r
7280   errorDialog = NULL;\r
7281 }\r
7282 \r
7283 LRESULT CALLBACK\r
7284 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7285 {\r
7286   HANDLE hwndText;\r
7287   RECT rChild;\r
7288 \r
7289   switch (message) {\r
7290   case WM_INITDIALOG:\r
7291     GetWindowRect(hDlg, &rChild);\r
7292 \r
7293     /*\r
7294     SetWindowPos(hDlg, NULL, rChild.left,\r
7295       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7296       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7297     */\r
7298 \r
7299     /* \r
7300         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7301         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7302         and it doesn't work when you resize the dialog.\r
7303         For now, just give it a default position.\r
7304     */\r
7305     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7306 \r
7307     errorDialog = hDlg;\r
7308     SetWindowText(hDlg, errorTitle);\r
7309     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7310     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7311     return FALSE;\r
7312 \r
7313   case WM_COMMAND:\r
7314     switch (LOWORD(wParam)) {\r
7315     case IDOK:\r
7316     case IDCANCEL:\r
7317       if (errorDialog == hDlg) errorDialog = NULL;\r
7318       DestroyWindow(hDlg);\r
7319       return TRUE;\r
7320 \r
7321     default:\r
7322       break;\r
7323     }\r
7324     break;\r
7325   }\r
7326   return FALSE;\r
7327 }\r
7328 \r
7329 #ifdef GOTHIC\r
7330 HWND gothicDialog = NULL;\r
7331 \r
7332 LRESULT CALLBACK\r
7333 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7334 {\r
7335   HANDLE hwndText;\r
7336   RECT rChild;\r
7337   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7338 \r
7339   switch (message) {\r
7340   case WM_INITDIALOG:\r
7341     GetWindowRect(hDlg, &rChild);\r
7342 \r
7343     SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,\r
7344                                                              SWP_NOZORDER);\r
7345 \r
7346     /* \r
7347         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7348         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7349         and it doesn't work when you resize the dialog.\r
7350         For now, just give it a default position.\r
7351     */\r
7352     gothicDialog = hDlg;\r
7353     SetWindowText(hDlg, errorTitle);\r
7354     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7355     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7356     return FALSE;\r
7357 \r
7358   case WM_COMMAND:\r
7359     switch (LOWORD(wParam)) {\r
7360     case IDOK:\r
7361     case IDCANCEL:\r
7362       if (errorDialog == hDlg) errorDialog = NULL;\r
7363       DestroyWindow(hDlg);\r
7364       return TRUE;\r
7365 \r
7366     default:\r
7367       break;\r
7368     }\r
7369     break;\r
7370   }\r
7371   return FALSE;\r
7372 }\r
7373 \r
7374 VOID\r
7375 GothicPopUp(char *title, VariantClass variant)\r
7376 {\r
7377   FARPROC lpProc;\r
7378   static char *lastTitle;\r
7379 \r
7380   strncpy(errorTitle, title, sizeof(errorTitle));\r
7381   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7382 \r
7383   if(lastTitle != title && gothicDialog != NULL) {\r
7384     DestroyWindow(gothicDialog);\r
7385     gothicDialog = NULL;\r
7386   }\r
7387   if(variant != VariantNormal && gothicDialog == NULL) {\r
7388     title = lastTitle;\r
7389     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7390     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7391                  hwndMain, (DLGPROC)lpProc);\r
7392     FreeProcInstance(lpProc);\r
7393   }\r
7394 }\r
7395 #endif\r
7396 \r
7397 /*---------------------------------------------------------------------------*\\r
7398  *\r
7399  *  Ics Interaction console functions\r
7400  *\r
7401 \*---------------------------------------------------------------------------*/\r
7402 \r
7403 #define HISTORY_SIZE 64\r
7404 static char *history[HISTORY_SIZE];\r
7405 int histIn = 0, histP = 0;\r
7406 \r
7407 VOID\r
7408 SaveInHistory(char *cmd)\r
7409 {\r
7410   if (history[histIn] != NULL) {\r
7411     free(history[histIn]);\r
7412     history[histIn] = NULL;\r
7413   }\r
7414   if (*cmd == NULLCHAR) return;\r
7415   history[histIn] = StrSave(cmd);\r
7416   histIn = (histIn + 1) % HISTORY_SIZE;\r
7417   if (history[histIn] != NULL) {\r
7418     free(history[histIn]);\r
7419     history[histIn] = NULL;\r
7420   }\r
7421   histP = histIn;\r
7422 }\r
7423 \r
7424 char *\r
7425 PrevInHistory(char *cmd)\r
7426 {\r
7427   int newhp;\r
7428   if (histP == histIn) {\r
7429     if (history[histIn] != NULL) free(history[histIn]);\r
7430     history[histIn] = StrSave(cmd);\r
7431   }\r
7432   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7433   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7434   histP = newhp;\r
7435   return history[histP];\r
7436 }\r
7437 \r
7438 char *\r
7439 NextInHistory()\r
7440 {\r
7441   if (histP == histIn) return NULL;\r
7442   histP = (histP + 1) % HISTORY_SIZE;\r
7443   return history[histP];\r
7444 }\r
7445 \r
7446 typedef struct {\r
7447   char *item;\r
7448   char *command;\r
7449   BOOLEAN getname;\r
7450   BOOLEAN immediate;\r
7451 } IcsTextMenuEntry;\r
7452 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7453 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7454 \r
7455 void\r
7456 ParseIcsTextMenu(char *icsTextMenuString)\r
7457 {\r
7458 //  int flags = 0;\r
7459   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7460   char *p = icsTextMenuString;\r
7461   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7462     free(e->item);\r
7463     e->item = NULL;\r
7464     if (e->command != NULL) {\r
7465       free(e->command);\r
7466       e->command = NULL;\r
7467     }\r
7468     e++;\r
7469   }\r
7470   e = icsTextMenuEntry;\r
7471   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7472     if (*p == ';' || *p == '\n') {\r
7473       e->item = strdup("-");\r
7474       e->command = NULL;\r
7475       p++;\r
7476     } else if (*p == '-') {\r
7477       e->item = strdup("-");\r
7478       e->command = NULL;\r
7479       p++;\r
7480       if (*p) p++;\r
7481     } else {\r
7482       char *q, *r, *s, *t;\r
7483       char c;\r
7484       q = strchr(p, ',');\r
7485       if (q == NULL) break;\r
7486       *q = NULLCHAR;\r
7487       r = strchr(q + 1, ',');\r
7488       if (r == NULL) break;\r
7489       *r = NULLCHAR;\r
7490       s = strchr(r + 1, ',');\r
7491       if (s == NULL) break;\r
7492       *s = NULLCHAR;\r
7493       c = ';';\r
7494       t = strchr(s + 1, c);\r
7495       if (t == NULL) {\r
7496         c = '\n';\r
7497         t = strchr(s + 1, c);\r
7498       }\r
7499       if (t != NULL) *t = NULLCHAR;\r
7500       e->item = strdup(p);\r
7501       e->command = strdup(q + 1);\r
7502       e->getname = *(r + 1) != '0';\r
7503       e->immediate = *(s + 1) != '0';\r
7504       *q = ',';\r
7505       *r = ',';\r
7506       *s = ',';\r
7507       if (t == NULL) break;\r
7508       *t = c;\r
7509       p = t + 1;\r
7510     }\r
7511     e++;\r
7512   } \r
7513 }\r
7514 \r
7515 HMENU\r
7516 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7517 {\r
7518   HMENU hmenu, h;\r
7519   int i = 0;\r
7520   hmenu = LoadMenu(hInst, "TextMenu");\r
7521   h = GetSubMenu(hmenu, 0);\r
7522   while (e->item) {\r
7523     if (strcmp(e->item, "-") == 0) {\r
7524       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7525     } else {\r
7526       if (e->item[0] == '|') {\r
7527         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7528                    IDM_CommandX + i, &e->item[1]);\r
7529       } else {\r
7530         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7531       }\r
7532     }\r
7533     e++;\r
7534     i++;\r
7535   } \r
7536   return hmenu;\r
7537 }\r
7538 \r
7539 WNDPROC consoleTextWindowProc;\r
7540 \r
7541 void\r
7542 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7543 {\r
7544   char buf[MSG_SIZ], name[MSG_SIZ];\r
7545   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7546   CHARRANGE sel;\r
7547 \r
7548   if (!getname) {\r
7549     SetWindowText(hInput, command);\r
7550     if (immediate) {\r
7551       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7552     } else {\r
7553       sel.cpMin = 999999;\r
7554       sel.cpMax = 999999;\r
7555       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7556       SetFocus(hInput);\r
7557     }\r
7558     return;\r
7559   }    \r
7560   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7561   if (sel.cpMin == sel.cpMax) {\r
7562     /* Expand to surrounding word */\r
7563     TEXTRANGE tr;\r
7564     do {\r
7565       tr.chrg.cpMax = sel.cpMin;\r
7566       tr.chrg.cpMin = --sel.cpMin;\r
7567       if (sel.cpMin < 0) break;\r
7568       tr.lpstrText = name;\r
7569       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7570     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7571     sel.cpMin++;\r
7572 \r
7573     do {\r
7574       tr.chrg.cpMin = sel.cpMax;\r
7575       tr.chrg.cpMax = ++sel.cpMax;\r
7576       tr.lpstrText = name;\r
7577       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7578     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7579     sel.cpMax--;\r
7580 \r
7581     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7582       MessageBeep(MB_ICONEXCLAMATION);\r
7583       return;\r
7584     }\r
7585     tr.chrg = sel;\r
7586     tr.lpstrText = name;\r
7587     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7588   } else {\r
7589     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7590       MessageBeep(MB_ICONEXCLAMATION);\r
7591       return;\r
7592     }\r
7593     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7594   }\r
7595   if (immediate) {\r
7596     sprintf(buf, "%s %s", command, name);\r
7597     SetWindowText(hInput, buf);\r
7598     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7599   } else {\r
7600     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7601     SetWindowText(hInput, buf);\r
7602     sel.cpMin = 999999;\r
7603     sel.cpMax = 999999;\r
7604     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7605     SetFocus(hInput);\r
7606   }\r
7607 }\r
7608 \r
7609 LRESULT CALLBACK \r
7610 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7611 {\r
7612   HWND hInput;\r
7613   CHARRANGE sel;\r
7614 \r
7615   switch (message) {\r
7616   case WM_KEYDOWN:\r
7617     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7618     switch (wParam) {\r
7619     case VK_PRIOR:\r
7620       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7621       return 0;\r
7622     case VK_NEXT:\r
7623       sel.cpMin = 999999;\r
7624       sel.cpMax = 999999;\r
7625       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7626       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7627       return 0;\r
7628     }\r
7629     break;\r
7630   case WM_CHAR:\r
7631    if(wParam != '\022') {\r
7632     if (wParam == '\t') {\r
7633       if (GetKeyState(VK_SHIFT) < 0) {\r
7634         /* shifted */\r
7635         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7636         if (buttonDesc[0].hwnd) {\r
7637           SetFocus(buttonDesc[0].hwnd);\r
7638         } else {\r
7639           SetFocus(hwndMain);\r
7640         }\r
7641       } else {\r
7642         /* unshifted */\r
7643         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7644       }\r
7645     } else {\r
7646       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7647       JAWS_DELETE( SetFocus(hInput); )\r
7648       SendMessage(hInput, message, wParam, lParam);\r
7649     }\r
7650     return 0;\r
7651    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7652   case WM_RBUTTONUP:\r
7653     if (GetKeyState(VK_SHIFT) & ~1) {\r
7654       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7655         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7656     } else {\r
7657       POINT pt;\r
7658       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7659       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7660       if (sel.cpMin == sel.cpMax) {\r
7661         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7662         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7663       }\r
7664       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7665         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7666       }\r
7667       pt.x = LOWORD(lParam);\r
7668       pt.y = HIWORD(lParam);\r
7669       MenuPopup(hwnd, pt, hmenu, -1);\r
7670     }\r
7671     return 0;\r
7672   case WM_PASTE:\r
7673     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7674     SetFocus(hInput);\r
7675     return SendMessage(hInput, message, wParam, lParam);\r
7676   case WM_MBUTTONDOWN:\r
7677     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7678   case WM_RBUTTONDOWN:\r
7679     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7680       /* Move selection here if it was empty */\r
7681       POINT pt;\r
7682       pt.x = LOWORD(lParam);\r
7683       pt.y = HIWORD(lParam);\r
7684       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7685       if (sel.cpMin == sel.cpMax) {\r
7686         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7687         sel.cpMax = sel.cpMin;\r
7688         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7689       }\r
7690       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7691     }\r
7692     return 0;\r
7693   case WM_COMMAND:\r
7694     switch (LOWORD(wParam)) {\r
7695     case IDM_QuickPaste:\r
7696       {\r
7697         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7698         if (sel.cpMin == sel.cpMax) {\r
7699           MessageBeep(MB_ICONEXCLAMATION);\r
7700           return 0;\r
7701         }\r
7702         SendMessage(hwnd, WM_COPY, 0, 0);\r
7703         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7704         SendMessage(hInput, WM_PASTE, 0, 0);\r
7705         SetFocus(hInput);\r
7706         return 0;\r
7707       }\r
7708     case IDM_Cut:\r
7709       SendMessage(hwnd, WM_CUT, 0, 0);\r
7710       return 0;\r
7711     case IDM_Paste:\r
7712       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7713       return 0;\r
7714     case IDM_Copy:\r
7715       SendMessage(hwnd, WM_COPY, 0, 0);\r
7716       return 0;\r
7717     default:\r
7718       {\r
7719         int i = LOWORD(wParam) - IDM_CommandX;\r
7720         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7721             icsTextMenuEntry[i].command != NULL) {\r
7722           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7723                    icsTextMenuEntry[i].getname,\r
7724                    icsTextMenuEntry[i].immediate);\r
7725           return 0;\r
7726         }\r
7727       }\r
7728       break;\r
7729     }\r
7730     break;\r
7731   }\r
7732   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7733 }\r
7734 \r
7735 WNDPROC consoleInputWindowProc;\r
7736 \r
7737 LRESULT CALLBACK\r
7738 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7739 {\r
7740   char buf[MSG_SIZ];\r
7741   char *p;\r
7742   static BOOL sendNextChar = FALSE;\r
7743   static BOOL quoteNextChar = FALSE;\r
7744   InputSource *is = consoleInputSource;\r
7745   CHARFORMAT cf;\r
7746   CHARRANGE sel;\r
7747 \r
7748   switch (message) {\r
7749   case WM_CHAR:\r
7750     if (!appData.localLineEditing || sendNextChar) {\r
7751       is->buf[0] = (CHAR) wParam;\r
7752       is->count = 1;\r
7753       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7754       sendNextChar = FALSE;\r
7755       return 0;\r
7756     }\r
7757     if (quoteNextChar) {\r
7758       buf[0] = (char) wParam;\r
7759       buf[1] = NULLCHAR;\r
7760       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7761       quoteNextChar = FALSE;\r
7762       return 0;\r
7763     }\r
7764     switch (wParam) {\r
7765     case '\r':   /* Enter key */\r
7766       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7767       if (consoleEcho) SaveInHistory(is->buf);\r
7768       is->buf[is->count++] = '\n';\r
7769       is->buf[is->count] = NULLCHAR;\r
7770       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7771       if (consoleEcho) {\r
7772         ConsoleOutput(is->buf, is->count, TRUE);\r
7773       } else if (appData.localLineEditing) {\r
7774         ConsoleOutput("\n", 1, TRUE);\r
7775       }\r
7776       /* fall thru */\r
7777     case '\033': /* Escape key */\r
7778       SetWindowText(hwnd, "");\r
7779       cf.cbSize = sizeof(CHARFORMAT);\r
7780       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7781       if (consoleEcho) {\r
7782         cf.crTextColor = textAttribs[ColorNormal].color;\r
7783       } else {\r
7784         cf.crTextColor = COLOR_ECHOOFF;\r
7785       }\r
7786       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7787       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7788       return 0;\r
7789     case '\t':   /* Tab key */\r
7790       if (GetKeyState(VK_SHIFT) < 0) {\r
7791         /* shifted */\r
7792         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7793       } else {\r
7794         /* unshifted */\r
7795         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7796         if (buttonDesc[0].hwnd) {\r
7797           SetFocus(buttonDesc[0].hwnd);\r
7798         } else {\r
7799           SetFocus(hwndMain);\r
7800         }\r
7801       }\r
7802       return 0;\r
7803     case '\023': /* Ctrl+S */\r
7804       sendNextChar = TRUE;\r
7805       return 0;\r
7806     case '\021': /* Ctrl+Q */\r
7807       quoteNextChar = TRUE;\r
7808       return 0;\r
7809     JAWS_REPLAY\r
7810     default:\r
7811       break;\r
7812     }\r
7813     break;\r
7814   case WM_KEYDOWN:\r
7815     switch (wParam) {\r
7816     case VK_UP:\r
7817       GetWindowText(hwnd, buf, MSG_SIZ);\r
7818       p = PrevInHistory(buf);\r
7819       if (p != NULL) {\r
7820         SetWindowText(hwnd, p);\r
7821         sel.cpMin = 999999;\r
7822         sel.cpMax = 999999;\r
7823         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7824         return 0;\r
7825       }\r
7826       break;\r
7827     case VK_DOWN:\r
7828       p = NextInHistory();\r
7829       if (p != NULL) {\r
7830         SetWindowText(hwnd, p);\r
7831         sel.cpMin = 999999;\r
7832         sel.cpMax = 999999;\r
7833         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7834         return 0;\r
7835       }\r
7836       break;\r
7837     case VK_HOME:\r
7838     case VK_END:\r
7839       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7840       /* fall thru */\r
7841     case VK_PRIOR:\r
7842     case VK_NEXT:\r
7843       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7844       return 0;\r
7845     }\r
7846     break;\r
7847   case WM_MBUTTONDOWN:\r
7848     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7849       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7850     break;\r
7851   case WM_RBUTTONUP:\r
7852     if (GetKeyState(VK_SHIFT) & ~1) {\r
7853       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7854         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7855     } else {\r
7856       POINT pt;\r
7857       HMENU hmenu;\r
7858       hmenu = LoadMenu(hInst, "InputMenu");\r
7859       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7860       if (sel.cpMin == sel.cpMax) {\r
7861         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7862         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7863       }\r
7864       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7865         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7866       }\r
7867       pt.x = LOWORD(lParam);\r
7868       pt.y = HIWORD(lParam);\r
7869       MenuPopup(hwnd, pt, hmenu, -1);\r
7870     }\r
7871     return 0;\r
7872   case WM_COMMAND:\r
7873     switch (LOWORD(wParam)) { \r
7874     case IDM_Undo:\r
7875       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7876       return 0;\r
7877     case IDM_SelectAll:\r
7878       sel.cpMin = 0;\r
7879       sel.cpMax = -1; /*999999?*/\r
7880       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7881       return 0;\r
7882     case IDM_Cut:\r
7883       SendMessage(hwnd, WM_CUT, 0, 0);\r
7884       return 0;\r
7885     case IDM_Paste:\r
7886       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7887       return 0;\r
7888     case IDM_Copy:\r
7889       SendMessage(hwnd, WM_COPY, 0, 0);\r
7890       return 0;\r
7891     }\r
7892     break;\r
7893   }\r
7894   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7895 }\r
7896 \r
7897 #define CO_MAX  100000\r
7898 #define CO_TRIM   1000\r
7899 \r
7900 LRESULT CALLBACK\r
7901 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7902 {\r
7903   static SnapData sd;\r
7904   HWND hText, hInput;\r
7905   RECT rect;\r
7906   static int sizeX, sizeY;\r
7907   int newSizeX, newSizeY;\r
7908   MINMAXINFO *mmi;\r
7909   WORD wMask;\r
7910 \r
7911   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7912   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7913 \r
7914   switch (message) {\r
7915   case WM_NOTIFY:\r
7916     if (((NMHDR*)lParam)->code == EN_LINK)\r
7917     {\r
7918       ENLINK *pLink = (ENLINK*)lParam;\r
7919       if (pLink->msg == WM_LBUTTONUP)\r
7920       {\r
7921         TEXTRANGE tr;\r
7922 \r
7923         tr.chrg = pLink->chrg;\r
7924         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7925         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7926         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7927         free(tr.lpstrText);\r
7928       }\r
7929     }\r
7930     break;\r
7931   case WM_INITDIALOG: /* message: initialize dialog box */\r
7932     hwndConsole = hDlg;\r
7933     SetFocus(hInput);\r
7934     consoleTextWindowProc = (WNDPROC)\r
7935       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7936     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7937     consoleInputWindowProc = (WNDPROC)\r
7938       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7939     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7940     Colorize(ColorNormal, TRUE);\r
7941     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7942     ChangedConsoleFont();\r
7943     GetClientRect(hDlg, &rect);\r
7944     sizeX = rect.right;\r
7945     sizeY = rect.bottom;\r
7946     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
7947         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
7948       WINDOWPLACEMENT wp;\r
7949       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7950       wp.length = sizeof(WINDOWPLACEMENT);\r
7951       wp.flags = 0;\r
7952       wp.showCmd = SW_SHOW;\r
7953       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7954       wp.rcNormalPosition.left = wpConsole.x;\r
7955       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7956       wp.rcNormalPosition.top = wpConsole.y;\r
7957       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7958       SetWindowPlacement(hDlg, &wp);\r
7959     }\r
7960 \r
7961    // [HGM] Chessknight's change 2004-07-13\r
7962    else { /* Determine Defaults */\r
7963        WINDOWPLACEMENT wp;\r
7964        wpConsole.x = wpMain.width + 1;\r
7965        wpConsole.y = wpMain.y;\r
7966        wpConsole.width = screenWidth -  wpMain.width;\r
7967        wpConsole.height = wpMain.height;\r
7968        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
7969        wp.length = sizeof(WINDOWPLACEMENT);\r
7970        wp.flags = 0;\r
7971        wp.showCmd = SW_SHOW;\r
7972        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7973        wp.rcNormalPosition.left = wpConsole.x;\r
7974        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
7975        wp.rcNormalPosition.top = wpConsole.y;\r
7976        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
7977        SetWindowPlacement(hDlg, &wp);\r
7978     }\r
7979 \r
7980    // Allow hText to highlight URLs and send notifications on them\r
7981    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
7982    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
7983    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
7984    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
7985 \r
7986     return FALSE;\r
7987 \r
7988   case WM_SETFOCUS:\r
7989     SetFocus(hInput);\r
7990     return 0;\r
7991 \r
7992   case WM_CLOSE:\r
7993     ExitEvent(0);\r
7994     /* not reached */\r
7995     break;\r
7996 \r
7997   case WM_SIZE:\r
7998     if (IsIconic(hDlg)) break;\r
7999     newSizeX = LOWORD(lParam);\r
8000     newSizeY = HIWORD(lParam);\r
8001     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8002       RECT rectText, rectInput;\r
8003       POINT pt;\r
8004       int newTextHeight, newTextWidth;\r
8005       GetWindowRect(hText, &rectText);\r
8006       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8007       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8008       if (newTextHeight < 0) {\r
8009         newSizeY += -newTextHeight;\r
8010         newTextHeight = 0;\r
8011       }\r
8012       SetWindowPos(hText, NULL, 0, 0,\r
8013         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8014       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8015       pt.x = rectInput.left;\r
8016       pt.y = rectInput.top + newSizeY - sizeY;\r
8017       ScreenToClient(hDlg, &pt);\r
8018       SetWindowPos(hInput, NULL, \r
8019         pt.x, pt.y, /* needs client coords */   \r
8020         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8021         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8022     }\r
8023     sizeX = newSizeX;\r
8024     sizeY = newSizeY;\r
8025     break;\r
8026 \r
8027   case WM_GETMINMAXINFO:\r
8028     /* Prevent resizing window too small */\r
8029     mmi = (MINMAXINFO *) lParam;\r
8030     mmi->ptMinTrackSize.x = 100;\r
8031     mmi->ptMinTrackSize.y = 100;\r
8032     break;\r
8033 \r
8034   /* [AS] Snapping */\r
8035   case WM_ENTERSIZEMOVE:\r
8036     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8037 \r
8038   case WM_SIZING:\r
8039     return OnSizing( &sd, hDlg, wParam, lParam );\r
8040 \r
8041   case WM_MOVING:\r
8042     return OnMoving( &sd, hDlg, wParam, lParam );\r
8043 \r
8044   case WM_EXITSIZEMOVE:\r
8045         UpdateICSWidth(hText);\r
8046     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8047   }\r
8048 \r
8049   return DefWindowProc(hDlg, message, wParam, lParam);\r
8050 }\r
8051 \r
8052 \r
8053 VOID\r
8054 ConsoleCreate()\r
8055 {\r
8056   HWND hCons;\r
8057   if (hwndConsole) return;\r
8058   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8059   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8060 }\r
8061 \r
8062 \r
8063 VOID\r
8064 ConsoleOutput(char* data, int length, int forceVisible)\r
8065 {\r
8066   HWND hText;\r
8067   int trim, exlen;\r
8068   char *p, *q;\r
8069   char buf[CO_MAX+1];\r
8070   POINT pEnd;\r
8071   RECT rect;\r
8072   static int delayLF = 0;\r
8073   CHARRANGE savesel, sel;\r
8074 \r
8075   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8076   p = data;\r
8077   q = buf;\r
8078   if (delayLF) {\r
8079     *q++ = '\r';\r
8080     *q++ = '\n';\r
8081     delayLF = 0;\r
8082   }\r
8083   while (length--) {\r
8084     if (*p == '\n') {\r
8085       if (*++p) {\r
8086         *q++ = '\r';\r
8087         *q++ = '\n';\r
8088       } else {\r
8089         delayLF = 1;\r
8090       }\r
8091     } else if (*p == '\007') {\r
8092        MyPlaySound(&sounds[(int)SoundBell]);\r
8093        p++;\r
8094     } else {\r
8095       *q++ = *p++;\r
8096     }\r
8097   }\r
8098   *q = NULLCHAR;\r
8099   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8100   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8101   /* Save current selection */\r
8102   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8103   exlen = GetWindowTextLength(hText);\r
8104   /* Find out whether current end of text is visible */\r
8105   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8106   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8107   /* Trim existing text if it's too long */\r
8108   if (exlen + (q - buf) > CO_MAX) {\r
8109     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8110     sel.cpMin = 0;\r
8111     sel.cpMax = trim;\r
8112     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8113     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8114     exlen -= trim;\r
8115     savesel.cpMin -= trim;\r
8116     savesel.cpMax -= trim;\r
8117     if (exlen < 0) exlen = 0;\r
8118     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8119     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8120   }\r
8121   /* Append the new text */\r
8122   sel.cpMin = exlen;\r
8123   sel.cpMax = exlen;\r
8124   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8125   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8126   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8127   if (forceVisible || exlen == 0 ||\r
8128       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8129        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8130     /* Scroll to make new end of text visible if old end of text\r
8131        was visible or new text is an echo of user typein */\r
8132     sel.cpMin = 9999999;\r
8133     sel.cpMax = 9999999;\r
8134     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8135     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8136     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8137     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8138   }\r
8139   if (savesel.cpMax == exlen || forceVisible) {\r
8140     /* Move insert point to new end of text if it was at the old\r
8141        end of text or if the new text is an echo of user typein */\r
8142     sel.cpMin = 9999999;\r
8143     sel.cpMax = 9999999;\r
8144     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8145   } else {\r
8146     /* Restore previous selection */\r
8147     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8148   }\r
8149   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8150 }\r
8151 \r
8152 /*---------*/\r
8153 \r
8154 \r
8155 void\r
8156 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8157 {\r
8158   char buf[100];\r
8159   char *str;\r
8160   COLORREF oldFg, oldBg;\r
8161   HFONT oldFont;\r
8162   RECT rect;\r
8163 \r
8164   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8165 \r
8166   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8167   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8168   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8169 \r
8170   rect.left = x;\r
8171   rect.right = x + squareSize;\r
8172   rect.top  = y;\r
8173   rect.bottom = y + squareSize;\r
8174   str = buf;\r
8175 \r
8176   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8177                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8178              y, ETO_CLIPPED|ETO_OPAQUE,\r
8179              &rect, str, strlen(str), NULL);\r
8180 \r
8181   (void) SetTextColor(hdc, oldFg);\r
8182   (void) SetBkColor(hdc, oldBg);\r
8183   (void) SelectObject(hdc, oldFont);\r
8184 }\r
8185 \r
8186 void\r
8187 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8188               RECT *rect, char *color, char *flagFell)\r
8189 {\r
8190   char buf[100];\r
8191   char *str;\r
8192   COLORREF oldFg, oldBg;\r
8193   HFONT oldFont;\r
8194 \r
8195   if (appData.clockMode) {\r
8196     if (tinyLayout)\r
8197       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8198     else\r
8199       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8200     str = buf;\r
8201   } else {\r
8202     str = color;\r
8203   }\r
8204 \r
8205   if (highlight) {\r
8206     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8207     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8208   } else {\r
8209     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8210     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8211   }\r
8212   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8213 \r
8214   JAWS_SILENCE\r
8215 \r
8216   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8217              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8218              rect, str, strlen(str), NULL);\r
8219   if(logoHeight > 0 && appData.clockMode) {\r
8220       RECT r;\r
8221       sprintf(buf, "%s %s", buf+7, flagFell);\r
8222       r.top = rect->top + logoHeight/2;\r
8223       r.left = rect->left;\r
8224       r.right = rect->right;\r
8225       r.bottom = rect->bottom;\r
8226       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8227                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8228                  &r, str, strlen(str), NULL);\r
8229   }\r
8230   (void) SetTextColor(hdc, oldFg);\r
8231   (void) SetBkColor(hdc, oldBg);\r
8232   (void) SelectObject(hdc, oldFont);\r
8233 }\r
8234 \r
8235 \r
8236 int\r
8237 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8238            OVERLAPPED *ovl)\r
8239 {\r
8240   int ok, err;\r
8241 \r
8242   /* [AS]  */\r
8243   if( count <= 0 ) {\r
8244     if (appData.debugMode) {\r
8245       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8246     }\r
8247 \r
8248     return ERROR_INVALID_USER_BUFFER;\r
8249   }\r
8250 \r
8251   ResetEvent(ovl->hEvent);\r
8252   ovl->Offset = ovl->OffsetHigh = 0;\r
8253   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8254   if (ok) {\r
8255     err = NO_ERROR;\r
8256   } else {\r
8257     err = GetLastError();\r
8258     if (err == ERROR_IO_PENDING) {\r
8259       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8260       if (ok)\r
8261         err = NO_ERROR;\r
8262       else\r
8263         err = GetLastError();\r
8264     }\r
8265   }\r
8266   return err;\r
8267 }\r
8268 \r
8269 int\r
8270 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8271             OVERLAPPED *ovl)\r
8272 {\r
8273   int ok, err;\r
8274 \r
8275   ResetEvent(ovl->hEvent);\r
8276   ovl->Offset = ovl->OffsetHigh = 0;\r
8277   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8278   if (ok) {\r
8279     err = NO_ERROR;\r
8280   } else {\r
8281     err = GetLastError();\r
8282     if (err == ERROR_IO_PENDING) {\r
8283       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8284       if (ok)\r
8285         err = NO_ERROR;\r
8286       else\r
8287         err = GetLastError();\r
8288     }\r
8289   }\r
8290   return err;\r
8291 }\r
8292 \r
8293 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8294 void CheckForInputBufferFull( InputSource * is )\r
8295 {\r
8296     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8297         /* Look for end of line */\r
8298         char * p = is->buf;\r
8299         \r
8300         while( p < is->next && *p != '\n' ) {\r
8301             p++;\r
8302         }\r
8303 \r
8304         if( p >= is->next ) {\r
8305             if (appData.debugMode) {\r
8306                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8307             }\r
8308 \r
8309             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8310             is->count = (DWORD) -1;\r
8311             is->next = is->buf;\r
8312         }\r
8313     }\r
8314 }\r
8315 \r
8316 DWORD\r
8317 InputThread(LPVOID arg)\r
8318 {\r
8319   InputSource *is;\r
8320   OVERLAPPED ovl;\r
8321 \r
8322   is = (InputSource *) arg;\r
8323   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8324   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8325   while (is->hThread != NULL) {\r
8326     is->error = DoReadFile(is->hFile, is->next,\r
8327                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8328                            &is->count, &ovl);\r
8329     if (is->error == NO_ERROR) {\r
8330       is->next += is->count;\r
8331     } else {\r
8332       if (is->error == ERROR_BROKEN_PIPE) {\r
8333         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8334         is->count = 0;\r
8335       } else {\r
8336         is->count = (DWORD) -1;\r
8337         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8338         break; \r
8339       }\r
8340     }\r
8341 \r
8342     CheckForInputBufferFull( is );\r
8343 \r
8344     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8345 \r
8346     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8347 \r
8348     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8349   }\r
8350 \r
8351   CloseHandle(ovl.hEvent);\r
8352   CloseHandle(is->hFile);\r
8353 \r
8354   if (appData.debugMode) {\r
8355     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8356   }\r
8357 \r
8358   return 0;\r
8359 }\r
8360 \r
8361 \r
8362 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8363 DWORD\r
8364 NonOvlInputThread(LPVOID arg)\r
8365 {\r
8366   InputSource *is;\r
8367   char *p, *q;\r
8368   int i;\r
8369   char prev;\r
8370 \r
8371   is = (InputSource *) arg;\r
8372   while (is->hThread != NULL) {\r
8373     is->error = ReadFile(is->hFile, is->next,\r
8374                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8375                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8376     if (is->error == NO_ERROR) {\r
8377       /* Change CRLF to LF */\r
8378       if (is->next > is->buf) {\r
8379         p = is->next - 1;\r
8380         i = is->count + 1;\r
8381       } else {\r
8382         p = is->next;\r
8383         i = is->count;\r
8384       }\r
8385       q = p;\r
8386       prev = NULLCHAR;\r
8387       while (i > 0) {\r
8388         if (prev == '\r' && *p == '\n') {\r
8389           *(q-1) = '\n';\r
8390           is->count--;\r
8391         } else { \r
8392           *q++ = *p;\r
8393         }\r
8394         prev = *p++;\r
8395         i--;\r
8396       }\r
8397       *q = NULLCHAR;\r
8398       is->next = q;\r
8399     } else {\r
8400       if (is->error == ERROR_BROKEN_PIPE) {\r
8401         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8402         is->count = 0; \r
8403       } else {\r
8404         is->count = (DWORD) -1;\r
8405       }\r
8406     }\r
8407 \r
8408     CheckForInputBufferFull( is );\r
8409 \r
8410     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8411 \r
8412     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8413 \r
8414     if (is->count < 0) break;  /* Quit on error */\r
8415   }\r
8416   CloseHandle(is->hFile);\r
8417   return 0;\r
8418 }\r
8419 \r
8420 DWORD\r
8421 SocketInputThread(LPVOID arg)\r
8422 {\r
8423   InputSource *is;\r
8424 \r
8425   is = (InputSource *) arg;\r
8426   while (is->hThread != NULL) {\r
8427     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8428     if ((int)is->count == SOCKET_ERROR) {\r
8429       is->count = (DWORD) -1;\r
8430       is->error = WSAGetLastError();\r
8431     } else {\r
8432       is->error = NO_ERROR;\r
8433       is->next += is->count;\r
8434       if (is->count == 0 && is->second == is) {\r
8435         /* End of file on stderr; quit with no message */\r
8436         break;\r
8437       }\r
8438     }\r
8439     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8440 \r
8441     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8442 \r
8443     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8444   }\r
8445   return 0;\r
8446 }\r
8447 \r
8448 VOID\r
8449 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8450 {\r
8451   InputSource *is;\r
8452 \r
8453   is = (InputSource *) lParam;\r
8454   if (is->lineByLine) {\r
8455     /* Feed in lines one by one */\r
8456     char *p = is->buf;\r
8457     char *q = p;\r
8458     while (q < is->next) {\r
8459       if (*q++ == '\n') {\r
8460         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8461         p = q;\r
8462       }\r
8463     }\r
8464     \r
8465     /* Move any partial line to the start of the buffer */\r
8466     q = is->buf;\r
8467     while (p < is->next) {\r
8468       *q++ = *p++;\r
8469     }\r
8470     is->next = q;\r
8471 \r
8472     if (is->error != NO_ERROR || is->count == 0) {\r
8473       /* Notify backend of the error.  Note: If there was a partial\r
8474          line at the end, it is not flushed through. */\r
8475       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8476     }\r
8477   } else {\r
8478     /* Feed in the whole chunk of input at once */\r
8479     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8480     is->next = is->buf;\r
8481   }\r
8482 }\r
8483 \r
8484 /*---------------------------------------------------------------------------*\\r
8485  *\r
8486  *  Menu enables. Used when setting various modes.\r
8487  *\r
8488 \*---------------------------------------------------------------------------*/\r
8489 \r
8490 typedef struct {\r
8491   int item;\r
8492   int flags;\r
8493 } Enables;\r
8494 \r
8495 VOID\r
8496 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8497 {\r
8498   while (enab->item > 0) {\r
8499     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8500     enab++;\r
8501   }\r
8502 }\r
8503 \r
8504 Enables gnuEnables[] = {\r
8505   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8506   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8507   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8510   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8511   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8513   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8515   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8516   { -1, -1 }\r
8517 };\r
8518 \r
8519 Enables icsEnables[] = {\r
8520   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8521   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8522   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8523   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8524   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8525   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8526   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8527   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8528   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8529   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8530   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8531   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8532   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8533   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8534   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8535   { -1, -1 }\r
8536 };\r
8537 \r
8538 #ifdef ZIPPY\r
8539 Enables zippyEnables[] = {\r
8540   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8541   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8542   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8543   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8544   { -1, -1 }\r
8545 };\r
8546 #endif\r
8547 \r
8548 Enables ncpEnables[] = {\r
8549   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8550   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8551   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8557   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8558   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8560   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8561   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8568   { -1, -1 }\r
8569 };\r
8570 \r
8571 Enables trainingOnEnables[] = {\r
8572   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8574   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8575   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8576   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8578   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8580   { -1, -1 }\r
8581 };\r
8582 \r
8583 Enables trainingOffEnables[] = {\r
8584   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8585   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8586   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8587   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8588   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8589   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8590   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8591   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8592   { -1, -1 }\r
8593 };\r
8594 \r
8595 /* These modify either ncpEnables or gnuEnables */\r
8596 Enables cmailEnables[] = {\r
8597   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8598   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8599   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8600   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8602   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8604   { -1, -1 }\r
8605 };\r
8606 \r
8607 Enables machineThinkingEnables[] = {\r
8608   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8616   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8617   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8618   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8619   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8620   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8621   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8622   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8623   { -1, -1 }\r
8624 };\r
8625 \r
8626 Enables userThinkingEnables[] = {\r
8627   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8628   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8631   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8632   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8633   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8634   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8635   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8636   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8637   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8638   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8639   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8640   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8641   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8642   { -1, -1 }\r
8643 };\r
8644 \r
8645 /*---------------------------------------------------------------------------*\\r
8646  *\r
8647  *  Front-end interface functions exported by XBoard.\r
8648  *  Functions appear in same order as prototypes in frontend.h.\r
8649  * \r
8650 \*---------------------------------------------------------------------------*/\r
8651 VOID\r
8652 ModeHighlight()\r
8653 {\r
8654   static UINT prevChecked = 0;\r
8655   static int prevPausing = 0;\r
8656   UINT nowChecked;\r
8657 \r
8658   if (pausing != prevPausing) {\r
8659     prevPausing = pausing;\r
8660     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8661                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8662     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8663   }\r
8664 \r
8665   switch (gameMode) {\r
8666   case BeginningOfGame:\r
8667     if (appData.icsActive)\r
8668       nowChecked = IDM_IcsClient;\r
8669     else if (appData.noChessProgram)\r
8670       nowChecked = IDM_EditGame;\r
8671     else\r
8672       nowChecked = IDM_MachineBlack;\r
8673     break;\r
8674   case MachinePlaysBlack:\r
8675     nowChecked = IDM_MachineBlack;\r
8676     break;\r
8677   case MachinePlaysWhite:\r
8678     nowChecked = IDM_MachineWhite;\r
8679     break;\r
8680   case TwoMachinesPlay:\r
8681     nowChecked = IDM_TwoMachines;\r
8682     break;\r
8683   case AnalyzeMode:\r
8684     nowChecked = IDM_AnalysisMode;\r
8685     break;\r
8686   case AnalyzeFile:\r
8687     nowChecked = IDM_AnalyzeFile;\r
8688     break;\r
8689   case EditGame:\r
8690     nowChecked = IDM_EditGame;\r
8691     break;\r
8692   case PlayFromGameFile:\r
8693     nowChecked = IDM_LoadGame;\r
8694     break;\r
8695   case EditPosition:\r
8696     nowChecked = IDM_EditPosition;\r
8697     break;\r
8698   case Training:\r
8699     nowChecked = IDM_Training;\r
8700     break;\r
8701   case IcsPlayingWhite:\r
8702   case IcsPlayingBlack:\r
8703   case IcsObserving:\r
8704   case IcsIdle:\r
8705     nowChecked = IDM_IcsClient;\r
8706     break;\r
8707   default:\r
8708   case EndOfGame:\r
8709     nowChecked = 0;\r
8710     break;\r
8711   }\r
8712   if (prevChecked != 0)\r
8713     (void) CheckMenuItem(GetMenu(hwndMain),\r
8714                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8715   if (nowChecked != 0)\r
8716     (void) CheckMenuItem(GetMenu(hwndMain),\r
8717                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8718 \r
8719   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8720     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8721                           MF_BYCOMMAND|MF_ENABLED);\r
8722   } else {\r
8723     (void) EnableMenuItem(GetMenu(hwndMain), \r
8724                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8725   }\r
8726 \r
8727   prevChecked = nowChecked;\r
8728 \r
8729   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8730   if (appData.icsActive) {\r
8731        if (appData.icsEngineAnalyze) {\r
8732                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8733                        MF_BYCOMMAND|MF_CHECKED);\r
8734        } else {\r
8735                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8736                        MF_BYCOMMAND|MF_UNCHECKED);\r
8737        }\r
8738   }\r
8739 }\r
8740 \r
8741 VOID\r
8742 SetICSMode()\r
8743 {\r
8744   HMENU hmenu = GetMenu(hwndMain);\r
8745   SetMenuEnables(hmenu, icsEnables);\r
8746   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8747     MF_BYPOSITION|MF_ENABLED);\r
8748 #ifdef ZIPPY\r
8749   if (appData.zippyPlay) {\r
8750     SetMenuEnables(hmenu, zippyEnables);\r
8751     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8752          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8753           MF_BYCOMMAND|MF_ENABLED);\r
8754   }\r
8755 #endif\r
8756 }\r
8757 \r
8758 VOID\r
8759 SetGNUMode()\r
8760 {\r
8761   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8762 }\r
8763 \r
8764 VOID\r
8765 SetNCPMode()\r
8766 {\r
8767   HMENU hmenu = GetMenu(hwndMain);\r
8768   SetMenuEnables(hmenu, ncpEnables);\r
8769   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8770     MF_BYPOSITION|MF_GRAYED);\r
8771     DrawMenuBar(hwndMain);\r
8772 }\r
8773 \r
8774 VOID\r
8775 SetCmailMode()\r
8776 {\r
8777   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8778 }\r
8779 \r
8780 VOID \r
8781 SetTrainingModeOn()\r
8782 {\r
8783   int i;\r
8784   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8785   for (i = 0; i < N_BUTTONS; i++) {\r
8786     if (buttonDesc[i].hwnd != NULL)\r
8787       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8788   }\r
8789   CommentPopDown();\r
8790 }\r
8791 \r
8792 VOID SetTrainingModeOff()\r
8793 {\r
8794   int i;\r
8795   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8796   for (i = 0; i < N_BUTTONS; i++) {\r
8797     if (buttonDesc[i].hwnd != NULL)\r
8798       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8799   }\r
8800 }\r
8801 \r
8802 \r
8803 VOID\r
8804 SetUserThinkingEnables()\r
8805 {\r
8806   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8807 }\r
8808 \r
8809 VOID\r
8810 SetMachineThinkingEnables()\r
8811 {\r
8812   HMENU hMenu = GetMenu(hwndMain);\r
8813   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8814 \r
8815   SetMenuEnables(hMenu, machineThinkingEnables);\r
8816 \r
8817   if (gameMode == MachinePlaysBlack) {\r
8818     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8819   } else if (gameMode == MachinePlaysWhite) {\r
8820     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8821   } else if (gameMode == TwoMachinesPlay) {\r
8822     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8823   }\r
8824 }\r
8825 \r
8826 \r
8827 VOID\r
8828 DisplayTitle(char *str)\r
8829 {\r
8830   char title[MSG_SIZ], *host;\r
8831   if (str[0] != NULLCHAR) {\r
8832     strcpy(title, str);\r
8833   } else if (appData.icsActive) {\r
8834     if (appData.icsCommPort[0] != NULLCHAR)\r
8835       host = "ICS";\r
8836     else \r
8837       host = appData.icsHost;\r
8838     sprintf(title, "%s: %s", szTitle, host);\r
8839   } else if (appData.noChessProgram) {\r
8840     strcpy(title, szTitle);\r
8841   } else {\r
8842     strcpy(title, szTitle);\r
8843     strcat(title, ": ");\r
8844     strcat(title, first.tidy);\r
8845   }\r
8846   SetWindowText(hwndMain, title);\r
8847 }\r
8848 \r
8849 \r
8850 VOID\r
8851 DisplayMessage(char *str1, char *str2)\r
8852 {\r
8853   HDC hdc;\r
8854   HFONT oldFont;\r
8855   int remain = MESSAGE_TEXT_MAX - 1;\r
8856   int len;\r
8857 \r
8858   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8859   messageText[0] = NULLCHAR;\r
8860   if (*str1) {\r
8861     len = strlen(str1);\r
8862     if (len > remain) len = remain;\r
8863     strncpy(messageText, str1, len);\r
8864     messageText[len] = NULLCHAR;\r
8865     remain -= len;\r
8866   }\r
8867   if (*str2 && remain >= 2) {\r
8868     if (*str1) {\r
8869       strcat(messageText, "  ");\r
8870       remain -= 2;\r
8871     }\r
8872     len = strlen(str2);\r
8873     if (len > remain) len = remain;\r
8874     strncat(messageText, str2, len);\r
8875   }\r
8876   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8877 \r
8878   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8879 \r
8880   SAYMACHINEMOVE();\r
8881 \r
8882   hdc = GetDC(hwndMain);\r
8883   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8884   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8885              &messageRect, messageText, strlen(messageText), NULL);\r
8886   (void) SelectObject(hdc, oldFont);\r
8887   (void) ReleaseDC(hwndMain, hdc);\r
8888 }\r
8889 \r
8890 VOID\r
8891 DisplayError(char *str, int error)\r
8892 {\r
8893   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8894   int len;\r
8895 \r
8896   if (error == 0) {\r
8897     strcpy(buf, str);\r
8898   } else {\r
8899     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8900                         NULL, error, LANG_NEUTRAL,\r
8901                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8902     if (len > 0) {\r
8903       sprintf(buf, "%s:\n%s", str, buf2);\r
8904     } else {\r
8905       ErrorMap *em = errmap;\r
8906       while (em->err != 0 && em->err != error) em++;\r
8907       if (em->err != 0) {\r
8908         sprintf(buf, "%s:\n%s", str, em->msg);\r
8909       } else {\r
8910         sprintf(buf, "%s:\nError code %d", str, error);\r
8911       }\r
8912     }\r
8913   }\r
8914   \r
8915   ErrorPopUp("Error", buf);\r
8916 }\r
8917 \r
8918 \r
8919 VOID\r
8920 DisplayMoveError(char *str)\r
8921 {\r
8922   fromX = fromY = -1;\r
8923   ClearHighlights();\r
8924   DrawPosition(FALSE, NULL);\r
8925   if (appData.popupMoveErrors) {\r
8926     ErrorPopUp("Error", str);\r
8927   } else {\r
8928     DisplayMessage(str, "");\r
8929     moveErrorMessageUp = TRUE;\r
8930   }\r
8931 }\r
8932 \r
8933 VOID\r
8934 DisplayFatalError(char *str, int error, int exitStatus)\r
8935 {\r
8936   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8937   int len;\r
8938   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8939 \r
8940   if (error != 0) {\r
8941     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8942                         NULL, error, LANG_NEUTRAL,\r
8943                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8944     if (len > 0) {\r
8945       sprintf(buf, "%s:\n%s", str, buf2);\r
8946     } else {\r
8947       ErrorMap *em = errmap;\r
8948       while (em->err != 0 && em->err != error) em++;\r
8949       if (em->err != 0) {\r
8950         sprintf(buf, "%s:\n%s", str, em->msg);\r
8951       } else {\r
8952         sprintf(buf, "%s:\nError code %d", str, error);\r
8953       }\r
8954     }\r
8955     str = buf;\r
8956   }\r
8957   if (appData.debugMode) {\r
8958     fprintf(debugFP, "%s: %s\n", label, str);\r
8959   }\r
8960   if (appData.popupExitMessage) {\r
8961     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8962                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8963   }\r
8964   ExitEvent(exitStatus);\r
8965 }\r
8966 \r
8967 \r
8968 VOID\r
8969 DisplayInformation(char *str)\r
8970 {\r
8971   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8972 }\r
8973 \r
8974 \r
8975 VOID\r
8976 DisplayNote(char *str)\r
8977 {\r
8978   ErrorPopUp("Note", str);\r
8979 }\r
8980 \r
8981 \r
8982 typedef struct {\r
8983   char *title, *question, *replyPrefix;\r
8984   ProcRef pr;\r
8985 } QuestionParams;\r
8986 \r
8987 LRESULT CALLBACK\r
8988 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8989 {\r
8990   static QuestionParams *qp;\r
8991   char reply[MSG_SIZ];\r
8992   int len, err;\r
8993 \r
8994   switch (message) {\r
8995   case WM_INITDIALOG:\r
8996     qp = (QuestionParams *) lParam;\r
8997     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8998     SetWindowText(hDlg, qp->title);\r
8999     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9000     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9001     return FALSE;\r
9002 \r
9003   case WM_COMMAND:\r
9004     switch (LOWORD(wParam)) {\r
9005     case IDOK:\r
9006       strcpy(reply, qp->replyPrefix);\r
9007       if (*reply) strcat(reply, " ");\r
9008       len = strlen(reply);\r
9009       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9010       strcat(reply, "\n");\r
9011       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9012       EndDialog(hDlg, TRUE);\r
9013       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9014       return TRUE;\r
9015     case IDCANCEL:\r
9016       EndDialog(hDlg, FALSE);\r
9017       return TRUE;\r
9018     default:\r
9019       break;\r
9020     }\r
9021     break;\r
9022   }\r
9023   return FALSE;\r
9024 }\r
9025 \r
9026 VOID\r
9027 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9028 {\r
9029     QuestionParams qp;\r
9030     FARPROC lpProc;\r
9031     \r
9032     qp.title = title;\r
9033     qp.question = question;\r
9034     qp.replyPrefix = replyPrefix;\r
9035     qp.pr = pr;\r
9036     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9037     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9038       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9039     FreeProcInstance(lpProc);\r
9040 }\r
9041 \r
9042 /* [AS] Pick FRC position */\r
9043 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9044 {\r
9045     static int * lpIndexFRC;\r
9046     BOOL index_is_ok;\r
9047     char buf[16];\r
9048 \r
9049     switch( message )\r
9050     {\r
9051     case WM_INITDIALOG:\r
9052         lpIndexFRC = (int *) lParam;\r
9053 \r
9054         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9055 \r
9056         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9057         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9058         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9059         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9060 \r
9061         break;\r
9062 \r
9063     case WM_COMMAND:\r
9064         switch( LOWORD(wParam) ) {\r
9065         case IDOK:\r
9066             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9067             EndDialog( hDlg, 0 );\r
9068             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9069             return TRUE;\r
9070         case IDCANCEL:\r
9071             EndDialog( hDlg, 1 );   \r
9072             return TRUE;\r
9073         case IDC_NFG_Edit:\r
9074             if( HIWORD(wParam) == EN_CHANGE ) {\r
9075                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9076 \r
9077                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9078             }\r
9079             return TRUE;\r
9080         case IDC_NFG_Random:\r
9081             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9082             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9083             return TRUE;\r
9084         }\r
9085 \r
9086         break;\r
9087     }\r
9088 \r
9089     return FALSE;\r
9090 }\r
9091 \r
9092 int NewGameFRC()\r
9093 {\r
9094     int result;\r
9095     int index = appData.defaultFrcPosition;\r
9096     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9097 \r
9098     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9099 \r
9100     if( result == 0 ) {\r
9101         appData.defaultFrcPosition = index;\r
9102     }\r
9103 \r
9104     return result;\r
9105 }\r
9106 \r
9107 /* [AS] Game list options */\r
9108 typedef struct {\r
9109     char id;\r
9110     char * name;\r
9111 } GLT_Item;\r
9112 \r
9113 static GLT_Item GLT_ItemInfo[] = {\r
9114     { GLT_EVENT,      "Event" },\r
9115     { GLT_SITE,       "Site" },\r
9116     { GLT_DATE,       "Date" },\r
9117     { GLT_ROUND,      "Round" },\r
9118     { GLT_PLAYERS,    "Players" },\r
9119     { GLT_RESULT,     "Result" },\r
9120     { GLT_WHITE_ELO,  "White Rating" },\r
9121     { GLT_BLACK_ELO,  "Black Rating" },\r
9122     { GLT_TIME_CONTROL,"Time Control" },\r
9123     { GLT_VARIANT,    "Variant" },\r
9124     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9125     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9126     { 0, 0 }\r
9127 };\r
9128 \r
9129 const char * GLT_FindItem( char id )\r
9130 {\r
9131     const char * result = 0;\r
9132 \r
9133     GLT_Item * list = GLT_ItemInfo;\r
9134 \r
9135     while( list->id != 0 ) {\r
9136         if( list->id == id ) {\r
9137             result = list->name;\r
9138             break;\r
9139         }\r
9140 \r
9141         list++;\r
9142     }\r
9143 \r
9144     return result;\r
9145 }\r
9146 \r
9147 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9148 {\r
9149     const char * name = GLT_FindItem( id );\r
9150 \r
9151     if( name != 0 ) {\r
9152         if( index >= 0 ) {\r
9153             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9154         }\r
9155         else {\r
9156             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9157         }\r
9158     }\r
9159 }\r
9160 \r
9161 void GLT_TagsToList( HWND hDlg, char * tags )\r
9162 {\r
9163     char * pc = tags;\r
9164 \r
9165     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9166 \r
9167     while( *pc ) {\r
9168         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9169         pc++;\r
9170     }\r
9171 \r
9172     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9173 \r
9174     pc = GLT_ALL_TAGS;\r
9175 \r
9176     while( *pc ) {\r
9177         if( strchr( tags, *pc ) == 0 ) {\r
9178             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9179         }\r
9180         pc++;\r
9181     }\r
9182 \r
9183     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9184 }\r
9185 \r
9186 char GLT_ListItemToTag( HWND hDlg, int index )\r
9187 {\r
9188     char result = '\0';\r
9189     char name[128];\r
9190 \r
9191     GLT_Item * list = GLT_ItemInfo;\r
9192 \r
9193     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9194         while( list->id != 0 ) {\r
9195             if( strcmp( list->name, name ) == 0 ) {\r
9196                 result = list->id;\r
9197                 break;\r
9198             }\r
9199 \r
9200             list++;\r
9201         }\r
9202     }\r
9203 \r
9204     return result;\r
9205 }\r
9206 \r
9207 void GLT_MoveSelection( HWND hDlg, int delta )\r
9208 {\r
9209     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9210     int idx2 = idx1 + delta;\r
9211     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9212 \r
9213     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9214         char buf[128];\r
9215 \r
9216         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9217         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9218         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9219         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9220     }\r
9221 }\r
9222 \r
9223 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9224 {\r
9225     static char glt[64];\r
9226     static char * lpUserGLT;\r
9227 \r
9228     switch( message )\r
9229     {\r
9230     case WM_INITDIALOG:\r
9231         lpUserGLT = (char *) lParam;\r
9232         \r
9233         strcpy( glt, lpUserGLT );\r
9234 \r
9235         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9236 \r
9237         /* Initialize list */\r
9238         GLT_TagsToList( hDlg, glt );\r
9239 \r
9240         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9241 \r
9242         break;\r
9243 \r
9244     case WM_COMMAND:\r
9245         switch( LOWORD(wParam) ) {\r
9246         case IDOK:\r
9247             {\r
9248                 char * pc = lpUserGLT;\r
9249                 int idx = 0;\r
9250 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9251                 char id;\r
9252 \r
9253                 do {\r
9254                     id = GLT_ListItemToTag( hDlg, idx );\r
9255 \r
9256                     *pc++ = id;\r
9257                     idx++;\r
9258                 } while( id != '\0' );\r
9259             }\r
9260             EndDialog( hDlg, 0 );\r
9261             return TRUE;\r
9262         case IDCANCEL:\r
9263             EndDialog( hDlg, 1 );\r
9264             return TRUE;\r
9265 \r
9266         case IDC_GLT_Default:\r
9267             strcpy( glt, GLT_DEFAULT_TAGS );\r
9268             GLT_TagsToList( hDlg, glt );\r
9269             return TRUE;\r
9270 \r
9271         case IDC_GLT_Restore:\r
9272             strcpy( glt, lpUserGLT );\r
9273             GLT_TagsToList( hDlg, glt );\r
9274             return TRUE;\r
9275 \r
9276         case IDC_GLT_Up:\r
9277             GLT_MoveSelection( hDlg, -1 );\r
9278             return TRUE;\r
9279 \r
9280         case IDC_GLT_Down:\r
9281             GLT_MoveSelection( hDlg, +1 );\r
9282             return TRUE;\r
9283         }\r
9284 \r
9285         break;\r
9286     }\r
9287 \r
9288     return FALSE;\r
9289 }\r
9290 \r
9291 int GameListOptions()\r
9292 {\r
9293     char glt[64];\r
9294     int result;\r
9295     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9296 \r
9297     strcpy( glt, appData.gameListTags );\r
9298 \r
9299     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9300 \r
9301     if( result == 0 ) {\r
9302         /* [AS] Memory leak here! */\r
9303         appData.gameListTags = strdup( glt ); \r
9304     }\r
9305 \r
9306     return result;\r
9307 }\r
9308 \r
9309 \r
9310 VOID\r
9311 DisplayIcsInteractionTitle(char *str)\r
9312 {\r
9313   char consoleTitle[MSG_SIZ];\r
9314 \r
9315   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9316   SetWindowText(hwndConsole, consoleTitle);\r
9317 }\r
9318 \r
9319 void\r
9320 DrawPosition(int fullRedraw, Board board)\r
9321 {\r
9322   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9323 }\r
9324 \r
9325 void NotifyFrontendLogin()\r
9326 {\r
9327         if (hwndConsole)\r
9328                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9329 }\r
9330 \r
9331 VOID\r
9332 ResetFrontEnd()\r
9333 {\r
9334   fromX = fromY = -1;\r
9335   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9336     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9337     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9338     dragInfo.lastpos = dragInfo.pos;\r
9339     dragInfo.start.x = dragInfo.start.y = -1;\r
9340     dragInfo.from = dragInfo.start;\r
9341     ReleaseCapture();\r
9342     DrawPosition(TRUE, NULL);\r
9343   }\r
9344 }\r
9345 \r
9346 \r
9347 VOID\r
9348 CommentPopUp(char *title, char *str)\r
9349 {\r
9350   HWND hwnd = GetActiveWindow();\r
9351   EitherCommentPopUp(0, title, str, FALSE);\r
9352   SAY(str);\r
9353   SetActiveWindow(hwnd);\r
9354 }\r
9355 \r
9356 VOID\r
9357 CommentPopDown(void)\r
9358 {\r
9359   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9360   if (commentDialog) {\r
9361     ShowWindow(commentDialog, SW_HIDE);\r
9362   }\r
9363   commentUp = FALSE;\r
9364 }\r
9365 \r
9366 VOID\r
9367 EditCommentPopUp(int index, char *title, char *str)\r
9368 {\r
9369   EitherCommentPopUp(index, title, str, TRUE);\r
9370 }\r
9371 \r
9372 \r
9373 VOID\r
9374 RingBell()\r
9375 {\r
9376   MyPlaySound(&sounds[(int)SoundMove]);\r
9377 }\r
9378 \r
9379 VOID PlayIcsWinSound()\r
9380 {\r
9381   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9382 }\r
9383 \r
9384 VOID PlayIcsLossSound()\r
9385 {\r
9386   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9387 }\r
9388 \r
9389 VOID PlayIcsDrawSound()\r
9390 {\r
9391   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9392 }\r
9393 \r
9394 VOID PlayIcsUnfinishedSound()\r
9395 {\r
9396   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9397 }\r
9398 \r
9399 VOID\r
9400 PlayAlarmSound()\r
9401 {\r
9402   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9403 }\r
9404 \r
9405 \r
9406 VOID\r
9407 EchoOn()\r
9408 {\r
9409   HWND hInput;\r
9410   consoleEcho = TRUE;\r
9411   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9412   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9413   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9414 }\r
9415 \r
9416 \r
9417 VOID\r
9418 EchoOff()\r
9419 {\r
9420   CHARFORMAT cf;\r
9421   HWND hInput;\r
9422   consoleEcho = FALSE;\r
9423   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9424   /* This works OK: set text and background both to the same color */\r
9425   cf = consoleCF;\r
9426   cf.crTextColor = COLOR_ECHOOFF;\r
9427   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9428   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9429 }\r
9430 \r
9431 /* No Raw()...? */\r
9432 \r
9433 void Colorize(ColorClass cc, int continuation)\r
9434 {\r
9435   currentColorClass = cc;\r
9436   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9437   consoleCF.crTextColor = textAttribs[cc].color;\r
9438   consoleCF.dwEffects = textAttribs[cc].effects;\r
9439   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9440 }\r
9441 \r
9442 char *\r
9443 UserName()\r
9444 {\r
9445   static char buf[MSG_SIZ];\r
9446   DWORD bufsiz = MSG_SIZ;\r
9447 \r
9448   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9449         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9450   }\r
9451   if (!GetUserName(buf, &bufsiz)) {\r
9452     /*DisplayError("Error getting user name", GetLastError());*/\r
9453     strcpy(buf, "User");\r
9454   }\r
9455   return buf;\r
9456 }\r
9457 \r
9458 char *\r
9459 HostName()\r
9460 {\r
9461   static char buf[MSG_SIZ];\r
9462   DWORD bufsiz = MSG_SIZ;\r
9463 \r
9464   if (!GetComputerName(buf, &bufsiz)) {\r
9465     /*DisplayError("Error getting host name", GetLastError());*/\r
9466     strcpy(buf, "Unknown");\r
9467   }\r
9468   return buf;\r
9469 }\r
9470 \r
9471 \r
9472 int\r
9473 ClockTimerRunning()\r
9474 {\r
9475   return clockTimerEvent != 0;\r
9476 }\r
9477 \r
9478 int\r
9479 StopClockTimer()\r
9480 {\r
9481   if (clockTimerEvent == 0) return FALSE;\r
9482   KillTimer(hwndMain, clockTimerEvent);\r
9483   clockTimerEvent = 0;\r
9484   return TRUE;\r
9485 }\r
9486 \r
9487 void\r
9488 StartClockTimer(long millisec)\r
9489 {\r
9490   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9491                              (UINT) millisec, NULL);\r
9492 }\r
9493 \r
9494 void\r
9495 DisplayWhiteClock(long timeRemaining, int highlight)\r
9496 {\r
9497   HDC hdc;\r
9498   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9499 \r
9500   if(appData.noGUI) return;\r
9501   hdc = GetDC(hwndMain);\r
9502   if (!IsIconic(hwndMain)) {\r
9503     DisplayAClock(hdc, timeRemaining, highlight, \r
9504                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9505   }\r
9506   if (highlight && iconCurrent == iconBlack) {\r
9507     iconCurrent = iconWhite;\r
9508     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9509     if (IsIconic(hwndMain)) {\r
9510       DrawIcon(hdc, 2, 2, iconCurrent);\r
9511     }\r
9512   }\r
9513   (void) ReleaseDC(hwndMain, hdc);\r
9514   if (hwndConsole)\r
9515     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9516 }\r
9517 \r
9518 void\r
9519 DisplayBlackClock(long timeRemaining, int highlight)\r
9520 {\r
9521   HDC hdc;\r
9522   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9523 \r
9524   if(appData.noGUI) return;\r
9525   hdc = GetDC(hwndMain);\r
9526   if (!IsIconic(hwndMain)) {\r
9527     DisplayAClock(hdc, timeRemaining, highlight, \r
9528                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9529   }\r
9530   if (highlight && iconCurrent == iconWhite) {\r
9531     iconCurrent = iconBlack;\r
9532     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9533     if (IsIconic(hwndMain)) {\r
9534       DrawIcon(hdc, 2, 2, iconCurrent);\r
9535     }\r
9536   }\r
9537   (void) ReleaseDC(hwndMain, hdc);\r
9538   if (hwndConsole)\r
9539     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9540 }\r
9541 \r
9542 \r
9543 int\r
9544 LoadGameTimerRunning()\r
9545 {\r
9546   return loadGameTimerEvent != 0;\r
9547 }\r
9548 \r
9549 int\r
9550 StopLoadGameTimer()\r
9551 {\r
9552   if (loadGameTimerEvent == 0) return FALSE;\r
9553   KillTimer(hwndMain, loadGameTimerEvent);\r
9554   loadGameTimerEvent = 0;\r
9555   return TRUE;\r
9556 }\r
9557 \r
9558 void\r
9559 StartLoadGameTimer(long millisec)\r
9560 {\r
9561   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9562                                 (UINT) millisec, NULL);\r
9563 }\r
9564 \r
9565 void\r
9566 AutoSaveGame()\r
9567 {\r
9568   char *defName;\r
9569   FILE *f;\r
9570   char fileTitle[MSG_SIZ];\r
9571 \r
9572   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9573   f = OpenFileDialog(hwndMain, "a", defName,\r
9574                      appData.oldSaveStyle ? "gam" : "pgn",\r
9575                      GAME_FILT, \r
9576                      "Save Game to File", NULL, fileTitle, NULL);\r
9577   if (f != NULL) {\r
9578     SaveGame(f, 0, "");\r
9579     fclose(f);\r
9580   }\r
9581 }\r
9582 \r
9583 \r
9584 void\r
9585 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9586 {\r
9587   if (delayedTimerEvent != 0) {\r
9588     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9589       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9590     }\r
9591     KillTimer(hwndMain, delayedTimerEvent);\r
9592     delayedTimerEvent = 0;\r
9593     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9594     delayedTimerCallback();\r
9595   }\r
9596   delayedTimerCallback = cb;\r
9597   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9598                                 (UINT) millisec, NULL);\r
9599 }\r
9600 \r
9601 DelayedEventCallback\r
9602 GetDelayedEvent()\r
9603 {\r
9604   if (delayedTimerEvent) {\r
9605     return delayedTimerCallback;\r
9606   } else {\r
9607     return NULL;\r
9608   }\r
9609 }\r
9610 \r
9611 void\r
9612 CancelDelayedEvent()\r
9613 {\r
9614   if (delayedTimerEvent) {\r
9615     KillTimer(hwndMain, delayedTimerEvent);\r
9616     delayedTimerEvent = 0;\r
9617   }\r
9618 }\r
9619 \r
9620 DWORD GetWin32Priority(int nice)\r
9621 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9622 /*\r
9623 REALTIME_PRIORITY_CLASS     0x00000100\r
9624 HIGH_PRIORITY_CLASS         0x00000080\r
9625 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9626 NORMAL_PRIORITY_CLASS       0x00000020\r
9627 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9628 IDLE_PRIORITY_CLASS         0x00000040\r
9629 */\r
9630         if (nice < -15) return 0x00000080;\r
9631         if (nice < 0)   return 0x00008000;\r
9632         if (nice == 0)  return 0x00000020;\r
9633         if (nice < 15)  return 0x00004000;\r
9634         return 0x00000040;\r
9635 }\r
9636 \r
9637 /* Start a child process running the given program.\r
9638    The process's standard output can be read from "from", and its\r
9639    standard input can be written to "to".\r
9640    Exit with fatal error if anything goes wrong.\r
9641    Returns an opaque pointer that can be used to destroy the process\r
9642    later.\r
9643 */\r
9644 int\r
9645 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9646 {\r
9647 #define BUFSIZE 4096\r
9648 \r
9649   HANDLE hChildStdinRd, hChildStdinWr,\r
9650     hChildStdoutRd, hChildStdoutWr;\r
9651   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9652   SECURITY_ATTRIBUTES saAttr;\r
9653   BOOL fSuccess;\r
9654   PROCESS_INFORMATION piProcInfo;\r
9655   STARTUPINFO siStartInfo;\r
9656   ChildProc *cp;\r
9657   char buf[MSG_SIZ];\r
9658   DWORD err;\r
9659 \r
9660   if (appData.debugMode) {\r
9661     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9662   }\r
9663 \r
9664   *pr = NoProc;\r
9665 \r
9666   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9667   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9668   saAttr.bInheritHandle = TRUE;\r
9669   saAttr.lpSecurityDescriptor = NULL;\r
9670 \r
9671   /*\r
9672    * The steps for redirecting child's STDOUT:\r
9673    *     1. Create anonymous pipe to be STDOUT for child.\r
9674    *     2. Create a noninheritable duplicate of read handle,\r
9675    *         and close the inheritable read handle.\r
9676    */\r
9677 \r
9678   /* Create a pipe for the child's STDOUT. */\r
9679   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9680     return GetLastError();\r
9681   }\r
9682 \r
9683   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9684   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9685                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9686                              FALSE,     /* not inherited */\r
9687                              DUPLICATE_SAME_ACCESS);\r
9688   if (! fSuccess) {\r
9689     return GetLastError();\r
9690   }\r
9691   CloseHandle(hChildStdoutRd);\r
9692 \r
9693   /*\r
9694    * The steps for redirecting child's STDIN:\r
9695    *     1. Create anonymous pipe to be STDIN for child.\r
9696    *     2. Create a noninheritable duplicate of write handle,\r
9697    *         and close the inheritable write handle.\r
9698    */\r
9699 \r
9700   /* Create a pipe for the child's STDIN. */\r
9701   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9702     return GetLastError();\r
9703   }\r
9704 \r
9705   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9706   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9707                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9708                              FALSE,     /* not inherited */\r
9709                              DUPLICATE_SAME_ACCESS);\r
9710   if (! fSuccess) {\r
9711     return GetLastError();\r
9712   }\r
9713   CloseHandle(hChildStdinWr);\r
9714 \r
9715   /* Arrange to (1) look in dir for the child .exe file, and\r
9716    * (2) have dir be the child's working directory.  Interpret\r
9717    * dir relative to the directory WinBoard loaded from. */\r
9718   GetCurrentDirectory(MSG_SIZ, buf);\r
9719   SetCurrentDirectory(installDir);\r
9720   SetCurrentDirectory(dir);\r
9721 \r
9722   /* Now create the child process. */\r
9723 \r
9724   siStartInfo.cb = sizeof(STARTUPINFO);\r
9725   siStartInfo.lpReserved = NULL;\r
9726   siStartInfo.lpDesktop = NULL;\r
9727   siStartInfo.lpTitle = NULL;\r
9728   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9729   siStartInfo.cbReserved2 = 0;\r
9730   siStartInfo.lpReserved2 = NULL;\r
9731   siStartInfo.hStdInput = hChildStdinRd;\r
9732   siStartInfo.hStdOutput = hChildStdoutWr;\r
9733   siStartInfo.hStdError = hChildStdoutWr;\r
9734 \r
9735   fSuccess = CreateProcess(NULL,\r
9736                            cmdLine,        /* command line */\r
9737                            NULL,           /* process security attributes */\r
9738                            NULL,           /* primary thread security attrs */\r
9739                            TRUE,           /* handles are inherited */\r
9740                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9741                            NULL,           /* use parent's environment */\r
9742                            NULL,\r
9743                            &siStartInfo, /* STARTUPINFO pointer */\r
9744                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9745 \r
9746   err = GetLastError();\r
9747   SetCurrentDirectory(buf); /* return to prev directory */\r
9748   if (! fSuccess) {\r
9749     return err;\r
9750   }\r
9751 \r
9752   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9753     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9754     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9755   }\r
9756 \r
9757   /* Close the handles we don't need in the parent */\r
9758   CloseHandle(piProcInfo.hThread);\r
9759   CloseHandle(hChildStdinRd);\r
9760   CloseHandle(hChildStdoutWr);\r
9761 \r
9762   /* Prepare return value */\r
9763   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9764   cp->kind = CPReal;\r
9765   cp->hProcess = piProcInfo.hProcess;\r
9766   cp->pid = piProcInfo.dwProcessId;\r
9767   cp->hFrom = hChildStdoutRdDup;\r
9768   cp->hTo = hChildStdinWrDup;\r
9769 \r
9770   *pr = (void *) cp;\r
9771 \r
9772   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9773      2000 where engines sometimes don't see the initial command(s)\r
9774      from WinBoard and hang.  I don't understand how that can happen,\r
9775      but the Sleep is harmless, so I've put it in.  Others have also\r
9776      reported what may be the same problem, so hopefully this will fix\r
9777      it for them too.  */\r
9778   Sleep(500);\r
9779 \r
9780   return NO_ERROR;\r
9781 }\r
9782 \r
9783 \r
9784 void\r
9785 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9786 {\r
9787   ChildProc *cp; int result;\r
9788 \r
9789   cp = (ChildProc *) pr;\r
9790   if (cp == NULL) return;\r
9791 \r
9792   switch (cp->kind) {\r
9793   case CPReal:\r
9794     /* TerminateProcess is considered harmful, so... */\r
9795     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9796     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9797     /* The following doesn't work because the chess program\r
9798        doesn't "have the same console" as WinBoard.  Maybe\r
9799        we could arrange for this even though neither WinBoard\r
9800        nor the chess program uses a console for stdio? */\r
9801     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9802 \r
9803     /* [AS] Special termination modes for misbehaving programs... */\r
9804     if( signal == 9 ) { \r
9805         result = TerminateProcess( cp->hProcess, 0 );\r
9806 \r
9807         if ( appData.debugMode) {\r
9808             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9809         }\r
9810     }\r
9811     else if( signal == 10 ) {\r
9812         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9813 \r
9814         if( dw != WAIT_OBJECT_0 ) {\r
9815             result = TerminateProcess( cp->hProcess, 0 );\r
9816 \r
9817             if ( appData.debugMode) {\r
9818                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9819             }\r
9820 \r
9821         }\r
9822     }\r
9823 \r
9824     CloseHandle(cp->hProcess);\r
9825     break;\r
9826 \r
9827   case CPComm:\r
9828     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9829     break;\r
9830 \r
9831   case CPSock:\r
9832     closesocket(cp->sock);\r
9833     WSACleanup();\r
9834     break;\r
9835 \r
9836   case CPRcmd:\r
9837     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9838     closesocket(cp->sock);\r
9839     closesocket(cp->sock2);\r
9840     WSACleanup();\r
9841     break;\r
9842   }\r
9843   free(cp);\r
9844 }\r
9845 \r
9846 void\r
9847 InterruptChildProcess(ProcRef pr)\r
9848 {\r
9849   ChildProc *cp;\r
9850 \r
9851   cp = (ChildProc *) pr;\r
9852   if (cp == NULL) return;\r
9853   switch (cp->kind) {\r
9854   case CPReal:\r
9855     /* The following doesn't work because the chess program\r
9856        doesn't "have the same console" as WinBoard.  Maybe\r
9857        we could arrange for this even though neither WinBoard\r
9858        nor the chess program uses a console for stdio */\r
9859     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9860     break;\r
9861 \r
9862   case CPComm:\r
9863   case CPSock:\r
9864     /* Can't interrupt */\r
9865     break;\r
9866 \r
9867   case CPRcmd:\r
9868     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9869     break;\r
9870   }\r
9871 }\r
9872 \r
9873 \r
9874 int\r
9875 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9876 {\r
9877   char cmdLine[MSG_SIZ];\r
9878 \r
9879   if (port[0] == NULLCHAR) {\r
9880     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9881   } else {\r
9882     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9883   }\r
9884   return StartChildProcess(cmdLine, "", pr);\r
9885 }\r
9886 \r
9887 \r
9888 /* Code to open TCP sockets */\r
9889 \r
9890 int\r
9891 OpenTCP(char *host, char *port, ProcRef *pr)\r
9892 {\r
9893   ChildProc *cp;\r
9894   int err;\r
9895   SOCKET s;\r
9896   struct sockaddr_in sa, mysa;\r
9897   struct hostent FAR *hp;\r
9898   unsigned short uport;\r
9899   WORD wVersionRequested;\r
9900   WSADATA wsaData;\r
9901 \r
9902   /* Initialize socket DLL */\r
9903   wVersionRequested = MAKEWORD(1, 1);\r
9904   err = WSAStartup(wVersionRequested, &wsaData);\r
9905   if (err != 0) return err;\r
9906 \r
9907   /* Make socket */\r
9908   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9909     err = WSAGetLastError();\r
9910     WSACleanup();\r
9911     return err;\r
9912   }\r
9913 \r
9914   /* Bind local address using (mostly) don't-care values.\r
9915    */\r
9916   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9917   mysa.sin_family = AF_INET;\r
9918   mysa.sin_addr.s_addr = INADDR_ANY;\r
9919   uport = (unsigned short) 0;\r
9920   mysa.sin_port = htons(uport);\r
9921   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9922       == SOCKET_ERROR) {\r
9923     err = WSAGetLastError();\r
9924     WSACleanup();\r
9925     return err;\r
9926   }\r
9927 \r
9928   /* Resolve remote host name */\r
9929   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9930   if (!(hp = gethostbyname(host))) {\r
9931     unsigned int b0, b1, b2, b3;\r
9932 \r
9933     err = WSAGetLastError();\r
9934 \r
9935     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9936       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9937       hp->h_addrtype = AF_INET;\r
9938       hp->h_length = 4;\r
9939       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9940       hp->h_addr_list[0] = (char *) malloc(4);\r
9941       hp->h_addr_list[0][0] = (char) b0;\r
9942       hp->h_addr_list[0][1] = (char) b1;\r
9943       hp->h_addr_list[0][2] = (char) b2;\r
9944       hp->h_addr_list[0][3] = (char) b3;\r
9945     } else {\r
9946       WSACleanup();\r
9947       return err;\r
9948     }\r
9949   }\r
9950   sa.sin_family = hp->h_addrtype;\r
9951   uport = (unsigned short) atoi(port);\r
9952   sa.sin_port = htons(uport);\r
9953   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9954 \r
9955   /* Make connection */\r
9956   if (connect(s, (struct sockaddr *) &sa,\r
9957               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9958     err = WSAGetLastError();\r
9959     WSACleanup();\r
9960     return err;\r
9961   }\r
9962 \r
9963   /* Prepare return value */\r
9964   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9965   cp->kind = CPSock;\r
9966   cp->sock = s;\r
9967   *pr = (ProcRef *) cp;\r
9968 \r
9969   return NO_ERROR;\r
9970 }\r
9971 \r
9972 int\r
9973 OpenCommPort(char *name, ProcRef *pr)\r
9974 {\r
9975   HANDLE h;\r
9976   COMMTIMEOUTS ct;\r
9977   ChildProc *cp;\r
9978   char fullname[MSG_SIZ];\r
9979 \r
9980   if (*name != '\\')\r
9981     sprintf(fullname, "\\\\.\\%s", name);\r
9982   else\r
9983     strcpy(fullname, name);\r
9984 \r
9985   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9986                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9987   if (h == (HANDLE) -1) {\r
9988     return GetLastError();\r
9989   }\r
9990   hCommPort = h;\r
9991 \r
9992   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9993 \r
9994   /* Accumulate characters until a 100ms pause, then parse */\r
9995   ct.ReadIntervalTimeout = 100;\r
9996   ct.ReadTotalTimeoutMultiplier = 0;\r
9997   ct.ReadTotalTimeoutConstant = 0;\r
9998   ct.WriteTotalTimeoutMultiplier = 0;\r
9999   ct.WriteTotalTimeoutConstant = 0;\r
10000   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10001 \r
10002   /* Prepare return value */\r
10003   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10004   cp->kind = CPComm;\r
10005   cp->hFrom = h;\r
10006   cp->hTo = h;\r
10007   *pr = (ProcRef *) cp;\r
10008 \r
10009   return NO_ERROR;\r
10010 }\r
10011 \r
10012 int\r
10013 OpenLoopback(ProcRef *pr)\r
10014 {\r
10015   DisplayFatalError("Not implemented", 0, 1);\r
10016   return NO_ERROR;\r
10017 }\r
10018 \r
10019 \r
10020 int\r
10021 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10022 {\r
10023   ChildProc *cp;\r
10024   int err;\r
10025   SOCKET s, s2, s3;\r
10026   struct sockaddr_in sa, mysa;\r
10027   struct hostent FAR *hp;\r
10028   unsigned short uport;\r
10029   WORD wVersionRequested;\r
10030   WSADATA wsaData;\r
10031   int fromPort;\r
10032   char stderrPortStr[MSG_SIZ];\r
10033 \r
10034   /* Initialize socket DLL */\r
10035   wVersionRequested = MAKEWORD(1, 1);\r
10036   err = WSAStartup(wVersionRequested, &wsaData);\r
10037   if (err != 0) return err;\r
10038 \r
10039   /* Resolve remote host name */\r
10040   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10041   if (!(hp = gethostbyname(host))) {\r
10042     unsigned int b0, b1, b2, b3;\r
10043 \r
10044     err = WSAGetLastError();\r
10045 \r
10046     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10047       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10048       hp->h_addrtype = AF_INET;\r
10049       hp->h_length = 4;\r
10050       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10051       hp->h_addr_list[0] = (char *) malloc(4);\r
10052       hp->h_addr_list[0][0] = (char) b0;\r
10053       hp->h_addr_list[0][1] = (char) b1;\r
10054       hp->h_addr_list[0][2] = (char) b2;\r
10055       hp->h_addr_list[0][3] = (char) b3;\r
10056     } else {\r
10057       WSACleanup();\r
10058       return err;\r
10059     }\r
10060   }\r
10061   sa.sin_family = hp->h_addrtype;\r
10062   uport = (unsigned short) 514;\r
10063   sa.sin_port = htons(uport);\r
10064   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10065 \r
10066   /* Bind local socket to unused "privileged" port address\r
10067    */\r
10068   s = INVALID_SOCKET;\r
10069   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10070   mysa.sin_family = AF_INET;\r
10071   mysa.sin_addr.s_addr = INADDR_ANY;\r
10072   for (fromPort = 1023;; fromPort--) {\r
10073     if (fromPort < 0) {\r
10074       WSACleanup();\r
10075       return WSAEADDRINUSE;\r
10076     }\r
10077     if (s == INVALID_SOCKET) {\r
10078       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10079         err = WSAGetLastError();\r
10080         WSACleanup();\r
10081         return err;\r
10082       }\r
10083     }\r
10084     uport = (unsigned short) fromPort;\r
10085     mysa.sin_port = htons(uport);\r
10086     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10087         == SOCKET_ERROR) {\r
10088       err = WSAGetLastError();\r
10089       if (err == WSAEADDRINUSE) continue;\r
10090       WSACleanup();\r
10091       return err;\r
10092     }\r
10093     if (connect(s, (struct sockaddr *) &sa,\r
10094       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10095       err = WSAGetLastError();\r
10096       if (err == WSAEADDRINUSE) {\r
10097         closesocket(s);\r
10098         s = -1;\r
10099         continue;\r
10100       }\r
10101       WSACleanup();\r
10102       return err;\r
10103     }\r
10104     break;\r
10105   }\r
10106 \r
10107   /* Bind stderr local socket to unused "privileged" port address\r
10108    */\r
10109   s2 = INVALID_SOCKET;\r
10110   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10111   mysa.sin_family = AF_INET;\r
10112   mysa.sin_addr.s_addr = INADDR_ANY;\r
10113   for (fromPort = 1023;; fromPort--) {\r
10114     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10115     if (fromPort < 0) {\r
10116       (void) closesocket(s);\r
10117       WSACleanup();\r
10118       return WSAEADDRINUSE;\r
10119     }\r
10120     if (s2 == INVALID_SOCKET) {\r
10121       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10122         err = WSAGetLastError();\r
10123         closesocket(s);\r
10124         WSACleanup();\r
10125         return err;\r
10126       }\r
10127     }\r
10128     uport = (unsigned short) fromPort;\r
10129     mysa.sin_port = htons(uport);\r
10130     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10131         == SOCKET_ERROR) {\r
10132       err = WSAGetLastError();\r
10133       if (err == WSAEADDRINUSE) continue;\r
10134       (void) closesocket(s);\r
10135       WSACleanup();\r
10136       return err;\r
10137     }\r
10138     if (listen(s2, 1) == SOCKET_ERROR) {\r
10139       err = WSAGetLastError();\r
10140       if (err == WSAEADDRINUSE) {\r
10141         closesocket(s2);\r
10142         s2 = INVALID_SOCKET;\r
10143         continue;\r
10144       }\r
10145       (void) closesocket(s);\r
10146       (void) closesocket(s2);\r
10147       WSACleanup();\r
10148       return err;\r
10149     }\r
10150     break;\r
10151   }\r
10152   prevStderrPort = fromPort; // remember port used\r
10153   sprintf(stderrPortStr, "%d", fromPort);\r
10154 \r
10155   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10156     err = WSAGetLastError();\r
10157     (void) closesocket(s);\r
10158     (void) closesocket(s2);\r
10159     WSACleanup();\r
10160     return err;\r
10161   }\r
10162 \r
10163   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10164     err = WSAGetLastError();\r
10165     (void) closesocket(s);\r
10166     (void) closesocket(s2);\r
10167     WSACleanup();\r
10168     return err;\r
10169   }\r
10170   if (*user == NULLCHAR) user = UserName();\r
10171   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10172     err = WSAGetLastError();\r
10173     (void) closesocket(s);\r
10174     (void) closesocket(s2);\r
10175     WSACleanup();\r
10176     return err;\r
10177   }\r
10178   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10179     err = WSAGetLastError();\r
10180     (void) closesocket(s);\r
10181     (void) closesocket(s2);\r
10182     WSACleanup();\r
10183     return err;\r
10184   }\r
10185 \r
10186   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10187     err = WSAGetLastError();\r
10188     (void) closesocket(s);\r
10189     (void) closesocket(s2);\r
10190     WSACleanup();\r
10191     return err;\r
10192   }\r
10193   (void) closesocket(s2);  /* Stop listening */\r
10194 \r
10195   /* Prepare return value */\r
10196   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10197   cp->kind = CPRcmd;\r
10198   cp->sock = s;\r
10199   cp->sock2 = s3;\r
10200   *pr = (ProcRef *) cp;\r
10201 \r
10202   return NO_ERROR;\r
10203 }\r
10204 \r
10205 \r
10206 InputSourceRef\r
10207 AddInputSource(ProcRef pr, int lineByLine,\r
10208                InputCallback func, VOIDSTAR closure)\r
10209 {\r
10210   InputSource *is, *is2 = NULL;\r
10211   ChildProc *cp = (ChildProc *) pr;\r
10212 \r
10213   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10214   is->lineByLine = lineByLine;\r
10215   is->func = func;\r
10216   is->closure = closure;\r
10217   is->second = NULL;\r
10218   is->next = is->buf;\r
10219   if (pr == NoProc) {\r
10220     is->kind = CPReal;\r
10221     consoleInputSource = is;\r
10222   } else {\r
10223     is->kind = cp->kind;\r
10224     /* \r
10225         [AS] Try to avoid a race condition if the thread is given control too early:\r
10226         we create all threads suspended so that the is->hThread variable can be\r
10227         safely assigned, then let the threads start with ResumeThread.\r
10228     */\r
10229     switch (cp->kind) {\r
10230     case CPReal:\r
10231       is->hFile = cp->hFrom;\r
10232       cp->hFrom = NULL; /* now owned by InputThread */\r
10233       is->hThread =\r
10234         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10235                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10236       break;\r
10237 \r
10238     case CPComm:\r
10239       is->hFile = cp->hFrom;\r
10240       cp->hFrom = NULL; /* now owned by InputThread */\r
10241       is->hThread =\r
10242         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10243                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10244       break;\r
10245 \r
10246     case CPSock:\r
10247       is->sock = cp->sock;\r
10248       is->hThread =\r
10249         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10250                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10251       break;\r
10252 \r
10253     case CPRcmd:\r
10254       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10255       *is2 = *is;\r
10256       is->sock = cp->sock;\r
10257       is->second = is2;\r
10258       is2->sock = cp->sock2;\r
10259       is2->second = is2;\r
10260       is->hThread =\r
10261         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10262                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10263       is2->hThread =\r
10264         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10265                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10266       break;\r
10267     }\r
10268 \r
10269     if( is->hThread != NULL ) {\r
10270         ResumeThread( is->hThread );\r
10271     }\r
10272 \r
10273     if( is2 != NULL && is2->hThread != NULL ) {\r
10274         ResumeThread( is2->hThread );\r
10275     }\r
10276   }\r
10277 \r
10278   return (InputSourceRef) is;\r
10279 }\r
10280 \r
10281 void\r
10282 RemoveInputSource(InputSourceRef isr)\r
10283 {\r
10284   InputSource *is;\r
10285 \r
10286   is = (InputSource *) isr;\r
10287   is->hThread = NULL;  /* tell thread to stop */\r
10288   CloseHandle(is->hThread);\r
10289   if (is->second != NULL) {\r
10290     is->second->hThread = NULL;\r
10291     CloseHandle(is->second->hThread);\r
10292   }\r
10293 }\r
10294 \r
10295 int no_wrap(char *message, int count)\r
10296 {\r
10297     ConsoleOutput(message, count, FALSE);\r
10298     return count;\r
10299 }\r
10300 \r
10301 int\r
10302 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10303 {\r
10304   DWORD dOutCount;\r
10305   int outCount = SOCKET_ERROR;\r
10306   ChildProc *cp = (ChildProc *) pr;\r
10307   static OVERLAPPED ovl;\r
10308   static int line = 0;\r
10309 \r
10310   if (pr == NoProc)\r
10311   {\r
10312     if (appData.noJoin || !appData.useInternalWrap)\r
10313       return no_wrap(message, count);\r
10314     else\r
10315     {\r
10316       int width = get_term_width();\r
10317       int len = wrap(NULL, message, count, width, &line);\r
10318       char *msg = malloc(len);\r
10319       int dbgchk;\r
10320 \r
10321       if (!msg)\r
10322         return no_wrap(message, count);\r
10323       else\r
10324       {\r
10325         dbgchk = wrap(msg, message, count, width, &line);\r
10326         if (dbgchk != len && appData.debugMode)\r
10327             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
10328         ConsoleOutput(msg, len, FALSE);\r
10329         free(msg);\r
10330         return len;\r
10331       }\r
10332     }\r
10333   }\r
10334 \r
10335   if (ovl.hEvent == NULL) {\r
10336     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10337   }\r
10338   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10339 \r
10340   switch (cp->kind) {\r
10341   case CPSock:\r
10342   case CPRcmd:\r
10343     outCount = send(cp->sock, message, count, 0);\r
10344     if (outCount == SOCKET_ERROR) {\r
10345       *outError = WSAGetLastError();\r
10346     } else {\r
10347       *outError = NO_ERROR;\r
10348     }\r
10349     break;\r
10350 \r
10351   case CPReal:\r
10352     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10353                   &dOutCount, NULL)) {\r
10354       *outError = NO_ERROR;\r
10355       outCount = (int) dOutCount;\r
10356     } else {\r
10357       *outError = GetLastError();\r
10358     }\r
10359     break;\r
10360 \r
10361   case CPComm:\r
10362     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10363                             &dOutCount, &ovl);\r
10364     if (*outError == NO_ERROR) {\r
10365       outCount = (int) dOutCount;\r
10366     }\r
10367     break;\r
10368   }\r
10369   return outCount;\r
10370 }\r
10371 \r
10372 int\r
10373 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10374                        long msdelay)\r
10375 {\r
10376   /* Ignore delay, not implemented for WinBoard */\r
10377   return OutputToProcess(pr, message, count, outError);\r
10378 }\r
10379 \r
10380 \r
10381 void\r
10382 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10383                         char *buf, int count, int error)\r
10384 {\r
10385   DisplayFatalError("Not implemented", 0, 1);\r
10386 }\r
10387 \r
10388 /* see wgamelist.c for Game List functions */\r
10389 /* see wedittags.c for Edit Tags functions */\r
10390 \r
10391 \r
10392 VOID\r
10393 ICSInitScript()\r
10394 {\r
10395   FILE *f;\r
10396   char buf[MSG_SIZ];\r
10397   char *dummy;\r
10398 \r
10399   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10400     f = fopen(buf, "r");\r
10401     if (f != NULL) {\r
10402       ProcessICSInitScript(f);\r
10403       fclose(f);\r
10404     }\r
10405   }\r
10406 }\r
10407 \r
10408 \r
10409 VOID\r
10410 StartAnalysisClock()\r
10411 {\r
10412   if (analysisTimerEvent) return;\r
10413   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10414                                         (UINT) 2000, NULL);\r
10415 }\r
10416 \r
10417 VOID\r
10418 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10419 {\r
10420   highlightInfo.sq[0].x = fromX;\r
10421   highlightInfo.sq[0].y = fromY;\r
10422   highlightInfo.sq[1].x = toX;\r
10423   highlightInfo.sq[1].y = toY;\r
10424 }\r
10425 \r
10426 VOID\r
10427 ClearHighlights()\r
10428 {\r
10429   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10430     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10431 }\r
10432 \r
10433 VOID\r
10434 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10435 {\r
10436   premoveHighlightInfo.sq[0].x = fromX;\r
10437   premoveHighlightInfo.sq[0].y = fromY;\r
10438   premoveHighlightInfo.sq[1].x = toX;\r
10439   premoveHighlightInfo.sq[1].y = toY;\r
10440 }\r
10441 \r
10442 VOID\r
10443 ClearPremoveHighlights()\r
10444 {\r
10445   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10446     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10447 }\r
10448 \r
10449 VOID\r
10450 ShutDownFrontEnd()\r
10451 {\r
10452   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10453   DeleteClipboardTempFiles();\r
10454 }\r
10455 \r
10456 void\r
10457 BoardToTop()\r
10458 {\r
10459     if (IsIconic(hwndMain))\r
10460       ShowWindow(hwndMain, SW_RESTORE);\r
10461 \r
10462     SetActiveWindow(hwndMain);\r
10463 }\r
10464 \r
10465 /*\r
10466  * Prototypes for animation support routines\r
10467  */\r
10468 static void ScreenSquare(int column, int row, POINT * pt);\r
10469 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10470      POINT frames[], int * nFrames);\r
10471 \r
10472 \r
10473 void\r
10474 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10475 {       // [HGM] atomic: animate blast wave\r
10476         int i;\r
10477 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10478         explodeInfo.fromX = fromX;\r
10479         explodeInfo.fromY = fromY;\r
10480         explodeInfo.toX = toX;\r
10481         explodeInfo.toY = toY;\r
10482         for(i=1; i<nFrames; i++) {\r
10483             explodeInfo.radius = (i*180)/(nFrames-1);\r
10484             DrawPosition(FALSE, NULL);\r
10485             Sleep(appData.animSpeed);\r
10486         }\r
10487         explodeInfo.radius = 0;\r
10488         DrawPosition(TRUE, NULL);\r
10489 }\r
10490 \r
10491 #define kFactor 4\r
10492 \r
10493 void\r
10494 AnimateMove(board, fromX, fromY, toX, toY)\r
10495      Board board;\r
10496      int fromX;\r
10497      int fromY;\r
10498      int toX;\r
10499      int toY;\r
10500 {\r
10501   ChessSquare piece;\r
10502   POINT start, finish, mid;\r
10503   POINT frames[kFactor * 2 + 1];\r
10504   int nFrames, n;\r
10505 \r
10506   if (!appData.animate) return;\r
10507   if (doingSizing) return;\r
10508   if (fromY < 0 || fromX < 0) return;\r
10509   piece = board[fromY][fromX];\r
10510   if (piece >= EmptySquare) return;\r
10511 \r
10512   ScreenSquare(fromX, fromY, &start);\r
10513   ScreenSquare(toX, toY, &finish);\r
10514 \r
10515   /* All pieces except knights move in straight line */\r
10516   if (piece != WhiteKnight && piece != BlackKnight) {\r
10517     mid.x = start.x + (finish.x - start.x) / 2;\r
10518     mid.y = start.y + (finish.y - start.y) / 2;\r
10519   } else {\r
10520     /* Knight: make diagonal movement then straight */\r
10521     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10522        mid.x = start.x + (finish.x - start.x) / 2;\r
10523        mid.y = finish.y;\r
10524      } else {\r
10525        mid.x = finish.x;\r
10526        mid.y = start.y + (finish.y - start.y) / 2;\r
10527      }\r
10528   }\r
10529   \r
10530   /* Don't use as many frames for very short moves */\r
10531   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10532     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10533   else\r
10534     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10535 \r
10536   animInfo.from.x = fromX;\r
10537   animInfo.from.y = fromY;\r
10538   animInfo.to.x = toX;\r
10539   animInfo.to.y = toY;\r
10540   animInfo.lastpos = start;\r
10541   animInfo.piece = piece;\r
10542   for (n = 0; n < nFrames; n++) {\r
10543     animInfo.pos = frames[n];\r
10544     DrawPosition(FALSE, NULL);\r
10545     animInfo.lastpos = animInfo.pos;\r
10546     Sleep(appData.animSpeed);\r
10547   }\r
10548   animInfo.pos = finish;\r
10549   DrawPosition(FALSE, NULL);\r
10550   animInfo.piece = EmptySquare;\r
10551   if(gameInfo.variant == VariantAtomic && \r
10552      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10553         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10554 }\r
10555 \r
10556 /*      Convert board position to corner of screen rect and color       */\r
10557 \r
10558 static void\r
10559 ScreenSquare(column, row, pt)\r
10560      int column; int row; POINT * pt;\r
10561 {\r
10562   if (flipView) {\r
10563     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10564     pt->y = lineGap + row * (squareSize + lineGap);\r
10565   } else {\r
10566     pt->x = lineGap + column * (squareSize + lineGap);\r
10567     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10568   }\r
10569 }\r
10570 \r
10571 /*      Generate a series of frame coords from start->mid->finish.\r
10572         The movement rate doubles until the half way point is\r
10573         reached, then halves back down to the final destination,\r
10574         which gives a nice slow in/out effect. The algorithmn\r
10575         may seem to generate too many intermediates for short\r
10576         moves, but remember that the purpose is to attract the\r
10577         viewers attention to the piece about to be moved and\r
10578         then to where it ends up. Too few frames would be less\r
10579         noticeable.                                             */\r
10580 \r
10581 static void\r
10582 Tween(start, mid, finish, factor, frames, nFrames)\r
10583      POINT * start; POINT * mid;\r
10584      POINT * finish; int factor;\r
10585      POINT frames[]; int * nFrames;\r
10586 {\r
10587   int n, fraction = 1, count = 0;\r
10588 \r
10589   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10590   for (n = 0; n < factor; n++)\r
10591     fraction *= 2;\r
10592   for (n = 0; n < factor; n++) {\r
10593     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10594     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10595     count ++;\r
10596     fraction = fraction / 2;\r
10597   }\r
10598   \r
10599   /* Midpoint */\r
10600   frames[count] = *mid;\r
10601   count ++;\r
10602   \r
10603   /* Slow out, stepping 1/2, then 1/4, ... */\r
10604   fraction = 2;\r
10605   for (n = 0; n < factor; n++) {\r
10606     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10607     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10608     count ++;\r
10609     fraction = fraction * 2;\r
10610   }\r
10611   *nFrames = count;\r
10612 }\r
10613 \r
10614 void\r
10615 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10616 {\r
10617     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10618 \r
10619     EvalGraphSet( first, last, current, pvInfoList );\r
10620 }\r
10621 \r
10622 void SetProgramStats( FrontEndProgramStats * stats )\r
10623 {\r
10624     EngineOutputUpdate( stats );\r
10625 }\r