adding support for different windows compiler
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 \r
74 #if __GNUC__\r
75 #include <errno.h>\r
76 #include <string.h>\r
77 #endif\r
78 \r
79 #include "common.h"\r
80 #include "winboard.h"\r
81 #include "frontend.h"\r
82 #include "backend.h"\r
83 #include "moves.h"\r
84 #include "wclipbrd.h"\r
85 #include "wgamelist.h"\r
86 #include "wedittags.h"\r
87 #include "woptions.h"\r
88 #include "wsockerr.h"\r
89 #include "defaults.h"\r
90 \r
91 #include "wsnap.h"\r
92 \r
93 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
94 \r
95   int myrandom(void);\r
96   void mysrandom(unsigned int seed);\r
97 \r
98 extern int whiteFlag, blackFlag;\r
99 Boolean flipClock = FALSE;\r
100 \r
101 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
102 \r
103 typedef struct {\r
104   ChessSquare piece;  \r
105   POINT pos;      /* window coordinates of current pos */\r
106   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
107   POINT from;     /* board coordinates of the piece's orig pos */\r
108   POINT to;       /* board coordinates of the piece's new pos */\r
109 } AnimInfo;\r
110 \r
111 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
112 \r
113 typedef struct {\r
114   POINT start;    /* window coordinates of start pos */\r
115   POINT pos;      /* window coordinates of current pos */\r
116   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
117   POINT from;     /* board coordinates of the piece's orig pos */\r
118 } DragInfo;\r
119 \r
120 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
121 \r
122 typedef struct {\r
123   POINT sq[2];    /* board coordinates of from, to squares */\r
124 } HighlightInfo;\r
125 \r
126 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
127 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
128 \r
129 /* Window class names */\r
130 char szAppName[] = "WinBoard";\r
131 char szConsoleName[] = "WBConsole";\r
132 \r
133 /* Title bar text */\r
134 char szTitle[] = "WinBoard";\r
135 char szConsoleTitle[] = "ICS Interaction";\r
136 \r
137 char *programName;\r
138 char *settingsFileName;\r
139 BOOLEAN saveSettingsOnExit;\r
140 char installDir[MSG_SIZ];\r
141 \r
142 BoardSize boardSize;\r
143 BOOLEAN chessProgram;\r
144 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
145 static int squareSize, lineGap, minorSize;\r
146 static int winWidth, winHeight;\r
147 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
148 static int logoHeight = 0;\r
149 static char messageText[MESSAGE_TEXT_MAX];\r
150 static int clockTimerEvent = 0;\r
151 static int loadGameTimerEvent = 0;\r
152 static int analysisTimerEvent = 0;\r
153 static DelayedEventCallback delayedTimerCallback;\r
154 static int delayedTimerEvent = 0;\r
155 static int buttonCount = 2;\r
156 char *icsTextMenuString;\r
157 char *icsNames;\r
158 char *firstChessProgramNames;\r
159 char *secondChessProgramNames;\r
160 \r
161 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
162 \r
163 #define PALETTESIZE 256\r
164 \r
165 HINSTANCE hInst;          /* current instance */\r
166 HWND hwndMain = NULL;        /* root window*/\r
167 HWND hwndConsole = NULL;\r
168 BOOLEAN alwaysOnTop = FALSE;\r
169 RECT boardRect;\r
170 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
171   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
172 HPALETTE hPal;\r
173 ColorClass currentColorClass;\r
174 \r
175 HWND hCommPort = NULL;    /* currently open comm port */\r
176 static HWND hwndPause;    /* pause button */\r
177 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
178 static HBRUSH lightSquareBrush, darkSquareBrush,\r
179   blackSquareBrush, /* [HGM] for band between board and holdings */\r
180   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
181 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
182 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
183 static HPEN gridPen = NULL;\r
184 static HPEN highlightPen = NULL;\r
185 static HPEN premovePen = NULL;\r
186 static NPLOGPALETTE pLogPal;\r
187 static BOOL paletteChanged = FALSE;\r
188 static HICON iconWhite, iconBlack, iconCurrent;\r
189 static int doingSizing = FALSE;\r
190 static int lastSizing = 0;\r
191 static int prevStderrPort;\r
192 \r
193 /* [AS] Support for background textures */\r
194 #define BACK_TEXTURE_MODE_DISABLED      0\r
195 #define BACK_TEXTURE_MODE_PLAIN         1\r
196 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
197 \r
198 static HBITMAP liteBackTexture = NULL;\r
199 static HBITMAP darkBackTexture = NULL;\r
200 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
201 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
202 static int backTextureSquareSize = 0;\r
203 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
204 \r
205 #if __GNUC__ && !defined(_winmajor)\r
206 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
207 #else\r
208 #define oldDialog (_winmajor < 4)\r
209 #endif\r
210 \r
211 char *defaultTextAttribs[] = \r
212 {\r
213   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
214   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
215   COLOR_NONE\r
216 };\r
217 \r
218 typedef struct {\r
219   char *name;\r
220   int squareSize;\r
221   int lineGap;\r
222   int smallLayout;\r
223   int tinyLayout;\r
224   int cliWidth, cliHeight;\r
225 } SizeInfo;\r
226 \r
227 SizeInfo sizeInfo[] = \r
228 {\r
229   { "tiny",     21, 0, 1, 1, 0, 0 },\r
230   { "teeny",    25, 1, 1, 1, 0, 0 },\r
231   { "dinky",    29, 1, 1, 1, 0, 0 },\r
232   { "petite",   33, 1, 1, 1, 0, 0 },\r
233   { "slim",     37, 2, 1, 0, 0, 0 },\r
234   { "small",    40, 2, 1, 0, 0, 0 },\r
235   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
236   { "middling", 49, 2, 0, 0, 0, 0 },\r
237   { "average",  54, 2, 0, 0, 0, 0 },\r
238   { "moderate", 58, 3, 0, 0, 0, 0 },\r
239   { "medium",   64, 3, 0, 0, 0, 0 },\r
240   { "bulky",    72, 3, 0, 0, 0, 0 },\r
241   { "large",    80, 3, 0, 0, 0, 0 },\r
242   { "big",      87, 3, 0, 0, 0, 0 },\r
243   { "huge",     95, 3, 0, 0, 0, 0 },\r
244   { "giant",    108, 3, 0, 0, 0, 0 },\r
245   { "colossal", 116, 4, 0, 0, 0, 0 },\r
246   { "titanic",  129, 4, 0, 0, 0, 0 },\r
247   { NULL, 0, 0, 0, 0, 0, 0 }\r
248 };\r
249 \r
250 #define MF(x) {x, {0, }, {0, }, 0}\r
251 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
252 {\r
253   { 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
254   { 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
255   { 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
256   { 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
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266   { 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
267   { 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
268   { 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
269   { 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
270   { 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
271 };\r
272 \r
273 MyFont *font[NUM_SIZES][NUM_FONTS];\r
274 \r
275 typedef struct {\r
276   char *label;\r
277   int id;\r
278   HWND hwnd;\r
279   WNDPROC wndproc;\r
280 } MyButtonDesc;\r
281 \r
282 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
283 #define N_BUTTONS 5\r
284 \r
285 MyButtonDesc buttonDesc[N_BUTTONS] =\r
286 {\r
287   {"<<", IDM_ToStart, NULL, NULL},\r
288   {"<", IDM_Backward, NULL, NULL},\r
289   {"P", IDM_Pause, NULL, NULL},\r
290   {">", IDM_Forward, NULL, NULL},\r
291   {">>", IDM_ToEnd, NULL, NULL},\r
292 };\r
293 \r
294 int tinyLayout = 0, smallLayout = 0;\r
295 #define MENU_BAR_ITEMS 6\r
296 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
297   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
298   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
299 };\r
300 \r
301 \r
302 MySound sounds[(int)NSoundClasses];\r
303 MyTextAttribs textAttribs[(int)NColorClasses];\r
304 \r
305 MyColorizeAttribs colorizeAttribs[] = {\r
306   { (COLORREF)0, 0, "Shout Text" },\r
307   { (COLORREF)0, 0, "SShout/CShout" },\r
308   { (COLORREF)0, 0, "Channel 1 Text" },\r
309   { (COLORREF)0, 0, "Channel Text" },\r
310   { (COLORREF)0, 0, "Kibitz Text" },\r
311   { (COLORREF)0, 0, "Tell Text" },\r
312   { (COLORREF)0, 0, "Challenge Text" },\r
313   { (COLORREF)0, 0, "Request Text" },\r
314   { (COLORREF)0, 0, "Seek Text" },\r
315   { (COLORREF)0, 0, "Normal Text" },\r
316   { (COLORREF)0, 0, "None" }\r
317 };\r
318 \r
319 \r
320 \r
321 static char *commentTitle;\r
322 static char *commentText;\r
323 static int commentIndex;\r
324 static Boolean editComment = FALSE;\r
325 HWND commentDialog = NULL;\r
326 BOOLEAN commentDialogUp = FALSE;\r
327 static int commentX, commentY, commentH, commentW;\r
328 \r
329 static char *analysisTitle;\r
330 static char *analysisText;\r
331 HWND analysisDialog = NULL;\r
332 BOOLEAN analysisDialogUp = FALSE;\r
333 static int analysisX, analysisY, analysisH, analysisW;\r
334 \r
335 char errorTitle[MSG_SIZ];\r
336 char errorMessage[2*MSG_SIZ];\r
337 HWND errorDialog = NULL;\r
338 BOOLEAN moveErrorMessageUp = FALSE;\r
339 BOOLEAN consoleEcho = TRUE;\r
340 CHARFORMAT consoleCF;\r
341 COLORREF consoleBackgroundColor;\r
342 \r
343 char *programVersion;\r
344 \r
345 #define CPReal 1\r
346 #define CPComm 2\r
347 #define CPSock 3\r
348 #define CPRcmd 4\r
349 typedef int CPKind;\r
350 \r
351 typedef struct {\r
352   CPKind kind;\r
353   HANDLE hProcess;\r
354   DWORD pid;\r
355   HANDLE hTo;\r
356   HANDLE hFrom;\r
357   SOCKET sock;\r
358   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
359 } ChildProc;\r
360 \r
361 #define INPUT_SOURCE_BUF_SIZE 4096\r
362 \r
363 typedef struct _InputSource {\r
364   CPKind kind;\r
365   HANDLE hFile;\r
366   SOCKET sock;\r
367   int lineByLine;\r
368   HANDLE hThread;\r
369   DWORD id;\r
370   char buf[INPUT_SOURCE_BUF_SIZE];\r
371   char *next;\r
372   DWORD count;\r
373   int error;\r
374   InputCallback func;\r
375   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
376   VOIDSTAR closure;\r
377 } InputSource;\r
378 \r
379 InputSource *consoleInputSource;\r
380 \r
381 DCB dcb;\r
382 \r
383 /* forward */\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
385 VOID ConsoleCreate();\r
386 LRESULT CALLBACK\r
387   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);\r
391 LRESULT CALLBACK\r
392   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
394 void ParseIcsTextMenu(char *icsTextMenuString);\r
395 VOID PopUpMoveDialog(char firstchar);\r
396 VOID PopUpNameDialog(char firstchar);\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
398 \r
399 /* [AS] */\r
400 int NewGameFRC();\r
401 int GameListOptions();\r
402 \r
403 HWND moveHistoryDialog = NULL;\r
404 BOOLEAN moveHistoryDialogUp = FALSE;\r
405 \r
406 WindowPlacement wpMoveHistory;\r
407 \r
408 HWND evalGraphDialog = NULL;\r
409 BOOLEAN evalGraphDialogUp = FALSE;\r
410 \r
411 WindowPlacement wpEvalGraph;\r
412 \r
413 HWND engineOutputDialog = NULL;\r
414 BOOLEAN engineOutputDialogUp = FALSE;\r
415 \r
416 WindowPlacement wpEngineOutput;\r
417 \r
418 VOID MoveHistoryPopUp();\r
419 VOID MoveHistoryPopDown();\r
420 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
421 BOOL MoveHistoryIsUp();\r
422 \r
423 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
424 VOID EvalGraphPopUp();\r
425 VOID EvalGraphPopDown();\r
426 BOOL EvalGraphIsUp();\r
427 \r
428 VOID EngineOutputPopUp();\r
429 VOID EngineOutputPopDown();\r
430 BOOL EngineOutputIsUp();\r
431 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
432 \r
433 VOID GothicPopUp(char *title, VariantClass variant);\r
434 /*\r
435  * Setting "frozen" should disable all user input other than deleting\r
436  * the window.  We do this while engines are initializing themselves.\r
437  */\r
438 static int frozen = 0;\r
439 static int oldMenuItemState[MENU_BAR_ITEMS];\r
440 void FreezeUI()\r
441 {\r
442   HMENU hmenu;\r
443   int i;\r
444 \r
445   if (frozen) return;\r
446   frozen = 1;\r
447   hmenu = GetMenu(hwndMain);\r
448   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
449     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
450   }\r
451   DrawMenuBar(hwndMain);\r
452 }\r
453 \r
454 /* Undo a FreezeUI */\r
455 void ThawUI()\r
456 {\r
457   HMENU hmenu;\r
458   int i;\r
459 \r
460   if (!frozen) return;\r
461   frozen = 0;\r
462   hmenu = GetMenu(hwndMain);\r
463   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
464     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
465   }\r
466   DrawMenuBar(hwndMain);\r
467 }\r
468 \r
469 /*---------------------------------------------------------------------------*\\r
470  *\r
471  * WinMain\r
472  *\r
473 \*---------------------------------------------------------------------------*/\r
474 \r
475 int APIENTRY\r
476 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
477         LPSTR lpCmdLine, int nCmdShow)\r
478 {\r
479   MSG msg;\r
480   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
481 \r
482   debugFP = stderr;\r
483 \r
484   LoadLibrary("RICHED32.DLL");\r
485   consoleCF.cbSize = sizeof(CHARFORMAT);\r
486 \r
487   if (!InitApplication(hInstance)) {\r
488     return (FALSE);\r
489   }\r
490   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
491     return (FALSE);\r
492   }\r
493 \r
494   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
495   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
496   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
497 \r
498   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
499 \r
500   while (GetMessage(&msg, /* message structure */\r
501                     NULL, /* handle of window receiving the message */\r
502                     0,    /* lowest message to examine */\r
503                     0))   /* highest message to examine */\r
504     {\r
505       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
506           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
507           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
508           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
509           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
510           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
511           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
512           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
513           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
514           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
515         TranslateMessage(&msg); /* Translates virtual key codes */\r
516         DispatchMessage(&msg);  /* Dispatches message to window */\r
517       }\r
518     }\r
519 \r
520 \r
521   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
522 }\r
523 \r
524 /*---------------------------------------------------------------------------*\\r
525  *\r
526  * Initialization functions\r
527  *\r
528 \*---------------------------------------------------------------------------*/\r
529 \r
530 BOOL\r
531 InitApplication(HINSTANCE hInstance)\r
532 {\r
533   WNDCLASS wc;\r
534 \r
535   /* Fill in window class structure with parameters that describe the */\r
536   /* main window. */\r
537 \r
538   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
539   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
540   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
541   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
542   wc.hInstance     = hInstance;         /* Owner of this class */\r
543   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
544   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
545   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
546   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
547   wc.lpszClassName = szAppName;                 /* Name to register as */\r
548 \r
549   /* Register the window class and return success/failure code. */\r
550   if (!RegisterClass(&wc)) return FALSE;\r
551 \r
552   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
553   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
554   wc.cbClsExtra    = 0;\r
555   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
556   wc.hInstance     = hInstance;\r
557   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
558   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
559   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
560   wc.lpszMenuName  = NULL;\r
561   wc.lpszClassName = szConsoleName;\r
562 \r
563   if (!RegisterClass(&wc)) return FALSE;\r
564   return TRUE;\r
565 }\r
566 \r
567 \r
568 /* Set by InitInstance, used by EnsureOnScreen */\r
569 int screenHeight, screenWidth;\r
570 \r
571 void\r
572 EnsureOnScreen(int *x, int *y)\r
573 {\r
574 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
575   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
576   if (*x > screenWidth - 32) *x = 0;\r
577   if (*y > screenHeight - 32) *y = 0;\r
578   if (*x < 0) *x = 0;\r
579   if (*y < 0) *y = 0;\r
580 //  if (*x < 10) *x = 10;\r
581 //  if (*y < gap) *y = gap;\r
582 }\r
583 \r
584 BOOL\r
585 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
586 {\r
587   HWND hwnd; /* Main window handle. */\r
588   int ibs;\r
589   WINDOWPLACEMENT wp;\r
590   char *filepart;\r
591 \r
592   hInst = hInstance;    /* Store instance handle in our global variable */\r
593 \r
594   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
595     *filepart = NULLCHAR;\r
596   } else {\r
597     GetCurrentDirectory(MSG_SIZ, installDir);\r
598   }\r
599   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
600   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
601   if (appData.debugMode) {\r
602     debugFP = fopen(appData.nameOfDebugFile, "w");\r
603     setbuf(debugFP, NULL);\r
604   }\r
605 \r
606   InitBackEnd1();\r
607 \r
608 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
609 //  InitEngineUCI( installDir, &second );\r
610 \r
611   /* Create a main window for this application instance. */\r
612   hwnd = CreateWindow(szAppName, szTitle,\r
613                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
614                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
615                       NULL, NULL, hInstance, NULL);\r
616   hwndMain = hwnd;\r
617 \r
618   /* If window could not be created, return "failure" */\r
619   if (!hwnd) {\r
620     return (FALSE);\r
621   }\r
622 \r
623   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
624   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
625       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
626 \r
627       if (first.programLogo == NULL && appData.debugMode) {\r
628           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
629       }\r
630   } else if(appData.autoLogo) {\r
631       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
632         char buf[MSG_SIZ];\r
633         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
634         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
635       }\r
636   }\r
637 \r
638   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
639       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
640 \r
641       if (second.programLogo == NULL && appData.debugMode) {\r
642           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
643       }\r
644   } else if(appData.autoLogo) {\r
645       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
646         char buf[MSG_SIZ];\r
647         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
648         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
649       }\r
650   }\r
651 \r
652   iconWhite = LoadIcon(hInstance, "icon_white");\r
653   iconBlack = LoadIcon(hInstance, "icon_black");\r
654   iconCurrent = iconWhite;\r
655   InitDrawingColors();\r
656   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
657   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
658   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
659     /* Compute window size for each board size, and use the largest\r
660        size that fits on this screen as the default. */\r
661     InitDrawingSizes((BoardSize)ibs, 0);\r
662     if (boardSize == (BoardSize)-1 &&\r
663         winHeight <= screenHeight\r
664            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
665         && winWidth <= screenWidth) {\r
666       boardSize = (BoardSize)ibs;\r
667     }\r
668   }\r
669 \r
670   InitDrawingSizes(boardSize, 0);\r
671   InitMenuChecks();\r
672   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
673 \r
674   /* [AS] Load textures if specified */\r
675   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
676   \r
677   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
678       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
679       liteBackTextureMode = appData.liteBackTextureMode;\r
680 \r
681       if (liteBackTexture == NULL && appData.debugMode) {\r
682           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
683       }\r
684   }\r
685   \r
686   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
687       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
688       darkBackTextureMode = appData.darkBackTextureMode;\r
689 \r
690       if (darkBackTexture == NULL && appData.debugMode) {\r
691           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
692       }\r
693   }\r
694 \r
695   mysrandom( (unsigned) time(NULL) );\r
696 \r
697   /* [AS] Restore layout */\r
698   if( wpMoveHistory.visible ) {\r
699       MoveHistoryPopUp();\r
700   }\r
701 \r
702   if( wpEvalGraph.visible ) {\r
703       EvalGraphPopUp();\r
704   }\r
705 \r
706   if( wpEngineOutput.visible ) {\r
707       EngineOutputPopUp();\r
708   }\r
709 \r
710   InitBackEnd2();\r
711 \r
712   /* Make the window visible; update its client area; and return "success" */\r
713   EnsureOnScreen(&boardX, &boardY);\r
714   wp.length = sizeof(WINDOWPLACEMENT);\r
715   wp.flags = 0;\r
716   wp.showCmd = nCmdShow;\r
717   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
718   wp.rcNormalPosition.left = boardX;\r
719   wp.rcNormalPosition.right = boardX + winWidth;\r
720   wp.rcNormalPosition.top = boardY;\r
721   wp.rcNormalPosition.bottom = boardY + winHeight;\r
722   SetWindowPlacement(hwndMain, &wp);\r
723 \r
724   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
725                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
726 \r
727 #if 0\r
728   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
729   if( gameInfo.variant != VariantFischeRandom ) {\r
730       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
731   }\r
732 #endif\r
733   if (hwndConsole) {\r
734 #if AOT_CONSOLE\r
735     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
736                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
737 #endif\r
738     ShowWindow(hwndConsole, nCmdShow);\r
739   }\r
740   UpdateWindow(hwnd);\r
741 \r
742   return TRUE;\r
743 \r
744 }\r
745 \r
746 \r
747 typedef enum {\r
748   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
749   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
750   ArgSettingsFilename\r
751 } ArgType;\r
752 \r
753 typedef struct {\r
754   char *argName;\r
755   ArgType argType;\r
756   /***\r
757   union {\r
758     String *pString;       // ArgString\r
759     int *pInt;             // ArgInt\r
760     float *pFloat;         // ArgFloat\r
761     Boolean *pBoolean;     // ArgBoolean\r
762     COLORREF *pColor;      // ArgColor\r
763     ColorClass cc;         // ArgAttribs\r
764     String *pFilename;     // ArgFilename\r
765     BoardSize *pBoardSize; // ArgBoardSize\r
766     int whichFont;         // ArgFont\r
767     DCB *pDCB;             // ArgCommSettings\r
768     String *pFilename;     // ArgSettingsFilename\r
769   } argLoc;\r
770   ***/\r
771   LPVOID argLoc;\r
772   BOOL save;\r
773 } ArgDescriptor;\r
774 \r
775 int junk;\r
776 ArgDescriptor argDescriptors[] = {\r
777   /* positional arguments */\r
778   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
779   { "", ArgNone, NULL },\r
780   /* keyword arguments */\r
781   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
782   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
783   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
784   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
785   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
786   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
787   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
788   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
789   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
790   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
791   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
792   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
793   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
794   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
795   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
796   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
797   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
798   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
799     FALSE },\r
800   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
801     FALSE },\r
802   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
803     FALSE },\r
804   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
805   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
806     FALSE },\r
807   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
808   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
809   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
810   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
811   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
812   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
813   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
814   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
815   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
816   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
817   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
818   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
819   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
820   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
821   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
822   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
823   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
824   /*!!bitmapDirectory?*/\r
825   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
826   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
827   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
828   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
829   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
830   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
831   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
832   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
833   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
834   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
835   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
836   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
837   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
838   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
839   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
840   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
841   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
842   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
843   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
844   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
845   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
846   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
847   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
848   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
849   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
850   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
851   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
852   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
853   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
854   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
855   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
856   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
857   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
858   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
859   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
860   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
861   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
862   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
863   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
864   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
865   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
866   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
867   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
868   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
869   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
870   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
871   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
872   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
873   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
874   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
875   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
876   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
877   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
878   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
879   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
880   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
881   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
882   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
883   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
884   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
885   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
886   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
887   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
888   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
889   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
890   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
891   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
892   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
893   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
894   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
895   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
896   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
897   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
898   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
899   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
900   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
901   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
902   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
903   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
904   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
905   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
906   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
907   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
908   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
909   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
910   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
911   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
912   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
913   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
914   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
915   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
916   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
917   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
918   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
919     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
920   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
921   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
922   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
923   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
924   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
925   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
926   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
927   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
928     TRUE }, /* must come after all fonts */\r
929   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
930   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
931     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
932   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
933   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
934   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
935   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
936   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
937   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
938   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
939   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
940   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
941   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
942   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
943   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
944   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
945   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
946   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
947   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
948   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
949   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
950   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
951   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
952   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
953   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
954   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
955   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
956   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
957   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
958   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
959   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
960 #if 0\r
961   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
962   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
963 #endif\r
964   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
965   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
966   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
967   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
968   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
969   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
970   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
971   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
972   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
973   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
974   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
975   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
976   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
977   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
978   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
979   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
980   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
981   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
982   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
983   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
984   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
985   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
986   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
987   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
988   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
989   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
990   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
991   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
992   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
993   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
994   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
995   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
996   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
997   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
998   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
999   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1000   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1001   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1002   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1003   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1004   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1005   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1006   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1007   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1008   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1009   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1010   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1011   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1012   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1013   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1014   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1015   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1016   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1017   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1018   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1019   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1020   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1021   { "highlightLastMove", ArgBoolean,\r
1022     (LPVOID) &appData.highlightLastMove, TRUE },\r
1023   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1024   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1025   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1026   { "highlightDragging", ArgBoolean,\r
1027     (LPVOID) &appData.highlightDragging, TRUE },\r
1028   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1029   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1030   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1031   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1032   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1033   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1034   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1035   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1036   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1037   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1038   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1039   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1040   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1041   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1042   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1043   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1044   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1045   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1046   { "soundShout", ArgFilename,\r
1047     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1048   { "soundSShout", ArgFilename,\r
1049     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1050   { "soundChannel1", ArgFilename,\r
1051     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1052   { "soundChannel", ArgFilename,\r
1053     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1054   { "soundKibitz", ArgFilename,\r
1055     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1056   { "soundTell", ArgFilename,\r
1057     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1058   { "soundChallenge", ArgFilename,\r
1059     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1060   { "soundRequest", ArgFilename,\r
1061     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1062   { "soundSeek", ArgFilename,\r
1063     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1064   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1065   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1066   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1067   { "soundIcsLoss", ArgFilename, \r
1068     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1069   { "soundIcsDraw", ArgFilename, \r
1070     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1071   { "soundIcsUnfinished", ArgFilename, \r
1072     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1073   { "soundIcsAlarm", ArgFilename, \r
1074     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1075   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1076   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1077   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1078   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1079   { "reuseChessPrograms", ArgBoolean,\r
1080     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1081   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1082   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1083   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1084   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1085   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1086   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1087   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1088   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1089   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1090   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1091   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1092   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1093   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1094   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1095   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1096   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1097   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1098   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1099   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1100   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1101   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1102   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1103   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1104   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1105   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1106   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1107   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1108   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1109   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1110   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1111   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1112   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1113   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1114   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1115   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1116   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1117   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1118     TRUE },\r
1119   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1120     TRUE },\r
1121   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1122   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1123   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1124   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1125   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1126   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1127   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1128   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1129   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1130   /* [AS] New features */\r
1131   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1132   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1133   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1134   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1135   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1136   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1137   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1138   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1139   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1140   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1141   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1142   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1143   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1144   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1145   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1146   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1147   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1148   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1149   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1150   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1151   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1152   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1153   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1154   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1155   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1156   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1157   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1158   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1159   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1160   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1161   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1162   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1163   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1164   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1165   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1166   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1167   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1168   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1169   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1170   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1171   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1172   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1173   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1174   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1175   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1176   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1177   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1178   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1179   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1180   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1181 \r
1182   /* [AS] Layout stuff */\r
1183   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1184   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1185   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1186   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1187   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1188 \r
1189   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1190   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1191   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1192   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1193   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1194 \r
1195   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1196   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1197   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1198   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1199   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1200 \r
1201   /* [HGM] board-size, adjudication and misc. options */\r
1202   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1203   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1204   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1205   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1206   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1207   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1208   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1209   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1210   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1211   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1212   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1213   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1214   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1215   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1216   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1217   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1218   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1219   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1220   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1221   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1222   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1223   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1224   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1225   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1226   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1227   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1228   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1229   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1230   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1231 \r
1232 #ifdef ZIPPY\r
1233   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1234   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1235   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1236   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1237   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1238   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1239   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1240   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1241   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1242   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1243   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1244   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1245   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1246     FALSE },\r
1247   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1248   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1249   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1250   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1251   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1252   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1253   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1254     FALSE },\r
1255   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1256   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1257   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1258   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1259   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1260   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1261   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1262   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1263   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1264   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1265   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1266   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1267   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1268   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1269   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1270   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1271   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1272   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1273 #endif\r
1274   /* [HGM] options for broadcasting and time odds */\r
1275   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1276   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1277   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1278   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1279   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1280   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1281   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1282   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1283   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1284   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1285   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1286   { NULL, ArgNone, NULL, FALSE }\r
1287 };\r
1288 \r
1289 \r
1290 /* Kludge for indirection files on command line */\r
1291 char* lastIndirectionFilename;\r
1292 ArgDescriptor argDescriptorIndirection =\r
1293 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1294 \r
1295 \r
1296 VOID\r
1297 ExitArgError(char *msg, char *badArg)\r
1298 {\r
1299   char buf[MSG_SIZ];\r
1300 \r
1301   sprintf(buf, "%s %s", msg, badArg);\r
1302   DisplayFatalError(buf, 0, 2);\r
1303   exit(2);\r
1304 }\r
1305 \r
1306 /* Command line font name parser.  NULL name means do nothing.\r
1307    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1308    For backward compatibility, syntax without the colon is also\r
1309    accepted, but font names with digits in them won't work in that case.\r
1310 */\r
1311 VOID\r
1312 ParseFontName(char *name, MyFontParams *mfp)\r
1313 {\r
1314   char *p, *q;\r
1315   if (name == NULL) return;\r
1316   p = name;\r
1317   q = strchr(p, ':');\r
1318   if (q) {\r
1319     if (q - p >= sizeof(mfp->faceName))\r
1320       ExitArgError("Font name too long:", name);\r
1321     memcpy(mfp->faceName, p, q - p);\r
1322     mfp->faceName[q - p] = NULLCHAR;\r
1323     p = q + 1;\r
1324   } else {\r
1325     q = mfp->faceName;\r
1326     while (*p && !isdigit(*p)) {\r
1327       *q++ = *p++;\r
1328       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1329         ExitArgError("Font name too long:", name);\r
1330     }\r
1331     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1332     *q = NULLCHAR;\r
1333   }\r
1334   if (!*p) ExitArgError("Font point size missing:", name);\r
1335   mfp->pointSize = (float) atof(p);\r
1336   mfp->bold = (strchr(p, 'b') != NULL);\r
1337   mfp->italic = (strchr(p, 'i') != NULL);\r
1338   mfp->underline = (strchr(p, 'u') != NULL);\r
1339   mfp->strikeout = (strchr(p, 's') != NULL);\r
1340 }\r
1341 \r
1342 /* Color name parser.\r
1343    X version accepts X color names, but this one\r
1344    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1345 COLORREF\r
1346 ParseColorName(char *name)\r
1347 {\r
1348   int red, green, blue, count;\r
1349   char buf[MSG_SIZ];\r
1350 \r
1351   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1352   if (count != 3) {\r
1353     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1354       &red, &green, &blue);\r
1355   }\r
1356   if (count != 3) {\r
1357     sprintf(buf, "Can't parse color name %s", name);\r
1358     DisplayError(buf, 0);\r
1359     return RGB(0, 0, 0);\r
1360   }\r
1361   return PALETTERGB(red, green, blue);\r
1362 }\r
1363 \r
1364 \r
1365 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1366 {\r
1367   char *e = argValue;\r
1368   int eff = 0;\r
1369 \r
1370   while (*e) {\r
1371     if (*e == 'b')      eff |= CFE_BOLD;\r
1372     else if (*e == 'i') eff |= CFE_ITALIC;\r
1373     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1374     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1375     else if (*e == '#' || isdigit(*e)) break;\r
1376     e++;\r
1377   }\r
1378   *effects = eff;\r
1379   *color   = ParseColorName(e);\r
1380 }\r
1381 \r
1382 \r
1383 BoardSize\r
1384 ParseBoardSize(char *name)\r
1385 {\r
1386   BoardSize bs = SizeTiny;\r
1387   while (sizeInfo[bs].name != NULL) {\r
1388     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1389     bs++;\r
1390   }\r
1391   ExitArgError("Unrecognized board size value", name);\r
1392   return bs; /* not reached */\r
1393 }\r
1394 \r
1395 \r
1396 char\r
1397 StringGet(void *getClosure)\r
1398 {\r
1399   char **p = (char **) getClosure;\r
1400   return *((*p)++);\r
1401 }\r
1402 \r
1403 char\r
1404 FileGet(void *getClosure)\r
1405 {\r
1406   int c;\r
1407   FILE* f = (FILE*) getClosure;\r
1408 \r
1409   c = getc(f);\r
1410   if (c == EOF)\r
1411     return NULLCHAR;\r
1412   else\r
1413     return (char) c;\r
1414 }\r
1415 \r
1416 /* Parse settings file named "name". If file found, return the\r
1417    full name in fullname and return TRUE; else return FALSE */\r
1418 BOOLEAN\r
1419 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1420 {\r
1421   char *dummy;\r
1422   FILE *f;\r
1423 \r
1424   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1425     f = fopen(fullname, "r");\r
1426     if (f != NULL) {\r
1427       ParseArgs(FileGet, f);\r
1428       fclose(f);\r
1429       return TRUE;\r
1430     }\r
1431   }\r
1432   return FALSE;\r
1433 }\r
1434 \r
1435 VOID\r
1436 ParseArgs(GetFunc get, void *cl)\r
1437 {\r
1438   char argName[ARG_MAX];\r
1439   char argValue[ARG_MAX];\r
1440   ArgDescriptor *ad;\r
1441   char start;\r
1442   char *q;\r
1443   int i, octval;\r
1444   char ch;\r
1445   int posarg = 0;\r
1446 \r
1447   ch = get(cl);\r
1448   for (;;) {\r
1449     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1450     if (ch == NULLCHAR) break;\r
1451     if (ch == ';') {\r
1452       /* Comment to end of line */\r
1453       ch = get(cl);\r
1454       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1455       continue;\r
1456     } else if (ch == '/' || ch == '-') {\r
1457       /* Switch */\r
1458       q = argName;\r
1459       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1460              ch != '\n' && ch != '\t') {\r
1461         *q++ = ch;\r
1462         ch = get(cl);\r
1463       }\r
1464       *q = NULLCHAR;\r
1465 \r
1466       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1467         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1468 \r
1469       if (ad->argName == NULL)\r
1470         ExitArgError("Unrecognized argument", argName);\r
1471 \r
1472     } else if (ch == '@') {\r
1473       /* Indirection file */\r
1474       ad = &argDescriptorIndirection;\r
1475       ch = get(cl);\r
1476     } else {\r
1477       /* Positional argument */\r
1478       ad = &argDescriptors[posarg++];\r
1479       strcpy(argName, ad->argName);\r
1480     }\r
1481 \r
1482     if (ad->argType == ArgTrue) {\r
1483       *(Boolean *) ad->argLoc = TRUE;\r
1484       continue;\r
1485     }\r
1486     if (ad->argType == ArgFalse) {\r
1487       *(Boolean *) ad->argLoc = FALSE;\r
1488       continue;\r
1489     }\r
1490 \r
1491     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1492     if (ch == NULLCHAR || ch == '\n') {\r
1493       ExitArgError("No value provided for argument", argName);\r
1494     }\r
1495     q = argValue;\r
1496     if (ch == '{') {\r
1497       // Quoting with { }.  No characters have to (or can) be escaped.\r
1498       // Thus the string cannot contain a '}' character.\r
1499       start = ch;\r
1500       ch = get(cl);\r
1501       while (start) {\r
1502         switch (ch) {\r
1503         case NULLCHAR:\r
1504           start = NULLCHAR;\r
1505           break;\r
1506           \r
1507         case '}':\r
1508           ch = get(cl);\r
1509           start = NULLCHAR;\r
1510           break;\r
1511 \r
1512         default:\r
1513           *q++ = ch;\r
1514           ch = get(cl);\r
1515           break;\r
1516         }\r
1517       }   \r
1518     } else if (ch == '\'' || ch == '"') {\r
1519       // Quoting with ' ' or " ", with \ as escape character.\r
1520       // Inconvenient for long strings that may contain Windows filenames.\r
1521       start = ch;\r
1522       ch = get(cl);\r
1523       while (start) {\r
1524         switch (ch) {\r
1525         case NULLCHAR:\r
1526           start = NULLCHAR;\r
1527           break;\r
1528 \r
1529         default:\r
1530         not_special:\r
1531           *q++ = ch;\r
1532           ch = get(cl);\r
1533           break;\r
1534 \r
1535         case '\'':\r
1536         case '\"':\r
1537           if (ch == start) {\r
1538             ch = get(cl);\r
1539             start = NULLCHAR;\r
1540             break;\r
1541           } else {\r
1542             goto not_special;\r
1543           }\r
1544 \r
1545         case '\\':\r
1546           if (ad->argType == ArgFilename\r
1547               || ad->argType == ArgSettingsFilename) {\r
1548               goto not_special;\r
1549           }\r
1550           ch = get(cl);\r
1551           switch (ch) {\r
1552           case NULLCHAR:\r
1553             ExitArgError("Incomplete \\ escape in value for", argName);\r
1554             break;\r
1555           case 'n':\r
1556             *q++ = '\n';\r
1557             ch = get(cl);\r
1558             break;\r
1559           case 'r':\r
1560             *q++ = '\r';\r
1561             ch = get(cl);\r
1562             break;\r
1563           case 't':\r
1564             *q++ = '\t';\r
1565             ch = get(cl);\r
1566             break;\r
1567           case 'b':\r
1568             *q++ = '\b';\r
1569             ch = get(cl);\r
1570             break;\r
1571           case 'f':\r
1572             *q++ = '\f';\r
1573             ch = get(cl);\r
1574             break;\r
1575           default:\r
1576             octval = 0;\r
1577             for (i = 0; i < 3; i++) {\r
1578               if (ch >= '0' && ch <= '7') {\r
1579                 octval = octval*8 + (ch - '0');\r
1580                 ch = get(cl);\r
1581               } else {\r
1582                 break;\r
1583               }\r
1584             }\r
1585             if (i > 0) {\r
1586               *q++ = (char) octval;\r
1587             } else {\r
1588               *q++ = ch;\r
1589               ch = get(cl);\r
1590             }\r
1591             break;\r
1592           }\r
1593           break;\r
1594         }\r
1595       }\r
1596     } else {\r
1597       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1598         *q++ = ch;\r
1599         ch = get(cl);\r
1600       }\r
1601     }\r
1602     *q = NULLCHAR;\r
1603 \r
1604     switch (ad->argType) {\r
1605     case ArgInt:\r
1606       *(int *) ad->argLoc = atoi(argValue);\r
1607       break;\r
1608 \r
1609     case ArgFloat:\r
1610       *(float *) ad->argLoc = (float) atof(argValue);\r
1611       break;\r
1612 \r
1613     case ArgString:\r
1614     case ArgFilename:\r
1615       *(char **) ad->argLoc = strdup(argValue);\r
1616       break;\r
1617 \r
1618     case ArgSettingsFilename:\r
1619       {\r
1620         char fullname[MSG_SIZ];\r
1621         if (ParseSettingsFile(argValue, fullname)) {\r
1622           if (ad->argLoc != NULL) {\r
1623             *(char **) ad->argLoc = strdup(fullname);\r
1624           }\r
1625         } else {\r
1626           if (ad->argLoc != NULL) {\r
1627           } else {\r
1628             ExitArgError("Failed to open indirection file", argValue);\r
1629           }\r
1630         }\r
1631       }\r
1632       break;\r
1633 \r
1634     case ArgBoolean:\r
1635       switch (argValue[0]) {\r
1636       case 't':\r
1637       case 'T':\r
1638         *(Boolean *) ad->argLoc = TRUE;\r
1639         break;\r
1640       case 'f':\r
1641       case 'F':\r
1642         *(Boolean *) ad->argLoc = FALSE;\r
1643         break;\r
1644       default:\r
1645         ExitArgError("Unrecognized boolean argument value", argValue);\r
1646         break;\r
1647       }\r
1648       break;\r
1649 \r
1650     case ArgColor:\r
1651       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1652       break;\r
1653 \r
1654     case ArgAttribs: {\r
1655       ColorClass cc = (ColorClass)ad->argLoc;\r
1656       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1657       }\r
1658       break;\r
1659       \r
1660     case ArgBoardSize:\r
1661       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1662       break;\r
1663 \r
1664     case ArgFont:\r
1665       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1666       break;\r
1667 \r
1668     case ArgCommSettings:\r
1669       ParseCommSettings(argValue, &dcb);\r
1670       break;\r
1671 \r
1672     case ArgNone:\r
1673       ExitArgError("Unrecognized argument", argValue);\r
1674       break;\r
1675     case ArgTrue:\r
1676     case ArgFalse: ;\r
1677     }\r
1678   }\r
1679 }\r
1680 \r
1681 VOID\r
1682 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1683 {\r
1684   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1685   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1686   DeleteDC(hdc);\r
1687   lf->lfWidth = 0;\r
1688   lf->lfEscapement = 0;\r
1689   lf->lfOrientation = 0;\r
1690   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1691   lf->lfItalic = mfp->italic;\r
1692   lf->lfUnderline = mfp->underline;\r
1693   lf->lfStrikeOut = mfp->strikeout;\r
1694   lf->lfCharSet = DEFAULT_CHARSET;\r
1695   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1696   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1697   lf->lfQuality = DEFAULT_QUALITY;\r
1698   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1699   strcpy(lf->lfFaceName, mfp->faceName);\r
1700 }\r
1701 \r
1702 VOID\r
1703 CreateFontInMF(MyFont *mf)\r
1704 {\r
1705   LFfromMFP(&mf->lf, &mf->mfp);\r
1706   if (mf->hf) DeleteObject(mf->hf);\r
1707   mf->hf = CreateFontIndirect(&mf->lf);\r
1708 }\r
1709 \r
1710 VOID\r
1711 SetDefaultTextAttribs()\r
1712 {\r
1713   ColorClass cc;\r
1714   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1715     ParseAttribs(&textAttribs[cc].color, \r
1716                  &textAttribs[cc].effects, \r
1717                  defaultTextAttribs[cc]);\r
1718   }\r
1719 }\r
1720 \r
1721 VOID\r
1722 SetDefaultSounds()\r
1723 {\r
1724   ColorClass cc;\r
1725   SoundClass sc;\r
1726   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1727     textAttribs[cc].sound.name = strdup("");\r
1728     textAttribs[cc].sound.data = NULL;\r
1729   }\r
1730   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1731     sounds[sc].name = strdup("");\r
1732     sounds[sc].data = NULL;\r
1733   }\r
1734   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1735 }\r
1736 \r
1737 VOID\r
1738 LoadAllSounds()\r
1739 {\r
1740   ColorClass cc;\r
1741   SoundClass sc;\r
1742   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1743     MyLoadSound(&textAttribs[cc].sound);\r
1744   }\r
1745   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1746     MyLoadSound(&sounds[sc]);\r
1747   }\r
1748 }\r
1749 \r
1750 VOID\r
1751 InitAppData(LPSTR lpCmdLine)\r
1752 {\r
1753   int i, j;\r
1754   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1755   char *dummy, *p;\r
1756 \r
1757   programName = szAppName;\r
1758 \r
1759   /* Initialize to defaults */\r
1760   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1761   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1762   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1763   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1764   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1765   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1766   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1767   SetDefaultTextAttribs();\r
1768   SetDefaultSounds();\r
1769   appData.movesPerSession = MOVES_PER_SESSION;\r
1770   appData.initString = INIT_STRING;\r
1771   appData.secondInitString = INIT_STRING;\r
1772   appData.firstComputerString = COMPUTER_STRING;\r
1773   appData.secondComputerString = COMPUTER_STRING;\r
1774   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1775   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1776   appData.firstPlaysBlack = FALSE;\r
1777   appData.noChessProgram = FALSE;\r
1778   chessProgram = FALSE;\r
1779   appData.firstHost = FIRST_HOST;\r
1780   appData.secondHost = SECOND_HOST;\r
1781   appData.firstDirectory = FIRST_DIRECTORY;\r
1782   appData.secondDirectory = SECOND_DIRECTORY;\r
1783   appData.bitmapDirectory = "";\r
1784   appData.remoteShell = REMOTE_SHELL;\r
1785   appData.remoteUser = "";\r
1786   appData.timeDelay = TIME_DELAY;\r
1787   appData.timeControl = TIME_CONTROL;\r
1788   appData.timeIncrement = TIME_INCREMENT;\r
1789   appData.icsActive = FALSE;\r
1790   appData.icsHost = "";\r
1791   appData.icsPort = ICS_PORT;\r
1792   appData.icsCommPort = ICS_COMM_PORT;\r
1793   appData.icsLogon = ICS_LOGON;\r
1794   appData.icsHelper = "";\r
1795   appData.useTelnet = FALSE;\r
1796   appData.telnetProgram = TELNET_PROGRAM;\r
1797   appData.gateway = "";\r
1798   appData.loadGameFile = "";\r
1799   appData.loadGameIndex = 0;\r
1800   appData.saveGameFile = "";\r
1801   appData.autoSaveGames = FALSE;\r
1802   appData.loadPositionFile = "";\r
1803   appData.loadPositionIndex = 1;\r
1804   appData.savePositionFile = "";\r
1805   appData.matchMode = FALSE;\r
1806   appData.matchGames = 0;\r
1807   appData.monoMode = FALSE;\r
1808   appData.debugMode = FALSE;\r
1809   appData.clockMode = TRUE;\r
1810   boardSize = (BoardSize) -1; /* determine by screen size */\r
1811   appData.Iconic = FALSE; /*unused*/\r
1812   appData.searchTime = "";\r
1813   appData.searchDepth = 0;\r
1814   appData.showCoords = FALSE;\r
1815   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1816   appData.autoCallFlag = FALSE;\r
1817   appData.flipView = FALSE;\r
1818   appData.autoFlipView = TRUE;\r
1819   appData.cmailGameName = "";\r
1820   appData.alwaysPromoteToQueen = FALSE;\r
1821   appData.oldSaveStyle = FALSE;\r
1822   appData.quietPlay = FALSE;\r
1823   appData.showThinking = FALSE;\r
1824   appData.ponderNextMove = TRUE;\r
1825   appData.periodicUpdates = TRUE;\r
1826   appData.popupExitMessage = TRUE;\r
1827   appData.popupMoveErrors = FALSE;\r
1828   appData.autoObserve = FALSE;\r
1829   appData.autoComment = FALSE;\r
1830   appData.animate = TRUE;\r
1831   appData.animSpeed = 10;\r
1832   appData.animateDragging = TRUE;\r
1833   appData.highlightLastMove = TRUE;\r
1834   appData.getMoveList = TRUE;\r
1835   appData.testLegality = TRUE;\r
1836   appData.premove = TRUE;\r
1837   appData.premoveWhite = FALSE;\r
1838   appData.premoveWhiteText = "";\r
1839   appData.premoveBlack = FALSE;\r
1840   appData.premoveBlackText = "";\r
1841   appData.icsAlarm = TRUE;\r
1842   appData.icsAlarmTime = 5000;\r
1843   appData.autoRaiseBoard = TRUE;\r
1844   appData.localLineEditing = TRUE;\r
1845   appData.colorize = TRUE;\r
1846   appData.reuseFirst = TRUE;\r
1847   appData.reuseSecond = TRUE;\r
1848   appData.blindfold = FALSE;\r
1849   appData.icsEngineAnalyze = FALSE;\r
1850   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1851   dcb.DCBlength = sizeof(DCB);\r
1852   dcb.BaudRate = 9600;\r
1853   dcb.fBinary = TRUE;\r
1854   dcb.fParity = FALSE;\r
1855   dcb.fOutxCtsFlow = FALSE;\r
1856   dcb.fOutxDsrFlow = FALSE;\r
1857   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1858   dcb.fDsrSensitivity = FALSE;\r
1859   dcb.fTXContinueOnXoff = TRUE;\r
1860   dcb.fOutX = FALSE;\r
1861   dcb.fInX = FALSE;\r
1862   dcb.fNull = FALSE;\r
1863   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1864   dcb.fAbortOnError = FALSE;\r
1865   /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */\r
1866   #if (defined(_MSC_VER) && _MSC_VER <= 1200) \r
1867         //dcb.wReserved = 0;\r
1868   #else\r
1869     dcb.wReserved = 0;\r
1870   #endif\r
1871   dcb.ByteSize = 7;\r
1872   dcb.Parity = SPACEPARITY;\r
1873   dcb.StopBits = ONESTOPBIT;\r
1874   settingsFileName = SETTINGS_FILE;\r
1875   saveSettingsOnExit = TRUE;\r
1876   boardX = CW_USEDEFAULT;\r
1877   boardY = CW_USEDEFAULT;\r
1878   consoleX = CW_USEDEFAULT; \r
1879   consoleY = CW_USEDEFAULT; \r
1880   consoleW = CW_USEDEFAULT;\r
1881   consoleH = CW_USEDEFAULT;\r
1882   analysisX = CW_USEDEFAULT; \r
1883   analysisY = CW_USEDEFAULT; \r
1884   analysisW = CW_USEDEFAULT;\r
1885   analysisH = CW_USEDEFAULT;\r
1886   commentX = CW_USEDEFAULT; \r
1887   commentY = CW_USEDEFAULT; \r
1888   commentW = CW_USEDEFAULT;\r
1889   commentH = CW_USEDEFAULT;\r
1890   editTagsX = CW_USEDEFAULT; \r
1891   editTagsY = CW_USEDEFAULT; \r
1892   editTagsW = CW_USEDEFAULT;\r
1893   editTagsH = CW_USEDEFAULT;\r
1894   gameListX = CW_USEDEFAULT; \r
1895   gameListY = CW_USEDEFAULT; \r
1896   gameListW = CW_USEDEFAULT;\r
1897   gameListH = CW_USEDEFAULT;\r
1898   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1899   icsNames = ICS_NAMES;\r
1900   firstChessProgramNames = FCP_NAMES;\r
1901   secondChessProgramNames = SCP_NAMES;\r
1902   appData.initialMode = "";\r
1903   appData.variant = "normal";\r
1904   appData.firstProtocolVersion = PROTOVER;\r
1905   appData.secondProtocolVersion = PROTOVER;\r
1906   appData.showButtonBar = TRUE;\r
1907 \r
1908    /* [AS] New properties (see comments in header file) */\r
1909   appData.firstScoreIsAbsolute = FALSE;\r
1910   appData.secondScoreIsAbsolute = FALSE;\r
1911   appData.saveExtendedInfoInPGN = FALSE;\r
1912   appData.hideThinkingFromHuman = FALSE;\r
1913   appData.liteBackTextureFile = "";\r
1914   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1915   appData.darkBackTextureFile = "";\r
1916   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1917   appData.renderPiecesWithFont = "";\r
1918   appData.fontToPieceTable = "";\r
1919   appData.fontBackColorWhite = 0;\r
1920   appData.fontForeColorWhite = 0;\r
1921   appData.fontBackColorBlack = 0;\r
1922   appData.fontForeColorBlack = 0;\r
1923   appData.fontPieceSize = 80;\r
1924   appData.overrideLineGap = 1;\r
1925   appData.adjudicateLossThreshold = 0;\r
1926   appData.delayBeforeQuit = 0;\r
1927   appData.delayAfterQuit = 0;\r
1928   appData.nameOfDebugFile = "winboard.debug";\r
1929   appData.pgnEventHeader = "Computer Chess Game";\r
1930   appData.defaultFrcPosition = -1;\r
1931   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1932   appData.saveOutOfBookInfo = TRUE;\r
1933   appData.showEvalInMoveHistory = TRUE;\r
1934   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1935   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1936   appData.highlightMoveWithArrow = FALSE;\r
1937   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1938   appData.useStickyWindows = TRUE;\r
1939   appData.adjudicateDrawMoves = 0;\r
1940   appData.autoDisplayComment = TRUE;\r
1941   appData.autoDisplayTags = TRUE;\r
1942   appData.firstIsUCI = FALSE;\r
1943   appData.secondIsUCI = FALSE;\r
1944   appData.firstHasOwnBookUCI = TRUE;\r
1945   appData.secondHasOwnBookUCI = TRUE;\r
1946   appData.polyglotDir = "";\r
1947   appData.usePolyglotBook = FALSE;\r
1948   appData.polyglotBook = "";\r
1949   appData.defaultHashSize = 64;\r
1950   appData.defaultCacheSizeEGTB = 4;\r
1951   appData.defaultPathEGTB = "c:\\egtb";\r
1952   appData.firstOptions = "";\r
1953   appData.secondOptions = "";\r
1954 \r
1955   InitWindowPlacement( &wpMoveHistory );\r
1956   InitWindowPlacement( &wpEvalGraph );\r
1957   InitWindowPlacement( &wpEngineOutput );\r
1958 \r
1959   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1960   appData.NrFiles      = -1;\r
1961   appData.NrRanks      = -1;\r
1962   appData.holdingsSize = -1;\r
1963   appData.testClaims   = FALSE;\r
1964   appData.checkMates   = FALSE;\r
1965   appData.materialDraws= FALSE;\r
1966   appData.trivialDraws = FALSE;\r
1967   appData.ruleMoves    = 51;\r
1968   appData.drawRepeats  = 6;\r
1969   appData.matchPause   = 10000;\r
1970   appData.alphaRank    = FALSE;\r
1971   appData.allWhite     = FALSE;\r
1972   appData.upsideDown   = FALSE;\r
1973   appData.serverPause  = 15;\r
1974   appData.serverMovesName   = NULL;\r
1975   appData.suppressLoadMoves = FALSE;\r
1976   appData.firstTimeOdds  = 1;\r
1977   appData.secondTimeOdds = 1;\r
1978   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1979   appData.secondAccumulateTC = 1;\r
1980   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1981   appData.secondNPS = -1;\r
1982   appData.engineComments = 1;\r
1983   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1984   appData.egtFormats = "";\r
1985 \r
1986 #ifdef ZIPPY\r
1987   appData.zippyTalk = ZIPPY_TALK;\r
1988   appData.zippyPlay = ZIPPY_PLAY;\r
1989   appData.zippyLines = ZIPPY_LINES;\r
1990   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1991   appData.zippyPassword = ZIPPY_PASSWORD;\r
1992   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1993   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1994   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1995   appData.zippyUseI = ZIPPY_USE_I;\r
1996   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1997   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1998   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1999   appData.zippyGameStart = ZIPPY_GAME_START;\r
2000   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2001   appData.zippyAbort = ZIPPY_ABORT;\r
2002   appData.zippyVariants = ZIPPY_VARIANTS;\r
2003   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2004   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2005 #endif\r
2006 \r
2007   /* Point font array elements to structures and\r
2008      parse default font names */\r
2009   for (i=0; i<NUM_FONTS; i++) {\r
2010     for (j=0; j<NUM_SIZES; j++) {\r
2011       font[j][i] = &fontRec[j][i];\r
2012       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2013     }\r
2014   }\r
2015   \r
2016   /* Parse default settings file if any */\r
2017   if (ParseSettingsFile(settingsFileName, buf)) {\r
2018     settingsFileName = strdup(buf);\r
2019   }\r
2020 \r
2021   /* Parse command line */\r
2022   ParseArgs(StringGet, &lpCmdLine);\r
2023 \r
2024   /* [HGM] make sure board size is acceptable */\r
2025   if(appData.NrFiles > BOARD_SIZE ||\r
2026      appData.NrRanks > BOARD_SIZE   )\r
2027       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2028 \r
2029   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2030    * with options from the command line, we now make an even higher priority\r
2031    * overrule by WB options attached to the engine command line. This so that\r
2032    * tournament managers can use WB options (such as /timeOdds) that follow\r
2033    * the engines.\r
2034    */\r
2035   if(appData.firstChessProgram != NULL) {\r
2036       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2037       static char *f = "first";\r
2038       char buf[MSG_SIZ], *q = buf;\r
2039       if(p != NULL) { // engine command line contains WinBoard options\r
2040           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2041           ParseArgs(StringGet, &q);\r
2042           p[-1] = 0; // cut them offengine command line\r
2043       }\r
2044   }\r
2045   // now do same for second chess program\r
2046   if(appData.secondChessProgram != NULL) {\r
2047       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2048       static char *s = "second";\r
2049       char buf[MSG_SIZ], *q = buf;\r
2050       if(p != NULL) { // engine command line contains WinBoard options\r
2051           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2052           ParseArgs(StringGet, &q);\r
2053           p[-1] = 0; // cut them offengine command line\r
2054       }\r
2055   }\r
2056 \r
2057 \r
2058   /* Propagate options that affect others */\r
2059   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2060   if (appData.icsActive || appData.noChessProgram) {\r
2061      chessProgram = FALSE;  /* not local chess program mode */\r
2062   }\r
2063 \r
2064   /* Open startup dialog if needed */\r
2065   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2066       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2067       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2068                         *appData.secondChessProgram == NULLCHAR))) {\r
2069     FARPROC lpProc;\r
2070     \r
2071     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2072     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2073     FreeProcInstance(lpProc);\r
2074   }\r
2075 \r
2076   /* Make sure save files land in the right (?) directory */\r
2077   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2078     appData.saveGameFile = strdup(buf);\r
2079   }\r
2080   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2081     appData.savePositionFile = strdup(buf);\r
2082   }\r
2083 \r
2084   /* Finish initialization for fonts and sounds */\r
2085   for (i=0; i<NUM_FONTS; i++) {\r
2086     for (j=0; j<NUM_SIZES; j++) {\r
2087       CreateFontInMF(font[j][i]);\r
2088     }\r
2089   }\r
2090   /* xboard, and older WinBoards, controlled the move sound with the\r
2091      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2092      always turn the option on (so that the backend will call us),\r
2093      then let the user turn the sound off by setting it to silence if\r
2094      desired.  To accommodate old winboard.ini files saved by old\r
2095      versions of WinBoard, we also turn off the sound if the option\r
2096      was initially set to false. */\r
2097   if (!appData.ringBellAfterMoves) {\r
2098     sounds[(int)SoundMove].name = strdup("");\r
2099     appData.ringBellAfterMoves = TRUE;\r
2100   }\r
2101   GetCurrentDirectory(MSG_SIZ, currDir);\r
2102   SetCurrentDirectory(installDir);\r
2103   LoadAllSounds();\r
2104   SetCurrentDirectory(currDir);\r
2105 \r
2106   p = icsTextMenuString;\r
2107   if (p[0] == '@') {\r
2108     FILE* f = fopen(p + 1, "r");\r
2109     if (f == NULL) {\r
2110       DisplayFatalError(p + 1, errno, 2);\r
2111       return;\r
2112     }\r
2113     i = fread(buf, 1, sizeof(buf)-1, f);\r
2114     fclose(f);\r
2115     buf[i] = NULLCHAR;\r
2116     p = buf;\r
2117   }\r
2118   ParseIcsTextMenu(strdup(p));\r
2119 }\r
2120 \r
2121 \r
2122 VOID\r
2123 InitMenuChecks()\r
2124 {\r
2125   HMENU hmenu = GetMenu(hwndMain);\r
2126 \r
2127   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2128                         MF_BYCOMMAND|((appData.icsActive &&\r
2129                                        *appData.icsCommPort != NULLCHAR) ?\r
2130                                       MF_ENABLED : MF_GRAYED));\r
2131   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2132                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2133                                      MF_CHECKED : MF_UNCHECKED));\r
2134 }\r
2135 \r
2136 \r
2137 VOID\r
2138 SaveSettings(char* name)\r
2139 {\r
2140   FILE *f;\r
2141   ArgDescriptor *ad;\r
2142   WINDOWPLACEMENT wp;\r
2143   char dir[MSG_SIZ];\r
2144 \r
2145   if (!hwndMain) return;\r
2146 \r
2147   GetCurrentDirectory(MSG_SIZ, dir);\r
2148   SetCurrentDirectory(installDir);\r
2149   f = fopen(name, "w");\r
2150   SetCurrentDirectory(dir);\r
2151   if (f == NULL) {\r
2152     DisplayError(name, errno);\r
2153     return;\r
2154   }\r
2155   fprintf(f, ";\n");\r
2156   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2157   fprintf(f, ";\n");\r
2158   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2159   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2160   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2161   fprintf(f, ";\n");\r
2162 \r
2163   wp.length = sizeof(WINDOWPLACEMENT);\r
2164   GetWindowPlacement(hwndMain, &wp);\r
2165   boardX = wp.rcNormalPosition.left;\r
2166   boardY = wp.rcNormalPosition.top;\r
2167 \r
2168   if (hwndConsole) {\r
2169     GetWindowPlacement(hwndConsole, &wp);\r
2170     consoleX = wp.rcNormalPosition.left;\r
2171     consoleY = wp.rcNormalPosition.top;\r
2172     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2173     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2174   }\r
2175 \r
2176   if (analysisDialog) {\r
2177     GetWindowPlacement(analysisDialog, &wp);\r
2178     analysisX = wp.rcNormalPosition.left;\r
2179     analysisY = wp.rcNormalPosition.top;\r
2180     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2181     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2182   }\r
2183 \r
2184   if (commentDialog) {\r
2185     GetWindowPlacement(commentDialog, &wp);\r
2186     commentX = wp.rcNormalPosition.left;\r
2187     commentY = wp.rcNormalPosition.top;\r
2188     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2189     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2190   }\r
2191 \r
2192   if (editTagsDialog) {\r
2193     GetWindowPlacement(editTagsDialog, &wp);\r
2194     editTagsX = wp.rcNormalPosition.left;\r
2195     editTagsY = wp.rcNormalPosition.top;\r
2196     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2197     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2198   }\r
2199 \r
2200   if (gameListDialog) {\r
2201     GetWindowPlacement(gameListDialog, &wp);\r
2202     gameListX = wp.rcNormalPosition.left;\r
2203     gameListY = wp.rcNormalPosition.top;\r
2204     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2205     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2206   }\r
2207 \r
2208   /* [AS] Move history */\r
2209   wpMoveHistory.visible = MoveHistoryIsUp();\r
2210   \r
2211   if( moveHistoryDialog ) {\r
2212     GetWindowPlacement(moveHistoryDialog, &wp);\r
2213     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2214     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2215     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2216     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2217   }\r
2218 \r
2219   /* [AS] Eval graph */\r
2220   wpEvalGraph.visible = EvalGraphIsUp();\r
2221 \r
2222   if( evalGraphDialog ) {\r
2223     GetWindowPlacement(evalGraphDialog, &wp);\r
2224     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2225     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2226     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2227     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2228   }\r
2229 \r
2230   /* [AS] Engine output */\r
2231   wpEngineOutput.visible = EngineOutputIsUp();\r
2232 \r
2233   if( engineOutputDialog ) {\r
2234     GetWindowPlacement(engineOutputDialog, &wp);\r
2235     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2236     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2237     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2238     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2239   }\r
2240 \r
2241   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2242     if (!ad->save) continue;\r
2243     switch (ad->argType) {\r
2244     case ArgString:\r
2245       {\r
2246         char *p = *(char **)ad->argLoc;\r
2247         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2248           /* Quote multiline values or \-containing values\r
2249              with { } if possible */\r
2250           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2251         } else {\r
2252           /* Else quote with " " */\r
2253           fprintf(f, "/%s=\"", ad->argName);\r
2254           while (*p) {\r
2255             if (*p == '\n') fprintf(f, "\n");\r
2256             else if (*p == '\r') fprintf(f, "\\r");\r
2257             else if (*p == '\t') fprintf(f, "\\t");\r
2258             else if (*p == '\b') fprintf(f, "\\b");\r
2259             else if (*p == '\f') fprintf(f, "\\f");\r
2260             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2261             else if (*p == '\"') fprintf(f, "\\\"");\r
2262             else if (*p == '\\') fprintf(f, "\\\\");\r
2263             else putc(*p, f);\r
2264             p++;\r
2265           }\r
2266           fprintf(f, "\"\n");\r
2267         }\r
2268       }\r
2269       break;\r
2270     case ArgInt:\r
2271       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2272       break;\r
2273     case ArgFloat:\r
2274       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2275       break;\r
2276     case ArgBoolean:\r
2277       fprintf(f, "/%s=%s\n", ad->argName, \r
2278         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2279       break;\r
2280     case ArgTrue:\r
2281       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2282       break;\r
2283     case ArgFalse:\r
2284       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2285       break;\r
2286     case ArgColor:\r
2287       {\r
2288         COLORREF color = *(COLORREF *)ad->argLoc;\r
2289         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2290           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2291       }\r
2292       break;\r
2293     case ArgAttribs:\r
2294       {\r
2295         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2296         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2297           (ta->effects & CFE_BOLD) ? "b" : "",\r
2298           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2299           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2300           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2301           (ta->effects) ? " " : "",\r
2302           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2303       }\r
2304       break;\r
2305     case ArgFilename:\r
2306       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2307         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2308       } else {\r
2309         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2310       }\r
2311       break;\r
2312     case ArgBoardSize:\r
2313       fprintf(f, "/%s=%s\n", ad->argName,\r
2314               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2315       break;\r
2316     case ArgFont:\r
2317       {\r
2318         int bs;\r
2319         for (bs=0; bs<NUM_SIZES; bs++) {\r
2320           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2321           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2322           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2323             ad->argName, mfp->faceName, mfp->pointSize,\r
2324             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2325             mfp->bold ? "b" : "",\r
2326             mfp->italic ? "i" : "",\r
2327             mfp->underline ? "u" : "",\r
2328             mfp->strikeout ? "s" : "");\r
2329         }\r
2330       }\r
2331       break;\r
2332     case ArgCommSettings:\r
2333       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2334     }\r
2335   }\r
2336   fclose(f);\r
2337 }\r
2338 \r
2339 \r
2340 \r
2341 /*---------------------------------------------------------------------------*\\r
2342  *\r
2343  * GDI board drawing routines\r
2344  *\r
2345 \*---------------------------------------------------------------------------*/\r
2346 \r
2347 /* [AS] Draw square using background texture */\r
2348 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2349 {\r
2350     XFORM   x;\r
2351 \r
2352     if( mode == 0 ) {\r
2353         return; /* Should never happen! */\r
2354     }\r
2355 \r
2356     SetGraphicsMode( dst, GM_ADVANCED );\r
2357 \r
2358     switch( mode ) {\r
2359     case 1:\r
2360         /* Identity */\r
2361         break;\r
2362     case 2:\r
2363         /* X reflection */\r
2364         x.eM11 = -1.0;\r
2365         x.eM12 = 0;\r
2366         x.eM21 = 0;\r
2367         x.eM22 = 1.0;\r
2368         x.eDx = (FLOAT) dw + dx - 1;\r
2369         x.eDy = 0;\r
2370         dx = 0;\r
2371         SetWorldTransform( dst, &x );\r
2372         break;\r
2373     case 3:\r
2374         /* Y reflection */\r
2375         x.eM11 = 1.0;\r
2376         x.eM12 = 0;\r
2377         x.eM21 = 0;\r
2378         x.eM22 = -1.0;\r
2379         x.eDx = 0;\r
2380         x.eDy = (FLOAT) dh + dy - 1;\r
2381         dy = 0;\r
2382         SetWorldTransform( dst, &x );\r
2383         break;\r
2384     case 4:\r
2385         /* X/Y flip */\r
2386         x.eM11 = 0;\r
2387         x.eM12 = 1.0;\r
2388         x.eM21 = 1.0;\r
2389         x.eM22 = 0;\r
2390         x.eDx = (FLOAT) dx;\r
2391         x.eDy = (FLOAT) dy;\r
2392         dx = 0;\r
2393         dy = 0;\r
2394         SetWorldTransform( dst, &x );\r
2395         break;\r
2396     }\r
2397 \r
2398     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2399 \r
2400     x.eM11 = 1.0;\r
2401     x.eM12 = 0;\r
2402     x.eM21 = 0;\r
2403     x.eM22 = 1.0;\r
2404     x.eDx = 0;\r
2405     x.eDy = 0;\r
2406     SetWorldTransform( dst, &x );\r
2407 \r
2408     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2409 }\r
2410 \r
2411 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2412 enum {\r
2413     PM_WP = (int) WhitePawn, \r
2414     PM_WN = (int) WhiteKnight, \r
2415     PM_WB = (int) WhiteBishop, \r
2416     PM_WR = (int) WhiteRook, \r
2417     PM_WQ = (int) WhiteQueen, \r
2418     PM_WF = (int) WhiteFerz, \r
2419     PM_WW = (int) WhiteWazir, \r
2420     PM_WE = (int) WhiteAlfil, \r
2421     PM_WM = (int) WhiteMan, \r
2422     PM_WO = (int) WhiteCannon, \r
2423     PM_WU = (int) WhiteUnicorn, \r
2424     PM_WH = (int) WhiteNightrider, \r
2425     PM_WA = (int) WhiteAngel, \r
2426     PM_WC = (int) WhiteMarshall, \r
2427     PM_WAB = (int) WhiteCardinal, \r
2428     PM_WD = (int) WhiteDragon, \r
2429     PM_WL = (int) WhiteLance, \r
2430     PM_WS = (int) WhiteCobra, \r
2431     PM_WV = (int) WhiteFalcon, \r
2432     PM_WSG = (int) WhiteSilver, \r
2433     PM_WG = (int) WhiteGrasshopper, \r
2434     PM_WK = (int) WhiteKing,\r
2435     PM_BP = (int) BlackPawn, \r
2436     PM_BN = (int) BlackKnight, \r
2437     PM_BB = (int) BlackBishop, \r
2438     PM_BR = (int) BlackRook, \r
2439     PM_BQ = (int) BlackQueen, \r
2440     PM_BF = (int) BlackFerz, \r
2441     PM_BW = (int) BlackWazir, \r
2442     PM_BE = (int) BlackAlfil, \r
2443     PM_BM = (int) BlackMan,\r
2444     PM_BO = (int) BlackCannon, \r
2445     PM_BU = (int) BlackUnicorn, \r
2446     PM_BH = (int) BlackNightrider, \r
2447     PM_BA = (int) BlackAngel, \r
2448     PM_BC = (int) BlackMarshall, \r
2449     PM_BG = (int) BlackGrasshopper, \r
2450     PM_BAB = (int) BlackCardinal,\r
2451     PM_BD = (int) BlackDragon,\r
2452     PM_BL = (int) BlackLance,\r
2453     PM_BS = (int) BlackCobra,\r
2454     PM_BV = (int) BlackFalcon,\r
2455     PM_BSG = (int) BlackSilver,\r
2456     PM_BK = (int) BlackKing\r
2457 };\r
2458 \r
2459 static HFONT hPieceFont = NULL;\r
2460 static HBITMAP hPieceMask[(int) EmptySquare];\r
2461 static HBITMAP hPieceFace[(int) EmptySquare];\r
2462 static int fontBitmapSquareSize = 0;\r
2463 static char pieceToFontChar[(int) EmptySquare] =\r
2464                               { 'p', 'n', 'b', 'r', 'q', \r
2465                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2466                       'k', 'o', 'm', 'v', 't', 'w', \r
2467                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2468                                                               'l' };\r
2469 \r
2470 extern BOOL SetCharTable( char *table, const char * map );\r
2471 /* [HGM] moved to backend.c */\r
2472 \r
2473 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2474 {\r
2475     HBRUSH hbrush;\r
2476     BYTE r1 = GetRValue( color );\r
2477     BYTE g1 = GetGValue( color );\r
2478     BYTE b1 = GetBValue( color );\r
2479     BYTE r2 = r1 / 2;\r
2480     BYTE g2 = g1 / 2;\r
2481     BYTE b2 = b1 / 2;\r
2482     RECT rc;\r
2483 \r
2484     /* Create a uniform background first */\r
2485     hbrush = CreateSolidBrush( color );\r
2486     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2487     FillRect( hdc, &rc, hbrush );\r
2488     DeleteObject( hbrush );\r
2489     \r
2490     if( mode == 1 ) {\r
2491         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2492         int steps = squareSize / 2;\r
2493         int i;\r
2494 \r
2495         for( i=0; i<steps; i++ ) {\r
2496             BYTE r = r1 - (r1-r2) * i / steps;\r
2497             BYTE g = g1 - (g1-g2) * i / steps;\r
2498             BYTE b = b1 - (b1-b2) * i / steps;\r
2499 \r
2500             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2501             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2502             FillRect( hdc, &rc, hbrush );\r
2503             DeleteObject(hbrush);\r
2504         }\r
2505     }\r
2506     else if( mode == 2 ) {\r
2507         /* Diagonal gradient, good more or less for every piece */\r
2508         POINT triangle[3];\r
2509         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2510         HBRUSH hbrush_old;\r
2511         int steps = squareSize;\r
2512         int i;\r
2513 \r
2514         triangle[0].x = squareSize - steps;\r
2515         triangle[0].y = squareSize;\r
2516         triangle[1].x = squareSize;\r
2517         triangle[1].y = squareSize;\r
2518         triangle[2].x = squareSize;\r
2519         triangle[2].y = squareSize - steps;\r
2520 \r
2521         for( i=0; i<steps; i++ ) {\r
2522             BYTE r = r1 - (r1-r2) * i / steps;\r
2523             BYTE g = g1 - (g1-g2) * i / steps;\r
2524             BYTE b = b1 - (b1-b2) * i / steps;\r
2525 \r
2526             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2527             hbrush_old = SelectObject( hdc, hbrush );\r
2528             Polygon( hdc, triangle, 3 );\r
2529             SelectObject( hdc, hbrush_old );\r
2530             DeleteObject(hbrush);\r
2531             triangle[0].x++;\r
2532             triangle[2].y++;\r
2533         }\r
2534 \r
2535         SelectObject( hdc, hpen );\r
2536     }\r
2537 }\r
2538 \r
2539 /*\r
2540     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2541     seems to work ok. The main problem here is to find the "inside" of a chess\r
2542     piece: follow the steps as explained below.\r
2543 */\r
2544 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2545 {\r
2546     HBITMAP hbm;\r
2547     HBITMAP hbm_old;\r
2548     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2549     RECT rc;\r
2550     SIZE sz;\r
2551     POINT pt;\r
2552     int backColor = whitePieceColor; \r
2553     int foreColor = blackPieceColor;\r
2554     \r
2555     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2556         backColor = appData.fontBackColorWhite;\r
2557         foreColor = appData.fontForeColorWhite;\r
2558     }\r
2559     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2560         backColor = appData.fontBackColorBlack;\r
2561         foreColor = appData.fontForeColorBlack;\r
2562     }\r
2563 \r
2564     /* Mask */\r
2565     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2566 \r
2567     hbm_old = SelectObject( hdc, hbm );\r
2568 \r
2569     rc.left = 0;\r
2570     rc.top = 0;\r
2571     rc.right = squareSize;\r
2572     rc.bottom = squareSize;\r
2573 \r
2574     /* Step 1: background is now black */\r
2575     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2576 \r
2577     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2578 \r
2579     pt.x = (squareSize - sz.cx) / 2;\r
2580     pt.y = (squareSize - sz.cy) / 2;\r
2581 \r
2582     SetBkMode( hdc, TRANSPARENT );\r
2583     SetTextColor( hdc, chroma );\r
2584     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2585     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2586 \r
2587     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2588     /* Step 3: the area outside the piece is filled with white */\r
2589 //    FloodFill( hdc, 0, 0, chroma );\r
2590     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2591     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2592     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2593     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2594     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2595     /* \r
2596         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2597         but if the start point is not inside the piece we're lost!\r
2598         There should be a better way to do this... if we could create a region or path\r
2599         from the fill operation we would be fine for example.\r
2600     */\r
2601 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2602     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2603 \r
2604     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2605         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2606         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2607 \r
2608         SelectObject( dc2, bm2 );\r
2609         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2610         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2611         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2612         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2613         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2614 \r
2615         DeleteDC( dc2 );\r
2616         DeleteObject( bm2 );\r
2617     }\r
2618 \r
2619     SetTextColor( hdc, 0 );\r
2620     /* \r
2621         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2622         draw the piece again in black for safety.\r
2623     */\r
2624     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2625 \r
2626     SelectObject( hdc, hbm_old );\r
2627 \r
2628     if( hPieceMask[index] != NULL ) {\r
2629         DeleteObject( hPieceMask[index] );\r
2630     }\r
2631 \r
2632     hPieceMask[index] = hbm;\r
2633 \r
2634     /* Face */\r
2635     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2636 \r
2637     SelectObject( hdc, hbm );\r
2638 \r
2639     {\r
2640         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2641         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2642         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2643 \r
2644         SelectObject( dc1, hPieceMask[index] );\r
2645         SelectObject( dc2, bm2 );\r
2646         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2647         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2648         \r
2649         /* \r
2650             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2651             the piece background and deletes (makes transparent) the rest.\r
2652             Thanks to that mask, we are free to paint the background with the greates\r
2653             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2654             We use this, to make gradients and give the pieces a "roundish" look.\r
2655         */\r
2656         SetPieceBackground( hdc, backColor, 2 );\r
2657         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2658 \r
2659         DeleteDC( dc2 );\r
2660         DeleteDC( dc1 );\r
2661         DeleteObject( bm2 );\r
2662     }\r
2663 \r
2664     SetTextColor( hdc, foreColor );\r
2665     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2666 \r
2667     SelectObject( hdc, hbm_old );\r
2668 \r
2669     if( hPieceFace[index] != NULL ) {\r
2670         DeleteObject( hPieceFace[index] );\r
2671     }\r
2672 \r
2673     hPieceFace[index] = hbm;\r
2674 }\r
2675 \r
2676 static int TranslatePieceToFontPiece( int piece )\r
2677 {\r
2678     switch( piece ) {\r
2679     case BlackPawn:\r
2680         return PM_BP;\r
2681     case BlackKnight:\r
2682         return PM_BN;\r
2683     case BlackBishop:\r
2684         return PM_BB;\r
2685     case BlackRook:\r
2686         return PM_BR;\r
2687     case BlackQueen:\r
2688         return PM_BQ;\r
2689     case BlackKing:\r
2690         return PM_BK;\r
2691     case WhitePawn:\r
2692         return PM_WP;\r
2693     case WhiteKnight:\r
2694         return PM_WN;\r
2695     case WhiteBishop:\r
2696         return PM_WB;\r
2697     case WhiteRook:\r
2698         return PM_WR;\r
2699     case WhiteQueen:\r
2700         return PM_WQ;\r
2701     case WhiteKing:\r
2702         return PM_WK;\r
2703 \r
2704     case BlackAngel:\r
2705         return PM_BA;\r
2706     case BlackMarshall:\r
2707         return PM_BC;\r
2708     case BlackFerz:\r
2709         return PM_BF;\r
2710     case BlackNightrider:\r
2711         return PM_BH;\r
2712     case BlackAlfil:\r
2713         return PM_BE;\r
2714     case BlackWazir:\r
2715         return PM_BW;\r
2716     case BlackUnicorn:\r
2717         return PM_BU;\r
2718     case BlackCannon:\r
2719         return PM_BO;\r
2720     case BlackGrasshopper:\r
2721         return PM_BG;\r
2722     case BlackMan:\r
2723         return PM_BM;\r
2724     case BlackSilver:\r
2725         return PM_BSG;\r
2726     case BlackLance:\r
2727         return PM_BL;\r
2728     case BlackFalcon:\r
2729         return PM_BV;\r
2730     case BlackCobra:\r
2731         return PM_BS;\r
2732     case BlackCardinal:\r
2733         return PM_BAB;\r
2734     case BlackDragon:\r
2735         return PM_BD;\r
2736 \r
2737     case WhiteAngel:\r
2738         return PM_WA;\r
2739     case WhiteMarshall:\r
2740         return PM_WC;\r
2741     case WhiteFerz:\r
2742         return PM_WF;\r
2743     case WhiteNightrider:\r
2744         return PM_WH;\r
2745     case WhiteAlfil:\r
2746         return PM_WE;\r
2747     case WhiteWazir:\r
2748         return PM_WW;\r
2749     case WhiteUnicorn:\r
2750         return PM_WU;\r
2751     case WhiteCannon:\r
2752         return PM_WO;\r
2753     case WhiteGrasshopper:\r
2754         return PM_WG;\r
2755     case WhiteMan:\r
2756         return PM_WM;\r
2757     case WhiteSilver:\r
2758         return PM_WSG;\r
2759     case WhiteLance:\r
2760         return PM_WL;\r
2761     case WhiteFalcon:\r
2762         return PM_WV;\r
2763     case WhiteCobra:\r
2764         return PM_WS;\r
2765     case WhiteCardinal:\r
2766         return PM_WAB;\r
2767     case WhiteDragon:\r
2768         return PM_WD;\r
2769     }\r
2770 \r
2771     return 0;\r
2772 }\r
2773 \r
2774 void CreatePiecesFromFont()\r
2775 {\r
2776     LOGFONT lf;\r
2777     HDC hdc_window = NULL;\r
2778     HDC hdc = NULL;\r
2779     HFONT hfont_old;\r
2780     int fontHeight;\r
2781     int i;\r
2782 \r
2783     if( fontBitmapSquareSize < 0 ) {\r
2784         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2785         return;\r
2786     }\r
2787 \r
2788     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2789         fontBitmapSquareSize = -1;\r
2790         return;\r
2791     }\r
2792 \r
2793     if( fontBitmapSquareSize != squareSize ) {\r
2794         hdc_window = GetDC( hwndMain );\r
2795         hdc = CreateCompatibleDC( hdc_window );\r
2796 \r
2797         if( hPieceFont != NULL ) {\r
2798             DeleteObject( hPieceFont );\r
2799         }\r
2800         else {\r
2801             for( i=0; i<=(int)BlackKing; i++ ) {\r
2802                 hPieceMask[i] = NULL;\r
2803                 hPieceFace[i] = NULL;\r
2804             }\r
2805         }\r
2806 \r
2807         fontHeight = 75;\r
2808 \r
2809         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2810             fontHeight = appData.fontPieceSize;\r
2811         }\r
2812 \r
2813         fontHeight = (fontHeight * squareSize) / 100;\r
2814 \r
2815         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2816         lf.lfWidth = 0;\r
2817         lf.lfEscapement = 0;\r
2818         lf.lfOrientation = 0;\r
2819         lf.lfWeight = FW_NORMAL;\r
2820         lf.lfItalic = 0;\r
2821         lf.lfUnderline = 0;\r
2822         lf.lfStrikeOut = 0;\r
2823         lf.lfCharSet = DEFAULT_CHARSET;\r
2824         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2825         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2826         lf.lfQuality = PROOF_QUALITY;\r
2827         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2828         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2829         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2830 \r
2831         hPieceFont = CreateFontIndirect( &lf );\r
2832 \r
2833         if( hPieceFont == NULL ) {\r
2834             fontBitmapSquareSize = -2;\r
2835         }\r
2836         else {\r
2837             /* Setup font-to-piece character table */\r
2838             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2839                 /* No (or wrong) global settings, try to detect the font */\r
2840                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2841                     /* Alpha */\r
2842                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2843                 }\r
2844                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2845                     /* DiagramTT* family */\r
2846                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2847                 }\r
2848                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2849                     /* Fairy symbols */\r
2850                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2851                 }\r
2852                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2853                     /* Good Companion (Some characters get warped as literal :-( */\r
2854                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2855                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2856                     SetCharTable(pieceToFontChar, s);\r
2857                 }\r
2858                 else {\r
2859                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2860                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2861                 }\r
2862             }\r
2863 \r
2864             /* Create bitmaps */\r
2865             hfont_old = SelectObject( hdc, hPieceFont );\r
2866 #if 0\r
2867             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2868             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2869             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2870             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2871             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2872             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2873             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2874             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2875             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2876             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2877             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2878             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2879 \r
2880             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2881             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2882             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2883             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2884             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2885             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2908             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2909             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2910             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2911             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2912 #else\r
2913             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2914                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2915                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2916 #endif\r
2917             SelectObject( hdc, hfont_old );\r
2918 \r
2919             fontBitmapSquareSize = squareSize;\r
2920         }\r
2921     }\r
2922 \r
2923     if( hdc != NULL ) {\r
2924         DeleteDC( hdc );\r
2925     }\r
2926 \r
2927     if( hdc_window != NULL ) {\r
2928         ReleaseDC( hwndMain, hdc_window );\r
2929     }\r
2930 }\r
2931 \r
2932 HBITMAP\r
2933 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2934 {\r
2935   char name[128];\r
2936 \r
2937   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2938   if (gameInfo.event &&\r
2939       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2940       strcmp(name, "k80s") == 0) {\r
2941     strcpy(name, "tim");\r
2942   }\r
2943   return LoadBitmap(hinst, name);\r
2944 }\r
2945 \r
2946 \r
2947 /* Insert a color into the program's logical palette\r
2948    structure.  This code assumes the given color is\r
2949    the result of the RGB or PALETTERGB macro, and it\r
2950    knows how those macros work (which is documented).\r
2951 */\r
2952 VOID\r
2953 InsertInPalette(COLORREF color)\r
2954 {\r
2955   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2956 \r
2957   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2958     DisplayFatalError("Too many colors", 0, 1);\r
2959     pLogPal->palNumEntries--;\r
2960     return;\r
2961   }\r
2962 \r
2963   pe->peFlags = (char) 0;\r
2964   pe->peRed = (char) (0xFF & color);\r
2965   pe->peGreen = (char) (0xFF & (color >> 8));\r
2966   pe->peBlue = (char) (0xFF & (color >> 16));\r
2967   return;\r
2968 }\r
2969 \r
2970 \r
2971 VOID\r
2972 InitDrawingColors()\r
2973 {\r
2974   if (pLogPal == NULL) {\r
2975     /* Allocate enough memory for a logical palette with\r
2976      * PALETTESIZE entries and set the size and version fields\r
2977      * of the logical palette structure.\r
2978      */\r
2979     pLogPal = (NPLOGPALETTE)\r
2980       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2981                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2982     pLogPal->palVersion    = 0x300;\r
2983   }\r
2984   pLogPal->palNumEntries = 0;\r
2985 \r
2986   InsertInPalette(lightSquareColor);\r
2987   InsertInPalette(darkSquareColor);\r
2988   InsertInPalette(whitePieceColor);\r
2989   InsertInPalette(blackPieceColor);\r
2990   InsertInPalette(highlightSquareColor);\r
2991   InsertInPalette(premoveHighlightColor);\r
2992 \r
2993   /*  create a logical color palette according the information\r
2994    *  in the LOGPALETTE structure.\r
2995    */\r
2996   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2997 \r
2998   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2999   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3000   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3001   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3002   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3003   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3004 \r
3005   /* [AS] Force rendering of the font-based pieces */\r
3006   if( fontBitmapSquareSize > 0 ) {\r
3007     fontBitmapSquareSize = 0;\r
3008   }\r
3009 }\r
3010 \r
3011 \r
3012 int\r
3013 BoardWidth(int boardSize, int n)\r
3014 { /* [HGM] argument n added to allow different width and height */\r
3015   int lineGap = sizeInfo[boardSize].lineGap;\r
3016 \r
3017   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3018       lineGap = appData.overrideLineGap;\r
3019   }\r
3020 \r
3021   return (n + 1) * lineGap +\r
3022           n * sizeInfo[boardSize].squareSize;\r
3023 }\r
3024 \r
3025 /* Respond to board resize by dragging edge */\r
3026 VOID\r
3027 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3028 {\r
3029   BoardSize newSize = NUM_SIZES - 1;\r
3030   static int recurse = 0;\r
3031   if (IsIconic(hwndMain)) return;\r
3032   if (recurse > 0) return;\r
3033   recurse++;\r
3034   while (newSize > 0) {\r
3035         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3036         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3037            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3038     newSize--;\r
3039   } \r
3040   boardSize = newSize;\r
3041   InitDrawingSizes(boardSize, flags);\r
3042   recurse--;\r
3043 }\r
3044 \r
3045 \r
3046 \r
3047 VOID\r
3048 InitDrawingSizes(BoardSize boardSize, int flags)\r
3049 {\r
3050   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3051   ChessSquare piece;\r
3052   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3053   HDC hdc;\r
3054   SIZE clockSize, messageSize;\r
3055   HFONT oldFont;\r
3056   char buf[MSG_SIZ];\r
3057   char *str;\r
3058   HMENU hmenu = GetMenu(hwndMain);\r
3059   RECT crect, wrect;\r
3060   int offby;\r
3061   LOGBRUSH logbrush;\r
3062 \r
3063   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3064   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3065 \r
3066   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3067   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3068 \r
3069   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3070   smallLayout = sizeInfo[boardSize].smallLayout;\r
3071   squareSize = sizeInfo[boardSize].squareSize;\r
3072   lineGap = sizeInfo[boardSize].lineGap;\r
3073   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3074 \r
3075   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3076       lineGap = appData.overrideLineGap;\r
3077   }\r
3078 \r
3079   if (tinyLayout != oldTinyLayout) {\r
3080     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3081     if (tinyLayout) {\r
3082       style &= ~WS_SYSMENU;\r
3083       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3084                  "&Minimize\tCtrl+F4");\r
3085     } else {\r
3086       style |= WS_SYSMENU;\r
3087       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3088     }\r
3089     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3090 \r
3091     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3092       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3093         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3094     }\r
3095     DrawMenuBar(hwndMain);\r
3096   }\r
3097 \r
3098   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3099   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3100 \r
3101   /* Get text area sizes */\r
3102   hdc = GetDC(hwndMain);\r
3103   if (appData.clockMode) {\r
3104     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3105   } else {\r
3106     sprintf(buf, "White");\r
3107   }\r
3108   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3109   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3110   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3111   str = "We only care about the height here";\r
3112   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3113   SelectObject(hdc, oldFont);\r
3114   ReleaseDC(hwndMain, hdc);\r
3115 \r
3116   /* Compute where everything goes */\r
3117   if(first.programLogo || second.programLogo) {\r
3118         /* [HGM] logo: if either logo is on, reserve space for it */\r
3119         logoHeight =  2*clockSize.cy;\r
3120         leftLogoRect.left   = OUTER_MARGIN;\r
3121         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3122         leftLogoRect.top    = OUTER_MARGIN;\r
3123         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3124 \r
3125         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3126         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3127         rightLogoRect.top    = OUTER_MARGIN;\r
3128         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3129 \r
3130 \r
3131     blackRect.left = leftLogoRect.right;\r
3132     blackRect.right = rightLogoRect.left;\r
3133     blackRect.top = OUTER_MARGIN;\r
3134     blackRect.bottom = blackRect.top + clockSize.cy;\r
3135 \r
3136     whiteRect.left = blackRect.left ;\r
3137     whiteRect.right = blackRect.right;\r
3138     whiteRect.top = blackRect.bottom;\r
3139     whiteRect.bottom = leftLogoRect.bottom;\r
3140   } else {\r
3141     whiteRect.left = OUTER_MARGIN;\r
3142     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3143     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3144     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3145 \r
3146     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3147     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3148     blackRect.top = whiteRect.top;\r
3149     blackRect.bottom = whiteRect.bottom;\r
3150   }\r
3151 \r
3152   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3153   if (appData.showButtonBar) {\r
3154     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3155       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3156   } else {\r
3157     messageRect.right = OUTER_MARGIN + boardWidth;\r
3158   }\r
3159   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3160   messageRect.bottom = messageRect.top + messageSize.cy;\r
3161 \r
3162   boardRect.left = OUTER_MARGIN;\r
3163   boardRect.right = boardRect.left + boardWidth;\r
3164   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3165   boardRect.bottom = boardRect.top + boardHeight;\r
3166 \r
3167   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3168   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3169   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3170   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3171   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3172     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3173   GetWindowRect(hwndMain, &wrect);\r
3174   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3175                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3176   /* compensate if menu bar wrapped */\r
3177   GetClientRect(hwndMain, &crect);\r
3178   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3179   winHeight += offby;\r
3180   switch (flags) {\r
3181   case WMSZ_TOPLEFT:\r
3182     SetWindowPos(hwndMain, NULL, \r
3183                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3184                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3185     break;\r
3186 \r
3187   case WMSZ_TOPRIGHT:\r
3188   case WMSZ_TOP:\r
3189     SetWindowPos(hwndMain, NULL, \r
3190                  wrect.left, wrect.bottom - winHeight, \r
3191                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3192     break;\r
3193 \r
3194   case WMSZ_BOTTOMLEFT:\r
3195   case WMSZ_LEFT:\r
3196     SetWindowPos(hwndMain, NULL, \r
3197                  wrect.right - winWidth, wrect.top, \r
3198                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3199     break;\r
3200 \r
3201   case WMSZ_BOTTOMRIGHT:\r
3202   case WMSZ_BOTTOM:\r
3203   case WMSZ_RIGHT:\r
3204   default:\r
3205     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3206                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3207     break;\r
3208   }\r
3209 \r
3210   hwndPause = NULL;\r
3211   for (i = 0; i < N_BUTTONS; i++) {\r
3212     if (buttonDesc[i].hwnd != NULL) {\r
3213       DestroyWindow(buttonDesc[i].hwnd);\r
3214       buttonDesc[i].hwnd = NULL;\r
3215     }\r
3216     if (appData.showButtonBar) {\r
3217       buttonDesc[i].hwnd =\r
3218         CreateWindow("BUTTON", buttonDesc[i].label,\r
3219                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3220                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3221                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3222                      (HMENU) buttonDesc[i].id,\r
3223                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3224       if (tinyLayout) {\r
3225         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3226                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3227                     MAKELPARAM(FALSE, 0));\r
3228       }\r
3229       if (buttonDesc[i].id == IDM_Pause)\r
3230         hwndPause = buttonDesc[i].hwnd;\r
3231       buttonDesc[i].wndproc = (WNDPROC)\r
3232         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3233     }\r
3234   }\r
3235   if (gridPen != NULL) DeleteObject(gridPen);\r
3236   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3237   if (premovePen != NULL) DeleteObject(premovePen);\r
3238   if (lineGap != 0) {\r
3239     logbrush.lbStyle = BS_SOLID;\r
3240     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3241     gridPen =\r
3242       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3243                    lineGap, &logbrush, 0, NULL);\r
3244     logbrush.lbColor = highlightSquareColor;\r
3245     highlightPen =\r
3246       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3247                    lineGap, &logbrush, 0, NULL);\r
3248 \r
3249     logbrush.lbColor = premoveHighlightColor; \r
3250     premovePen =\r
3251       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3252                    lineGap, &logbrush, 0, NULL);\r
3253 \r
3254     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3255     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3256       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3257       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3258         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3259       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3260         BOARD_WIDTH * (squareSize + lineGap);\r
3261         lineGap / 2 + (i * (squareSize + lineGap));\r
3262       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3263     }\r
3264     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3265       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3266       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3267         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3268         lineGap / 2 + (i * (squareSize + lineGap));\r
3269       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3270         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3271       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3272     }\r
3273   }\r
3274 \r
3275   /* [HGM] Licensing requirement */\r
3276 #ifdef GOTHIC\r
3277   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3278 #endif\r
3279 #ifdef FALCON\r
3280   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3281 #endif\r
3282   GothicPopUp( "", VariantNormal);\r
3283 \r
3284 \r
3285 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3286   oldBoardSize = boardSize;\r
3287   oldTinyLayout = tinyLayout;\r
3288 \r
3289   /* Load piece bitmaps for this board size */\r
3290   for (i=0; i<=2; i++) {\r
3291     for (piece = WhitePawn;\r
3292          (int) piece < (int) BlackPawn;\r
3293          piece = (ChessSquare) ((int) piece + 1)) {\r
3294       if (pieceBitmap[i][piece] != NULL)\r
3295         DeleteObject(pieceBitmap[i][piece]);\r
3296     }\r
3297   }\r
3298 \r
3299   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3300   // Orthodox Chess pieces\r
3301   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3302   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3303   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3304   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3305   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3306   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3307   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3308   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3309   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3310   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3311   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3312   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3313   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3314   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3315   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3316   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3317     // in Shogi, Hijack the unused Queen for Lance\r
3318     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3319     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3320     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3321   } else {\r
3322     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3323     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3324     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3325   }\r
3326 \r
3327   if(squareSize <= 72 && squareSize >= 33) { \r
3328     /* A & C are available in most sizes now */\r
3329     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3330       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3331       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3332       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3333       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3334       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3335       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3336       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3337       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3338       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3339       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3340       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3341       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3342     } else { // Smirf-like\r
3343       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3344       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3345       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3346     }\r
3347     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3348       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3349       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3350       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3351     } else { // WinBoard standard\r
3352       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3353       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3354       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3355     }\r
3356   }\r
3357 \r
3358 \r
3359   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3360     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3361     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3362     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3363     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3364     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3365     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3366     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3367     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3368     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3369     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3370     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3371     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3372     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3373     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3374     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3375     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3376     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3377     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3378     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3379     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3380     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3381     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3382     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3383     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3384     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3385     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3386     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3387     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3388     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3389     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3390 \r
3391     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3392       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3393       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3394       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3395       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3396       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3397       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3398       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3399       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3400       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3401       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3402       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3403       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3404     } else {\r
3405       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3406       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3407       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3408       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3409       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3410       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3411       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3412       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3413       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3414       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3415       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3416       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3417     }\r
3418 \r
3419   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3420     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3421     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3422     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3423     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3424     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3425     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3426     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3427     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3428     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3429     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3430     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3431     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3432     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3433     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3434   }\r
3435 \r
3436 \r
3437   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3438   /* special Shogi support in this size */\r
3439   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3440       for (piece = WhitePawn;\r
3441            (int) piece < (int) BlackPawn;\r
3442            piece = (ChessSquare) ((int) piece + 1)) {\r
3443         if (pieceBitmap[i][piece] != NULL)\r
3444           DeleteObject(pieceBitmap[i][piece]);\r
3445       }\r
3446     }\r
3447   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3448   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3449   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3450   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3451   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3452   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3453   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3454   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3455   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3456   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3457   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3458   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3459   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3460   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3461   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3462   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3463   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3464   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3465   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3466   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3467   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3468   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3469   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3470   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3471   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3472   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3473   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3474   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3475   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3476   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3477   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3478   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3479   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3480   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3481   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3482   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3483   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3484   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3485   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3486   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3487   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3488   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3489   minorSize = 0;\r
3490   }\r
3491 }\r
3492 \r
3493 HBITMAP\r
3494 PieceBitmap(ChessSquare p, int kind)\r
3495 {\r
3496   if ((int) p >= (int) BlackPawn)\r
3497     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3498 \r
3499   return pieceBitmap[kind][(int) p];\r
3500 }\r
3501 \r
3502 /***************************************************************/\r
3503 \r
3504 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3505 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3506 /*\r
3507 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3508 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3509 */\r
3510 \r
3511 VOID\r
3512 SquareToPos(int row, int column, int * x, int * y)\r
3513 {\r
3514   if (flipView) {\r
3515     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3516     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3517   } else {\r
3518     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3519     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3520   }\r
3521 }\r
3522 \r
3523 VOID\r
3524 DrawCoordsOnDC(HDC hdc)\r
3525 {\r
3526   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
3527   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
3528   char str[2] = { NULLCHAR, NULLCHAR };\r
3529   int oldMode, oldAlign, x, y, start, i;\r
3530   HFONT oldFont;\r
3531   HBRUSH oldBrush;\r
3532 \r
3533   if (!appData.showCoords)\r
3534     return;\r
3535 \r
3536   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3537 \r
3538   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3539   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3540   oldAlign = GetTextAlign(hdc);\r
3541   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3542 \r
3543   y = boardRect.top + lineGap;\r
3544   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3545 \r
3546   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3547   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3548     str[0] = files[start + i];\r
3549     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3550     y += squareSize + lineGap;\r
3551   }\r
3552 \r
3553   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3554 \r
3555   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3556   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3557     str[0] = ranks[start + i];\r
3558     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3559     x += squareSize + lineGap;\r
3560   }    \r
3561 \r
3562   SelectObject(hdc, oldBrush);\r
3563   SetBkMode(hdc, oldMode);\r
3564   SetTextAlign(hdc, oldAlign);\r
3565   SelectObject(hdc, oldFont);\r
3566 }\r
3567 \r
3568 VOID\r
3569 DrawGridOnDC(HDC hdc)\r
3570 {\r
3571   HPEN oldPen;\r
3572  \r
3573   if (lineGap != 0) {\r
3574     oldPen = SelectObject(hdc, gridPen);\r
3575     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3576     SelectObject(hdc, oldPen);\r
3577   }\r
3578 }\r
3579 \r
3580 #define HIGHLIGHT_PEN 0\r
3581 #define PREMOVE_PEN   1\r
3582 \r
3583 VOID\r
3584 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3585 {\r
3586   int x1, y1;\r
3587   HPEN oldPen, hPen;\r
3588   if (lineGap == 0) return;\r
3589   if (flipView) {\r
3590     x1 = boardRect.left +\r
3591       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3592     y1 = boardRect.top +\r
3593       lineGap/2 + y * (squareSize + lineGap);\r
3594   } else {\r
3595     x1 = boardRect.left +\r
3596       lineGap/2 + x * (squareSize + lineGap);\r
3597     y1 = boardRect.top +\r
3598       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3599   }\r
3600   hPen = pen ? premovePen : highlightPen;\r
3601   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3602   MoveToEx(hdc, x1, y1, NULL);\r
3603   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3604   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3605   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3606   LineTo(hdc, x1, y1);\r
3607   SelectObject(hdc, oldPen);\r
3608 }\r
3609 \r
3610 VOID\r
3611 DrawHighlightsOnDC(HDC hdc)\r
3612 {\r
3613   int i;\r
3614   for (i=0; i<2; i++) {\r
3615     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3616       DrawHighlightOnDC(hdc, TRUE,\r
3617                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3618                         HIGHLIGHT_PEN);\r
3619   }\r
3620   for (i=0; i<2; i++) {\r
3621     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3622         premoveHighlightInfo.sq[i].y >= 0) {\r
3623         DrawHighlightOnDC(hdc, TRUE,\r
3624                           premoveHighlightInfo.sq[i].x, \r
3625                           premoveHighlightInfo.sq[i].y,\r
3626                           PREMOVE_PEN);\r
3627     }\r
3628   }\r
3629 }\r
3630 \r
3631 /* Note: sqcolor is used only in monoMode */\r
3632 /* Note that this code is largely duplicated in woptions.c,\r
3633    function DrawSampleSquare, so that needs to be updated too */\r
3634 VOID\r
3635 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3636 {\r
3637   HBITMAP oldBitmap;\r
3638   HBRUSH oldBrush;\r
3639   int tmpSize;\r
3640 \r
3641   if (appData.blindfold) return;\r
3642 \r
3643   /* [AS] Use font-based pieces if needed */\r
3644   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3645     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3646     CreatePiecesFromFont();\r
3647 \r
3648     if( fontBitmapSquareSize == squareSize ) {\r
3649         int index = TranslatePieceToFontPiece(piece);\r
3650 \r
3651         SelectObject( tmphdc, hPieceMask[ index ] );\r
3652 \r
3653         BitBlt( hdc,\r
3654             x, y,\r
3655             squareSize, squareSize,\r
3656             tmphdc,\r
3657             0, 0,\r
3658             SRCAND );\r
3659 \r
3660         SelectObject( tmphdc, hPieceFace[ index ] );\r
3661 \r
3662         BitBlt( hdc,\r
3663             x, y,\r
3664             squareSize, squareSize,\r
3665             tmphdc,\r
3666             0, 0,\r
3667             SRCPAINT );\r
3668 \r
3669         return;\r
3670     }\r
3671   }\r
3672 \r
3673   if (appData.monoMode) {\r
3674     SelectObject(tmphdc, PieceBitmap(piece, \r
3675       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3676     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3677            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3678   } else {\r
3679     tmpSize = squareSize;\r
3680     if(minorSize &&\r
3681         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3682          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3683       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3684       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3685       x += (squareSize - minorSize)>>1;\r
3686       y += squareSize - minorSize - 2;\r
3687       tmpSize = minorSize;\r
3688     }\r
3689     if (color || appData.allWhite ) {\r
3690       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3691       if( color )\r
3692               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3693       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3694       if(appData.upsideDown && color==flipView)\r
3695         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3696       else\r
3697         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3698 #if 0\r
3699       /* Use black piece color for outline of white pieces */\r
3700       /* Not sure this looks really good (though xboard does it).\r
3701          Maybe better to have another selectable color, default black */\r
3702       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3703       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3704       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3705 #else\r
3706       /* Use black for outline of white pieces */\r
3707       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3708       if(appData.upsideDown && color==flipView)\r
3709         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3710       else\r
3711         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3712 #endif\r
3713     } else {\r
3714 #if 0\r
3715       /* Use white piece color for details of black pieces */\r
3716       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3717          WHITE_PIECE ones aren't always the right shape. */\r
3718       /* Not sure this looks really good (though xboard does it).\r
3719          Maybe better to have another selectable color, default medium gray? */\r
3720       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3721       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3722       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3723       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3724       SelectObject(hdc, blackPieceBrush);\r
3725       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3726 #else\r
3727       /* Use square color for details of black pieces */\r
3728       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3729       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3730       if(appData.upsideDown && !flipView)\r
3731         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3732       else\r
3733         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3734 #endif\r
3735     }\r
3736     SelectObject(hdc, oldBrush);\r
3737     SelectObject(tmphdc, oldBitmap);\r
3738   }\r
3739 }\r
3740 \r
3741 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3742 int GetBackTextureMode( int algo )\r
3743 {\r
3744     int result = BACK_TEXTURE_MODE_DISABLED;\r
3745 \r
3746     switch( algo ) \r
3747     {\r
3748         case BACK_TEXTURE_MODE_PLAIN:\r
3749             result = 1; /* Always use identity map */\r
3750             break;\r
3751         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3752             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3753             break;\r
3754     }\r
3755 \r
3756     return result;\r
3757 }\r
3758 \r
3759 /* \r
3760     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3761     to handle redraws cleanly (as random numbers would always be different).\r
3762 */\r
3763 VOID RebuildTextureSquareInfo()\r
3764 {\r
3765     BITMAP bi;\r
3766     int lite_w = 0;\r
3767     int lite_h = 0;\r
3768     int dark_w = 0;\r
3769     int dark_h = 0;\r
3770     int row;\r
3771     int col;\r
3772 \r
3773     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3774 \r
3775     if( liteBackTexture != NULL ) {\r
3776         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3777             lite_w = bi.bmWidth;\r
3778             lite_h = bi.bmHeight;\r
3779         }\r
3780     }\r
3781 \r
3782     if( darkBackTexture != NULL ) {\r
3783         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3784             dark_w = bi.bmWidth;\r
3785             dark_h = bi.bmHeight;\r
3786         }\r
3787     }\r
3788 \r
3789     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3790         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3791             if( (col + row) & 1 ) {\r
3792                 /* Lite square */\r
3793                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3794                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3795                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3796                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3797                 }\r
3798             }\r
3799             else {\r
3800                 /* Dark square */\r
3801                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3802                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3803                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3804                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3805                 }\r
3806             }\r
3807         }\r
3808     }\r
3809 }\r
3810 \r
3811 /* [AS] Arrow highlighting support */\r
3812 \r
3813 static int A_WIDTH = 5; /* Width of arrow body */\r
3814 \r
3815 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3816 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3817 \r
3818 static double Sqr( double x )\r
3819 {\r
3820     return x*x;\r
3821 }\r
3822 \r
3823 static int Round( double x )\r
3824 {\r
3825     return (int) (x + 0.5);\r
3826 }\r
3827 \r
3828 /* Draw an arrow between two points using current settings */\r
3829 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3830 {\r
3831     POINT arrow[7];\r
3832     double dx, dy, j, k, x, y;\r
3833 \r
3834     if( d_x == s_x ) {\r
3835         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3836 \r
3837         arrow[0].x = s_x + A_WIDTH;\r
3838         arrow[0].y = s_y;\r
3839 \r
3840         arrow[1].x = s_x + A_WIDTH;\r
3841         arrow[1].y = d_y - h;\r
3842 \r
3843         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3844         arrow[2].y = d_y - h;\r
3845 \r
3846         arrow[3].x = d_x;\r
3847         arrow[3].y = d_y;\r
3848 \r
3849         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3850         arrow[4].y = d_y - h;\r
3851 \r
3852         arrow[5].x = s_x - A_WIDTH;\r
3853         arrow[5].y = d_y - h;\r
3854 \r
3855         arrow[6].x = s_x - A_WIDTH;\r
3856         arrow[6].y = s_y;\r
3857     }\r
3858     else if( d_y == s_y ) {\r
3859         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3860 \r
3861         arrow[0].x = s_x;\r
3862         arrow[0].y = s_y + A_WIDTH;\r
3863 \r
3864         arrow[1].x = d_x - w;\r
3865         arrow[1].y = s_y + A_WIDTH;\r
3866 \r
3867         arrow[2].x = d_x - w;\r
3868         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3869 \r
3870         arrow[3].x = d_x;\r
3871         arrow[3].y = d_y;\r
3872 \r
3873         arrow[4].x = d_x - w;\r
3874         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3875 \r
3876         arrow[5].x = d_x - w;\r
3877         arrow[5].y = s_y - A_WIDTH;\r
3878 \r
3879         arrow[6].x = s_x;\r
3880         arrow[6].y = s_y - A_WIDTH;\r
3881     }\r
3882     else {\r
3883         /* [AS] Needed a lot of paper for this! :-) */\r
3884         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3885         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3886   \r
3887         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3888 \r
3889         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3890 \r
3891         x = s_x;\r
3892         y = s_y;\r
3893 \r
3894         arrow[0].x = Round(x - j);\r
3895         arrow[0].y = Round(y + j*dx);\r
3896 \r
3897         arrow[1].x = Round(x + j);\r
3898         arrow[1].y = Round(y - j*dx);\r
3899 \r
3900         if( d_x > s_x ) {\r
3901             x = (double) d_x - k;\r
3902             y = (double) d_y - k*dy;\r
3903         }\r
3904         else {\r
3905             x = (double) d_x + k;\r
3906             y = (double) d_y + k*dy;\r
3907         }\r
3908 \r
3909         arrow[2].x = Round(x + j);\r
3910         arrow[2].y = Round(y - j*dx);\r
3911 \r
3912         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3913         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3914 \r
3915         arrow[4].x = d_x;\r
3916         arrow[4].y = d_y;\r
3917 \r
3918         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3919         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3920 \r
3921         arrow[6].x = Round(x - j);\r
3922         arrow[6].y = Round(y + j*dx);\r
3923     }\r
3924 \r
3925     Polygon( hdc, arrow, 7 );\r
3926 }\r
3927 \r
3928 /* [AS] Draw an arrow between two squares */\r
3929 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3930 {\r
3931     int s_x, s_y, d_x, d_y;\r
3932     HPEN hpen;\r
3933     HPEN holdpen;\r
3934     HBRUSH hbrush;\r
3935     HBRUSH holdbrush;\r
3936     LOGBRUSH stLB;\r
3937 \r
3938     if( s_col == d_col && s_row == d_row ) {\r
3939         return;\r
3940     }\r
3941 \r
3942     /* Get source and destination points */\r
3943     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3944     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3945 \r
3946     if( d_y > s_y ) {\r
3947         d_y += squareSize / 4;\r
3948     }\r
3949     else if( d_y < s_y ) {\r
3950         d_y += 3 * squareSize / 4;\r
3951     }\r
3952     else {\r
3953         d_y += squareSize / 2;\r
3954     }\r
3955 \r
3956     if( d_x > s_x ) {\r
3957         d_x += squareSize / 4;\r
3958     }\r
3959     else if( d_x < s_x ) {\r
3960         d_x += 3 * squareSize / 4;\r
3961     }\r
3962     else {\r
3963         d_x += squareSize / 2;\r
3964     }\r
3965 \r
3966     s_x += squareSize / 2;\r
3967     s_y += squareSize / 2;\r
3968 \r
3969     /* Adjust width */\r
3970     A_WIDTH = squareSize / 14;\r
3971 \r
3972     /* Draw */\r
3973     stLB.lbStyle = BS_SOLID;\r
3974     stLB.lbColor = appData.highlightArrowColor;\r
3975     stLB.lbHatch = 0;\r
3976 \r
3977     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3978     holdpen = SelectObject( hdc, hpen );\r
3979     hbrush = CreateBrushIndirect( &stLB );\r
3980     holdbrush = SelectObject( hdc, hbrush );\r
3981 \r
3982     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3983 \r
3984     SelectObject( hdc, holdpen );\r
3985     SelectObject( hdc, holdbrush );\r
3986     DeleteObject( hpen );\r
3987     DeleteObject( hbrush );\r
3988 }\r
3989 \r
3990 BOOL HasHighlightInfo()\r
3991 {\r
3992     BOOL result = FALSE;\r
3993 \r
3994     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3995         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3996     {\r
3997         result = TRUE;\r
3998     }\r
3999 \r
4000     return result;\r
4001 }\r
4002 \r
4003 BOOL IsDrawArrowEnabled()\r
4004 {\r
4005     BOOL result = FALSE;\r
4006 \r
4007     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4008         result = TRUE;\r
4009     }\r
4010 \r
4011     return result;\r
4012 }\r
4013 \r
4014 VOID DrawArrowHighlight( HDC hdc )\r
4015 {\r
4016     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4017         DrawArrowBetweenSquares( hdc,\r
4018             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4019             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4020     }\r
4021 }\r
4022 \r
4023 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4024 {\r
4025     HRGN result = NULL;\r
4026 \r
4027     if( HasHighlightInfo() ) {\r
4028         int x1, y1, x2, y2;\r
4029         int sx, sy, dx, dy;\r
4030 \r
4031         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4032         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4033 \r
4034         sx = MIN( x1, x2 );\r
4035         sy = MIN( y1, y2 );\r
4036         dx = MAX( x1, x2 ) + squareSize;\r
4037         dy = MAX( y1, y2 ) + squareSize;\r
4038 \r
4039         result = CreateRectRgn( sx, sy, dx, dy );\r
4040     }\r
4041 \r
4042     return result;\r
4043 }\r
4044 \r
4045 /*\r
4046     Warning: this function modifies the behavior of several other functions. \r
4047     \r
4048     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4049     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4050     repaint is scattered all over the place, which is not good for features such as\r
4051     "arrow highlighting" that require a full repaint of the board.\r
4052 \r
4053     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4054     user interaction, when speed is not so important) but especially to avoid errors\r
4055     in the displayed graphics.\r
4056 \r
4057     In such patched places, I always try refer to this function so there is a single\r
4058     place to maintain knowledge.\r
4059     \r
4060     To restore the original behavior, just return FALSE unconditionally.\r
4061 */\r
4062 BOOL IsFullRepaintPreferrable()\r
4063 {\r
4064     BOOL result = FALSE;\r
4065 \r
4066     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4067         /* Arrow may appear on the board */\r
4068         result = TRUE;\r
4069     }\r
4070 \r
4071     return result;\r
4072 }\r
4073 \r
4074 /* \r
4075     This function is called by DrawPosition to know whether a full repaint must\r
4076     be forced or not.\r
4077 \r
4078     Only DrawPosition may directly call this function, which makes use of \r
4079     some state information. Other function should call DrawPosition specifying \r
4080     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4081 */\r
4082 BOOL DrawPositionNeedsFullRepaint()\r
4083 {\r
4084     BOOL result = FALSE;\r
4085 \r
4086     /* \r
4087         Probably a slightly better policy would be to trigger a full repaint\r
4088         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4089         but animation is fast enough that it's difficult to notice.\r
4090     */\r
4091     if( animInfo.piece == EmptySquare ) {\r
4092         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4093             result = TRUE;\r
4094         }\r
4095     }\r
4096 \r
4097     return result;\r
4098 }\r
4099 \r
4100 VOID\r
4101 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4102 {\r
4103   int row, column, x, y, square_color, piece_color;\r
4104   ChessSquare piece;\r
4105   HBRUSH oldBrush;\r
4106   HDC texture_hdc = NULL;\r
4107 \r
4108   /* [AS] Initialize background textures if needed */\r
4109   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4110       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4111       if( backTextureSquareSize != squareSize \r
4112        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4113           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4114           backTextureSquareSize = squareSize;\r
4115           RebuildTextureSquareInfo();\r
4116       }\r
4117 \r
4118       texture_hdc = CreateCompatibleDC( hdc );\r
4119   }\r
4120 \r
4121   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4122     for (column = 0; column < BOARD_WIDTH; column++) {\r
4123   \r
4124       SquareToPos(row, column, &x, &y);\r
4125 \r
4126       piece = board[row][column];\r
4127 \r
4128       square_color = ((column + row) % 2) == 1;\r
4129       if( gameInfo.variant == VariantXiangqi ) {\r
4130           square_color = !InPalace(row, column);\r
4131           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4132           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4133       }\r
4134       piece_color = (int) piece < (int) BlackPawn;\r
4135 \r
4136 \r
4137       /* [HGM] holdings file: light square or black */\r
4138       if(column == BOARD_LEFT-2) {\r
4139             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4140                 square_color = 1;\r
4141             else {\r
4142                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4143                 continue;\r
4144             }\r
4145       } else\r
4146       if(column == BOARD_RGHT + 1 ) {\r
4147             if( row < gameInfo.holdingsSize )\r
4148                 square_color = 1;\r
4149             else {\r
4150                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4151                 continue;\r
4152             }\r
4153       }\r
4154       if(column == BOARD_LEFT-1 ) /* left align */\r
4155             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4156       else if( column == BOARD_RGHT) /* right align */\r
4157             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4158       else\r
4159       if (appData.monoMode) {\r
4160         if (piece == EmptySquare) {\r
4161           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4162                  square_color ? WHITENESS : BLACKNESS);\r
4163         } else {\r
4164           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4165         }\r
4166       } \r
4167       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4168           /* [AS] Draw the square using a texture bitmap */\r
4169           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4170           int r = row, c = column; // [HGM] do not flip board in flipView\r
4171           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4172 \r
4173           DrawTile( x, y, \r
4174               squareSize, squareSize, \r
4175               hdc, \r
4176               texture_hdc,\r
4177               backTextureSquareInfo[r][c].mode,\r
4178               backTextureSquareInfo[r][c].x,\r
4179               backTextureSquareInfo[r][c].y );\r
4180 \r
4181           SelectObject( texture_hdc, hbm );\r
4182 \r
4183           if (piece != EmptySquare) {\r
4184               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4185           }\r
4186       }\r
4187       else {\r
4188         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4189 \r
4190         oldBrush = SelectObject(hdc, brush );\r
4191         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4192         SelectObject(hdc, oldBrush);\r
4193         if (piece != EmptySquare)\r
4194           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4195       }\r
4196     }\r
4197   }\r
4198 \r
4199   if( texture_hdc != NULL ) {\r
4200     DeleteDC( texture_hdc );\r
4201   }\r
4202 }\r
4203 \r
4204 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4205 void fputDW(FILE *f, int x)\r
4206 {\r
4207         fputc(x     & 255, f);\r
4208         fputc(x>>8  & 255, f);\r
4209         fputc(x>>16 & 255, f);\r
4210         fputc(x>>24 & 255, f);\r
4211 }\r
4212 \r
4213 #define MAX_CLIPS 200   /* more than enough */\r
4214 \r
4215 VOID\r
4216 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4217 {\r
4218   HBITMAP bufferBitmap;\r
4219   BITMAP bi;\r
4220   RECT Rect;\r
4221   HDC tmphdc;\r
4222   HBITMAP hbm;\r
4223   int w = 100, h = 50;\r
4224 \r
4225   if(cps->programLogo == NULL) return;\r
4226 //  GetClientRect(hwndMain, &Rect);\r
4227 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4228 //                                      Rect.bottom-Rect.top+1);\r
4229   tmphdc = CreateCompatibleDC(hdc);\r
4230   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4231   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4232             w = bi.bmWidth;\r
4233             h = bi.bmHeight;\r
4234   }\r
4235   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4236                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4237   SelectObject(tmphdc, hbm);\r
4238   DeleteDC(tmphdc);\r
4239 }\r
4240 \r
4241 VOID\r
4242 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4243 {\r
4244   static Board lastReq, lastDrawn;\r
4245   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4246   static int lastDrawnFlipView = 0;\r
4247   static int lastReqValid = 0, lastDrawnValid = 0;\r
4248   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4249   HDC tmphdc;\r
4250   HDC hdcmem;\r
4251   HBITMAP bufferBitmap;\r
4252   HBITMAP oldBitmap;\r
4253   RECT Rect;\r
4254   HRGN clips[MAX_CLIPS];\r
4255   ChessSquare dragged_piece = EmptySquare;\r
4256 \r
4257   /* I'm undecided on this - this function figures out whether a full\r
4258    * repaint is necessary on its own, so there's no real reason to have the\r
4259    * caller tell it that.  I think this can safely be set to FALSE - but\r
4260    * if we trust the callers not to request full repaints unnessesarily, then\r
4261    * we could skip some clipping work.  In other words, only request a full\r
4262    * redraw when the majority of pieces have changed positions (ie. flip, \r
4263    * gamestart and similar)  --Hawk\r
4264    */\r
4265   Boolean fullrepaint = repaint;\r
4266 \r
4267   if( DrawPositionNeedsFullRepaint() ) {\r
4268       fullrepaint = TRUE;\r
4269   }\r
4270 \r
4271 #if 0\r
4272   if( fullrepaint ) {\r
4273       static int repaint_count = 0;\r
4274       char buf[128];\r
4275 \r
4276       repaint_count++;\r
4277       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4278       OutputDebugString( buf );\r
4279   }\r
4280 #endif\r
4281 \r
4282   if (board == NULL) {\r
4283     if (!lastReqValid) {\r
4284       return;\r
4285     }\r
4286     board = lastReq;\r
4287   } else {\r
4288     CopyBoard(lastReq, board);\r
4289     lastReqValid = 1;\r
4290   }\r
4291 \r
4292   if (doingSizing) {\r
4293     return;\r
4294   }\r
4295 \r
4296   if (IsIconic(hwndMain)) {\r
4297     return;\r
4298   }\r
4299 \r
4300   if (hdc == NULL) {\r
4301     hdc = GetDC(hwndMain);\r
4302     if (!appData.monoMode) {\r
4303       SelectPalette(hdc, hPal, FALSE);\r
4304       RealizePalette(hdc);\r
4305     }\r
4306     releaseDC = TRUE;\r
4307   } else {\r
4308     releaseDC = FALSE;\r
4309   }\r
4310 \r
4311 #if 0\r
4312   fprintf(debugFP, "*******************************\n"\r
4313                    "repaint = %s\n"\r
4314                    "dragInfo.from (%d,%d)\n"\r
4315                    "dragInfo.start (%d,%d)\n"\r
4316                    "dragInfo.pos (%d,%d)\n"\r
4317                    "dragInfo.lastpos (%d,%d)\n", \r
4318                     repaint ? "TRUE" : "FALSE",\r
4319                     dragInfo.from.x, dragInfo.from.y, \r
4320                     dragInfo.start.x, dragInfo.start.y,\r
4321                     dragInfo.pos.x, dragInfo.pos.y,\r
4322                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4323   fprintf(debugFP, "prev:  ");\r
4324   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4325     for (column = 0; column < BOARD_WIDTH; column++) {\r
4326       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4327     }\r
4328   }\r
4329   fprintf(debugFP, "\n");\r
4330   fprintf(debugFP, "board: ");\r
4331   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4332     for (column = 0; column < BOARD_WIDTH; column++) {\r
4333       fprintf(debugFP, "%d ", board[row][column]);\r
4334     }\r
4335   }\r
4336   fprintf(debugFP, "\n");\r
4337   fflush(debugFP);\r
4338 #endif\r
4339 \r
4340   /* Create some work-DCs */\r
4341   hdcmem = CreateCompatibleDC(hdc);\r
4342   tmphdc = CreateCompatibleDC(hdc);\r
4343 \r
4344   /* If dragging is in progress, we temporarely remove the piece */\r
4345   /* [HGM] or temporarily decrease count if stacked              */\r
4346   /*       !! Moved to before board compare !!                   */\r
4347   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4348     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4349     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4350             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4351         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4352     } else \r
4353     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4354             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4355         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4356     } else \r
4357         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4358   }\r
4359 \r
4360   /* Figure out which squares need updating by comparing the \r
4361    * newest board with the last drawn board and checking if\r
4362    * flipping has changed.\r
4363    */\r
4364   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4365     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4366       for (column = 0; column < BOARD_WIDTH; column++) {\r
4367         if (lastDrawn[row][column] != board[row][column]) {\r
4368           SquareToPos(row, column, &x, &y);\r
4369           clips[num_clips++] =\r
4370             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4371         }\r
4372       }\r
4373     }\r
4374     for (i=0; i<2; i++) {\r
4375       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4376           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4377         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4378             lastDrawnHighlight.sq[i].y >= 0) {\r
4379           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4380                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4381           clips[num_clips++] =\r
4382             CreateRectRgn(x - lineGap, y - lineGap, \r
4383                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4384         }\r
4385         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4386           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4387           clips[num_clips++] =\r
4388             CreateRectRgn(x - lineGap, y - lineGap, \r
4389                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4390         }\r
4391       }\r
4392     }\r
4393     for (i=0; i<2; i++) {\r
4394       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4395           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4396         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4397             lastDrawnPremove.sq[i].y >= 0) {\r
4398           SquareToPos(lastDrawnPremove.sq[i].y,\r
4399                       lastDrawnPremove.sq[i].x, &x, &y);\r
4400           clips[num_clips++] =\r
4401             CreateRectRgn(x - lineGap, y - lineGap, \r
4402                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4403         }\r
4404         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4405             premoveHighlightInfo.sq[i].y >= 0) {\r
4406           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4407                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4408           clips[num_clips++] =\r
4409             CreateRectRgn(x - lineGap, y - lineGap, \r
4410                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4411         }\r
4412       }\r
4413     }\r
4414   } else {\r
4415     fullrepaint = TRUE;\r
4416   }\r
4417 \r
4418   /* Create a buffer bitmap - this is the actual bitmap\r
4419    * being written to.  When all the work is done, we can\r
4420    * copy it to the real DC (the screen).  This avoids\r
4421    * the problems with flickering.\r
4422    */\r
4423   GetClientRect(hwndMain, &Rect);\r
4424   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4425                                         Rect.bottom-Rect.top+1);\r
4426   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4427   if (!appData.monoMode) {\r
4428     SelectPalette(hdcmem, hPal, FALSE);\r
4429   }\r
4430 \r
4431   /* Create clips for dragging */\r
4432   if (!fullrepaint) {\r
4433     if (dragInfo.from.x >= 0) {\r
4434       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4435       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4436     }\r
4437     if (dragInfo.start.x >= 0) {\r
4438       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4439       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4440     }\r
4441     if (dragInfo.pos.x >= 0) {\r
4442       x = dragInfo.pos.x - squareSize / 2;\r
4443       y = dragInfo.pos.y - squareSize / 2;\r
4444       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4445     }\r
4446     if (dragInfo.lastpos.x >= 0) {\r
4447       x = dragInfo.lastpos.x - squareSize / 2;\r
4448       y = dragInfo.lastpos.y - squareSize / 2;\r
4449       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4450     }\r
4451   }\r
4452 \r
4453   /* Are we animating a move?  \r
4454    * If so, \r
4455    *   - remove the piece from the board (temporarely)\r
4456    *   - calculate the clipping region\r
4457    */\r
4458   if (!fullrepaint) {\r
4459     if (animInfo.piece != EmptySquare) {\r
4460       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4461       x = boardRect.left + animInfo.lastpos.x;\r
4462       y = boardRect.top + animInfo.lastpos.y;\r
4463       x2 = boardRect.left + animInfo.pos.x;\r
4464       y2 = boardRect.top + animInfo.pos.y;\r
4465       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4466       /* Slight kludge.  The real problem is that after AnimateMove is\r
4467          done, the position on the screen does not match lastDrawn.\r
4468          This currently causes trouble only on e.p. captures in\r
4469          atomic, where the piece moves to an empty square and then\r
4470          explodes.  The old and new positions both had an empty square\r
4471          at the destination, but animation has drawn a piece there and\r
4472          we have to remember to erase it. */\r
4473       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4474     }\r
4475   }\r
4476 \r
4477   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4478   if (num_clips == 0)\r
4479     fullrepaint = TRUE;\r
4480 \r
4481   /* Set clipping on the memory DC */\r
4482   if (!fullrepaint) {\r
4483     SelectClipRgn(hdcmem, clips[0]);\r
4484     for (x = 1; x < num_clips; x++) {\r
4485       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4486         abort();  // this should never ever happen!\r
4487     }\r
4488   }\r
4489 \r
4490   /* Do all the drawing to the memory DC */\r
4491   DrawGridOnDC(hdcmem);\r
4492   DrawHighlightsOnDC(hdcmem);\r
4493   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4494 \r
4495   if(logoHeight) {\r
4496         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4497         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4498   }\r
4499 \r
4500   if( appData.highlightMoveWithArrow ) {\r
4501     DrawArrowHighlight(hdcmem);\r
4502   }\r
4503 \r
4504   DrawCoordsOnDC(hdcmem);\r
4505 \r
4506   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4507                  /* to make sure lastDrawn contains what is actually drawn */\r
4508 \r
4509   /* Put the dragged piece back into place and draw it (out of place!) */\r
4510     if (dragged_piece != EmptySquare) {\r
4511     /* [HGM] or restack */\r
4512     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4513                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4514     else\r
4515     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4516                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4517     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4518     x = dragInfo.pos.x - squareSize / 2;\r
4519     y = dragInfo.pos.y - squareSize / 2;\r
4520     DrawPieceOnDC(hdcmem, dragged_piece,\r
4521                   ((int) dragged_piece < (int) BlackPawn), \r
4522                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4523   }   \r
4524   \r
4525   /* Put the animated piece back into place and draw it */\r
4526   if (animInfo.piece != EmptySquare) {\r
4527     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4528     x = boardRect.left + animInfo.pos.x;\r
4529     y = boardRect.top + animInfo.pos.y;\r
4530     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4531                   ((int) animInfo.piece < (int) BlackPawn),\r
4532                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4533   }\r
4534 \r
4535   /* Release the bufferBitmap by selecting in the old bitmap \r
4536    * and delete the memory DC\r
4537    */\r
4538   SelectObject(hdcmem, oldBitmap);\r
4539   DeleteDC(hdcmem);\r
4540 \r
4541   /* Set clipping on the target DC */\r
4542   if (!fullrepaint) {\r
4543     SelectClipRgn(hdc, clips[0]);\r
4544     for (x = 1; x < num_clips; x++) {\r
4545       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4546         abort();   // this should never ever happen!\r
4547     } \r
4548   }\r
4549 \r
4550   /* Copy the new bitmap onto the screen in one go.\r
4551    * This way we avoid any flickering\r
4552    */\r
4553   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4554   BitBlt(hdc, boardRect.left, boardRect.top,\r
4555          boardRect.right - boardRect.left,\r
4556          boardRect.bottom - boardRect.top,\r
4557          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4558   if(saveDiagFlag) { \r
4559     BITMAP b; int i, j, m, w, wb, fac=0; char pData[1000000]; \r
4560     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4561 \r
4562     GetObject(bufferBitmap, sizeof(b), &b);\r
4563     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4564         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4565         bih.biWidth = b.bmWidth;\r
4566         bih.biHeight = b.bmHeight;\r
4567         bih.biPlanes = 1;\r
4568         bih.biBitCount = b.bmBitsPixel;\r
4569         bih.biCompression = 0;\r
4570         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4571         bih.biXPelsPerMeter = 0;\r
4572         bih.biYPelsPerMeter = 0;\r
4573         bih.biClrUsed = 0;\r
4574         bih.biClrImportant = 0;\r
4575 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4576 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4577         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4578 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4579 \r
4580 #if 1\r
4581         wb = b.bmWidthBytes;\r
4582         // count colors\r
4583         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4584                 int k = ((int*) pData)[i];\r
4585                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4586                 if(j >= 16) break;\r
4587                 color[j] = k;\r
4588                 if(j >= nrColors) nrColors = j+1;\r
4589         }\r
4590         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4591                 INT p = 0;\r
4592                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4593                     for(w=0; w<(wb>>2); w+=2) {\r
4594                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4595                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4596                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4597                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4598                         pData[p++] = m | j<<4;\r
4599                     }\r
4600                     while(p&3) pData[p++] = 0;\r
4601                 }\r
4602                 fac = 3;\r
4603                 wb = (wb+31>>5)<<2;\r
4604         }\r
4605         // write BITMAPFILEHEADER\r
4606         fprintf(diagFile, "BM");\r
4607         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4608         fputDW(diagFile, 0);\r
4609         fputDW(diagFile, 0x36 + (fac?64:0));\r
4610         // write BITMAPINFOHEADER\r
4611         fputDW(diagFile, 40);\r
4612         fputDW(diagFile, b.bmWidth);\r
4613         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4614         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4615         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4616         fputDW(diagFile, 0);\r
4617         fputDW(diagFile, 0);\r
4618         fputDW(diagFile, 0);\r
4619         fputDW(diagFile, 0);\r
4620         fputDW(diagFile, 0);\r
4621         fputDW(diagFile, 0);\r
4622         // write color table\r
4623         if(fac)\r
4624         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4625         // write bitmap data\r
4626         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4627                 fputc(pData[i], diagFile);\r
4628 #endif\r
4629      }\r
4630   }\r
4631 \r
4632   SelectObject(tmphdc, oldBitmap);\r
4633 \r
4634   /* Massive cleanup */\r
4635   for (x = 0; x < num_clips; x++)\r
4636     DeleteObject(clips[x]);\r
4637 \r
4638   DeleteDC(tmphdc);\r
4639   DeleteObject(bufferBitmap);\r
4640 \r
4641   if (releaseDC) \r
4642     ReleaseDC(hwndMain, hdc);\r
4643   \r
4644   if (lastDrawnFlipView != flipView) {\r
4645     if (flipView)\r
4646       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4647     else\r
4648       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4649   }\r
4650 \r
4651 /*  CopyBoard(lastDrawn, board);*/\r
4652   lastDrawnHighlight = highlightInfo;\r
4653   lastDrawnPremove   = premoveHighlightInfo;\r
4654   lastDrawnFlipView = flipView;\r
4655   lastDrawnValid = 1;\r
4656 }\r
4657 \r
4658 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4659 int\r
4660 SaveDiagram(f)\r
4661      FILE *f;\r
4662 {\r
4663     time_t tm;\r
4664     char *fen;\r
4665 \r
4666     saveDiagFlag = 1; diagFile = f;\r
4667     HDCDrawPosition(NULL, TRUE, NULL);\r
4668 \r
4669     saveDiagFlag = 0;\r
4670 \r
4671 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4672     \r
4673     fclose(f);\r
4674     return TRUE;\r
4675 }\r
4676 \r
4677 \r
4678 /*---------------------------------------------------------------------------*\\r
4679 | CLIENT PAINT PROCEDURE\r
4680 |   This is the main event-handler for the WM_PAINT message.\r
4681 |\r
4682 \*---------------------------------------------------------------------------*/\r
4683 VOID\r
4684 PaintProc(HWND hwnd)\r
4685 {\r
4686   HDC         hdc;\r
4687   PAINTSTRUCT ps;\r
4688   HFONT       oldFont;\r
4689 \r
4690   if(hdc = BeginPaint(hwnd, &ps)) {\r
4691     if (IsIconic(hwnd)) {\r
4692       DrawIcon(hdc, 2, 2, iconCurrent);\r
4693     } else {\r
4694       if (!appData.monoMode) {\r
4695         SelectPalette(hdc, hPal, FALSE);\r
4696         RealizePalette(hdc);\r
4697       }\r
4698       HDCDrawPosition(hdc, 1, NULL);\r
4699       oldFont =\r
4700         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4701       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4702                  ETO_CLIPPED|ETO_OPAQUE,\r
4703                  &messageRect, messageText, strlen(messageText), NULL);\r
4704       SelectObject(hdc, oldFont);\r
4705       DisplayBothClocks();\r
4706     }\r
4707     EndPaint(hwnd,&ps);\r
4708   }\r
4709 \r
4710   return;\r
4711 }\r
4712 \r
4713 \r
4714 /*\r
4715  * If the user selects on a border boundary, return -1; if off the board,\r
4716  *   return -2.  Otherwise map the event coordinate to the square.\r
4717  * The offset boardRect.left or boardRect.top must already have been\r
4718  *   subtracted from x.\r
4719  */\r
4720 int\r
4721 EventToSquare(int x)\r
4722 {\r
4723   if (x <= 0)\r
4724     return -2;\r
4725   if (x < lineGap)\r
4726     return -1;\r
4727   x -= lineGap;\r
4728   if ((x % (squareSize + lineGap)) >= squareSize)\r
4729     return -1;\r
4730   x /= (squareSize + lineGap);\r
4731   if (x >= BOARD_SIZE)\r
4732     return -2;\r
4733   return x;\r
4734 }\r
4735 \r
4736 typedef struct {\r
4737   char piece;\r
4738   int command;\r
4739   char* name;\r
4740 } DropEnable;\r
4741 \r
4742 DropEnable dropEnables[] = {\r
4743   { 'P', DP_Pawn, "Pawn" },\r
4744   { 'N', DP_Knight, "Knight" },\r
4745   { 'B', DP_Bishop, "Bishop" },\r
4746   { 'R', DP_Rook, "Rook" },\r
4747   { 'Q', DP_Queen, "Queen" },\r
4748 };\r
4749 \r
4750 VOID\r
4751 SetupDropMenu(HMENU hmenu)\r
4752 {\r
4753   int i, count, enable;\r
4754   char *p;\r
4755   extern char white_holding[], black_holding[];\r
4756   char item[MSG_SIZ];\r
4757 \r
4758   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4759     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4760                dropEnables[i].piece);\r
4761     count = 0;\r
4762     while (p && *p++ == dropEnables[i].piece) count++;\r
4763     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4764     enable = count > 0 || !appData.testLegality\r
4765       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4766                       && !appData.icsActive);\r
4767     ModifyMenu(hmenu, dropEnables[i].command,\r
4768                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4769                dropEnables[i].command, item);\r
4770   }\r
4771 }\r
4772 \r
4773 static int fromX = -1, fromY = -1, toX, toY;\r
4774 \r
4775 /* Event handler for mouse messages */\r
4776 VOID\r
4777 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4778 {\r
4779   int x, y;\r
4780   POINT pt;\r
4781   static int recursive = 0;\r
4782   HMENU hmenu;\r
4783   BOOLEAN needsRedraw = FALSE;\r
4784   BOOLEAN saveAnimate;\r
4785   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4786   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4787   ChessMove moveType;\r
4788 \r
4789   if (recursive) {\r
4790     if (message == WM_MBUTTONUP) {\r
4791       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4792          to the middle button: we simulate pressing the left button too!\r
4793          */\r
4794       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4795       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4796     }\r
4797     return;\r
4798   }\r
4799   recursive++;\r
4800   \r
4801   pt.x = LOWORD(lParam);\r
4802   pt.y = HIWORD(lParam);\r
4803   x = EventToSquare(pt.x - boardRect.left);\r
4804   y = EventToSquare(pt.y - boardRect.top);\r
4805   if (!flipView && y >= 0) {\r
4806     y = BOARD_HEIGHT - 1 - y;\r
4807   }\r
4808   if (flipView && x >= 0) {\r
4809     x = BOARD_WIDTH - 1 - x;\r
4810   }\r
4811 \r
4812   switch (message) {\r
4813   case WM_LBUTTONDOWN:\r
4814     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4815         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4816         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4817         if(gameInfo.holdingsWidth && \r
4818                 (WhiteOnMove(currentMove) \r
4819                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4820                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4821             // click in right holdings, for determining promotion piece\r
4822             ChessSquare p = boards[currentMove][y][x];\r
4823             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4824             if(p != EmptySquare) {\r
4825                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4826                 fromX = fromY = -1;\r
4827                 break;\r
4828             }\r
4829         }\r
4830         DrawPosition(FALSE, boards[currentMove]);\r
4831         break;\r
4832     }\r
4833     ErrorPopDown();\r
4834     sameAgain = FALSE;\r
4835     if (y == -2) {\r
4836       /* Downclick vertically off board; check if on clock */\r
4837       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4838         if (gameMode == EditPosition) {\r
4839           SetWhiteToPlayEvent();\r
4840         } else if (gameMode == IcsPlayingBlack ||\r
4841                    gameMode == MachinePlaysWhite) {\r
4842           CallFlagEvent();\r
4843         } else if (gameMode == EditGame) {\r
4844           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4845         }\r
4846       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4847         if (gameMode == EditPosition) {\r
4848           SetBlackToPlayEvent();\r
4849         } else if (gameMode == IcsPlayingWhite ||\r
4850                    gameMode == MachinePlaysBlack) {\r
4851           CallFlagEvent();\r
4852         } else if (gameMode == EditGame) {\r
4853           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4854         }\r
4855       }\r
4856       if (!appData.highlightLastMove) {\r
4857         ClearHighlights();\r
4858         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4859       }\r
4860       fromX = fromY = -1;\r
4861       dragInfo.start.x = dragInfo.start.y = -1;\r
4862       dragInfo.from = dragInfo.start;\r
4863       break;\r
4864     } else if (x < 0 || y < 0\r
4865       /* [HGM] block clicks between board and holdings */\r
4866               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4867               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4868               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4869         /* EditPosition, empty square, or different color piece;\r
4870            click-click move is possible */\r
4871                                ) {\r
4872       break;\r
4873     } else if (fromX == x && fromY == y) {\r
4874       /* Downclick on same square again */\r
4875       ClearHighlights();\r
4876       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4877       sameAgain = TRUE;  \r
4878     } else if (fromX != -1 &&\r
4879                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4880                                                                         ) {\r
4881       /* Downclick on different square. */\r
4882       /* [HGM] if on holdings file, should count as new first click ! */\r
4883       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4884         toX = x;\r
4885         toY = y;\r
4886         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4887            to make sure move is legal before showing promotion popup */\r
4888         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4889         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4890                 fromX = fromY = -1; \r
4891                 ClearHighlights();\r
4892                 DrawPosition(FALSE, boards[currentMove]);\r
4893                 break; \r
4894         } else \r
4895         if(moveType != ImpossibleMove) {\r
4896           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4897           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4898              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4899               appData.alwaysPromoteToQueen) {\r
4900                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4901                   if (!appData.highlightLastMove) {\r
4902                       ClearHighlights();\r
4903                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4904                   }\r
4905           } else\r
4906           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4907                   SetHighlights(fromX, fromY, toX, toY);\r
4908                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4909                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4910                      If promotion to Q is legal, all are legal! */\r
4911                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4912                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4913                     // kludge to temporarily execute move on display, wthout promotng yet\r
4914                     promotionChoice = TRUE;\r
4915                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4916                     boards[currentMove][toY][toX] = p;\r
4917                     DrawPosition(FALSE, boards[currentMove]);\r
4918                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4919                     boards[currentMove][toY][toX] = q;\r
4920                   } else\r
4921                   PromotionPopup(hwnd);\r
4922           } else {       /* not a promotion */\r
4923              if (appData.animate || appData.highlightLastMove) {\r
4924                  SetHighlights(fromX, fromY, toX, toY);\r
4925              } else {\r
4926                  ClearHighlights();\r
4927              }\r
4928              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4929              fromX = fromY = -1;\r
4930              if (appData.animate && !appData.highlightLastMove) {\r
4931                   ClearHighlights();\r
4932                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4933              }\r
4934           }\r
4935           break;\r
4936         }\r
4937         if (gotPremove) {\r
4938             /* [HGM] it seemed that braces were missing here */\r
4939             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4940             fromX = fromY = -1;\r
4941             break;\r
4942         }\r
4943       }\r
4944       ClearHighlights();\r
4945       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4946     }\r
4947     /* First downclick, or restart on a square with same color piece */\r
4948     if (!frozen && OKToStartUserMove(x, y)) {\r
4949       fromX = x;\r
4950       fromY = y;\r
4951       dragInfo.lastpos = pt;\r
4952       dragInfo.from.x = fromX;\r
4953       dragInfo.from.y = fromY;\r
4954       dragInfo.start = dragInfo.from;\r
4955       SetCapture(hwndMain);\r
4956     } else {\r
4957       fromX = fromY = -1;\r
4958       dragInfo.start.x = dragInfo.start.y = -1;\r
4959       dragInfo.from = dragInfo.start;\r
4960       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4961     }\r
4962     break;\r
4963 \r
4964   case WM_LBUTTONUP:\r
4965     ReleaseCapture();\r
4966     if (fromX == -1) break;\r
4967     if (x == fromX && y == fromY) {\r
4968       dragInfo.from.x = dragInfo.from.y = -1;\r
4969       /* Upclick on same square */\r
4970       if (sameAgain) {\r
4971         /* Clicked same square twice: abort click-click move */\r
4972         fromX = fromY = -1;\r
4973         gotPremove = 0;\r
4974         ClearPremoveHighlights();\r
4975       } else {\r
4976         /* First square clicked: start click-click move */\r
4977         SetHighlights(fromX, fromY, -1, -1);\r
4978       }\r
4979       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4980     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4981       /* Errant click; ignore */\r
4982       break;\r
4983     } else {\r
4984       /* Finish drag move. */\r
4985     if (appData.debugMode) {\r
4986         fprintf(debugFP, "release\n");\r
4987     }\r
4988       dragInfo.from.x = dragInfo.from.y = -1;\r
4989       toX = x;\r
4990       toY = y;\r
4991       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4992       appData.animate = appData.animate && !appData.animateDragging;\r
4993       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4994       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4995                 fromX = fromY = -1; \r
4996                 ClearHighlights();\r
4997                 DrawPosition(FALSE, boards[currentMove]);\r
4998                 break; \r
4999       } else \r
5000       if(moveType != ImpossibleMove) {\r
5001           /* [HGM] use move type to determine if move is promotion.\r
5002              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5003           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5004              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5005               appData.alwaysPromoteToQueen) \r
5006                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5007           else \r
5008           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5009                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5010                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5011                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5012                     // kludge to temporarily execute move on display, wthout promotng yet\r
5013                     promotionChoice = TRUE;\r
5014                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5015                     boards[currentMove][toY][toX] = p;\r
5016                     DrawPosition(FALSE, boards[currentMove]);\r
5017                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5018                     boards[currentMove][toY][toX] = q;\r
5019                     break;\r
5020                   } else\r
5021                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5022         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5023       }\r
5024       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5025       appData.animate = saveAnimate;\r
5026       fromX = fromY = -1;\r
5027       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5028         ClearHighlights();\r
5029       }\r
5030       if (appData.animate || appData.animateDragging ||\r
5031           appData.highlightDragging || gotPremove) {\r
5032         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5033       }\r
5034     }\r
5035     dragInfo.start.x = dragInfo.start.y = -1; \r
5036     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5037     break;\r
5038 \r
5039   case WM_MOUSEMOVE:\r
5040     if ((appData.animateDragging || appData.highlightDragging)\r
5041         && (wParam & MK_LBUTTON)\r
5042         && dragInfo.from.x >= 0) \r
5043     {\r
5044       BOOL full_repaint = FALSE;\r
5045 \r
5046       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5047       if (appData.animateDragging) {\r
5048         dragInfo.pos = pt;\r
5049       }\r
5050       if (appData.highlightDragging) {\r
5051         SetHighlights(fromX, fromY, x, y);\r
5052         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5053             full_repaint = TRUE;\r
5054         }\r
5055       }\r
5056       \r
5057       DrawPosition( full_repaint, NULL);\r
5058       \r
5059       dragInfo.lastpos = dragInfo.pos;\r
5060     }\r
5061     break;\r
5062 \r
5063   case WM_MOUSEWHEEL: // [DM]\r
5064     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5065        /* Mouse Wheel is being rolled forward\r
5066         * Play moves forward\r
5067         */\r
5068        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5069                 if(lastDir == 1) ForwardEvent(); else lastDir = 1; // [HGM] suppress first event in each direction\r
5070        /* Mouse Wheel is being rolled backward\r
5071         * Play moves backward\r
5072         */\r
5073        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5074                 if(lastDir == -1) BackwardEvent(); else lastDir = -1;\r
5075     }\r
5076     break;\r
5077 \r
5078   case WM_MBUTTONDOWN:\r
5079   case WM_RBUTTONDOWN:\r
5080     ErrorPopDown();\r
5081     ReleaseCapture();\r
5082     fromX = fromY = -1;\r
5083     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5084     dragInfo.start.x = dragInfo.start.y = -1;\r
5085     dragInfo.from = dragInfo.start;\r
5086     dragInfo.lastpos = dragInfo.pos;\r
5087     if (appData.highlightDragging) {\r
5088       ClearHighlights();\r
5089     }\r
5090     if(y == -2) {\r
5091       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5092       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5093           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5094       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5095           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5096       }\r
5097     }\r
5098     DrawPosition(TRUE, NULL);\r
5099 \r
5100     switch (gameMode) {\r
5101     case EditPosition:\r
5102     case IcsExamining:\r
5103       if (x < 0 || y < 0) break;\r
5104       fromX = x;\r
5105       fromY = y;\r
5106       if (message == WM_MBUTTONDOWN) {\r
5107         buttonCount = 3;  /* even if system didn't think so */\r
5108         if (wParam & MK_SHIFT) \r
5109           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5110         else\r
5111           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5112       } else { /* message == WM_RBUTTONDOWN */\r
5113 #if 0\r
5114         if (buttonCount == 3) {\r
5115           if (wParam & MK_SHIFT) \r
5116             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5117           else\r
5118             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5119         } else {\r
5120           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5121         }\r
5122 #else\r
5123         /* Just have one menu, on the right button.  Windows users don't\r
5124            think to try the middle one, and sometimes other software steals\r
5125            it, or it doesn't really exist. */\r
5126         if(gameInfo.variant != VariantShogi)\r
5127             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5128         else\r
5129             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5130 #endif\r
5131       }\r
5132       break;\r
5133     case IcsPlayingWhite:\r
5134     case IcsPlayingBlack:\r
5135     case EditGame:\r
5136     case MachinePlaysWhite:\r
5137     case MachinePlaysBlack:\r
5138       if (appData.testLegality &&\r
5139           gameInfo.variant != VariantBughouse &&\r
5140           gameInfo.variant != VariantCrazyhouse) break;\r
5141       if (x < 0 || y < 0) break;\r
5142       fromX = x;\r
5143       fromY = y;\r
5144       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5145       SetupDropMenu(hmenu);\r
5146       MenuPopup(hwnd, pt, hmenu, -1);\r
5147       break;\r
5148     default:\r
5149       break;\r
5150     }\r
5151     break;\r
5152   }\r
5153 \r
5154   recursive--;\r
5155 }\r
5156 \r
5157 /* Preprocess messages for buttons in main window */\r
5158 LRESULT CALLBACK\r
5159 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5160 {\r
5161   int id = GetWindowLong(hwnd, GWL_ID);\r
5162   int i, dir;\r
5163 \r
5164   for (i=0; i<N_BUTTONS; i++) {\r
5165     if (buttonDesc[i].id == id) break;\r
5166   }\r
5167   if (i == N_BUTTONS) return 0;\r
5168   switch (message) {\r
5169   case WM_KEYDOWN:\r
5170     switch (wParam) {\r
5171     case VK_LEFT:\r
5172     case VK_RIGHT:\r
5173       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5174       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5175       return TRUE;\r
5176     }\r
5177     break;\r
5178   case WM_CHAR:\r
5179     switch (wParam) {\r
5180     case '\r':\r
5181       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5182       return TRUE;\r
5183     case '\t':\r
5184       if (appData.icsActive) {\r
5185         if (GetKeyState(VK_SHIFT) < 0) {\r
5186           /* shifted */\r
5187           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5188           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5189           SetFocus(h);\r
5190         } else {\r
5191           /* unshifted */\r
5192           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5193           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5194           SetFocus(h);\r
5195         }\r
5196         return TRUE;\r
5197       }\r
5198       break;\r
5199     default:\r
5200       if (appData.icsActive) {\r
5201         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5202         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5203         SetFocus(h);\r
5204         SendMessage(h, WM_CHAR, wParam, lParam);\r
5205         return TRUE;\r
5206       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5207         PopUpMoveDialog((char)wParam);\r
5208       }\r
5209       break;\r
5210     }\r
5211     break;\r
5212   }\r
5213   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5214 }\r
5215 \r
5216 /* Process messages for Promotion dialog box */\r
5217 LRESULT CALLBACK\r
5218 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5219 {\r
5220   char promoChar;\r
5221 \r
5222   switch (message) {\r
5223   case WM_INITDIALOG: /* message: initialize dialog box */\r
5224     /* Center the dialog over the application window */\r
5225     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5226     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5227       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5228        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5229                SW_SHOW : SW_HIDE);\r
5230     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5231     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5232        (PieceToChar(WhiteAngel) >= 'A' &&\r
5233         PieceToChar(WhiteAngel) != '~' ||\r
5234         PieceToChar(BlackAngel) >= 'A' &&\r
5235         PieceToChar(BlackAngel) != '~'   ) ?\r
5236                SW_SHOW : SW_HIDE);\r
5237     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5238        (PieceToChar(WhiteMarshall) >= 'A' &&\r
5239         PieceToChar(WhiteMarshall) != '~' ||\r
5240         PieceToChar(BlackMarshall) >= 'A' &&\r
5241         PieceToChar(BlackMarshall) != '~'   ) ?\r
5242                SW_SHOW : SW_HIDE);\r
5243     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5244     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5245        gameInfo.variant != VariantShogi ?\r
5246                SW_SHOW : SW_HIDE);\r
5247     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5248        gameInfo.variant != VariantShogi ?\r
5249                SW_SHOW : SW_HIDE);\r
5250     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5251        gameInfo.variant == VariantShogi ?\r
5252                SW_SHOW : SW_HIDE);\r
5253     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5254        gameInfo.variant == VariantShogi ?\r
5255                SW_SHOW : SW_HIDE);\r
5256     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5257        gameInfo.variant == VariantSuper ?\r
5258                SW_SHOW : SW_HIDE);\r
5259     return TRUE;\r
5260 \r
5261   case WM_COMMAND: /* message: received a command */\r
5262     switch (LOWORD(wParam)) {\r
5263     case IDCANCEL:\r
5264       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5265       ClearHighlights();\r
5266       DrawPosition(FALSE, NULL);\r
5267       return TRUE;\r
5268     case PB_King:\r
5269       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5270       break;\r
5271     case PB_Queen:\r
5272       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5273       break;\r
5274     case PB_Rook:\r
5275       promoChar = PieceToChar(BlackRook);\r
5276       break;\r
5277     case PB_Bishop:\r
5278       promoChar = PieceToChar(BlackBishop);\r
5279       break;\r
5280     case PB_Chancellor:\r
5281       promoChar = PieceToChar(BlackMarshall);\r
5282       break;\r
5283     case PB_Archbishop:\r
5284       promoChar = PieceToChar(BlackAngel);\r
5285       break;\r
5286     case PB_Knight:\r
5287       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5288       break;\r
5289     default:\r
5290       return FALSE;\r
5291     }\r
5292     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5293     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5294        only show the popup when we are already sure the move is valid or\r
5295        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5296        will figure out it is a promotion from the promoChar. */\r
5297     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5298     if (!appData.highlightLastMove) {\r
5299       ClearHighlights();\r
5300       DrawPosition(FALSE, NULL);\r
5301     }\r
5302     return TRUE;\r
5303   }\r
5304   return FALSE;\r
5305 }\r
5306 \r
5307 /* Pop up promotion dialog */\r
5308 VOID\r
5309 PromotionPopup(HWND hwnd)\r
5310 {\r
5311   FARPROC lpProc;\r
5312 \r
5313   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5314   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5315     hwnd, (DLGPROC)lpProc);\r
5316   FreeProcInstance(lpProc);\r
5317 }\r
5318 \r
5319 /* Toggle ShowThinking */\r
5320 VOID\r
5321 ToggleShowThinking()\r
5322 {\r
5323   appData.showThinking = !appData.showThinking;\r
5324   ShowThinkingEvent();\r
5325 }\r
5326 \r
5327 VOID\r
5328 LoadGameDialog(HWND hwnd, char* title)\r
5329 {\r
5330   UINT number = 0;\r
5331   FILE *f;\r
5332   char fileTitle[MSG_SIZ];\r
5333   f = OpenFileDialog(hwnd, "rb", "",\r
5334                      appData.oldSaveStyle ? "gam" : "pgn",\r
5335                      GAME_FILT,\r
5336                      title, &number, fileTitle, NULL);\r
5337   if (f != NULL) {\r
5338     cmailMsgLoaded = FALSE;\r
5339     if (number == 0) {\r
5340       int error = GameListBuild(f);\r
5341       if (error) {\r
5342         DisplayError("Cannot build game list", error);\r
5343       } else if (!ListEmpty(&gameList) &&\r
5344                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5345         GameListPopUp(f, fileTitle);\r
5346         return;\r
5347       }\r
5348       GameListDestroy();\r
5349       number = 1;\r
5350     }\r
5351     LoadGame(f, number, fileTitle, FALSE);\r
5352   }\r
5353 }\r
5354 \r
5355 VOID\r
5356 ChangedConsoleFont()\r
5357 {\r
5358   CHARFORMAT cfmt;\r
5359   CHARRANGE tmpsel, sel;\r
5360   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5361   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5362   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5363   PARAFORMAT paraf;\r
5364 \r
5365   cfmt.cbSize = sizeof(CHARFORMAT);\r
5366   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5367   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5368   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5369    * size.  This was undocumented in the version of MSVC++ that I had\r
5370    * when I wrote the code, but is apparently documented now.\r
5371    */\r
5372   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5373   cfmt.bCharSet = f->lf.lfCharSet;\r
5374   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5375   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5376   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5377   /* Why are the following seemingly needed too? */\r
5378   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5379   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5380   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5381   tmpsel.cpMin = 0;\r
5382   tmpsel.cpMax = -1; /*999999?*/\r
5383   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5384   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5385   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5386    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5387    */\r
5388   paraf.cbSize = sizeof(paraf);\r
5389   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5390   paraf.dxStartIndent = 0;\r
5391   paraf.dxOffset = WRAP_INDENT;\r
5392   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5393   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5394 }\r
5395 \r
5396 /*---------------------------------------------------------------------------*\\r
5397  *\r
5398  * Window Proc for main window\r
5399  *\r
5400 \*---------------------------------------------------------------------------*/\r
5401 \r
5402 /* Process messages for main window, etc. */\r
5403 LRESULT CALLBACK\r
5404 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5405 {\r
5406   FARPROC lpProc;\r
5407   int wmId, wmEvent;\r
5408   char *defName;\r
5409   FILE *f;\r
5410   UINT number;\r
5411   char fileTitle[MSG_SIZ];\r
5412   char buf[MSG_SIZ];\r
5413   static SnapData sd;\r
5414 \r
5415   switch (message) {\r
5416 \r
5417   case WM_PAINT: /* message: repaint portion of window */\r
5418     PaintProc(hwnd);\r
5419     break;\r
5420 \r
5421   case WM_ERASEBKGND:\r
5422     if (IsIconic(hwnd)) {\r
5423       /* Cheat; change the message */\r
5424       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5425     } else {\r
5426       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5427     }\r
5428     break;\r
5429 \r
5430   case WM_LBUTTONDOWN:\r
5431   case WM_MBUTTONDOWN:\r
5432   case WM_RBUTTONDOWN:\r
5433   case WM_LBUTTONUP:\r
5434   case WM_MBUTTONUP:\r
5435   case WM_RBUTTONUP:\r
5436   case WM_MOUSEMOVE:\r
5437   case WM_MOUSEWHEEL:\r
5438     MouseEvent(hwnd, message, wParam, lParam);\r
5439     break;\r
5440 \r
5441   case WM_CHAR:\r
5442     \r
5443     if (appData.icsActive) {\r
5444       if (wParam == '\t') {\r
5445         if (GetKeyState(VK_SHIFT) < 0) {\r
5446           /* shifted */\r
5447           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5448           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5449           SetFocus(h);\r
5450         } else {\r
5451           /* unshifted */\r
5452           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5453           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5454           SetFocus(h);\r
5455         }\r
5456       } else {\r
5457         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5458         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5459         SetFocus(h);\r
5460         SendMessage(h, message, wParam, lParam);\r
5461       }\r
5462     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5463       PopUpMoveDialog((char)wParam);\r
5464     }\r
5465     break;\r
5466 \r
5467   case WM_PALETTECHANGED:\r
5468     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5469       int nnew;\r
5470       HDC hdc = GetDC(hwndMain);\r
5471       SelectPalette(hdc, hPal, TRUE);\r
5472       nnew = RealizePalette(hdc);\r
5473       if (nnew > 0) {\r
5474         paletteChanged = TRUE;\r
5475 #if 0\r
5476         UpdateColors(hdc);\r
5477 #else\r
5478         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5479 #endif\r
5480       }\r
5481       ReleaseDC(hwnd, hdc);\r
5482     }\r
5483     break;\r
5484 \r
5485   case WM_QUERYNEWPALETTE:\r
5486     if (!appData.monoMode /*&& paletteChanged*/) {\r
5487       int nnew;\r
5488       HDC hdc = GetDC(hwndMain);\r
5489       paletteChanged = FALSE;\r
5490       SelectPalette(hdc, hPal, FALSE);\r
5491       nnew = RealizePalette(hdc);\r
5492       if (nnew > 0) {\r
5493         InvalidateRect(hwnd, &boardRect, FALSE);\r
5494       }\r
5495       ReleaseDC(hwnd, hdc);\r
5496       return TRUE;\r
5497     }\r
5498     return FALSE;\r
5499 \r
5500   case WM_COMMAND: /* message: command from application menu */\r
5501     wmId    = LOWORD(wParam);\r
5502     wmEvent = HIWORD(wParam);\r
5503 \r
5504     switch (wmId) {\r
5505     case IDM_NewGame:\r
5506       ResetGameEvent();\r
5507       AnalysisPopDown();\r
5508       break;\r
5509 \r
5510     case IDM_NewGameFRC:\r
5511       if( NewGameFRC() == 0 ) {\r
5512         ResetGameEvent();\r
5513         AnalysisPopDown();\r
5514       }\r
5515       break;\r
5516 \r
5517     case IDM_NewVariant:\r
5518       NewVariantPopup(hwnd);\r
5519       break;\r
5520 \r
5521     case IDM_LoadGame:\r
5522       LoadGameDialog(hwnd, "Load Game from File");\r
5523       break;\r
5524 \r
5525     case IDM_LoadNextGame:\r
5526       ReloadGame(1);\r
5527       break;\r
5528 \r
5529     case IDM_LoadPrevGame:\r
5530       ReloadGame(-1);\r
5531       break;\r
5532 \r
5533     case IDM_ReloadGame:\r
5534       ReloadGame(0);\r
5535       break;\r
5536 \r
5537     case IDM_LoadPosition:\r
5538       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5539         Reset(FALSE, TRUE);\r
5540       }\r
5541       number = 1;\r
5542       f = OpenFileDialog(hwnd, "rb", "",\r
5543                          appData.oldSaveStyle ? "pos" : "fen",\r
5544                          POSITION_FILT,\r
5545                          "Load Position from File", &number, fileTitle, NULL);\r
5546       if (f != NULL) {\r
5547         LoadPosition(f, number, fileTitle);\r
5548       }\r
5549       break;\r
5550 \r
5551     case IDM_LoadNextPosition:\r
5552       ReloadPosition(1);\r
5553       break;\r
5554 \r
5555     case IDM_LoadPrevPosition:\r
5556       ReloadPosition(-1);\r
5557       break;\r
5558 \r
5559     case IDM_ReloadPosition:\r
5560       ReloadPosition(0);\r
5561       break;\r
5562 \r
5563     case IDM_SaveGame:\r
5564       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5565       f = OpenFileDialog(hwnd, "a", defName,\r
5566                          appData.oldSaveStyle ? "gam" : "pgn",\r
5567                          GAME_FILT,\r
5568                          "Save Game to File", NULL, fileTitle, NULL);\r
5569       if (f != NULL) {\r
5570         SaveGame(f, 0, "");\r
5571       }\r
5572       break;\r
5573 \r
5574     case IDM_SavePosition:\r
5575       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5576       f = OpenFileDialog(hwnd, "a", defName,\r
5577                          appData.oldSaveStyle ? "pos" : "fen",\r
5578                          POSITION_FILT,\r
5579                          "Save Position to File", NULL, fileTitle, NULL);\r
5580       if (f != NULL) {\r
5581         SavePosition(f, 0, "");\r
5582       }\r
5583       break;\r
5584 \r
5585     case IDM_SaveDiagram:\r
5586       defName = "diagram";\r
5587       f = OpenFileDialog(hwnd, "wb", defName,\r
5588                          "bmp",\r
5589                          DIAGRAM_FILT,\r
5590                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5591       if (f != NULL) {\r
5592         SaveDiagram(f);\r
5593       }\r
5594       break;\r
5595 \r
5596     case IDM_CopyGame:\r
5597       CopyGameToClipboard();\r
5598       break;\r
5599 \r
5600     case IDM_PasteGame:\r
5601       PasteGameFromClipboard();\r
5602       break;\r
5603 \r
5604     case IDM_CopyGameListToClipboard:\r
5605       CopyGameListToClipboard();\r
5606       break;\r
5607 \r
5608     /* [AS] Autodetect FEN or PGN data */\r
5609     case IDM_PasteAny:\r
5610       PasteGameOrFENFromClipboard();\r
5611       break;\r
5612 \r
5613     /* [AS] Move history */\r
5614     case IDM_ShowMoveHistory:\r
5615         if( MoveHistoryIsUp() ) {\r
5616             MoveHistoryPopDown();\r
5617         }\r
5618         else {\r
5619             MoveHistoryPopUp();\r
5620         }\r
5621         break;\r
5622 \r
5623     /* [AS] Eval graph */\r
5624     case IDM_ShowEvalGraph:\r
5625         if( EvalGraphIsUp() ) {\r
5626             EvalGraphPopDown();\r
5627         }\r
5628         else {\r
5629             EvalGraphPopUp();\r
5630         }\r
5631         break;\r
5632 \r
5633     /* [AS] Engine output */\r
5634     case IDM_ShowEngineOutput:\r
5635         if( EngineOutputIsUp() ) {\r
5636             EngineOutputPopDown();\r
5637         }\r
5638         else {\r
5639             EngineOutputPopUp();\r
5640         }\r
5641         break;\r
5642 \r
5643     /* [AS] User adjudication */\r
5644     case IDM_UserAdjudication_White:\r
5645         UserAdjudicationEvent( +1 );\r
5646         break;\r
5647 \r
5648     case IDM_UserAdjudication_Black:\r
5649         UserAdjudicationEvent( -1 );\r
5650         break;\r
5651 \r
5652     case IDM_UserAdjudication_Draw:\r
5653         UserAdjudicationEvent( 0 );\r
5654         break;\r
5655 \r
5656     /* [AS] Game list options dialog */\r
5657     case IDM_GameListOptions:\r
5658       GameListOptions();\r
5659       break;\r
5660 \r
5661     case IDM_CopyPosition:\r
5662       CopyFENToClipboard();\r
5663       break;\r
5664 \r
5665     case IDM_PastePosition:\r
5666       PasteFENFromClipboard();\r
5667       break;\r
5668 \r
5669     case IDM_MailMove:\r
5670       MailMoveEvent();\r
5671       break;\r
5672 \r
5673     case IDM_ReloadCMailMsg:\r
5674       Reset(TRUE, TRUE);\r
5675       ReloadCmailMsgEvent(FALSE);\r
5676       break;\r
5677 \r
5678     case IDM_Minimize:\r
5679       ShowWindow(hwnd, SW_MINIMIZE);\r
5680       break;\r
5681 \r
5682     case IDM_Exit:\r
5683       ExitEvent(0);\r
5684       break;\r
5685 \r
5686     case IDM_MachineWhite:\r
5687       MachineWhiteEvent();\r
5688       /*\r
5689        * refresh the tags dialog only if it's visible\r
5690        */\r
5691       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5692           char *tags;\r
5693           tags = PGNTags(&gameInfo);\r
5694           TagsPopUp(tags, CmailMsg());\r
5695           free(tags);\r
5696       }\r
5697       break;\r
5698 \r
5699     case IDM_MachineBlack:\r
5700       MachineBlackEvent();\r
5701       /*\r
5702        * refresh the tags dialog only if it's visible\r
5703        */\r
5704       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5705           char *tags;\r
5706           tags = PGNTags(&gameInfo);\r
5707           TagsPopUp(tags, CmailMsg());\r
5708           free(tags);\r
5709       }\r
5710       break;\r
5711 \r
5712     case IDM_TwoMachines:\r
5713       TwoMachinesEvent();\r
5714       /*\r
5715        * refresh the tags dialog only if it's visible\r
5716        */\r
5717       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5718           char *tags;\r
5719           tags = PGNTags(&gameInfo);\r
5720           TagsPopUp(tags, CmailMsg());\r
5721           free(tags);\r
5722       }\r
5723       break;\r
5724 \r
5725     case IDM_AnalysisMode:\r
5726       if (!first.analysisSupport) {\r
5727         sprintf(buf, "%s does not support analysis", first.tidy);\r
5728         DisplayError(buf, 0);\r
5729       } else {\r
5730         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5731         if (appData.icsActive) {\r
5732                if (gameMode != IcsObserving) {\r
5733                        sprintf(buf, "You are not observing a game");\r
5734                        DisplayError(buf, 0);\r
5735                        /* secure check */\r
5736                        if (appData.icsEngineAnalyze) {\r
5737                                if (appData.debugMode) \r
5738                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5739                                ExitAnalyzeMode();\r
5740                                ModeHighlight();\r
5741                                break;\r
5742                        }\r
5743                        break;\r
5744                } else {\r
5745                        /* if enable, user want disable icsEngineAnalyze */\r
5746                        if (appData.icsEngineAnalyze) {\r
5747                                ExitAnalyzeMode();\r
5748                                ModeHighlight();\r
5749                                break;\r
5750                        }\r
5751                        appData.icsEngineAnalyze = TRUE;\r
5752                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5753                }\r
5754         } \r
5755         if (!appData.showThinking) ToggleShowThinking();\r
5756         AnalyzeModeEvent();\r
5757       }\r
5758       break;\r
5759 \r
5760     case IDM_AnalyzeFile:\r
5761       if (!first.analysisSupport) {\r
5762         char buf[MSG_SIZ];\r
5763         sprintf(buf, "%s does not support analysis", first.tidy);\r
5764         DisplayError(buf, 0);\r
5765       } else {\r
5766         if (!appData.showThinking) ToggleShowThinking();\r
5767         AnalyzeFileEvent();\r
5768         LoadGameDialog(hwnd, "Analyze Game from File");\r
5769         AnalysisPeriodicEvent(1);\r
5770       }\r
5771       break;\r
5772 \r
5773     case IDM_IcsClient:\r
5774       IcsClientEvent();\r
5775       break;\r
5776 \r
5777     case IDM_EditGame:\r
5778       EditGameEvent();\r
5779       break;\r
5780 \r
5781     case IDM_EditPosition:\r
5782       EditPositionEvent();\r
5783       break;\r
5784 \r
5785     case IDM_Training:\r
5786       TrainingEvent();\r
5787       break;\r
5788 \r
5789     case IDM_ShowGameList:\r
5790       ShowGameListProc();\r
5791       break;\r
5792 \r
5793     case IDM_EditTags:\r
5794       EditTagsProc();\r
5795       break;\r
5796 \r
5797     case IDM_EditComment:\r
5798       if (commentDialogUp && editComment) {\r
5799         CommentPopDown();\r
5800       } else {\r
5801         EditCommentEvent();\r
5802       }\r
5803       break;\r
5804 \r
5805     case IDM_Pause:\r
5806       PauseEvent();\r
5807       break;\r
5808 \r
5809     case IDM_Accept:\r
5810       AcceptEvent();\r
5811       break;\r
5812 \r
5813     case IDM_Decline:\r
5814       DeclineEvent();\r
5815       break;\r
5816 \r
5817     case IDM_Rematch:\r
5818       RematchEvent();\r
5819       break;\r
5820 \r
5821     case IDM_CallFlag:\r
5822       CallFlagEvent();\r
5823       break;\r
5824 \r
5825     case IDM_Draw:\r
5826       DrawEvent();\r
5827       break;\r
5828 \r
5829     case IDM_Adjourn:\r
5830       AdjournEvent();\r
5831       break;\r
5832 \r
5833     case IDM_Abort:\r
5834       AbortEvent();\r
5835       break;\r
5836 \r
5837     case IDM_Resign:\r
5838       ResignEvent();\r
5839       break;\r
5840 \r
5841     case IDM_StopObserving:\r
5842       StopObservingEvent();\r
5843       break;\r
5844 \r
5845     case IDM_StopExamining:\r
5846       StopExaminingEvent();\r
5847       break;\r
5848 \r
5849     case IDM_TypeInMove:\r
5850       PopUpMoveDialog('\000');\r
5851       break;\r
5852 \r
5853     case IDM_TypeInName:\r
5854       PopUpNameDialog('\000');\r
5855       break;\r
5856 \r
5857     case IDM_Backward:\r
5858       BackwardEvent();\r
5859       SetFocus(hwndMain);\r
5860       break;\r
5861 \r
5862     case IDM_Forward:\r
5863       ForwardEvent();\r
5864       SetFocus(hwndMain);\r
5865       break;\r
5866 \r
5867     case IDM_ToStart:\r
5868       ToStartEvent();\r
5869       SetFocus(hwndMain);\r
5870       break;\r
5871 \r
5872     case IDM_ToEnd:\r
5873       ToEndEvent();\r
5874       SetFocus(hwndMain);\r
5875       break;\r
5876 \r
5877     case IDM_Revert:\r
5878       RevertEvent();\r
5879       break;\r
5880 \r
5881     case IDM_TruncateGame:\r
5882       TruncateGameEvent();\r
5883       break;\r
5884 \r
5885     case IDM_MoveNow:\r
5886       MoveNowEvent();\r
5887       break;\r
5888 \r
5889     case IDM_RetractMove:\r
5890       RetractMoveEvent();\r
5891       break;\r
5892 \r
5893     case IDM_FlipView:\r
5894       flipView = !flipView;\r
5895       DrawPosition(FALSE, NULL);\r
5896       break;\r
5897 \r
5898     case IDM_FlipClock:\r
5899       flipClock = !flipClock;\r
5900       DisplayBothClocks();\r
5901       break;\r
5902 \r
5903     case IDM_GeneralOptions:\r
5904       GeneralOptionsPopup(hwnd);\r
5905       DrawPosition(TRUE, NULL);\r
5906       break;\r
5907 \r
5908     case IDM_BoardOptions:\r
5909       BoardOptionsPopup(hwnd);\r
5910       break;\r
5911 \r
5912     case IDM_EnginePlayOptions:\r
5913       EnginePlayOptionsPopup(hwnd);\r
5914       break;\r
5915 \r
5916     case IDM_OptionsUCI:\r
5917       UciOptionsPopup(hwnd);\r
5918       break;\r
5919 \r
5920     case IDM_IcsOptions:\r
5921       IcsOptionsPopup(hwnd);\r
5922       break;\r
5923 \r
5924     case IDM_Fonts:\r
5925       FontsOptionsPopup(hwnd);\r
5926       break;\r
5927 \r
5928     case IDM_Sounds:\r
5929       SoundOptionsPopup(hwnd);\r
5930       break;\r
5931 \r
5932     case IDM_CommPort:\r
5933       CommPortOptionsPopup(hwnd);\r
5934       break;\r
5935 \r
5936     case IDM_LoadOptions:\r
5937       LoadOptionsPopup(hwnd);\r
5938       break;\r
5939 \r
5940     case IDM_SaveOptions:\r
5941       SaveOptionsPopup(hwnd);\r
5942       break;\r
5943 \r
5944     case IDM_TimeControl:\r
5945       TimeControlOptionsPopup(hwnd);\r
5946       break;\r
5947 \r
5948     case IDM_SaveSettings:\r
5949       SaveSettings(settingsFileName);\r
5950       break;\r
5951 \r
5952     case IDM_SaveSettingsOnExit:\r
5953       saveSettingsOnExit = !saveSettingsOnExit;\r
5954       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5955                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5956                                          MF_CHECKED : MF_UNCHECKED));\r
5957       break;\r
5958 \r
5959     case IDM_Hint:\r
5960       HintEvent();\r
5961       break;\r
5962 \r
5963     case IDM_Book:\r
5964       BookEvent();\r
5965       break;\r
5966 \r
5967     case IDM_AboutGame:\r
5968       AboutGameEvent();\r
5969       break;\r
5970 \r
5971     case IDM_Debug:\r
5972       appData.debugMode = !appData.debugMode;\r
5973       if (appData.debugMode) {\r
5974         char dir[MSG_SIZ];\r
5975         GetCurrentDirectory(MSG_SIZ, dir);\r
5976         SetCurrentDirectory(installDir);\r
5977         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5978         SetCurrentDirectory(dir);\r
5979         setbuf(debugFP, NULL);\r
5980       } else {\r
5981         fclose(debugFP);\r
5982         debugFP = NULL;\r
5983       }\r
5984       break;\r
5985 \r
5986     case IDM_HELPCONTENTS:\r
5987       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5988         MessageBox (GetFocus(),\r
5989                     "Unable to activate help",\r
5990                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5991       }\r
5992       break;\r
5993 \r
5994     case IDM_HELPSEARCH:\r
5995       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5996         MessageBox (GetFocus(),\r
5997                     "Unable to activate help",\r
5998                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5999       }\r
6000       break;\r
6001 \r
6002     case IDM_HELPHELP:\r
6003       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6004         MessageBox (GetFocus(),\r
6005                     "Unable to activate help",\r
6006                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6007       }\r
6008       break;\r
6009 \r
6010     case IDM_ABOUT:\r
6011       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6012       DialogBox(hInst, \r
6013         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6014         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6015       FreeProcInstance(lpProc);\r
6016       break;\r
6017 \r
6018     case IDM_DirectCommand1:\r
6019       AskQuestionEvent("Direct Command",\r
6020                        "Send to chess program:", "", "1");\r
6021       break;\r
6022     case IDM_DirectCommand2:\r
6023       AskQuestionEvent("Direct Command",\r
6024                        "Send to second chess program:", "", "2");\r
6025       break;\r
6026 \r
6027     case EP_WhitePawn:\r
6028       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6029       fromX = fromY = -1;\r
6030       break;\r
6031 \r
6032     case EP_WhiteKnight:\r
6033       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6034       fromX = fromY = -1;\r
6035       break;\r
6036 \r
6037     case EP_WhiteBishop:\r
6038       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6039       fromX = fromY = -1;\r
6040       break;\r
6041 \r
6042     case EP_WhiteRook:\r
6043       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6044       fromX = fromY = -1;\r
6045       break;\r
6046 \r
6047     case EP_WhiteQueen:\r
6048       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6049       fromX = fromY = -1;\r
6050       break;\r
6051 \r
6052     case EP_WhiteFerz:\r
6053       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6054       fromX = fromY = -1;\r
6055       break;\r
6056 \r
6057     case EP_WhiteWazir:\r
6058       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6059       fromX = fromY = -1;\r
6060       break;\r
6061 \r
6062     case EP_WhiteAlfil:\r
6063       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6064       fromX = fromY = -1;\r
6065       break;\r
6066 \r
6067     case EP_WhiteCannon:\r
6068       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6069       fromX = fromY = -1;\r
6070       break;\r
6071 \r
6072     case EP_WhiteCardinal:\r
6073       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6074       fromX = fromY = -1;\r
6075       break;\r
6076 \r
6077     case EP_WhiteMarshall:\r
6078       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6079       fromX = fromY = -1;\r
6080       break;\r
6081 \r
6082     case EP_WhiteKing:\r
6083       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6084       fromX = fromY = -1;\r
6085       break;\r
6086 \r
6087     case EP_BlackPawn:\r
6088       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6089       fromX = fromY = -1;\r
6090       break;\r
6091 \r
6092     case EP_BlackKnight:\r
6093       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6094       fromX = fromY = -1;\r
6095       break;\r
6096 \r
6097     case EP_BlackBishop:\r
6098       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6099       fromX = fromY = -1;\r
6100       break;\r
6101 \r
6102     case EP_BlackRook:\r
6103       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6104       fromX = fromY = -1;\r
6105       break;\r
6106 \r
6107     case EP_BlackQueen:\r
6108       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6109       fromX = fromY = -1;\r
6110       break;\r
6111 \r
6112     case EP_BlackFerz:\r
6113       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6114       fromX = fromY = -1;\r
6115       break;\r
6116 \r
6117     case EP_BlackWazir:\r
6118       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6119       fromX = fromY = -1;\r
6120       break;\r
6121 \r
6122     case EP_BlackAlfil:\r
6123       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6124       fromX = fromY = -1;\r
6125       break;\r
6126 \r
6127     case EP_BlackCannon:\r
6128       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6129       fromX = fromY = -1;\r
6130       break;\r
6131 \r
6132     case EP_BlackCardinal:\r
6133       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6134       fromX = fromY = -1;\r
6135       break;\r
6136 \r
6137     case EP_BlackMarshall:\r
6138       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6139       fromX = fromY = -1;\r
6140       break;\r
6141 \r
6142     case EP_BlackKing:\r
6143       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6144       fromX = fromY = -1;\r
6145       break;\r
6146 \r
6147     case EP_EmptySquare:\r
6148       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6149       fromX = fromY = -1;\r
6150       break;\r
6151 \r
6152     case EP_ClearBoard:\r
6153       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6154       fromX = fromY = -1;\r
6155       break;\r
6156 \r
6157     case EP_White:\r
6158       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6159       fromX = fromY = -1;\r
6160       break;\r
6161 \r
6162     case EP_Black:\r
6163       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6164       fromX = fromY = -1;\r
6165       break;\r
6166 \r
6167     case EP_Promote:\r
6168       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6169       fromX = fromY = -1;\r
6170       break;\r
6171 \r
6172     case EP_Demote:\r
6173       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6174       fromX = fromY = -1;\r
6175       break;\r
6176 \r
6177     case DP_Pawn:\r
6178       DropMenuEvent(WhitePawn, fromX, fromY);\r
6179       fromX = fromY = -1;\r
6180       break;\r
6181 \r
6182     case DP_Knight:\r
6183       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6184       fromX = fromY = -1;\r
6185       break;\r
6186 \r
6187     case DP_Bishop:\r
6188       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6189       fromX = fromY = -1;\r
6190       break;\r
6191 \r
6192     case DP_Rook:\r
6193       DropMenuEvent(WhiteRook, fromX, fromY);\r
6194       fromX = fromY = -1;\r
6195       break;\r
6196 \r
6197     case DP_Queen:\r
6198       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6199       fromX = fromY = -1;\r
6200       break;\r
6201 \r
6202     default:\r
6203       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6204     }\r
6205     break;\r
6206 \r
6207   case WM_TIMER:\r
6208     switch (wParam) {\r
6209     case CLOCK_TIMER_ID:\r
6210       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6211       clockTimerEvent = 0;\r
6212       DecrementClocks(); /* call into back end */\r
6213       break;\r
6214     case LOAD_GAME_TIMER_ID:\r
6215       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6216       loadGameTimerEvent = 0;\r
6217       AutoPlayGameLoop(); /* call into back end */\r
6218       break;\r
6219     case ANALYSIS_TIMER_ID:\r
6220       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6221                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6222         AnalysisPeriodicEvent(0);\r
6223       } else {\r
6224         KillTimer(hwnd, analysisTimerEvent);\r
6225         analysisTimerEvent = 0;\r
6226       }\r
6227       break;\r
6228     case DELAYED_TIMER_ID:\r
6229       KillTimer(hwnd, delayedTimerEvent);\r
6230       delayedTimerEvent = 0;\r
6231       delayedTimerCallback();\r
6232       break;\r
6233     }\r
6234     break;\r
6235 \r
6236   case WM_USER_Input:\r
6237     InputEvent(hwnd, message, wParam, lParam);\r
6238     break;\r
6239 \r
6240   /* [AS] Also move "attached" child windows */\r
6241   case WM_WINDOWPOSCHANGING:\r
6242     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6243         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6244 \r
6245         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6246             /* Window is moving */\r
6247             RECT rcMain;\r
6248 \r
6249             GetWindowRect( hwnd, &rcMain );\r
6250             \r
6251             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6252             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6253             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6254         }\r
6255     }\r
6256     break;\r
6257 \r
6258   /* [AS] Snapping */\r
6259   case WM_ENTERSIZEMOVE:\r
6260     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6261     if (hwnd == hwndMain) {\r
6262       doingSizing = TRUE;\r
6263       lastSizing = 0;\r
6264     }\r
6265     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6266     break;\r
6267 \r
6268   case WM_SIZING:\r
6269     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6270     if (hwnd == hwndMain) {\r
6271       lastSizing = wParam;\r
6272     }\r
6273     break;\r
6274 \r
6275   case WM_MOVING:\r
6276     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6277       return OnMoving( &sd, hwnd, wParam, lParam );\r
6278 \r
6279   case WM_EXITSIZEMOVE:\r
6280     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6281     if (hwnd == hwndMain) {\r
6282       RECT client;\r
6283       doingSizing = FALSE;\r
6284       InvalidateRect(hwnd, &boardRect, FALSE);\r
6285       GetClientRect(hwnd, &client);\r
6286       ResizeBoard(client.right, client.bottom, lastSizing);\r
6287       lastSizing = 0;\r
6288       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6289     }\r
6290     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6291     break;\r
6292 \r
6293   case WM_DESTROY: /* message: window being destroyed */\r
6294     PostQuitMessage(0);\r
6295     break;\r
6296 \r
6297   case WM_CLOSE:\r
6298     if (hwnd == hwndMain) {\r
6299       ExitEvent(0);\r
6300     }\r
6301     break;\r
6302 \r
6303   default:      /* Passes it on if unprocessed */\r
6304     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6305   }\r
6306   return 0;\r
6307 }\r
6308 \r
6309 /*---------------------------------------------------------------------------*\\r
6310  *\r
6311  * Misc utility routines\r
6312  *\r
6313 \*---------------------------------------------------------------------------*/\r
6314 \r
6315 /*\r
6316  * Decent random number generator, at least not as bad as Windows\r
6317  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6318  */\r
6319 unsigned int randstate;\r
6320 \r
6321 int\r
6322 myrandom(void)\r
6323 {\r
6324   randstate = randstate * 1664525 + 1013904223;\r
6325   return (int) randstate & 0x7fffffff;\r
6326 }\r
6327 \r
6328 void\r
6329 mysrandom(unsigned int seed)\r
6330 {\r
6331   randstate = seed;\r
6332 }\r
6333 \r
6334 \r
6335 /* \r
6336  * returns TRUE if user selects a different color, FALSE otherwise \r
6337  */\r
6338 \r
6339 BOOL\r
6340 ChangeColor(HWND hwnd, COLORREF *which)\r
6341 {\r
6342   static BOOL firstTime = TRUE;\r
6343   static DWORD customColors[16];\r
6344   CHOOSECOLOR cc;\r
6345   COLORREF newcolor;\r
6346   int i;\r
6347   ColorClass ccl;\r
6348 \r
6349   if (firstTime) {\r
6350     /* Make initial colors in use available as custom colors */\r
6351     /* Should we put the compiled-in defaults here instead? */\r
6352     i = 0;\r
6353     customColors[i++] = lightSquareColor & 0xffffff;\r
6354     customColors[i++] = darkSquareColor & 0xffffff;\r
6355     customColors[i++] = whitePieceColor & 0xffffff;\r
6356     customColors[i++] = blackPieceColor & 0xffffff;\r
6357     customColors[i++] = highlightSquareColor & 0xffffff;\r
6358     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6359 \r
6360     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6361       customColors[i++] = textAttribs[ccl].color;\r
6362     }\r
6363     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6364     firstTime = FALSE;\r
6365   }\r
6366 \r
6367   cc.lStructSize = sizeof(cc);\r
6368   cc.hwndOwner = hwnd;\r
6369   cc.hInstance = NULL;\r
6370   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6371   cc.lpCustColors = (LPDWORD) customColors;\r
6372   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6373 \r
6374   if (!ChooseColor(&cc)) return FALSE;\r
6375 \r
6376   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6377   if (newcolor == *which) return FALSE;\r
6378   *which = newcolor;\r
6379   return TRUE;\r
6380 \r
6381   /*\r
6382   InitDrawingColors();\r
6383   InvalidateRect(hwnd, &boardRect, FALSE);\r
6384   */\r
6385 }\r
6386 \r
6387 BOOLEAN\r
6388 MyLoadSound(MySound *ms)\r
6389 {\r
6390   BOOL ok = FALSE;\r
6391   struct stat st;\r
6392   FILE *f;\r
6393 \r
6394   if (ms->data) free(ms->data);\r
6395   ms->data = NULL;\r
6396 \r
6397   switch (ms->name[0]) {\r
6398   case NULLCHAR:\r
6399     /* Silence */\r
6400     ok = TRUE;\r
6401     break;\r
6402   case '$':\r
6403     /* System sound from Control Panel.  Don't preload here. */\r
6404     ok = TRUE;\r
6405     break;\r
6406   case '!':\r
6407     if (ms->name[1] == NULLCHAR) {\r
6408       /* "!" alone = silence */\r
6409       ok = TRUE;\r
6410     } else {\r
6411       /* Builtin wave resource.  Error if not found. */\r
6412       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6413       if (h == NULL) break;\r
6414       ms->data = (void *)LoadResource(hInst, h);\r
6415       if (h == NULL) break;\r
6416       ok = TRUE;\r
6417     }\r
6418     break;\r
6419   default:\r
6420     /* .wav file.  Error if not found. */\r
6421     f = fopen(ms->name, "rb");\r
6422     if (f == NULL) break;\r
6423     if (fstat(fileno(f), &st) < 0) break;\r
6424     ms->data = malloc(st.st_size);\r
6425     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6426     fclose(f);\r
6427     ok = TRUE;\r
6428     break;\r
6429   }\r
6430   if (!ok) {\r
6431     char buf[MSG_SIZ];\r
6432     sprintf(buf, "Error loading sound %s", ms->name);\r
6433     DisplayError(buf, GetLastError());\r
6434   }\r
6435   return ok;\r
6436 }\r
6437 \r
6438 BOOLEAN\r
6439 MyPlaySound(MySound *ms)\r
6440 {\r
6441   BOOLEAN ok = FALSE;\r
6442   switch (ms->name[0]) {\r
6443   case NULLCHAR:\r
6444     /* Silence */\r
6445     ok = TRUE;\r
6446     break;\r
6447   case '$':\r
6448     /* System sound from Control Panel (deprecated feature).\r
6449        "$" alone or an unset sound name gets default beep (still in use). */\r
6450     if (ms->name[1]) {\r
6451       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6452     }\r
6453     if (!ok) ok = MessageBeep(MB_OK);\r
6454     break; \r
6455   case '!':\r
6456     /* Builtin wave resource, or "!" alone for silence */\r
6457     if (ms->name[1]) {\r
6458       if (ms->data == NULL) return FALSE;\r
6459       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6460     } else {\r
6461       ok = TRUE;\r
6462     }\r
6463     break;\r
6464   default:\r
6465     /* .wav file.  Error if not found. */\r
6466     if (ms->data == NULL) return FALSE;\r
6467     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6468     break;\r
6469   }\r
6470   /* Don't print an error: this can happen innocently if the sound driver\r
6471      is busy; for instance, if another instance of WinBoard is playing\r
6472      a sound at about the same time. */\r
6473 #if 0\r
6474   if (!ok) {\r
6475     char buf[MSG_SIZ];\r
6476     sprintf(buf, "Error playing sound %s", ms->name);\r
6477     DisplayError(buf, GetLastError());\r
6478   }\r
6479 #endif\r
6480   return ok;\r
6481 }\r
6482 \r
6483 \r
6484 LRESULT CALLBACK\r
6485 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6486 {\r
6487   BOOL ok;\r
6488   OPENFILENAME *ofn;\r
6489   static UINT *number; /* gross that this is static */\r
6490 \r
6491   switch (message) {\r
6492   case WM_INITDIALOG: /* message: initialize dialog box */\r
6493     /* Center the dialog over the application window */\r
6494     ofn = (OPENFILENAME *) lParam;\r
6495     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6496       number = (UINT *) ofn->lCustData;\r
6497       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6498     } else {\r
6499       number = NULL;\r
6500     }\r
6501     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6502     return FALSE;  /* Allow for further processing */\r
6503 \r
6504   case WM_COMMAND:\r
6505     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6506       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6507     }\r
6508     return FALSE;  /* Allow for further processing */\r
6509   }\r
6510   return FALSE;\r
6511 }\r
6512 \r
6513 UINT APIENTRY\r
6514 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6515 {\r
6516   static UINT *number;\r
6517   OPENFILENAME *ofname;\r
6518   OFNOTIFY *ofnot;\r
6519   switch (uiMsg) {\r
6520   case WM_INITDIALOG:\r
6521     ofname = (OPENFILENAME *)lParam;\r
6522     number = (UINT *)(ofname->lCustData);\r
6523     break;\r
6524   case WM_NOTIFY:\r
6525     ofnot = (OFNOTIFY *)lParam;\r
6526     if (ofnot->hdr.code == CDN_FILEOK) {\r
6527       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6528     }\r
6529     break;\r
6530   }\r
6531   return 0;\r
6532 }\r
6533 \r
6534 \r
6535 FILE *\r
6536 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6537                char *nameFilt, char *dlgTitle, UINT *number,\r
6538                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6539 {\r
6540   OPENFILENAME openFileName;\r
6541   char buf1[MSG_SIZ];\r
6542   FILE *f;\r
6543 \r
6544   if (fileName == NULL) fileName = buf1;\r
6545   if (defName == NULL) {\r
6546     strcpy(fileName, "*.");\r
6547     strcat(fileName, defExt);\r
6548   } else {\r
6549     strcpy(fileName, defName);\r
6550   }\r
6551   if (fileTitle) strcpy(fileTitle, "");\r
6552   if (number) *number = 0;\r
6553 \r
6554   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6555   openFileName.hwndOwner         = hwnd;\r
6556   openFileName.hInstance         = (HANDLE) hInst;\r
6557   openFileName.lpstrFilter       = nameFilt;\r
6558   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6559   openFileName.nMaxCustFilter    = 0L;\r
6560   openFileName.nFilterIndex      = 1L;\r
6561   openFileName.lpstrFile         = fileName;\r
6562   openFileName.nMaxFile          = MSG_SIZ;\r
6563   openFileName.lpstrFileTitle    = fileTitle;\r
6564   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6565   openFileName.lpstrInitialDir   = NULL;\r
6566   openFileName.lpstrTitle        = dlgTitle;\r
6567   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6568     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6569     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6570     | (oldDialog ? 0 : OFN_EXPLORER);\r
6571   openFileName.nFileOffset       = 0;\r
6572   openFileName.nFileExtension    = 0;\r
6573   openFileName.lpstrDefExt       = defExt;\r
6574   openFileName.lCustData         = (LONG) number;\r
6575   openFileName.lpfnHook          = oldDialog ?\r
6576     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6577   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6578 \r
6579   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6580                         GetOpenFileName(&openFileName)) {\r
6581     /* open the file */\r
6582     f = fopen(openFileName.lpstrFile, write);\r
6583     if (f == NULL) {\r
6584       MessageBox(hwnd, "File open failed", NULL,\r
6585                  MB_OK|MB_ICONEXCLAMATION);\r
6586       return NULL;\r
6587     }\r
6588   } else {\r
6589     int err = CommDlgExtendedError();\r
6590     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6591     return FALSE;\r
6592   }\r
6593   return f;\r
6594 }\r
6595 \r
6596 \r
6597 \r
6598 VOID APIENTRY\r
6599 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6600 {\r
6601   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6602 \r
6603   /*\r
6604    * Get the first pop-up menu in the menu template. This is the\r
6605    * menu that TrackPopupMenu displays.\r
6606    */\r
6607   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6608 \r
6609   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6610 \r
6611   /*\r
6612    * TrackPopup uses screen coordinates, so convert the\r
6613    * coordinates of the mouse click to screen coordinates.\r
6614    */\r
6615   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6616 \r
6617   /* Draw and track the floating pop-up menu. */\r
6618   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6619                  pt.x, pt.y, 0, hwnd, NULL);\r
6620 \r
6621   /* Destroy the menu.*/\r
6622   DestroyMenu(hmenu);\r
6623 }\r
6624    \r
6625 typedef struct {\r
6626   HWND hDlg, hText;\r
6627   int sizeX, sizeY, newSizeX, newSizeY;\r
6628   HDWP hdwp;\r
6629 } ResizeEditPlusButtonsClosure;\r
6630 \r
6631 BOOL CALLBACK\r
6632 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6633 {\r
6634   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6635   RECT rect;\r
6636   POINT pt;\r
6637 \r
6638   if (hChild == cl->hText) return TRUE;\r
6639   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6640   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6641   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6642   ScreenToClient(cl->hDlg, &pt);\r
6643   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6644     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6645   return TRUE;\r
6646 }\r
6647 \r
6648 /* Resize a dialog that has a (rich) edit field filling most of\r
6649    the top, with a row of buttons below */\r
6650 VOID\r
6651 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6652 {\r
6653   RECT rectText;\r
6654   int newTextHeight, newTextWidth;\r
6655   ResizeEditPlusButtonsClosure cl;\r
6656   \r
6657   /*if (IsIconic(hDlg)) return;*/\r
6658   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6659   \r
6660   cl.hdwp = BeginDeferWindowPos(8);\r
6661 \r
6662   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6663   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6664   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6665   if (newTextHeight < 0) {\r
6666     newSizeY += -newTextHeight;\r
6667     newTextHeight = 0;\r
6668   }\r
6669   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6670     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6671 \r
6672   cl.hDlg = hDlg;\r
6673   cl.hText = hText;\r
6674   cl.sizeX = sizeX;\r
6675   cl.sizeY = sizeY;\r
6676   cl.newSizeX = newSizeX;\r
6677   cl.newSizeY = newSizeY;\r
6678   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6679 \r
6680   EndDeferWindowPos(cl.hdwp);\r
6681 }\r
6682 \r
6683 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6684 {\r
6685     RECT    rChild, rParent;\r
6686     int     wChild, hChild, wParent, hParent;\r
6687     int     wScreen, hScreen, xNew, yNew;\r
6688     HDC     hdc;\r
6689 \r
6690     /* Get the Height and Width of the child window */\r
6691     GetWindowRect (hwndChild, &rChild);\r
6692     wChild = rChild.right - rChild.left;\r
6693     hChild = rChild.bottom - rChild.top;\r
6694 \r
6695     /* Get the Height and Width of the parent window */\r
6696     GetWindowRect (hwndParent, &rParent);\r
6697     wParent = rParent.right - rParent.left;\r
6698     hParent = rParent.bottom - rParent.top;\r
6699 \r
6700     /* Get the display limits */\r
6701     hdc = GetDC (hwndChild);\r
6702     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6703     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6704     ReleaseDC(hwndChild, hdc);\r
6705 \r
6706     /* Calculate new X position, then adjust for screen */\r
6707     xNew = rParent.left + ((wParent - wChild) /2);\r
6708     if (xNew < 0) {\r
6709         xNew = 0;\r
6710     } else if ((xNew+wChild) > wScreen) {\r
6711         xNew = wScreen - wChild;\r
6712     }\r
6713 \r
6714     /* Calculate new Y position, then adjust for screen */\r
6715     if( mode == 0 ) {\r
6716         yNew = rParent.top  + ((hParent - hChild) /2);\r
6717     }\r
6718     else {\r
6719         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6720     }\r
6721 \r
6722     if (yNew < 0) {\r
6723         yNew = 0;\r
6724     } else if ((yNew+hChild) > hScreen) {\r
6725         yNew = hScreen - hChild;\r
6726     }\r
6727 \r
6728     /* Set it, and return */\r
6729     return SetWindowPos (hwndChild, NULL,\r
6730                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6731 }\r
6732 \r
6733 /* Center one window over another */\r
6734 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6735 {\r
6736     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6737 }\r
6738 \r
6739 /*---------------------------------------------------------------------------*\\r
6740  *\r
6741  * Startup Dialog functions\r
6742  *\r
6743 \*---------------------------------------------------------------------------*/\r
6744 void\r
6745 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6746 {\r
6747   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6748 \r
6749   while (*cd != NULL) {\r
6750     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6751     cd++;\r
6752   }\r
6753 }\r
6754 \r
6755 void\r
6756 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6757 {\r
6758   char buf1[ARG_MAX];\r
6759   int len;\r
6760 \r
6761   if (str[0] == '@') {\r
6762     FILE* f = fopen(str + 1, "r");\r
6763     if (f == NULL) {\r
6764       DisplayFatalError(str + 1, errno, 2);\r
6765       return;\r
6766     }\r
6767     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6768     fclose(f);\r
6769     buf1[len] = NULLCHAR;\r
6770     str = buf1;\r
6771   }\r
6772 \r
6773   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6774 \r
6775   for (;;) {\r
6776     char buf[MSG_SIZ];\r
6777     char *end = strchr(str, '\n');\r
6778     if (end == NULL) return;\r
6779     memcpy(buf, str, end - str);\r
6780     buf[end - str] = NULLCHAR;\r
6781     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6782     str = end + 1;\r
6783   }\r
6784 }\r
6785 \r
6786 void\r
6787 SetStartupDialogEnables(HWND hDlg)\r
6788 {\r
6789   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6790     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6791     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6792   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6793     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6794   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6795     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6796   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6797     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6798   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6799     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6800     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6801     IsDlgButtonChecked(hDlg, OPT_View));\r
6802 }\r
6803 \r
6804 char *\r
6805 QuoteForFilename(char *filename)\r
6806 {\r
6807   int dquote, space;\r
6808   dquote = strchr(filename, '"') != NULL;\r
6809   space = strchr(filename, ' ') != NULL;\r
6810   if (dquote || space) {\r
6811     if (dquote) {\r
6812       return "'";\r
6813     } else {\r
6814       return "\"";\r
6815     }\r
6816   } else {\r
6817     return "";\r
6818   }\r
6819 }\r
6820 \r
6821 VOID\r
6822 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6823 {\r
6824   char buf[MSG_SIZ];\r
6825   char *q;\r
6826 \r
6827   InitComboStringsFromOption(hwndCombo, nthnames);\r
6828   q = QuoteForFilename(nthcp);\r
6829   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6830   if (*nthdir != NULLCHAR) {\r
6831     q = QuoteForFilename(nthdir);\r
6832     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6833   }\r
6834   if (*nthcp == NULLCHAR) {\r
6835     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6836   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6837     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6838     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6839   }\r
6840 }\r
6841 \r
6842 LRESULT CALLBACK\r
6843 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6844 {\r
6845   char buf[MSG_SIZ];\r
6846   HANDLE hwndCombo;\r
6847   char *p;\r
6848 \r
6849   switch (message) {\r
6850   case WM_INITDIALOG:\r
6851     /* Center the dialog */\r
6852     CenterWindow (hDlg, GetDesktopWindow());\r
6853     /* Initialize the dialog items */\r
6854     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6855                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6856                   firstChessProgramNames);\r
6857     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6858                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6859                   secondChessProgramNames);\r
6860     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6861     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6862     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6863     if (*appData.icsHelper != NULLCHAR) {\r
6864       char *q = QuoteForFilename(appData.icsHelper);\r
6865       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6866     }\r
6867     if (*appData.icsHost == NULLCHAR) {\r
6868       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6869       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6870     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6871       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6872       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6873     }\r
6874 \r
6875     if (appData.icsActive) {\r
6876       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6877     }\r
6878     else if (appData.noChessProgram) {\r
6879       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6880     }\r
6881     else {\r
6882       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6883     }\r
6884 \r
6885     SetStartupDialogEnables(hDlg);\r
6886     return TRUE;\r
6887 \r
6888   case WM_COMMAND:\r
6889     switch (LOWORD(wParam)) {\r
6890     case IDOK:\r
6891       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6892         strcpy(buf, "/fcp=");\r
6893         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6894         p = buf;\r
6895         ParseArgs(StringGet, &p);\r
6896         strcpy(buf, "/scp=");\r
6897         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6898         p = buf;\r
6899         ParseArgs(StringGet, &p);\r
6900         appData.noChessProgram = FALSE;\r
6901         appData.icsActive = FALSE;\r
6902       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6903         strcpy(buf, "/ics /icshost=");\r
6904         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6905         p = buf;\r
6906         ParseArgs(StringGet, &p);\r
6907         if (appData.zippyPlay) {\r
6908           strcpy(buf, "/fcp=");\r
6909           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6910           p = buf;\r
6911           ParseArgs(StringGet, &p);\r
6912         }\r
6913       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6914         appData.noChessProgram = TRUE;\r
6915         appData.icsActive = FALSE;\r
6916       } else {\r
6917         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6918                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6919         return TRUE;\r
6920       }\r
6921       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6922         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6923         p = buf;\r
6924         ParseArgs(StringGet, &p);\r
6925       }\r
6926       EndDialog(hDlg, TRUE);\r
6927       return TRUE;\r
6928 \r
6929     case IDCANCEL:\r
6930       ExitEvent(0);\r
6931       return TRUE;\r
6932 \r
6933     case IDM_HELPCONTENTS:\r
6934       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6935         MessageBox (GetFocus(),\r
6936                     "Unable to activate help",\r
6937                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6938       }\r
6939       break;\r
6940 \r
6941     default:\r
6942       SetStartupDialogEnables(hDlg);\r
6943       break;\r
6944     }\r
6945     break;\r
6946   }\r
6947   return FALSE;\r
6948 }\r
6949 \r
6950 /*---------------------------------------------------------------------------*\\r
6951  *\r
6952  * About box dialog functions\r
6953  *\r
6954 \*---------------------------------------------------------------------------*/\r
6955 \r
6956 /* Process messages for "About" dialog box */\r
6957 LRESULT CALLBACK\r
6958 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6959 {\r
6960   switch (message) {\r
6961   case WM_INITDIALOG: /* message: initialize dialog box */\r
6962     /* Center the dialog over the application window */\r
6963     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6964     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6965     return (TRUE);\r
6966 \r
6967   case WM_COMMAND: /* message: received a command */\r
6968     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6969         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6970       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6971       return (TRUE);\r
6972     }\r
6973     break;\r
6974   }\r
6975   return (FALSE);\r
6976 }\r
6977 \r
6978 /*---------------------------------------------------------------------------*\\r
6979  *\r
6980  * Comment Dialog functions\r
6981  *\r
6982 \*---------------------------------------------------------------------------*/\r
6983 \r
6984 LRESULT CALLBACK\r
6985 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6986 {\r
6987   static HANDLE hwndText = NULL;\r
6988   int len, newSizeX, newSizeY, flags;\r
6989   static int sizeX, sizeY;\r
6990   char *str;\r
6991   RECT rect;\r
6992   MINMAXINFO *mmi;\r
6993 \r
6994   switch (message) {\r
6995   case WM_INITDIALOG: /* message: initialize dialog box */\r
6996     /* Initialize the dialog items */\r
6997     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6998     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6999     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7000     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7001     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7002     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7003     SetWindowText(hDlg, commentTitle);\r
7004     if (editComment) {\r
7005       SetFocus(hwndText);\r
7006     } else {\r
7007       SetFocus(GetDlgItem(hDlg, IDOK));\r
7008     }\r
7009     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7010                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7011                 MAKELPARAM(FALSE, 0));\r
7012     /* Size and position the dialog */\r
7013     if (!commentDialog) {\r
7014       commentDialog = hDlg;\r
7015       flags = SWP_NOZORDER;\r
7016       GetClientRect(hDlg, &rect);\r
7017       sizeX = rect.right;\r
7018       sizeY = rect.bottom;\r
7019       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7020           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7021         WINDOWPLACEMENT wp;\r
7022         EnsureOnScreen(&commentX, &commentY);\r
7023         wp.length = sizeof(WINDOWPLACEMENT);\r
7024         wp.flags = 0;\r
7025         wp.showCmd = SW_SHOW;\r
7026         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7027         wp.rcNormalPosition.left = commentX;\r
7028         wp.rcNormalPosition.right = commentX + commentW;\r
7029         wp.rcNormalPosition.top = commentY;\r
7030         wp.rcNormalPosition.bottom = commentY + commentH;\r
7031         SetWindowPlacement(hDlg, &wp);\r
7032 \r
7033         GetClientRect(hDlg, &rect);\r
7034         newSizeX = rect.right;\r
7035         newSizeY = rect.bottom;\r
7036         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7037                               newSizeX, newSizeY);\r
7038         sizeX = newSizeX;\r
7039         sizeY = newSizeY;\r
7040       }\r
7041     }\r
7042     return FALSE;\r
7043 \r
7044   case WM_COMMAND: /* message: received a command */\r
7045     switch (LOWORD(wParam)) {\r
7046     case IDOK:\r
7047       if (editComment) {\r
7048         char *p, *q;\r
7049         /* Read changed options from the dialog box */\r
7050         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7051         len = GetWindowTextLength(hwndText);\r
7052         str = (char *) malloc(len + 1);\r
7053         GetWindowText(hwndText, str, len + 1);\r
7054         p = q = str;\r
7055         while (*q) {\r
7056           if (*q == '\r')\r
7057             q++;\r
7058           else\r
7059             *p++ = *q++;\r
7060         }\r
7061         *p = NULLCHAR;\r
7062         ReplaceComment(commentIndex, str);\r
7063         free(str);\r
7064       }\r
7065       CommentPopDown();\r
7066       return TRUE;\r
7067 \r
7068     case IDCANCEL:\r
7069     case OPT_CancelComment:\r
7070       CommentPopDown();\r
7071       return TRUE;\r
7072 \r
7073     case OPT_ClearComment:\r
7074       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7075       break;\r
7076 \r
7077     case OPT_EditComment:\r
7078       EditCommentEvent();\r
7079       return TRUE;\r
7080 \r
7081     default:\r
7082       break;\r
7083     }\r
7084     break;\r
7085 \r
7086   case WM_SIZE:\r
7087     newSizeX = LOWORD(lParam);\r
7088     newSizeY = HIWORD(lParam);\r
7089     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7090     sizeX = newSizeX;\r
7091     sizeY = newSizeY;\r
7092     break;\r
7093 \r
7094   case WM_GETMINMAXINFO:\r
7095     /* Prevent resizing window too small */\r
7096     mmi = (MINMAXINFO *) lParam;\r
7097     mmi->ptMinTrackSize.x = 100;\r
7098     mmi->ptMinTrackSize.y = 100;\r
7099     break;\r
7100   }\r
7101   return FALSE;\r
7102 }\r
7103 \r
7104 VOID\r
7105 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7106 {\r
7107   FARPROC lpProc;\r
7108   char *p, *q;\r
7109 \r
7110   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7111 \r
7112   if (str == NULL) str = "";\r
7113   p = (char *) malloc(2 * strlen(str) + 2);\r
7114   q = p;\r
7115   while (*str) {\r
7116     if (*str == '\n') *q++ = '\r';\r
7117     *q++ = *str++;\r
7118   }\r
7119   *q = NULLCHAR;\r
7120   if (commentText != NULL) free(commentText);\r
7121 \r
7122   commentIndex = index;\r
7123   commentTitle = title;\r
7124   commentText = p;\r
7125   editComment = edit;\r
7126 \r
7127   if (commentDialog) {\r
7128     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7129     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7130   } else {\r
7131     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7132     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7133                  hwndMain, (DLGPROC)lpProc);\r
7134     FreeProcInstance(lpProc);\r
7135   }\r
7136   commentDialogUp = TRUE;\r
7137 }\r
7138 \r
7139 \r
7140 /*---------------------------------------------------------------------------*\\r
7141  *\r
7142  * Type-in move dialog functions\r
7143  * \r
7144 \*---------------------------------------------------------------------------*/\r
7145 \r
7146 LRESULT CALLBACK\r
7147 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7148 {\r
7149   char move[MSG_SIZ];\r
7150   HWND hInput;\r
7151   ChessMove moveType;\r
7152   int fromX, fromY, toX, toY;\r
7153   char promoChar;\r
7154 \r
7155   switch (message) {\r
7156   case WM_INITDIALOG:\r
7157     move[0] = (char) lParam;\r
7158     move[1] = NULLCHAR;\r
7159     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7160     hInput = GetDlgItem(hDlg, OPT_Move);\r
7161     SetWindowText(hInput, move);\r
7162     SetFocus(hInput);\r
7163     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7164     return FALSE;\r
7165 \r
7166   case WM_COMMAND:\r
7167     switch (LOWORD(wParam)) {\r
7168     case IDOK:\r
7169       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7170         gameMode != Training) {\r
7171         DisplayMoveError("Displayed move is not current");\r
7172       } else {\r
7173         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7174         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7175           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7176           if (gameMode != Training)\r
7177               forwardMostMove = currentMove;\r
7178           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7179         } else {\r
7180           DisplayMoveError("Could not parse move");\r
7181         }\r
7182       }\r
7183       EndDialog(hDlg, TRUE);\r
7184       return TRUE;\r
7185     case IDCANCEL:\r
7186       EndDialog(hDlg, FALSE);\r
7187       return TRUE;\r
7188     default:\r
7189       break;\r
7190     }\r
7191     break;\r
7192   }\r
7193   return FALSE;\r
7194 }\r
7195 \r
7196 VOID\r
7197 PopUpMoveDialog(char firstchar)\r
7198 {\r
7199     FARPROC lpProc;\r
7200     \r
7201     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7202         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7203         gameMode == AnalyzeMode || gameMode == EditGame || \r
7204         gameMode == EditPosition || gameMode == IcsExamining ||\r
7205         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7206         gameMode == Training) {\r
7207       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7208       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7209         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7210       FreeProcInstance(lpProc);\r
7211     }\r
7212 }\r
7213 \r
7214 /*---------------------------------------------------------------------------*\\r
7215  *\r
7216  * Type-in name dialog functions\r
7217  * \r
7218 \*---------------------------------------------------------------------------*/\r
7219 \r
7220 LRESULT CALLBACK\r
7221 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7222 {\r
7223   char move[MSG_SIZ];\r
7224   HWND hInput;\r
7225 \r
7226   switch (message) {\r
7227   case WM_INITDIALOG:\r
7228     move[0] = (char) lParam;\r
7229     move[1] = NULLCHAR;\r
7230     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7231     hInput = GetDlgItem(hDlg, OPT_Name);\r
7232     SetWindowText(hInput, move);\r
7233     SetFocus(hInput);\r
7234     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7235     return FALSE;\r
7236 \r
7237   case WM_COMMAND:\r
7238     switch (LOWORD(wParam)) {\r
7239     case IDOK:\r
7240       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7241       appData.userName = strdup(move);\r
7242 \r
7243       EndDialog(hDlg, TRUE);\r
7244       return TRUE;\r
7245     case IDCANCEL:\r
7246       EndDialog(hDlg, FALSE);\r
7247       return TRUE;\r
7248     default:\r
7249       break;\r
7250     }\r
7251     break;\r
7252   }\r
7253   return FALSE;\r
7254 }\r
7255 \r
7256 VOID\r
7257 PopUpNameDialog(char firstchar)\r
7258 {\r
7259     FARPROC lpProc;\r
7260     \r
7261       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7262       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7263         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7264       FreeProcInstance(lpProc);\r
7265 }\r
7266 \r
7267 /*---------------------------------------------------------------------------*\\r
7268  *\r
7269  *  Error dialogs\r
7270  * \r
7271 \*---------------------------------------------------------------------------*/\r
7272 \r
7273 /* Nonmodal error box */\r
7274 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7275                              WPARAM wParam, LPARAM lParam);\r
7276 \r
7277 VOID\r
7278 ErrorPopUp(char *title, char *content)\r
7279 {\r
7280   FARPROC lpProc;\r
7281   char *p, *q;\r
7282   BOOLEAN modal = hwndMain == NULL;\r
7283 \r
7284   p = content;\r
7285   q = errorMessage;\r
7286   while (*p) {\r
7287     if (*p == '\n') {\r
7288       if (modal) {\r
7289         *q++ = ' ';\r
7290         p++;\r
7291       } else {\r
7292         *q++ = '\r';\r
7293         *q++ = *p++;\r
7294       }\r
7295     } else {\r
7296       *q++ = *p++;\r
7297     }\r
7298   }\r
7299   *q = NULLCHAR;\r
7300   strncpy(errorTitle, title, sizeof(errorTitle));\r
7301   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7302   \r
7303   if (modal) {\r
7304     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7305   } else {\r
7306     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7307     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7308                  hwndMain, (DLGPROC)lpProc);\r
7309     FreeProcInstance(lpProc);\r
7310   }\r
7311 }\r
7312 \r
7313 VOID\r
7314 ErrorPopDown()\r
7315 {\r
7316   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7317   if (errorDialog == NULL) return;\r
7318   DestroyWindow(errorDialog);\r
7319   errorDialog = NULL;\r
7320 }\r
7321 \r
7322 LRESULT CALLBACK\r
7323 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7324 {\r
7325   HANDLE hwndText;\r
7326   RECT rChild;\r
7327 \r
7328   switch (message) {\r
7329   case WM_INITDIALOG:\r
7330     GetWindowRect(hDlg, &rChild);\r
7331 \r
7332     /*\r
7333     SetWindowPos(hDlg, NULL, rChild.left,\r
7334       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7335       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7336     */\r
7337 \r
7338     /* \r
7339         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7340         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7341         and it doesn't work when you resize the dialog.\r
7342         For now, just give it a default position.\r
7343     */\r
7344     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7345 \r
7346     errorDialog = hDlg;\r
7347     SetWindowText(hDlg, errorTitle);\r
7348     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7349     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7350     return FALSE;\r
7351 \r
7352   case WM_COMMAND:\r
7353     switch (LOWORD(wParam)) {\r
7354     case IDOK:\r
7355     case IDCANCEL:\r
7356       if (errorDialog == hDlg) errorDialog = NULL;\r
7357       DestroyWindow(hDlg);\r
7358       return TRUE;\r
7359 \r
7360     default:\r
7361       break;\r
7362     }\r
7363     break;\r
7364   }\r
7365   return FALSE;\r
7366 }\r
7367 \r
7368 #ifdef GOTHIC\r
7369 HWND gothicDialog = NULL;\r
7370 \r
7371 LRESULT CALLBACK\r
7372 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7373 {\r
7374   HANDLE hwndText;\r
7375   RECT rChild;\r
7376   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7377 \r
7378   switch (message) {\r
7379   case WM_INITDIALOG:\r
7380     GetWindowRect(hDlg, &rChild);\r
7381 \r
7382     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7383                                                              SWP_NOZORDER);\r
7384 \r
7385     /* \r
7386         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7387         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7388         and it doesn't work when you resize the dialog.\r
7389         For now, just give it a default position.\r
7390     */\r
7391     gothicDialog = hDlg;\r
7392     SetWindowText(hDlg, errorTitle);\r
7393     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7394     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7395     return FALSE;\r
7396 \r
7397   case WM_COMMAND:\r
7398     switch (LOWORD(wParam)) {\r
7399     case IDOK:\r
7400     case IDCANCEL:\r
7401       if (errorDialog == hDlg) errorDialog = NULL;\r
7402       DestroyWindow(hDlg);\r
7403       return TRUE;\r
7404 \r
7405     default:\r
7406       break;\r
7407     }\r
7408     break;\r
7409   }\r
7410   return FALSE;\r
7411 }\r
7412 \r
7413 VOID\r
7414 GothicPopUp(char *title, VariantClass variant)\r
7415 {\r
7416   FARPROC lpProc;\r
7417   char *p, *q;\r
7418   BOOLEAN modal = hwndMain == NULL;\r
7419   static char *lastTitle;\r
7420 \r
7421   strncpy(errorTitle, title, sizeof(errorTitle));\r
7422   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7423 \r
7424   if(lastTitle != title && gothicDialog != NULL) {\r
7425     DestroyWindow(gothicDialog);\r
7426     gothicDialog = NULL;\r
7427   }\r
7428   if(variant != VariantNormal && gothicDialog == NULL) {\r
7429     title = lastTitle;\r
7430     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7431     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7432                  hwndMain, (DLGPROC)lpProc);\r
7433     FreeProcInstance(lpProc);\r
7434   }\r
7435 }\r
7436 #endif\r
7437 \r
7438 /*---------------------------------------------------------------------------*\\r
7439  *\r
7440  *  Ics Interaction console functions\r
7441  *\r
7442 \*---------------------------------------------------------------------------*/\r
7443 \r
7444 #define HISTORY_SIZE 64\r
7445 static char *history[HISTORY_SIZE];\r
7446 int histIn = 0, histP = 0;\r
7447 \r
7448 VOID\r
7449 SaveInHistory(char *cmd)\r
7450 {\r
7451   if (history[histIn] != NULL) {\r
7452     free(history[histIn]);\r
7453     history[histIn] = NULL;\r
7454   }\r
7455   if (*cmd == NULLCHAR) return;\r
7456   history[histIn] = StrSave(cmd);\r
7457   histIn = (histIn + 1) % HISTORY_SIZE;\r
7458   if (history[histIn] != NULL) {\r
7459     free(history[histIn]);\r
7460     history[histIn] = NULL;\r
7461   }\r
7462   histP = histIn;\r
7463 }\r
7464 \r
7465 char *\r
7466 PrevInHistory(char *cmd)\r
7467 {\r
7468   int newhp;\r
7469   if (histP == histIn) {\r
7470     if (history[histIn] != NULL) free(history[histIn]);\r
7471     history[histIn] = StrSave(cmd);\r
7472   }\r
7473   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7474   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7475   histP = newhp;\r
7476   return history[histP];\r
7477 }\r
7478 \r
7479 char *\r
7480 NextInHistory()\r
7481 {\r
7482   if (histP == histIn) return NULL;\r
7483   histP = (histP + 1) % HISTORY_SIZE;\r
7484   return history[histP];\r
7485 }\r
7486 \r
7487 typedef struct {\r
7488   char *item;\r
7489   char *command;\r
7490   BOOLEAN getname;\r
7491   BOOLEAN immediate;\r
7492 } IcsTextMenuEntry;\r
7493 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7494 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7495 \r
7496 void\r
7497 ParseIcsTextMenu(char *icsTextMenuString)\r
7498 {\r
7499   int flags = 0;\r
7500   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7501   char *p = icsTextMenuString;\r
7502   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7503     free(e->item);\r
7504     e->item = NULL;\r
7505     if (e->command != NULL) {\r
7506       free(e->command);\r
7507       e->command = NULL;\r
7508     }\r
7509     e++;\r
7510   }\r
7511   e = icsTextMenuEntry;\r
7512   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7513     if (*p == ';' || *p == '\n') {\r
7514       e->item = strdup("-");\r
7515       e->command = NULL;\r
7516       p++;\r
7517     } else if (*p == '-') {\r
7518       e->item = strdup("-");\r
7519       e->command = NULL;\r
7520       p++;\r
7521       if (*p) p++;\r
7522     } else {\r
7523       char *q, *r, *s, *t;\r
7524       char c;\r
7525       q = strchr(p, ',');\r
7526       if (q == NULL) break;\r
7527       *q = NULLCHAR;\r
7528       r = strchr(q + 1, ',');\r
7529       if (r == NULL) break;\r
7530       *r = NULLCHAR;\r
7531       s = strchr(r + 1, ',');\r
7532       if (s == NULL) break;\r
7533       *s = NULLCHAR;\r
7534       c = ';';\r
7535       t = strchr(s + 1, c);\r
7536       if (t == NULL) {\r
7537         c = '\n';\r
7538         t = strchr(s + 1, c);\r
7539       }\r
7540       if (t != NULL) *t = NULLCHAR;\r
7541       e->item = strdup(p);\r
7542       e->command = strdup(q + 1);\r
7543       e->getname = *(r + 1) != '0';\r
7544       e->immediate = *(s + 1) != '0';\r
7545       *q = ',';\r
7546       *r = ',';\r
7547       *s = ',';\r
7548       if (t == NULL) break;\r
7549       *t = c;\r
7550       p = t + 1;\r
7551     }\r
7552     e++;\r
7553   } \r
7554 }\r
7555 \r
7556 HMENU\r
7557 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7558 {\r
7559   HMENU hmenu, h;\r
7560   int i = 0;\r
7561   hmenu = LoadMenu(hInst, "TextMenu");\r
7562   h = GetSubMenu(hmenu, 0);\r
7563   while (e->item) {\r
7564     if (strcmp(e->item, "-") == 0) {\r
7565       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7566     } else {\r
7567       if (e->item[0] == '|') {\r
7568         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7569                    IDM_CommandX + i, &e->item[1]);\r
7570       } else {\r
7571         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7572       }\r
7573     }\r
7574     e++;\r
7575     i++;\r
7576   } \r
7577   return hmenu;\r
7578 }\r
7579 \r
7580 WNDPROC consoleTextWindowProc;\r
7581 \r
7582 void\r
7583 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7584 {\r
7585   char buf[MSG_SIZ], name[MSG_SIZ];\r
7586   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7587   CHARRANGE sel;\r
7588 \r
7589   if (!getname) {\r
7590     SetWindowText(hInput, command);\r
7591     if (immediate) {\r
7592       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7593     } else {\r
7594       sel.cpMin = 999999;\r
7595       sel.cpMax = 999999;\r
7596       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7597       SetFocus(hInput);\r
7598     }\r
7599     return;\r
7600   }    \r
7601   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7602   if (sel.cpMin == sel.cpMax) {\r
7603     /* Expand to surrounding word */\r
7604     TEXTRANGE tr;\r
7605     do {\r
7606       tr.chrg.cpMax = sel.cpMin;\r
7607       tr.chrg.cpMin = --sel.cpMin;\r
7608       if (sel.cpMin < 0) break;\r
7609       tr.lpstrText = name;\r
7610       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7611     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7612     sel.cpMin++;\r
7613 \r
7614     do {\r
7615       tr.chrg.cpMin = sel.cpMax;\r
7616       tr.chrg.cpMax = ++sel.cpMax;\r
7617       tr.lpstrText = name;\r
7618       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7619     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7620     sel.cpMax--;\r
7621 \r
7622     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7623       MessageBeep(MB_ICONEXCLAMATION);\r
7624       return;\r
7625     }\r
7626     tr.chrg = sel;\r
7627     tr.lpstrText = name;\r
7628     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7629   } else {\r
7630     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7631       MessageBeep(MB_ICONEXCLAMATION);\r
7632       return;\r
7633     }\r
7634     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7635   }\r
7636   if (immediate) {\r
7637     sprintf(buf, "%s %s", command, name);\r
7638     SetWindowText(hInput, buf);\r
7639     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7640   } else {\r
7641     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7642     SetWindowText(hInput, buf);\r
7643     sel.cpMin = 999999;\r
7644     sel.cpMax = 999999;\r
7645     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7646     SetFocus(hInput);\r
7647   }\r
7648 }\r
7649 \r
7650 LRESULT CALLBACK \r
7651 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7652 {\r
7653   HWND hInput;\r
7654   CHARRANGE sel;\r
7655 \r
7656   switch (message) {\r
7657   case WM_KEYDOWN:\r
7658     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7659     switch (wParam) {\r
7660     case VK_PRIOR:\r
7661       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7662       return 0;\r
7663     case VK_NEXT:\r
7664       sel.cpMin = 999999;\r
7665       sel.cpMax = 999999;\r
7666       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7667       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7668       return 0;\r
7669     }\r
7670     break;\r
7671   case WM_CHAR:\r
7672     if (wParam == '\t') {\r
7673       if (GetKeyState(VK_SHIFT) < 0) {\r
7674         /* shifted */\r
7675         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7676         if (buttonDesc[0].hwnd) {\r
7677           SetFocus(buttonDesc[0].hwnd);\r
7678         } else {\r
7679           SetFocus(hwndMain);\r
7680         }\r
7681       } else {\r
7682         /* unshifted */\r
7683         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7684       }\r
7685     } else {\r
7686       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7687       SetFocus(hInput);\r
7688       SendMessage(hInput, message, wParam, lParam);\r
7689     }\r
7690     return 0;\r
7691   case WM_PASTE:\r
7692     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7693     SetFocus(hInput);\r
7694     return SendMessage(hInput, message, wParam, lParam);\r
7695   case WM_MBUTTONDOWN:\r
7696     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7697   case WM_RBUTTONDOWN:\r
7698     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7699       /* Move selection here if it was empty */\r
7700       POINT pt;\r
7701       pt.x = LOWORD(lParam);\r
7702       pt.y = HIWORD(lParam);\r
7703       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7704       if (sel.cpMin == sel.cpMax) {\r
7705         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7706         sel.cpMax = sel.cpMin;\r
7707         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7708       }\r
7709       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7710     }\r
7711     return 0;\r
7712   case WM_RBUTTONUP:\r
7713     if (GetKeyState(VK_SHIFT) & ~1) {\r
7714       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7715         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7716     } else {\r
7717       POINT pt;\r
7718       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7719       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7720       if (sel.cpMin == sel.cpMax) {\r
7721         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7722         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7723       }\r
7724       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7725         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7726       }\r
7727       pt.x = LOWORD(lParam);\r
7728       pt.y = HIWORD(lParam);\r
7729       MenuPopup(hwnd, pt, hmenu, -1);\r
7730     }\r
7731     return 0;\r
7732   case WM_COMMAND:\r
7733     switch (LOWORD(wParam)) {\r
7734     case IDM_QuickPaste:\r
7735       {\r
7736         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7737         if (sel.cpMin == sel.cpMax) {\r
7738           MessageBeep(MB_ICONEXCLAMATION);\r
7739           return 0;\r
7740         }\r
7741         SendMessage(hwnd, WM_COPY, 0, 0);\r
7742         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7743         SendMessage(hInput, WM_PASTE, 0, 0);\r
7744         SetFocus(hInput);\r
7745         return 0;\r
7746       }\r
7747     case IDM_Cut:\r
7748       SendMessage(hwnd, WM_CUT, 0, 0);\r
7749       return 0;\r
7750     case IDM_Paste:\r
7751       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7752       return 0;\r
7753     case IDM_Copy:\r
7754       SendMessage(hwnd, WM_COPY, 0, 0);\r
7755       return 0;\r
7756     default:\r
7757       {\r
7758         int i = LOWORD(wParam) - IDM_CommandX;\r
7759         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7760             icsTextMenuEntry[i].command != NULL) {\r
7761           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7762                    icsTextMenuEntry[i].getname,\r
7763                    icsTextMenuEntry[i].immediate);\r
7764           return 0;\r
7765         }\r
7766       }\r
7767       break;\r
7768     }\r
7769     break;\r
7770   }\r
7771   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7772 }\r
7773 \r
7774 WNDPROC consoleInputWindowProc;\r
7775 \r
7776 LRESULT CALLBACK\r
7777 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7778 {\r
7779   char buf[MSG_SIZ];\r
7780   char *p;\r
7781   static BOOL sendNextChar = FALSE;\r
7782   static BOOL quoteNextChar = FALSE;\r
7783   InputSource *is = consoleInputSource;\r
7784   CHARFORMAT cf;\r
7785   CHARRANGE sel;\r
7786 \r
7787   switch (message) {\r
7788   case WM_CHAR:\r
7789     if (!appData.localLineEditing || sendNextChar) {\r
7790       is->buf[0] = (CHAR) wParam;\r
7791       is->count = 1;\r
7792       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7793       sendNextChar = FALSE;\r
7794       return 0;\r
7795     }\r
7796     if (quoteNextChar) {\r
7797       buf[0] = (char) wParam;\r
7798       buf[1] = NULLCHAR;\r
7799       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7800       quoteNextChar = FALSE;\r
7801       return 0;\r
7802     }\r
7803     switch (wParam) {\r
7804     case '\r':   /* Enter key */\r
7805       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7806       if (consoleEcho) SaveInHistory(is->buf);\r
7807       is->buf[is->count++] = '\n';\r
7808       is->buf[is->count] = NULLCHAR;\r
7809       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7810       if (consoleEcho) {\r
7811         ConsoleOutput(is->buf, is->count, TRUE);\r
7812       } else if (appData.localLineEditing) {\r
7813         ConsoleOutput("\n", 1, TRUE);\r
7814       }\r
7815       /* fall thru */\r
7816     case '\033': /* Escape key */\r
7817       SetWindowText(hwnd, "");\r
7818       cf.cbSize = sizeof(CHARFORMAT);\r
7819       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7820       if (consoleEcho) {\r
7821         cf.crTextColor = textAttribs[ColorNormal].color;\r
7822       } else {\r
7823         cf.crTextColor = COLOR_ECHOOFF;\r
7824       }\r
7825       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7826       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7827       return 0;\r
7828     case '\t':   /* Tab key */\r
7829       if (GetKeyState(VK_SHIFT) < 0) {\r
7830         /* shifted */\r
7831         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7832       } else {\r
7833         /* unshifted */\r
7834         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7835         if (buttonDesc[0].hwnd) {\r
7836           SetFocus(buttonDesc[0].hwnd);\r
7837         } else {\r
7838           SetFocus(hwndMain);\r
7839         }\r
7840       }\r
7841       return 0;\r
7842     case '\023': /* Ctrl+S */\r
7843       sendNextChar = TRUE;\r
7844       return 0;\r
7845     case '\021': /* Ctrl+Q */\r
7846       quoteNextChar = TRUE;\r
7847       return 0;\r
7848     default:\r
7849       break;\r
7850     }\r
7851     break;\r
7852   case WM_KEYDOWN:\r
7853     switch (wParam) {\r
7854     case VK_UP:\r
7855       GetWindowText(hwnd, buf, MSG_SIZ);\r
7856       p = PrevInHistory(buf);\r
7857       if (p != NULL) {\r
7858         SetWindowText(hwnd, p);\r
7859         sel.cpMin = 999999;\r
7860         sel.cpMax = 999999;\r
7861         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7862         return 0;\r
7863       }\r
7864       break;\r
7865     case VK_DOWN:\r
7866       p = NextInHistory();\r
7867       if (p != NULL) {\r
7868         SetWindowText(hwnd, p);\r
7869         sel.cpMin = 999999;\r
7870         sel.cpMax = 999999;\r
7871         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7872         return 0;\r
7873       }\r
7874       break;\r
7875     case VK_HOME:\r
7876     case VK_END:\r
7877       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7878       /* fall thru */\r
7879     case VK_PRIOR:\r
7880     case VK_NEXT:\r
7881       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7882       return 0;\r
7883     }\r
7884     break;\r
7885   case WM_MBUTTONDOWN:\r
7886     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7887       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7888     break;\r
7889   case WM_RBUTTONUP:\r
7890     if (GetKeyState(VK_SHIFT) & ~1) {\r
7891       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7892         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7893     } else {\r
7894       POINT pt;\r
7895       HMENU hmenu;\r
7896       hmenu = LoadMenu(hInst, "InputMenu");\r
7897       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7898       if (sel.cpMin == sel.cpMax) {\r
7899         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7900         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7901       }\r
7902       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7903         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7904       }\r
7905       pt.x = LOWORD(lParam);\r
7906       pt.y = HIWORD(lParam);\r
7907       MenuPopup(hwnd, pt, hmenu, -1);\r
7908     }\r
7909     return 0;\r
7910   case WM_COMMAND:\r
7911     switch (LOWORD(wParam)) { \r
7912     case IDM_Undo:\r
7913       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7914       return 0;\r
7915     case IDM_SelectAll:\r
7916       sel.cpMin = 0;\r
7917       sel.cpMax = -1; /*999999?*/\r
7918       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7919       return 0;\r
7920     case IDM_Cut:\r
7921       SendMessage(hwnd, WM_CUT, 0, 0);\r
7922       return 0;\r
7923     case IDM_Paste:\r
7924       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7925       return 0;\r
7926     case IDM_Copy:\r
7927       SendMessage(hwnd, WM_COPY, 0, 0);\r
7928       return 0;\r
7929     }\r
7930     break;\r
7931   }\r
7932   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7933 }\r
7934 \r
7935 #define CO_MAX  100000\r
7936 #define CO_TRIM   1000\r
7937 \r
7938 LRESULT CALLBACK\r
7939 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7940 {\r
7941   static SnapData sd;\r
7942   static HWND hText, hInput, hFocus;\r
7943   InputSource *is = consoleInputSource;\r
7944   RECT rect;\r
7945   static int sizeX, sizeY;\r
7946   int newSizeX, newSizeY;\r
7947   MINMAXINFO *mmi;\r
7948 \r
7949   switch (message) {\r
7950   case WM_INITDIALOG: /* message: initialize dialog box */\r
7951     hwndConsole = hDlg;\r
7952     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7953     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7954     SetFocus(hInput);\r
7955     consoleTextWindowProc = (WNDPROC)\r
7956       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7957     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7958     consoleInputWindowProc = (WNDPROC)\r
7959       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7960     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7961     Colorize(ColorNormal, TRUE);\r
7962     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7963     ChangedConsoleFont();\r
7964     GetClientRect(hDlg, &rect);\r
7965     sizeX = rect.right;\r
7966     sizeY = rect.bottom;\r
7967     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7968         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7969       WINDOWPLACEMENT wp;\r
7970       EnsureOnScreen(&consoleX, &consoleY);\r
7971       wp.length = sizeof(WINDOWPLACEMENT);\r
7972       wp.flags = 0;\r
7973       wp.showCmd = SW_SHOW;\r
7974       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7975       wp.rcNormalPosition.left = consoleX;\r
7976       wp.rcNormalPosition.right = consoleX + consoleW;\r
7977       wp.rcNormalPosition.top = consoleY;\r
7978       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7979       SetWindowPlacement(hDlg, &wp);\r
7980     }\r
7981 #if 0 \r
7982    // [HGM] Chessknight's change 2004-07-13\r
7983    else { /* Determine Defaults */\r
7984        WINDOWPLACEMENT wp;\r
7985        consoleX = winWidth + 1;\r
7986        consoleY = boardY;\r
7987        consoleW = screenWidth -  winWidth;\r
7988        consoleH = winHeight;\r
7989        EnsureOnScreen(&consoleX, &consoleY);\r
7990        wp.length = sizeof(WINDOWPLACEMENT);\r
7991        wp.flags = 0;\r
7992        wp.showCmd = SW_SHOW;\r
7993        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7994        wp.rcNormalPosition.left = consoleX;\r
7995        wp.rcNormalPosition.right = consoleX + consoleW;\r
7996        wp.rcNormalPosition.top = consoleY;\r
7997        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7998        SetWindowPlacement(hDlg, &wp);\r
7999     }\r
8000 #endif\r
8001     return FALSE;\r
8002 \r
8003   case WM_SETFOCUS:\r
8004     SetFocus(hInput);\r
8005     return 0;\r
8006 \r
8007   case WM_CLOSE:\r
8008     ExitEvent(0);\r
8009     /* not reached */\r
8010     break;\r
8011 \r
8012   case WM_SIZE:\r
8013     if (IsIconic(hDlg)) break;\r
8014     newSizeX = LOWORD(lParam);\r
8015     newSizeY = HIWORD(lParam);\r
8016     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8017       RECT rectText, rectInput;\r
8018       POINT pt;\r
8019       int newTextHeight, newTextWidth;\r
8020       GetWindowRect(hText, &rectText);\r
8021       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8022       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8023       if (newTextHeight < 0) {\r
8024         newSizeY += -newTextHeight;\r
8025         newTextHeight = 0;\r
8026       }\r
8027       SetWindowPos(hText, NULL, 0, 0,\r
8028         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8029       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8030       pt.x = rectInput.left;\r
8031       pt.y = rectInput.top + newSizeY - sizeY;\r
8032       ScreenToClient(hDlg, &pt);\r
8033       SetWindowPos(hInput, NULL, \r
8034         pt.x, pt.y, /* needs client coords */   \r
8035         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8036         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8037     }\r
8038     sizeX = newSizeX;\r
8039     sizeY = newSizeY;\r
8040     break;\r
8041 \r
8042   case WM_GETMINMAXINFO:\r
8043     /* Prevent resizing window too small */\r
8044     mmi = (MINMAXINFO *) lParam;\r
8045     mmi->ptMinTrackSize.x = 100;\r
8046     mmi->ptMinTrackSize.y = 100;\r
8047     break;\r
8048 \r
8049   /* [AS] Snapping */\r
8050   case WM_ENTERSIZEMOVE:\r
8051     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8052 \r
8053   case WM_SIZING:\r
8054     return OnSizing( &sd, hDlg, wParam, lParam );\r
8055 \r
8056   case WM_MOVING:\r
8057     return OnMoving( &sd, hDlg, wParam, lParam );\r
8058 \r
8059   case WM_EXITSIZEMOVE:\r
8060     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8061   }\r
8062 \r
8063   return DefWindowProc(hDlg, message, wParam, lParam);\r
8064 }\r
8065 \r
8066 \r
8067 VOID\r
8068 ConsoleCreate()\r
8069 {\r
8070   HWND hCons;\r
8071   if (hwndConsole) return;\r
8072   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8073   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8074 }\r
8075 \r
8076 \r
8077 VOID\r
8078 ConsoleOutput(char* data, int length, int forceVisible)\r
8079 {\r
8080   HWND hText;\r
8081   int trim, exlen;\r
8082   char *p, *q;\r
8083   char buf[CO_MAX+1];\r
8084   POINT pEnd;\r
8085   RECT rect;\r
8086   static int delayLF = 0;\r
8087   CHARRANGE savesel, sel;\r
8088 \r
8089   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8090   p = data;\r
8091   q = buf;\r
8092   if (delayLF) {\r
8093     *q++ = '\r';\r
8094     *q++ = '\n';\r
8095     delayLF = 0;\r
8096   }\r
8097   while (length--) {\r
8098     if (*p == '\n') {\r
8099       if (*++p) {\r
8100         *q++ = '\r';\r
8101         *q++ = '\n';\r
8102       } else {\r
8103         delayLF = 1;\r
8104       }\r
8105     } else if (*p == '\007') {\r
8106        MyPlaySound(&sounds[(int)SoundBell]);\r
8107        p++;\r
8108     } else {\r
8109       *q++ = *p++;\r
8110     }\r
8111   }\r
8112   *q = NULLCHAR;\r
8113   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8114   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8115   /* Save current selection */\r
8116   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8117   exlen = GetWindowTextLength(hText);\r
8118   /* Find out whether current end of text is visible */\r
8119   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8120   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8121   /* Trim existing text if it's too long */\r
8122   if (exlen + (q - buf) > CO_MAX) {\r
8123     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8124     sel.cpMin = 0;\r
8125     sel.cpMax = trim;\r
8126     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8127     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8128     exlen -= trim;\r
8129     savesel.cpMin -= trim;\r
8130     savesel.cpMax -= trim;\r
8131     if (exlen < 0) exlen = 0;\r
8132     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8133     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8134   }\r
8135   /* Append the new text */\r
8136   sel.cpMin = exlen;\r
8137   sel.cpMax = exlen;\r
8138   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8139   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8140   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8141   if (forceVisible || exlen == 0 ||\r
8142       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8143        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8144     /* Scroll to make new end of text visible if old end of text\r
8145        was visible or new text is an echo of user typein */\r
8146     sel.cpMin = 9999999;\r
8147     sel.cpMax = 9999999;\r
8148     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8149     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8150     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8151     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8152   }\r
8153   if (savesel.cpMax == exlen || forceVisible) {\r
8154     /* Move insert point to new end of text if it was at the old\r
8155        end of text or if the new text is an echo of user typein */\r
8156     sel.cpMin = 9999999;\r
8157     sel.cpMax = 9999999;\r
8158     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8159   } else {\r
8160     /* Restore previous selection */\r
8161     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8162   }\r
8163   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8164 }\r
8165 \r
8166 /*---------*/\r
8167 \r
8168 \r
8169 void\r
8170 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8171 {\r
8172   char buf[100];\r
8173   char *str;\r
8174   COLORREF oldFg, oldBg;\r
8175   HFONT oldFont;\r
8176   RECT rect;\r
8177 \r
8178   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8179 \r
8180   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8181   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8182   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8183 \r
8184   rect.left = x;\r
8185   rect.right = x + squareSize;\r
8186   rect.top  = y;\r
8187   rect.bottom = y + squareSize;\r
8188   str = buf;\r
8189 \r
8190   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8191                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8192              y, ETO_CLIPPED|ETO_OPAQUE,\r
8193              &rect, str, strlen(str), NULL);\r
8194 \r
8195   (void) SetTextColor(hdc, oldFg);\r
8196   (void) SetBkColor(hdc, oldBg);\r
8197   (void) SelectObject(hdc, oldFont);\r
8198 }\r
8199 \r
8200 void\r
8201 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8202               RECT *rect, char *color, char *flagFell)\r
8203 {\r
8204   char buf[100];\r
8205   char *str;\r
8206   COLORREF oldFg, oldBg;\r
8207   HFONT oldFont;\r
8208 \r
8209   if (appData.clockMode) {\r
8210     if (tinyLayout)\r
8211       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8212     else\r
8213       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8214     str = buf;\r
8215   } else {\r
8216     str = color;\r
8217   }\r
8218 \r
8219   if (highlight) {\r
8220     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8221     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8222   } else {\r
8223     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8224     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8225   }\r
8226   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8227 \r
8228   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8229              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8230              rect, str, strlen(str), NULL);\r
8231 \r
8232   (void) SetTextColor(hdc, oldFg);\r
8233   (void) SetBkColor(hdc, oldBg);\r
8234   (void) SelectObject(hdc, oldFont);\r
8235 }\r
8236 \r
8237 \r
8238 int\r
8239 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8240            OVERLAPPED *ovl)\r
8241 {\r
8242   int ok, err;\r
8243 \r
8244   /* [AS]  */\r
8245   if( count <= 0 ) {\r
8246     if (appData.debugMode) {\r
8247       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8248     }\r
8249 \r
8250     return ERROR_INVALID_USER_BUFFER;\r
8251   }\r
8252 \r
8253   ResetEvent(ovl->hEvent);\r
8254   ovl->Offset = ovl->OffsetHigh = 0;\r
8255   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8256   if (ok) {\r
8257     err = NO_ERROR;\r
8258   } else {\r
8259     err = GetLastError();\r
8260     if (err == ERROR_IO_PENDING) {\r
8261       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8262       if (ok)\r
8263         err = NO_ERROR;\r
8264       else\r
8265         err = GetLastError();\r
8266     }\r
8267   }\r
8268   return err;\r
8269 }\r
8270 \r
8271 int\r
8272 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8273             OVERLAPPED *ovl)\r
8274 {\r
8275   int ok, err;\r
8276 \r
8277   ResetEvent(ovl->hEvent);\r
8278   ovl->Offset = ovl->OffsetHigh = 0;\r
8279   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8280   if (ok) {\r
8281     err = NO_ERROR;\r
8282   } else {\r
8283     err = GetLastError();\r
8284     if (err == ERROR_IO_PENDING) {\r
8285       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8286       if (ok)\r
8287         err = NO_ERROR;\r
8288       else\r
8289         err = GetLastError();\r
8290     }\r
8291   }\r
8292   return err;\r
8293 }\r
8294 \r
8295 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8296 void CheckForInputBufferFull( InputSource * is )\r
8297 {\r
8298     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8299         /* Look for end of line */\r
8300         char * p = is->buf;\r
8301         \r
8302         while( p < is->next && *p != '\n' ) {\r
8303             p++;\r
8304         }\r
8305 \r
8306         if( p >= is->next ) {\r
8307             if (appData.debugMode) {\r
8308                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
8309             }\r
8310 \r
8311             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8312             is->count = (DWORD) -1;\r
8313             is->next = is->buf;\r
8314         }\r
8315     }\r
8316 }\r
8317 \r
8318 DWORD\r
8319 InputThread(LPVOID arg)\r
8320 {\r
8321   InputSource *is;\r
8322   OVERLAPPED ovl;\r
8323 \r
8324   is = (InputSource *) arg;\r
8325   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8326   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8327   while (is->hThread != NULL) {\r
8328     is->error = DoReadFile(is->hFile, is->next,\r
8329                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8330                            &is->count, &ovl);\r
8331     if (is->error == NO_ERROR) {\r
8332       is->next += is->count;\r
8333     } else {\r
8334       if (is->error == ERROR_BROKEN_PIPE) {\r
8335         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8336         is->count = 0;\r
8337       } else {\r
8338         is->count = (DWORD) -1;\r
8339         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8340         break; \r
8341       }\r
8342     }\r
8343 \r
8344     CheckForInputBufferFull( is );\r
8345 \r
8346     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8347 \r
8348     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8349 \r
8350     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8351   }\r
8352 \r
8353   CloseHandle(ovl.hEvent);\r
8354   CloseHandle(is->hFile);\r
8355 \r
8356   if (appData.debugMode) {\r
8357     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
8358   }\r
8359 \r
8360   return 0;\r
8361 }\r
8362 \r
8363 \r
8364 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8365 DWORD\r
8366 NonOvlInputThread(LPVOID arg)\r
8367 {\r
8368   InputSource *is;\r
8369   char *p, *q;\r
8370   int i;\r
8371   char prev;\r
8372 \r
8373   is = (InputSource *) arg;\r
8374   while (is->hThread != NULL) {\r
8375     is->error = ReadFile(is->hFile, is->next,\r
8376                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8377                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8378     if (is->error == NO_ERROR) {\r
8379       /* Change CRLF to LF */\r
8380       if (is->next > is->buf) {\r
8381         p = is->next - 1;\r
8382         i = is->count + 1;\r
8383       } else {\r
8384         p = is->next;\r
8385         i = is->count;\r
8386       }\r
8387       q = p;\r
8388       prev = NULLCHAR;\r
8389       while (i > 0) {\r
8390         if (prev == '\r' && *p == '\n') {\r
8391           *(q-1) = '\n';\r
8392           is->count--;\r
8393         } else { \r
8394           *q++ = *p;\r
8395         }\r
8396         prev = *p++;\r
8397         i--;\r
8398       }\r
8399       *q = NULLCHAR;\r
8400       is->next = q;\r
8401     } else {\r
8402       if (is->error == ERROR_BROKEN_PIPE) {\r
8403         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8404         is->count = 0; \r
8405       } else {\r
8406         is->count = (DWORD) -1;\r
8407       }\r
8408     }\r
8409 \r
8410     CheckForInputBufferFull( is );\r
8411 \r
8412     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8413 \r
8414     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8415 \r
8416     if (is->count < 0) break;  /* Quit on error */\r
8417   }\r
8418   CloseHandle(is->hFile);\r
8419   return 0;\r
8420 }\r
8421 \r
8422 DWORD\r
8423 SocketInputThread(LPVOID arg)\r
8424 {\r
8425   InputSource *is;\r
8426 \r
8427   is = (InputSource *) arg;\r
8428   while (is->hThread != NULL) {\r
8429     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8430     if ((int)is->count == SOCKET_ERROR) {\r
8431       is->count = (DWORD) -1;\r
8432       is->error = WSAGetLastError();\r
8433     } else {\r
8434       is->error = NO_ERROR;\r
8435       is->next += is->count;\r
8436       if (is->count == 0 && is->second == is) {\r
8437         /* End of file on stderr; quit with no message */\r
8438         break;\r
8439       }\r
8440     }\r
8441     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8442 \r
8443     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8444 \r
8445     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8446   }\r
8447   return 0;\r
8448 }\r
8449 \r
8450 VOID\r
8451 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8452 {\r
8453   InputSource *is;\r
8454 \r
8455   is = (InputSource *) lParam;\r
8456   if (is->lineByLine) {\r
8457     /* Feed in lines one by one */\r
8458     char *p = is->buf;\r
8459     char *q = p;\r
8460     while (q < is->next) {\r
8461       if (*q++ == '\n') {\r
8462         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8463         p = q;\r
8464       }\r
8465     }\r
8466     \r
8467     /* Move any partial line to the start of the buffer */\r
8468     q = is->buf;\r
8469     while (p < is->next) {\r
8470       *q++ = *p++;\r
8471     }\r
8472     is->next = q;\r
8473 \r
8474     if (is->error != NO_ERROR || is->count == 0) {\r
8475       /* Notify backend of the error.  Note: If there was a partial\r
8476          line at the end, it is not flushed through. */\r
8477       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8478     }\r
8479   } else {\r
8480     /* Feed in the whole chunk of input at once */\r
8481     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8482     is->next = is->buf;\r
8483   }\r
8484 }\r
8485 \r
8486 /*---------------------------------------------------------------------------*\\r
8487  *\r
8488  *  Menu enables. Used when setting various modes.\r
8489  *\r
8490 \*---------------------------------------------------------------------------*/\r
8491 \r
8492 typedef struct {\r
8493   int item;\r
8494   int flags;\r
8495 } Enables;\r
8496 \r
8497 VOID\r
8498 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8499 {\r
8500   while (enab->item > 0) {\r
8501     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8502     enab++;\r
8503   }\r
8504 }\r
8505 \r
8506 Enables gnuEnables[] = {\r
8507   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8510   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8511   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8513   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8514   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8515   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8516   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8517   { -1, -1 }\r
8518 };\r
8519 \r
8520 Enables icsEnables[] = {\r
8521   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8522   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8523   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8524   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8525   { IDM_TwoMachines, 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   { -1, -1 }\r
8534 };\r
8535 \r
8536 #ifdef ZIPPY\r
8537 Enables zippyEnables[] = {\r
8538   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8539   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8540   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8541   { -1, -1 }\r
8542 };\r
8543 #endif\r
8544 \r
8545 Enables ncpEnables[] = {\r
8546   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8547   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8548   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8550   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8551   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8554   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8555   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8557   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8558   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8559   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8560   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8561   { -1, -1 }\r
8562 };\r
8563 \r
8564 Enables trainingOnEnables[] = {\r
8565   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8568   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8569   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8570   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8572   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8573   { -1, -1 }\r
8574 };\r
8575 \r
8576 Enables trainingOffEnables[] = {\r
8577   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8578   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8579   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8580   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8581   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8582   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8583   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8584   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8585   { -1, -1 }\r
8586 };\r
8587 \r
8588 /* These modify either ncpEnables or gnuEnables */\r
8589 Enables cmailEnables[] = {\r
8590   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8591   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8592   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8593   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8595   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8596   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8597   { -1, -1 }\r
8598 };\r
8599 \r
8600 Enables machineThinkingEnables[] = {\r
8601   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8612   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8613   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8614   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8615   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8616   { -1, -1 }\r
8617 };\r
8618 \r
8619 Enables userThinkingEnables[] = {\r
8620   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8621   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8622   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8623   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8624   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8625   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8627   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8628   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8631   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8632   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8633   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8634   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8635   { -1, -1 }\r
8636 };\r
8637 \r
8638 /*---------------------------------------------------------------------------*\\r
8639  *\r
8640  *  Front-end interface functions exported by XBoard.\r
8641  *  Functions appear in same order as prototypes in frontend.h.\r
8642  * \r
8643 \*---------------------------------------------------------------------------*/\r
8644 VOID\r
8645 ModeHighlight()\r
8646 {\r
8647   static UINT prevChecked = 0;\r
8648   static int prevPausing = 0;\r
8649   UINT nowChecked;\r
8650 \r
8651   if (pausing != prevPausing) {\r
8652     prevPausing = pausing;\r
8653     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8654                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8655     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8656   }\r
8657 \r
8658   switch (gameMode) {\r
8659   case BeginningOfGame:\r
8660     if (appData.icsActive)\r
8661       nowChecked = IDM_IcsClient;\r
8662     else if (appData.noChessProgram)\r
8663       nowChecked = IDM_EditGame;\r
8664     else\r
8665       nowChecked = IDM_MachineBlack;\r
8666     break;\r
8667   case MachinePlaysBlack:\r
8668     nowChecked = IDM_MachineBlack;\r
8669     break;\r
8670   case MachinePlaysWhite:\r
8671     nowChecked = IDM_MachineWhite;\r
8672     break;\r
8673   case TwoMachinesPlay:\r
8674     nowChecked = IDM_TwoMachines;\r
8675     break;\r
8676   case AnalyzeMode:\r
8677     nowChecked = IDM_AnalysisMode;\r
8678     break;\r
8679   case AnalyzeFile:\r
8680     nowChecked = IDM_AnalyzeFile;\r
8681     break;\r
8682   case EditGame:\r
8683     nowChecked = IDM_EditGame;\r
8684     break;\r
8685   case PlayFromGameFile:\r
8686     nowChecked = IDM_LoadGame;\r
8687     break;\r
8688   case EditPosition:\r
8689     nowChecked = IDM_EditPosition;\r
8690     break;\r
8691   case Training:\r
8692     nowChecked = IDM_Training;\r
8693     break;\r
8694   case IcsPlayingWhite:\r
8695   case IcsPlayingBlack:\r
8696   case IcsObserving:\r
8697   case IcsIdle:\r
8698     nowChecked = IDM_IcsClient;\r
8699     break;\r
8700   default:\r
8701   case EndOfGame:\r
8702     nowChecked = 0;\r
8703     break;\r
8704   }\r
8705   if (prevChecked != 0)\r
8706     (void) CheckMenuItem(GetMenu(hwndMain),\r
8707                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8708   if (nowChecked != 0)\r
8709     (void) CheckMenuItem(GetMenu(hwndMain),\r
8710                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8711 \r
8712   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8713     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8714                           MF_BYCOMMAND|MF_ENABLED);\r
8715   } else {\r
8716     (void) EnableMenuItem(GetMenu(hwndMain), \r
8717                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8718   }\r
8719 \r
8720   prevChecked = nowChecked;\r
8721 \r
8722   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8723   if (appData.icsActive) {\r
8724        if (appData.icsEngineAnalyze) {\r
8725                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8726                        MF_BYCOMMAND|MF_CHECKED);\r
8727        } else {\r
8728                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8729                        MF_BYCOMMAND|MF_UNCHECKED);\r
8730        }\r
8731   }\r
8732 }\r
8733 \r
8734 VOID\r
8735 SetICSMode()\r
8736 {\r
8737   HMENU hmenu = GetMenu(hwndMain);\r
8738   SetMenuEnables(hmenu, icsEnables);\r
8739   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8740     MF_BYPOSITION|MF_ENABLED);\r
8741 #ifdef ZIPPY\r
8742   if (appData.zippyPlay) {\r
8743     SetMenuEnables(hmenu, zippyEnables);\r
8744     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8745          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8746           MF_BYCOMMAND|MF_ENABLED);\r
8747   }\r
8748 #endif\r
8749 }\r
8750 \r
8751 VOID\r
8752 SetGNUMode()\r
8753 {\r
8754   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8755 }\r
8756 \r
8757 VOID\r
8758 SetNCPMode()\r
8759 {\r
8760   HMENU hmenu = GetMenu(hwndMain);\r
8761   SetMenuEnables(hmenu, ncpEnables);\r
8762   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8763     MF_BYPOSITION|MF_GRAYED);\r
8764     DrawMenuBar(hwndMain);\r
8765 }\r
8766 \r
8767 VOID\r
8768 SetCmailMode()\r
8769 {\r
8770   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8771 }\r
8772 \r
8773 VOID \r
8774 SetTrainingModeOn()\r
8775 {\r
8776   int i;\r
8777   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8778   for (i = 0; i < N_BUTTONS; i++) {\r
8779     if (buttonDesc[i].hwnd != NULL)\r
8780       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8781   }\r
8782   CommentPopDown();\r
8783 }\r
8784 \r
8785 VOID SetTrainingModeOff()\r
8786 {\r
8787   int i;\r
8788   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8789   for (i = 0; i < N_BUTTONS; i++) {\r
8790     if (buttonDesc[i].hwnd != NULL)\r
8791       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8792   }\r
8793 }\r
8794 \r
8795 \r
8796 VOID\r
8797 SetUserThinkingEnables()\r
8798 {\r
8799   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8800 }\r
8801 \r
8802 VOID\r
8803 SetMachineThinkingEnables()\r
8804 {\r
8805   HMENU hMenu = GetMenu(hwndMain);\r
8806   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8807 \r
8808   SetMenuEnables(hMenu, machineThinkingEnables);\r
8809 \r
8810   if (gameMode == MachinePlaysBlack) {\r
8811     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8812   } else if (gameMode == MachinePlaysWhite) {\r
8813     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8814   } else if (gameMode == TwoMachinesPlay) {\r
8815     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8816   }\r
8817 }\r
8818 \r
8819 \r
8820 VOID\r
8821 DisplayTitle(char *str)\r
8822 {\r
8823   char title[MSG_SIZ], *host;\r
8824   if (str[0] != NULLCHAR) {\r
8825     strcpy(title, str);\r
8826   } else if (appData.icsActive) {\r
8827     if (appData.icsCommPort[0] != NULLCHAR)\r
8828       host = "ICS";\r
8829     else \r
8830       host = appData.icsHost;\r
8831     sprintf(title, "%s: %s", szTitle, host);\r
8832   } else if (appData.noChessProgram) {\r
8833     strcpy(title, szTitle);\r
8834   } else {\r
8835     strcpy(title, szTitle);\r
8836     strcat(title, ": ");\r
8837     strcat(title, first.tidy);\r
8838   }\r
8839   SetWindowText(hwndMain, title);\r
8840 }\r
8841 \r
8842 \r
8843 VOID\r
8844 DisplayMessage(char *str1, char *str2)\r
8845 {\r
8846   HDC hdc;\r
8847   HFONT oldFont;\r
8848   int remain = MESSAGE_TEXT_MAX - 1;\r
8849   int len;\r
8850 \r
8851   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8852   messageText[0] = NULLCHAR;\r
8853   if (*str1) {\r
8854     len = strlen(str1);\r
8855     if (len > remain) len = remain;\r
8856     strncpy(messageText, str1, len);\r
8857     messageText[len] = NULLCHAR;\r
8858     remain -= len;\r
8859   }\r
8860   if (*str2 && remain >= 2) {\r
8861     if (*str1) {\r
8862       strcat(messageText, "  ");\r
8863       remain -= 2;\r
8864     }\r
8865     len = strlen(str2);\r
8866     if (len > remain) len = remain;\r
8867     strncat(messageText, str2, len);\r
8868   }\r
8869   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8870 \r
8871   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8872   hdc = GetDC(hwndMain);\r
8873   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8874   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8875              &messageRect, messageText, strlen(messageText), NULL);\r
8876   (void) SelectObject(hdc, oldFont);\r
8877   (void) ReleaseDC(hwndMain, hdc);\r
8878 }\r
8879 \r
8880 VOID\r
8881 DisplayError(char *str, int error)\r
8882 {\r
8883   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8884   int len;\r
8885 \r
8886   if (error == 0) {\r
8887     strcpy(buf, str);\r
8888   } else {\r
8889     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8890                         NULL, error, LANG_NEUTRAL,\r
8891                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8892     if (len > 0) {\r
8893       sprintf(buf, "%s:\n%s", str, buf2);\r
8894     } else {\r
8895       ErrorMap *em = errmap;\r
8896       while (em->err != 0 && em->err != error) em++;\r
8897       if (em->err != 0) {\r
8898         sprintf(buf, "%s:\n%s", str, em->msg);\r
8899       } else {\r
8900         sprintf(buf, "%s:\nError code %d", str, error);\r
8901       }\r
8902     }\r
8903   }\r
8904   \r
8905   ErrorPopUp("Error", buf);\r
8906 }\r
8907 \r
8908 \r
8909 VOID\r
8910 DisplayMoveError(char *str)\r
8911 {\r
8912   fromX = fromY = -1;\r
8913   ClearHighlights();\r
8914   DrawPosition(FALSE, NULL);\r
8915   if (appData.popupMoveErrors) {\r
8916     ErrorPopUp("Error", str);\r
8917   } else {\r
8918     DisplayMessage(str, "");\r
8919     moveErrorMessageUp = TRUE;\r
8920   }\r
8921 }\r
8922 \r
8923 VOID\r
8924 DisplayFatalError(char *str, int error, int exitStatus)\r
8925 {\r
8926   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8927   int len;\r
8928   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8929 \r
8930   if (error != 0) {\r
8931     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8932                         NULL, error, LANG_NEUTRAL,\r
8933                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8934     if (len > 0) {\r
8935       sprintf(buf, "%s:\n%s", str, buf2);\r
8936     } else {\r
8937       ErrorMap *em = errmap;\r
8938       while (em->err != 0 && em->err != error) em++;\r
8939       if (em->err != 0) {\r
8940         sprintf(buf, "%s:\n%s", str, em->msg);\r
8941       } else {\r
8942         sprintf(buf, "%s:\nError code %d", str, error);\r
8943       }\r
8944     }\r
8945     str = buf;\r
8946   }\r
8947   if (appData.debugMode) {\r
8948     fprintf(debugFP, "%s: %s\n", label, str);\r
8949   }\r
8950   if (appData.popupExitMessage) {\r
8951     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8952                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8953   }\r
8954   ExitEvent(exitStatus);\r
8955 }\r
8956 \r
8957 \r
8958 VOID\r
8959 DisplayInformation(char *str)\r
8960 {\r
8961   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8962 }\r
8963 \r
8964 \r
8965 VOID\r
8966 DisplayNote(char *str)\r
8967 {\r
8968   ErrorPopUp("Note", str);\r
8969 }\r
8970 \r
8971 \r
8972 typedef struct {\r
8973   char *title, *question, *replyPrefix;\r
8974   ProcRef pr;\r
8975 } QuestionParams;\r
8976 \r
8977 LRESULT CALLBACK\r
8978 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8979 {\r
8980   static QuestionParams *qp;\r
8981   char reply[MSG_SIZ];\r
8982   int len, err;\r
8983 \r
8984   switch (message) {\r
8985   case WM_INITDIALOG:\r
8986     qp = (QuestionParams *) lParam;\r
8987     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8988     SetWindowText(hDlg, qp->title);\r
8989     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8990     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8991     return FALSE;\r
8992 \r
8993   case WM_COMMAND:\r
8994     switch (LOWORD(wParam)) {\r
8995     case IDOK:\r
8996       strcpy(reply, qp->replyPrefix);\r
8997       if (*reply) strcat(reply, " ");\r
8998       len = strlen(reply);\r
8999       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9000       strcat(reply, "\n");\r
9001       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9002       EndDialog(hDlg, TRUE);\r
9003       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9004       return TRUE;\r
9005     case IDCANCEL:\r
9006       EndDialog(hDlg, FALSE);\r
9007       return TRUE;\r
9008     default:\r
9009       break;\r
9010     }\r
9011     break;\r
9012   }\r
9013   return FALSE;\r
9014 }\r
9015 \r
9016 VOID\r
9017 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9018 {\r
9019     QuestionParams qp;\r
9020     FARPROC lpProc;\r
9021     \r
9022     qp.title = title;\r
9023     qp.question = question;\r
9024     qp.replyPrefix = replyPrefix;\r
9025     qp.pr = pr;\r
9026     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9027     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9028       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9029     FreeProcInstance(lpProc);\r
9030 }\r
9031 \r
9032 /* [AS] Pick FRC position */\r
9033 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9034 {\r
9035     static int * lpIndexFRC;\r
9036     BOOL index_is_ok;\r
9037     char buf[16];\r
9038 \r
9039     switch( message )\r
9040     {\r
9041     case WM_INITDIALOG:\r
9042         lpIndexFRC = (int *) lParam;\r
9043 \r
9044         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9045 \r
9046         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9047         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9048         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9049         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9050 \r
9051         break;\r
9052 \r
9053     case WM_COMMAND:\r
9054         switch( LOWORD(wParam) ) {\r
9055         case IDOK:\r
9056             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9057             EndDialog( hDlg, 0 );\r
9058             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9059             return TRUE;\r
9060         case IDCANCEL:\r
9061             EndDialog( hDlg, 1 );   \r
9062             return TRUE;\r
9063         case IDC_NFG_Edit:\r
9064             if( HIWORD(wParam) == EN_CHANGE ) {\r
9065                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9066 \r
9067                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9068             }\r
9069             return TRUE;\r
9070         case IDC_NFG_Random:\r
9071             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9072             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9073             return TRUE;\r
9074         }\r
9075 \r
9076         break;\r
9077     }\r
9078 \r
9079     return FALSE;\r
9080 }\r
9081 \r
9082 int NewGameFRC()\r
9083 {\r
9084     int result;\r
9085     int index = appData.defaultFrcPosition;\r
9086     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9087 \r
9088     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9089 \r
9090     if( result == 0 ) {\r
9091         appData.defaultFrcPosition = index;\r
9092     }\r
9093 \r
9094     return result;\r
9095 }\r
9096 \r
9097 /* [AS] Game list options */\r
9098 typedef struct {\r
9099     char id;\r
9100     char * name;\r
9101 } GLT_Item;\r
9102 \r
9103 static GLT_Item GLT_ItemInfo[] = {\r
9104     { GLT_EVENT,      "Event" },\r
9105     { GLT_SITE,       "Site" },\r
9106     { GLT_DATE,       "Date" },\r
9107     { GLT_ROUND,      "Round" },\r
9108     { GLT_PLAYERS,    "Players" },\r
9109     { GLT_RESULT,     "Result" },\r
9110     { GLT_WHITE_ELO,  "White Rating" },\r
9111     { GLT_BLACK_ELO,  "Black Rating" },\r
9112     { GLT_TIME_CONTROL,"Time Control" },\r
9113     { GLT_VARIANT,    "Variant" },\r
9114     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9115     { 0, 0 }\r
9116 };\r
9117 \r
9118 const char * GLT_FindItem( char id )\r
9119 {\r
9120     const char * result = 0;\r
9121 \r
9122     GLT_Item * list = GLT_ItemInfo;\r
9123 \r
9124     while( list->id != 0 ) {\r
9125         if( list->id == id ) {\r
9126             result = list->name;\r
9127             break;\r
9128         }\r
9129 \r
9130         list++;\r
9131     }\r
9132 \r
9133     return result;\r
9134 }\r
9135 \r
9136 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9137 {\r
9138     const char * name = GLT_FindItem( id );\r
9139 \r
9140     if( name != 0 ) {\r
9141         if( index >= 0 ) {\r
9142             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9143         }\r
9144         else {\r
9145             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9146         }\r
9147     }\r
9148 }\r
9149 \r
9150 void GLT_TagsToList( HWND hDlg, char * tags )\r
9151 {\r
9152     char * pc = tags;\r
9153 \r
9154     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9155 \r
9156     while( *pc ) {\r
9157         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9158         pc++;\r
9159     }\r
9160 \r
9161     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9162 \r
9163     pc = GLT_ALL_TAGS;\r
9164 \r
9165     while( *pc ) {\r
9166         if( strchr( tags, *pc ) == 0 ) {\r
9167             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9168         }\r
9169         pc++;\r
9170     }\r
9171 \r
9172     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9173 }\r
9174 \r
9175 char GLT_ListItemToTag( HWND hDlg, int index )\r
9176 {\r
9177     char result = '\0';\r
9178     char name[128];\r
9179 \r
9180     GLT_Item * list = GLT_ItemInfo;\r
9181 \r
9182     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9183         while( list->id != 0 ) {\r
9184             if( strcmp( list->name, name ) == 0 ) {\r
9185                 result = list->id;\r
9186                 break;\r
9187             }\r
9188 \r
9189             list++;\r
9190         }\r
9191     }\r
9192 \r
9193     return result;\r
9194 }\r
9195 \r
9196 void GLT_MoveSelection( HWND hDlg, int delta )\r
9197 {\r
9198     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9199     int idx2 = idx1 + delta;\r
9200     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9201 \r
9202     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9203         char buf[128];\r
9204 \r
9205         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9206         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9207         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9208         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9209     }\r
9210 }\r
9211 \r
9212 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9213 {\r
9214     static char glt[64];\r
9215     static char * lpUserGLT;\r
9216 \r
9217     switch( message )\r
9218     {\r
9219     case WM_INITDIALOG:\r
9220         lpUserGLT = (char *) lParam;\r
9221         \r
9222         strcpy( glt, lpUserGLT );\r
9223 \r
9224         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9225 \r
9226         /* Initialize list */\r
9227         GLT_TagsToList( hDlg, glt );\r
9228 \r
9229         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9230 \r
9231         break;\r
9232 \r
9233     case WM_COMMAND:\r
9234         switch( LOWORD(wParam) ) {\r
9235         case IDOK:\r
9236             {\r
9237                 char * pc = lpUserGLT;\r
9238                 int idx = 0;\r
9239                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9240                 char id;\r
9241 \r
9242                 do {\r
9243                     id = GLT_ListItemToTag( hDlg, idx );\r
9244 \r
9245                     *pc++ = id;\r
9246                     idx++;\r
9247                 } while( id != '\0' );\r
9248             }\r
9249             EndDialog( hDlg, 0 );\r
9250             return TRUE;\r
9251         case IDCANCEL:\r
9252             EndDialog( hDlg, 1 );\r
9253             return TRUE;\r
9254 \r
9255         case IDC_GLT_Default:\r
9256             strcpy( glt, GLT_DEFAULT_TAGS );\r
9257             GLT_TagsToList( hDlg, glt );\r
9258             return TRUE;\r
9259 \r
9260         case IDC_GLT_Restore:\r
9261             strcpy( glt, lpUserGLT );\r
9262             GLT_TagsToList( hDlg, glt );\r
9263             return TRUE;\r
9264 \r
9265         case IDC_GLT_Up:\r
9266             GLT_MoveSelection( hDlg, -1 );\r
9267             return TRUE;\r
9268 \r
9269         case IDC_GLT_Down:\r
9270             GLT_MoveSelection( hDlg, +1 );\r
9271             return TRUE;\r
9272         }\r
9273 \r
9274         break;\r
9275     }\r
9276 \r
9277     return FALSE;\r
9278 }\r
9279 \r
9280 int GameListOptions()\r
9281 {\r
9282     char glt[64];\r
9283     int result;\r
9284     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9285 \r
9286     strcpy( glt, appData.gameListTags );\r
9287 \r
9288     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9289 \r
9290     if( result == 0 ) {\r
9291         /* [AS] Memory leak here! */\r
9292         appData.gameListTags = strdup( glt ); \r
9293     }\r
9294 \r
9295     return result;\r
9296 }\r
9297 \r
9298 \r
9299 VOID\r
9300 DisplayIcsInteractionTitle(char *str)\r
9301 {\r
9302   char consoleTitle[MSG_SIZ];\r
9303 \r
9304   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9305   SetWindowText(hwndConsole, consoleTitle);\r
9306 }\r
9307 \r
9308 void\r
9309 DrawPosition(int fullRedraw, Board board)\r
9310 {\r
9311   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9312 }\r
9313 \r
9314 \r
9315 VOID\r
9316 ResetFrontEnd()\r
9317 {\r
9318   fromX = fromY = -1;\r
9319   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9320     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9321     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9322     dragInfo.lastpos = dragInfo.pos;\r
9323     dragInfo.start.x = dragInfo.start.y = -1;\r
9324     dragInfo.from = dragInfo.start;\r
9325     ReleaseCapture();\r
9326     DrawPosition(TRUE, NULL);\r
9327   }\r
9328 }\r
9329 \r
9330 \r
9331 VOID\r
9332 CommentPopUp(char *title, char *str)\r
9333 {\r
9334   HWND hwnd = GetActiveWindow();\r
9335   EitherCommentPopUp(0, title, str, FALSE);\r
9336   SetActiveWindow(hwnd);\r
9337 }\r
9338 \r
9339 VOID\r
9340 CommentPopDown(void)\r
9341 {\r
9342   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9343   if (commentDialog) {\r
9344     ShowWindow(commentDialog, SW_HIDE);\r
9345   }\r
9346   commentDialogUp = FALSE;\r
9347 }\r
9348 \r
9349 VOID\r
9350 EditCommentPopUp(int index, char *title, char *str)\r
9351 {\r
9352   EitherCommentPopUp(index, title, str, TRUE);\r
9353 }\r
9354 \r
9355 \r
9356 VOID\r
9357 RingBell()\r
9358 {\r
9359   MyPlaySound(&sounds[(int)SoundMove]);\r
9360 }\r
9361 \r
9362 VOID PlayIcsWinSound()\r
9363 {\r
9364   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9365 }\r
9366 \r
9367 VOID PlayIcsLossSound()\r
9368 {\r
9369   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9370 }\r
9371 \r
9372 VOID PlayIcsDrawSound()\r
9373 {\r
9374   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9375 }\r
9376 \r
9377 VOID PlayIcsUnfinishedSound()\r
9378 {\r
9379   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9380 }\r
9381 \r
9382 VOID\r
9383 PlayAlarmSound()\r
9384 {\r
9385   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9386 }\r
9387 \r
9388 \r
9389 VOID\r
9390 EchoOn()\r
9391 {\r
9392   HWND hInput;\r
9393   consoleEcho = TRUE;\r
9394   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9395   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9396   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9397 }\r
9398 \r
9399 \r
9400 VOID\r
9401 EchoOff()\r
9402 {\r
9403   CHARFORMAT cf;\r
9404   HWND hInput;\r
9405   consoleEcho = FALSE;\r
9406   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9407   /* This works OK: set text and background both to the same color */\r
9408   cf = consoleCF;\r
9409   cf.crTextColor = COLOR_ECHOOFF;\r
9410   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9411   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9412 }\r
9413 \r
9414 /* No Raw()...? */\r
9415 \r
9416 void Colorize(ColorClass cc, int continuation)\r
9417 {\r
9418   currentColorClass = cc;\r
9419   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9420   consoleCF.crTextColor = textAttribs[cc].color;\r
9421   consoleCF.dwEffects = textAttribs[cc].effects;\r
9422   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9423 }\r
9424 \r
9425 char *\r
9426 UserName()\r
9427 {\r
9428   static char buf[MSG_SIZ];\r
9429   DWORD bufsiz = MSG_SIZ;\r
9430 \r
9431   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9432         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9433   }\r
9434   if (!GetUserName(buf, &bufsiz)) {\r
9435     /*DisplayError("Error getting user name", GetLastError());*/\r
9436     strcpy(buf, "User");\r
9437   }\r
9438   return buf;\r
9439 }\r
9440 \r
9441 char *\r
9442 HostName()\r
9443 {\r
9444   static char buf[MSG_SIZ];\r
9445   DWORD bufsiz = MSG_SIZ;\r
9446 \r
9447   if (!GetComputerName(buf, &bufsiz)) {\r
9448     /*DisplayError("Error getting host name", GetLastError());*/\r
9449     strcpy(buf, "Unknown");\r
9450   }\r
9451   return buf;\r
9452 }\r
9453 \r
9454 \r
9455 int\r
9456 ClockTimerRunning()\r
9457 {\r
9458   return clockTimerEvent != 0;\r
9459 }\r
9460 \r
9461 int\r
9462 StopClockTimer()\r
9463 {\r
9464   if (clockTimerEvent == 0) return FALSE;\r
9465   KillTimer(hwndMain, clockTimerEvent);\r
9466   clockTimerEvent = 0;\r
9467   return TRUE;\r
9468 }\r
9469 \r
9470 void\r
9471 StartClockTimer(long millisec)\r
9472 {\r
9473   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9474                              (UINT) millisec, NULL);\r
9475 }\r
9476 \r
9477 void\r
9478 DisplayWhiteClock(long timeRemaining, int highlight)\r
9479 {\r
9480   HDC hdc;\r
9481   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9482 \r
9483   if(appData.noGUI) return;\r
9484   hdc = GetDC(hwndMain);\r
9485   if (!IsIconic(hwndMain)) {\r
9486     DisplayAClock(hdc, timeRemaining, highlight, \r
9487                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9488   }\r
9489   if (highlight && iconCurrent == iconBlack) {\r
9490     iconCurrent = iconWhite;\r
9491     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9492     if (IsIconic(hwndMain)) {\r
9493       DrawIcon(hdc, 2, 2, iconCurrent);\r
9494     }\r
9495   }\r
9496   (void) ReleaseDC(hwndMain, hdc);\r
9497   if (hwndConsole)\r
9498     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9499 }\r
9500 \r
9501 void\r
9502 DisplayBlackClock(long timeRemaining, int highlight)\r
9503 {\r
9504   HDC hdc;\r
9505   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9506 \r
9507   if(appData.noGUI) return;\r
9508   hdc = GetDC(hwndMain);\r
9509   if (!IsIconic(hwndMain)) {\r
9510     DisplayAClock(hdc, timeRemaining, highlight, \r
9511                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9512   }\r
9513   if (highlight && iconCurrent == iconWhite) {\r
9514     iconCurrent = iconBlack;\r
9515     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9516     if (IsIconic(hwndMain)) {\r
9517       DrawIcon(hdc, 2, 2, iconCurrent);\r
9518     }\r
9519   }\r
9520   (void) ReleaseDC(hwndMain, hdc);\r
9521   if (hwndConsole)\r
9522     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9523 }\r
9524 \r
9525 \r
9526 int\r
9527 LoadGameTimerRunning()\r
9528 {\r
9529   return loadGameTimerEvent != 0;\r
9530 }\r
9531 \r
9532 int\r
9533 StopLoadGameTimer()\r
9534 {\r
9535   if (loadGameTimerEvent == 0) return FALSE;\r
9536   KillTimer(hwndMain, loadGameTimerEvent);\r
9537   loadGameTimerEvent = 0;\r
9538   return TRUE;\r
9539 }\r
9540 \r
9541 void\r
9542 StartLoadGameTimer(long millisec)\r
9543 {\r
9544   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9545                                 (UINT) millisec, NULL);\r
9546 }\r
9547 \r
9548 void\r
9549 AutoSaveGame()\r
9550 {\r
9551   char *defName;\r
9552   FILE *f;\r
9553   char fileTitle[MSG_SIZ];\r
9554 \r
9555   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9556   f = OpenFileDialog(hwndMain, "a", defName,\r
9557                      appData.oldSaveStyle ? "gam" : "pgn",\r
9558                      GAME_FILT, \r
9559                      "Save Game to File", NULL, fileTitle, NULL);\r
9560   if (f != NULL) {\r
9561     SaveGame(f, 0, "");\r
9562     fclose(f);\r
9563   }\r
9564 }\r
9565 \r
9566 \r
9567 void\r
9568 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9569 {\r
9570   if (delayedTimerEvent != 0) {\r
9571     if (appData.debugMode) {\r
9572       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9573     }\r
9574     KillTimer(hwndMain, delayedTimerEvent);\r
9575     delayedTimerEvent = 0;\r
9576     delayedTimerCallback();\r
9577   }\r
9578   delayedTimerCallback = cb;\r
9579   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9580                                 (UINT) millisec, NULL);\r
9581 }\r
9582 \r
9583 DelayedEventCallback\r
9584 GetDelayedEvent()\r
9585 {\r
9586   if (delayedTimerEvent) {\r
9587     return delayedTimerCallback;\r
9588   } else {\r
9589     return NULL;\r
9590   }\r
9591 }\r
9592 \r
9593 void\r
9594 CancelDelayedEvent()\r
9595 {\r
9596   if (delayedTimerEvent) {\r
9597     KillTimer(hwndMain, delayedTimerEvent);\r
9598     delayedTimerEvent = 0;\r
9599   }\r
9600 }\r
9601 \r
9602 DWORD GetWin32Priority(int nice)\r
9603 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9604 /*\r
9605 REALTIME_PRIORITY_CLASS     0x00000100\r
9606 HIGH_PRIORITY_CLASS         0x00000080\r
9607 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9608 NORMAL_PRIORITY_CLASS       0x00000020\r
9609 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9610 IDLE_PRIORITY_CLASS         0x00000040\r
9611 */\r
9612         if (nice < -15) return 0x00000080;\r
9613         if (nice < 0)   return 0x00008000;\r
9614         if (nice == 0)  return 0x00000020;\r
9615         if (nice < 15)  return 0x00004000;\r
9616         return 0x00000040;\r
9617 }\r
9618 \r
9619 /* Start a child process running the given program.\r
9620    The process's standard output can be read from "from", and its\r
9621    standard input can be written to "to".\r
9622    Exit with fatal error if anything goes wrong.\r
9623    Returns an opaque pointer that can be used to destroy the process\r
9624    later.\r
9625 */\r
9626 int\r
9627 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9628 {\r
9629 #define BUFSIZE 4096\r
9630 \r
9631   HANDLE hChildStdinRd, hChildStdinWr,\r
9632     hChildStdoutRd, hChildStdoutWr;\r
9633   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9634   SECURITY_ATTRIBUTES saAttr;\r
9635   BOOL fSuccess;\r
9636   PROCESS_INFORMATION piProcInfo;\r
9637   STARTUPINFO siStartInfo;\r
9638   ChildProc *cp;\r
9639   char buf[MSG_SIZ];\r
9640   DWORD err;\r
9641 \r
9642   if (appData.debugMode) {\r
9643     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9644   }\r
9645 \r
9646   *pr = NoProc;\r
9647 \r
9648   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9649   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9650   saAttr.bInheritHandle = TRUE;\r
9651   saAttr.lpSecurityDescriptor = NULL;\r
9652 \r
9653   /*\r
9654    * The steps for redirecting child's STDOUT:\r
9655    *     1. Create anonymous pipe to be STDOUT for child.\r
9656    *     2. Create a noninheritable duplicate of read handle,\r
9657    *         and close the inheritable read handle.\r
9658    */\r
9659 \r
9660   /* Create a pipe for the child's STDOUT. */\r
9661   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9662     return GetLastError();\r
9663   }\r
9664 \r
9665   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9666   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9667                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9668                              FALSE,     /* not inherited */\r
9669                              DUPLICATE_SAME_ACCESS);\r
9670   if (! fSuccess) {\r
9671     return GetLastError();\r
9672   }\r
9673   CloseHandle(hChildStdoutRd);\r
9674 \r
9675   /*\r
9676    * The steps for redirecting child's STDIN:\r
9677    *     1. Create anonymous pipe to be STDIN for child.\r
9678    *     2. Create a noninheritable duplicate of write handle,\r
9679    *         and close the inheritable write handle.\r
9680    */\r
9681 \r
9682   /* Create a pipe for the child's STDIN. */\r
9683   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9684     return GetLastError();\r
9685   }\r
9686 \r
9687   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9688   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9689                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9690                              FALSE,     /* not inherited */\r
9691                              DUPLICATE_SAME_ACCESS);\r
9692   if (! fSuccess) {\r
9693     return GetLastError();\r
9694   }\r
9695   CloseHandle(hChildStdinWr);\r
9696 \r
9697   /* Arrange to (1) look in dir for the child .exe file, and\r
9698    * (2) have dir be the child's working directory.  Interpret\r
9699    * dir relative to the directory WinBoard loaded from. */\r
9700   GetCurrentDirectory(MSG_SIZ, buf);\r
9701   SetCurrentDirectory(installDir);\r
9702   SetCurrentDirectory(dir);\r
9703 \r
9704   /* Now create the child process. */\r
9705 \r
9706   siStartInfo.cb = sizeof(STARTUPINFO);\r
9707   siStartInfo.lpReserved = NULL;\r
9708   siStartInfo.lpDesktop = NULL;\r
9709   siStartInfo.lpTitle = NULL;\r
9710   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9711   siStartInfo.cbReserved2 = 0;\r
9712   siStartInfo.lpReserved2 = NULL;\r
9713   siStartInfo.hStdInput = hChildStdinRd;\r
9714   siStartInfo.hStdOutput = hChildStdoutWr;\r
9715   siStartInfo.hStdError = hChildStdoutWr;\r
9716 \r
9717   fSuccess = CreateProcess(NULL,\r
9718                            cmdLine,        /* command line */\r
9719                            NULL,           /* process security attributes */\r
9720                            NULL,           /* primary thread security attrs */\r
9721                            TRUE,           /* handles are inherited */\r
9722                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9723                            NULL,           /* use parent's environment */\r
9724                            NULL,\r
9725                            &siStartInfo, /* STARTUPINFO pointer */\r
9726                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9727 \r
9728   err = GetLastError();\r
9729   SetCurrentDirectory(buf); /* return to prev directory */\r
9730   if (! fSuccess) {\r
9731     return err;\r
9732   }\r
9733 \r
9734   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9735     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9736     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9737   }\r
9738 \r
9739   /* Close the handles we don't need in the parent */\r
9740   CloseHandle(piProcInfo.hThread);\r
9741   CloseHandle(hChildStdinRd);\r
9742   CloseHandle(hChildStdoutWr);\r
9743 \r
9744   /* Prepare return value */\r
9745   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9746   cp->kind = CPReal;\r
9747   cp->hProcess = piProcInfo.hProcess;\r
9748   cp->pid = piProcInfo.dwProcessId;\r
9749   cp->hFrom = hChildStdoutRdDup;\r
9750   cp->hTo = hChildStdinWrDup;\r
9751 \r
9752   *pr = (void *) cp;\r
9753 \r
9754   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9755      2000 where engines sometimes don't see the initial command(s)\r
9756      from WinBoard and hang.  I don't understand how that can happen,\r
9757      but the Sleep is harmless, so I've put it in.  Others have also\r
9758      reported what may be the same problem, so hopefully this will fix\r
9759      it for them too.  */\r
9760   Sleep(500);\r
9761 \r
9762   return NO_ERROR;\r
9763 }\r
9764 \r
9765 \r
9766 void\r
9767 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9768 {\r
9769   ChildProc *cp; int result;\r
9770 \r
9771   cp = (ChildProc *) pr;\r
9772   if (cp == NULL) return;\r
9773 \r
9774   switch (cp->kind) {\r
9775   case CPReal:\r
9776     /* TerminateProcess is considered harmful, so... */\r
9777     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9778     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9779     /* The following doesn't work because the chess program\r
9780        doesn't "have the same console" as WinBoard.  Maybe\r
9781        we could arrange for this even though neither WinBoard\r
9782        nor the chess program uses a console for stdio? */\r
9783     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9784 \r
9785     /* [AS] Special termination modes for misbehaving programs... */\r
9786     if( signal == 9 ) { \r
9787         result = TerminateProcess( cp->hProcess, 0 );\r
9788 \r
9789         if ( appData.debugMode) {\r
9790             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9791         }\r
9792     }\r
9793     else if( signal == 10 ) {\r
9794         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9795 \r
9796         if( dw != WAIT_OBJECT_0 ) {\r
9797             result = TerminateProcess( cp->hProcess, 0 );\r
9798 \r
9799             if ( appData.debugMode) {\r
9800                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9801             }\r
9802 \r
9803         }\r
9804     }\r
9805 \r
9806     CloseHandle(cp->hProcess);\r
9807     break;\r
9808 \r
9809   case CPComm:\r
9810     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9811     break;\r
9812 \r
9813   case CPSock:\r
9814     closesocket(cp->sock);\r
9815     WSACleanup();\r
9816     break;\r
9817 \r
9818   case CPRcmd:\r
9819     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9820     closesocket(cp->sock);\r
9821     closesocket(cp->sock2);\r
9822     WSACleanup();\r
9823     break;\r
9824   }\r
9825   free(cp);\r
9826 }\r
9827 \r
9828 void\r
9829 InterruptChildProcess(ProcRef pr)\r
9830 {\r
9831   ChildProc *cp;\r
9832 \r
9833   cp = (ChildProc *) pr;\r
9834   if (cp == NULL) return;\r
9835   switch (cp->kind) {\r
9836   case CPReal:\r
9837     /* The following doesn't work because the chess program\r
9838        doesn't "have the same console" as WinBoard.  Maybe\r
9839        we could arrange for this even though neither WinBoard\r
9840        nor the chess program uses a console for stdio */\r
9841     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9842     break;\r
9843 \r
9844   case CPComm:\r
9845   case CPSock:\r
9846     /* Can't interrupt */\r
9847     break;\r
9848 \r
9849   case CPRcmd:\r
9850     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9851     break;\r
9852   }\r
9853 }\r
9854 \r
9855 \r
9856 int\r
9857 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9858 {\r
9859   char cmdLine[MSG_SIZ];\r
9860 \r
9861   if (port[0] == NULLCHAR) {\r
9862     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9863   } else {\r
9864     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9865   }\r
9866   return StartChildProcess(cmdLine, "", pr);\r
9867 }\r
9868 \r
9869 \r
9870 /* Code to open TCP sockets */\r
9871 \r
9872 int\r
9873 OpenTCP(char *host, char *port, ProcRef *pr)\r
9874 {\r
9875   ChildProc *cp;\r
9876   int err;\r
9877   SOCKET s;\r
9878   struct sockaddr_in sa, mysa;\r
9879   struct hostent FAR *hp;\r
9880   unsigned short uport;\r
9881   WORD wVersionRequested;\r
9882   WSADATA wsaData;\r
9883 \r
9884   /* Initialize socket DLL */\r
9885   wVersionRequested = MAKEWORD(1, 1);\r
9886   err = WSAStartup(wVersionRequested, &wsaData);\r
9887   if (err != 0) return err;\r
9888 \r
9889   /* Make socket */\r
9890   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9891     err = WSAGetLastError();\r
9892     WSACleanup();\r
9893     return err;\r
9894   }\r
9895 \r
9896   /* Bind local address using (mostly) don't-care values.\r
9897    */\r
9898   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9899   mysa.sin_family = AF_INET;\r
9900   mysa.sin_addr.s_addr = INADDR_ANY;\r
9901   uport = (unsigned short) 0;\r
9902   mysa.sin_port = htons(uport);\r
9903   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9904       == SOCKET_ERROR) {\r
9905     err = WSAGetLastError();\r
9906     WSACleanup();\r
9907     return err;\r
9908   }\r
9909 \r
9910   /* Resolve remote host name */\r
9911   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9912   if (!(hp = gethostbyname(host))) {\r
9913     unsigned int b0, b1, b2, b3;\r
9914 \r
9915     err = WSAGetLastError();\r
9916 \r
9917     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9918       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9919       hp->h_addrtype = AF_INET;\r
9920       hp->h_length = 4;\r
9921       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9922       hp->h_addr_list[0] = (char *) malloc(4);\r
9923       hp->h_addr_list[0][0] = (char) b0;\r
9924       hp->h_addr_list[0][1] = (char) b1;\r
9925       hp->h_addr_list[0][2] = (char) b2;\r
9926       hp->h_addr_list[0][3] = (char) b3;\r
9927     } else {\r
9928       WSACleanup();\r
9929       return err;\r
9930     }\r
9931   }\r
9932   sa.sin_family = hp->h_addrtype;\r
9933   uport = (unsigned short) atoi(port);\r
9934   sa.sin_port = htons(uport);\r
9935   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9936 \r
9937   /* Make connection */\r
9938   if (connect(s, (struct sockaddr *) &sa,\r
9939               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9940     err = WSAGetLastError();\r
9941     WSACleanup();\r
9942     return err;\r
9943   }\r
9944 \r
9945   /* Prepare return value */\r
9946   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9947   cp->kind = CPSock;\r
9948   cp->sock = s;\r
9949   *pr = (ProcRef *) cp;\r
9950 \r
9951   return NO_ERROR;\r
9952 }\r
9953 \r
9954 int\r
9955 OpenCommPort(char *name, ProcRef *pr)\r
9956 {\r
9957   HANDLE h;\r
9958   COMMTIMEOUTS ct;\r
9959   ChildProc *cp;\r
9960   char fullname[MSG_SIZ];\r
9961 \r
9962   if (*name != '\\')\r
9963     sprintf(fullname, "\\\\.\\%s", name);\r
9964   else\r
9965     strcpy(fullname, name);\r
9966 \r
9967   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9968                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9969   if (h == (HANDLE) -1) {\r
9970     return GetLastError();\r
9971   }\r
9972   hCommPort = h;\r
9973 \r
9974   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9975 \r
9976   /* Accumulate characters until a 100ms pause, then parse */\r
9977   ct.ReadIntervalTimeout = 100;\r
9978   ct.ReadTotalTimeoutMultiplier = 0;\r
9979   ct.ReadTotalTimeoutConstant = 0;\r
9980   ct.WriteTotalTimeoutMultiplier = 0;\r
9981   ct.WriteTotalTimeoutConstant = 0;\r
9982   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9983 \r
9984   /* Prepare return value */\r
9985   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9986   cp->kind = CPComm;\r
9987   cp->hFrom = h;\r
9988   cp->hTo = h;\r
9989   *pr = (ProcRef *) cp;\r
9990 \r
9991   return NO_ERROR;\r
9992 }\r
9993 \r
9994 int\r
9995 OpenLoopback(ProcRef *pr)\r
9996 {\r
9997   DisplayFatalError("Not implemented", 0, 1);\r
9998   return NO_ERROR;\r
9999 }\r
10000 \r
10001 \r
10002 int\r
10003 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10004 {\r
10005   ChildProc *cp;\r
10006   int err;\r
10007   SOCKET s, s2, s3;\r
10008   struct sockaddr_in sa, mysa;\r
10009   struct hostent FAR *hp;\r
10010   unsigned short uport;\r
10011   WORD wVersionRequested;\r
10012   WSADATA wsaData;\r
10013   int fromPort;\r
10014   char stderrPortStr[MSG_SIZ];\r
10015 \r
10016   /* Initialize socket DLL */\r
10017   wVersionRequested = MAKEWORD(1, 1);\r
10018   err = WSAStartup(wVersionRequested, &wsaData);\r
10019   if (err != 0) return err;\r
10020 \r
10021   /* Resolve remote host name */\r
10022   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10023   if (!(hp = gethostbyname(host))) {\r
10024     unsigned int b0, b1, b2, b3;\r
10025 \r
10026     err = WSAGetLastError();\r
10027 \r
10028     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10029       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10030       hp->h_addrtype = AF_INET;\r
10031       hp->h_length = 4;\r
10032       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10033       hp->h_addr_list[0] = (char *) malloc(4);\r
10034       hp->h_addr_list[0][0] = (char) b0;\r
10035       hp->h_addr_list[0][1] = (char) b1;\r
10036       hp->h_addr_list[0][2] = (char) b2;\r
10037       hp->h_addr_list[0][3] = (char) b3;\r
10038     } else {\r
10039       WSACleanup();\r
10040       return err;\r
10041     }\r
10042   }\r
10043   sa.sin_family = hp->h_addrtype;\r
10044   uport = (unsigned short) 514;\r
10045   sa.sin_port = htons(uport);\r
10046   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10047 \r
10048   /* Bind local socket to unused "privileged" port address\r
10049    */\r
10050   s = INVALID_SOCKET;\r
10051   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10052   mysa.sin_family = AF_INET;\r
10053   mysa.sin_addr.s_addr = INADDR_ANY;\r
10054   for (fromPort = 1023;; fromPort--) {\r
10055     if (fromPort < 0) {\r
10056       WSACleanup();\r
10057       return WSAEADDRINUSE;\r
10058     }\r
10059     if (s == INVALID_SOCKET) {\r
10060       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10061         err = WSAGetLastError();\r
10062         WSACleanup();\r
10063         return err;\r
10064       }\r
10065     }\r
10066     uport = (unsigned short) fromPort;\r
10067     mysa.sin_port = htons(uport);\r
10068     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10069         == SOCKET_ERROR) {\r
10070       err = WSAGetLastError();\r
10071       if (err == WSAEADDRINUSE) continue;\r
10072       WSACleanup();\r
10073       return err;\r
10074     }\r
10075     if (connect(s, (struct sockaddr *) &sa,\r
10076       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10077       err = WSAGetLastError();\r
10078       if (err == WSAEADDRINUSE) {\r
10079         closesocket(s);\r
10080         s = -1;\r
10081         continue;\r
10082       }\r
10083       WSACleanup();\r
10084       return err;\r
10085     }\r
10086     break;\r
10087   }\r
10088 \r
10089   /* Bind stderr local socket to unused "privileged" port address\r
10090    */\r
10091   s2 = INVALID_SOCKET;\r
10092   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10093   mysa.sin_family = AF_INET;\r
10094   mysa.sin_addr.s_addr = INADDR_ANY;\r
10095   for (fromPort = 1023;; fromPort--) {\r
10096     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10097     if (fromPort < 0) {\r
10098       (void) closesocket(s);\r
10099       WSACleanup();\r
10100       return WSAEADDRINUSE;\r
10101     }\r
10102     if (s2 == INVALID_SOCKET) {\r
10103       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10104         err = WSAGetLastError();\r
10105         closesocket(s);\r
10106         WSACleanup();\r
10107         return err;\r
10108       }\r
10109     }\r
10110     uport = (unsigned short) fromPort;\r
10111     mysa.sin_port = htons(uport);\r
10112     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10113         == SOCKET_ERROR) {\r
10114       err = WSAGetLastError();\r
10115       if (err == WSAEADDRINUSE) continue;\r
10116       (void) closesocket(s);\r
10117       WSACleanup();\r
10118       return err;\r
10119     }\r
10120     if (listen(s2, 1) == SOCKET_ERROR) {\r
10121       err = WSAGetLastError();\r
10122       if (err == WSAEADDRINUSE) {\r
10123         closesocket(s2);\r
10124         s2 = INVALID_SOCKET;\r
10125         continue;\r
10126       }\r
10127       (void) closesocket(s);\r
10128       (void) closesocket(s2);\r
10129       WSACleanup();\r
10130       return err;\r
10131     }\r
10132     break;\r
10133   }\r
10134   prevStderrPort = fromPort; // remember port used\r
10135   sprintf(stderrPortStr, "%d", fromPort);\r
10136 \r
10137   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10138     err = WSAGetLastError();\r
10139     (void) closesocket(s);\r
10140     (void) closesocket(s2);\r
10141     WSACleanup();\r
10142     return err;\r
10143   }\r
10144 \r
10145   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10146     err = WSAGetLastError();\r
10147     (void) closesocket(s);\r
10148     (void) closesocket(s2);\r
10149     WSACleanup();\r
10150     return err;\r
10151   }\r
10152   if (*user == NULLCHAR) user = UserName();\r
10153   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10154     err = WSAGetLastError();\r
10155     (void) closesocket(s);\r
10156     (void) closesocket(s2);\r
10157     WSACleanup();\r
10158     return err;\r
10159   }\r
10160   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10161     err = WSAGetLastError();\r
10162     (void) closesocket(s);\r
10163     (void) closesocket(s2);\r
10164     WSACleanup();\r
10165     return err;\r
10166   }\r
10167 \r
10168   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10169     err = WSAGetLastError();\r
10170     (void) closesocket(s);\r
10171     (void) closesocket(s2);\r
10172     WSACleanup();\r
10173     return err;\r
10174   }\r
10175   (void) closesocket(s2);  /* Stop listening */\r
10176 \r
10177   /* Prepare return value */\r
10178   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10179   cp->kind = CPRcmd;\r
10180   cp->sock = s;\r
10181   cp->sock2 = s3;\r
10182   *pr = (ProcRef *) cp;\r
10183 \r
10184   return NO_ERROR;\r
10185 }\r
10186 \r
10187 \r
10188 InputSourceRef\r
10189 AddInputSource(ProcRef pr, int lineByLine,\r
10190                InputCallback func, VOIDSTAR closure)\r
10191 {\r
10192   InputSource *is, *is2 = NULL;\r
10193   ChildProc *cp = (ChildProc *) pr;\r
10194 \r
10195   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10196   is->lineByLine = lineByLine;\r
10197   is->func = func;\r
10198   is->closure = closure;\r
10199   is->second = NULL;\r
10200   is->next = is->buf;\r
10201   if (pr == NoProc) {\r
10202     is->kind = CPReal;\r
10203     consoleInputSource = is;\r
10204   } else {\r
10205     is->kind = cp->kind;\r
10206     /* \r
10207         [AS] Try to avoid a race condition if the thread is given control too early:\r
10208         we create all threads suspended so that the is->hThread variable can be\r
10209         safely assigned, then let the threads start with ResumeThread.\r
10210     */\r
10211     switch (cp->kind) {\r
10212     case CPReal:\r
10213       is->hFile = cp->hFrom;\r
10214       cp->hFrom = NULL; /* now owned by InputThread */\r
10215       is->hThread =\r
10216         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10217                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10218       break;\r
10219 \r
10220     case CPComm:\r
10221       is->hFile = cp->hFrom;\r
10222       cp->hFrom = NULL; /* now owned by InputThread */\r
10223       is->hThread =\r
10224         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10225                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10226       break;\r
10227 \r
10228     case CPSock:\r
10229       is->sock = cp->sock;\r
10230       is->hThread =\r
10231         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10232                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10233       break;\r
10234 \r
10235     case CPRcmd:\r
10236       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10237       *is2 = *is;\r
10238       is->sock = cp->sock;\r
10239       is->second = is2;\r
10240       is2->sock = cp->sock2;\r
10241       is2->second = is2;\r
10242       is->hThread =\r
10243         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10244                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10245       is2->hThread =\r
10246         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10247                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10248       break;\r
10249     }\r
10250 \r
10251     if( is->hThread != NULL ) {\r
10252         ResumeThread( is->hThread );\r
10253     }\r
10254 \r
10255     if( is2 != NULL && is2->hThread != NULL ) {\r
10256         ResumeThread( is2->hThread );\r
10257     }\r
10258   }\r
10259 \r
10260   return (InputSourceRef) is;\r
10261 }\r
10262 \r
10263 void\r
10264 RemoveInputSource(InputSourceRef isr)\r
10265 {\r
10266   InputSource *is;\r
10267 \r
10268   is = (InputSource *) isr;\r
10269   is->hThread = NULL;  /* tell thread to stop */\r
10270   CloseHandle(is->hThread);\r
10271   if (is->second != NULL) {\r
10272     is->second->hThread = NULL;\r
10273     CloseHandle(is->second->hThread);\r
10274   }\r
10275 }\r
10276 \r
10277 \r
10278 int\r
10279 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10280 {\r
10281   DWORD dOutCount;\r
10282   int outCount = SOCKET_ERROR;\r
10283   ChildProc *cp = (ChildProc *) pr;\r
10284   static OVERLAPPED ovl;\r
10285 \r
10286   if (pr == NoProc) {\r
10287     ConsoleOutput(message, count, FALSE);\r
10288     return count;\r
10289   } \r
10290 \r
10291   if (ovl.hEvent == NULL) {\r
10292     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10293   }\r
10294   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10295 \r
10296   switch (cp->kind) {\r
10297   case CPSock:\r
10298   case CPRcmd:\r
10299     outCount = send(cp->sock, message, count, 0);\r
10300     if (outCount == SOCKET_ERROR) {\r
10301       *outError = WSAGetLastError();\r
10302     } else {\r
10303       *outError = NO_ERROR;\r
10304     }\r
10305     break;\r
10306 \r
10307   case CPReal:\r
10308     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10309                   &dOutCount, NULL)) {\r
10310       *outError = NO_ERROR;\r
10311       outCount = (int) dOutCount;\r
10312     } else {\r
10313       *outError = GetLastError();\r
10314     }\r
10315     break;\r
10316 \r
10317   case CPComm:\r
10318     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10319                             &dOutCount, &ovl);\r
10320     if (*outError == NO_ERROR) {\r
10321       outCount = (int) dOutCount;\r
10322     }\r
10323     break;\r
10324   }\r
10325   return outCount;\r
10326 }\r
10327 \r
10328 int\r
10329 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10330                        long msdelay)\r
10331 {\r
10332   /* Ignore delay, not implemented for WinBoard */\r
10333   return OutputToProcess(pr, message, count, outError);\r
10334 }\r
10335 \r
10336 \r
10337 void\r
10338 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10339                         char *buf, int count, int error)\r
10340 {\r
10341   DisplayFatalError("Not implemented", 0, 1);\r
10342 }\r
10343 \r
10344 /* see wgamelist.c for Game List functions */\r
10345 /* see wedittags.c for Edit Tags functions */\r
10346 \r
10347 \r
10348 VOID\r
10349 ICSInitScript()\r
10350 {\r
10351   FILE *f;\r
10352   char buf[MSG_SIZ];\r
10353   char *dummy;\r
10354 \r
10355   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10356     f = fopen(buf, "r");\r
10357     if (f != NULL) {\r
10358       ProcessICSInitScript(f);\r
10359       fclose(f);\r
10360     }\r
10361   }\r
10362 }\r
10363 \r
10364 \r
10365 VOID\r
10366 StartAnalysisClock()\r
10367 {\r
10368   if (analysisTimerEvent) return;\r
10369   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10370                                         (UINT) 2000, NULL);\r
10371 }\r
10372 \r
10373 LRESULT CALLBACK\r
10374 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10375 {\r
10376   static HANDLE hwndText;\r
10377   RECT rect;\r
10378   static int sizeX, sizeY;\r
10379   int newSizeX, newSizeY, flags;\r
10380   MINMAXINFO *mmi;\r
10381 \r
10382   switch (message) {\r
10383   case WM_INITDIALOG: /* message: initialize dialog box */\r
10384     /* Initialize the dialog items */\r
10385     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10386     SetWindowText(hDlg, analysisTitle);\r
10387     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10388     /* Size and position the dialog */\r
10389     if (!analysisDialog) {\r
10390       analysisDialog = hDlg;\r
10391       flags = SWP_NOZORDER;\r
10392       GetClientRect(hDlg, &rect);\r
10393       sizeX = rect.right;\r
10394       sizeY = rect.bottom;\r
10395       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10396           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10397         WINDOWPLACEMENT wp;\r
10398         EnsureOnScreen(&analysisX, &analysisY);\r
10399         wp.length = sizeof(WINDOWPLACEMENT);\r
10400         wp.flags = 0;\r
10401         wp.showCmd = SW_SHOW;\r
10402         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10403         wp.rcNormalPosition.left = analysisX;\r
10404         wp.rcNormalPosition.right = analysisX + analysisW;\r
10405         wp.rcNormalPosition.top = analysisY;\r
10406         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10407         SetWindowPlacement(hDlg, &wp);\r
10408 \r
10409         GetClientRect(hDlg, &rect);\r
10410         newSizeX = rect.right;\r
10411         newSizeY = rect.bottom;\r
10412         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10413                               newSizeX, newSizeY);\r
10414         sizeX = newSizeX;\r
10415         sizeY = newSizeY;\r
10416       }\r
10417     }\r
10418     return FALSE;\r
10419 \r
10420   case WM_COMMAND: /* message: received a command */\r
10421     switch (LOWORD(wParam)) {\r
10422     case IDCANCEL:\r
10423       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10424           ExitAnalyzeMode();\r
10425           ModeHighlight();\r
10426           return TRUE;\r
10427       }\r
10428       EditGameEvent();\r
10429       return TRUE;\r
10430     default:\r
10431       break;\r
10432     }\r
10433     break;\r
10434 \r
10435   case WM_SIZE:\r
10436     newSizeX = LOWORD(lParam);\r
10437     newSizeY = HIWORD(lParam);\r
10438     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10439     sizeX = newSizeX;\r
10440     sizeY = newSizeY;\r
10441     break;\r
10442 \r
10443   case WM_GETMINMAXINFO:\r
10444     /* Prevent resizing window too small */\r
10445     mmi = (MINMAXINFO *) lParam;\r
10446     mmi->ptMinTrackSize.x = 100;\r
10447     mmi->ptMinTrackSize.y = 100;\r
10448     break;\r
10449   }\r
10450   return FALSE;\r
10451 }\r
10452 \r
10453 VOID\r
10454 AnalysisPopUp(char* title, char* str)\r
10455 {\r
10456   FARPROC lpProc;\r
10457   char *p, *q;\r
10458 \r
10459   /* [AS] */\r
10460   EngineOutputPopUp();\r
10461   return;\r
10462 \r
10463   if (str == NULL) str = "";\r
10464   p = (char *) malloc(2 * strlen(str) + 2);\r
10465   q = p;\r
10466   while (*str) {\r
10467     if (*str == '\n') *q++ = '\r';\r
10468     *q++ = *str++;\r
10469   }\r
10470   *q = NULLCHAR;\r
10471   if (analysisText != NULL) free(analysisText);\r
10472   analysisText = p;\r
10473 \r
10474   if (analysisDialog) {\r
10475     SetWindowText(analysisDialog, title);\r
10476     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10477     ShowWindow(analysisDialog, SW_SHOW);\r
10478   } else {\r
10479     analysisTitle = title;\r
10480     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10481     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10482                  hwndMain, (DLGPROC)lpProc);\r
10483     FreeProcInstance(lpProc);\r
10484   }\r
10485   analysisDialogUp = TRUE;  \r
10486 }\r
10487 \r
10488 VOID\r
10489 AnalysisPopDown()\r
10490 {\r
10491   if (analysisDialog) {\r
10492     ShowWindow(analysisDialog, SW_HIDE);\r
10493   }\r
10494   analysisDialogUp = FALSE;  \r
10495 }\r
10496 \r
10497 \r
10498 VOID\r
10499 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10500 {\r
10501   highlightInfo.sq[0].x = fromX;\r
10502   highlightInfo.sq[0].y = fromY;\r
10503   highlightInfo.sq[1].x = toX;\r
10504   highlightInfo.sq[1].y = toY;\r
10505 }\r
10506 \r
10507 VOID\r
10508 ClearHighlights()\r
10509 {\r
10510   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10511     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10512 }\r
10513 \r
10514 VOID\r
10515 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10516 {\r
10517   premoveHighlightInfo.sq[0].x = fromX;\r
10518   premoveHighlightInfo.sq[0].y = fromY;\r
10519   premoveHighlightInfo.sq[1].x = toX;\r
10520   premoveHighlightInfo.sq[1].y = toY;\r
10521 }\r
10522 \r
10523 VOID\r
10524 ClearPremoveHighlights()\r
10525 {\r
10526   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10527     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10528 }\r
10529 \r
10530 VOID\r
10531 ShutDownFrontEnd()\r
10532 {\r
10533   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10534   DeleteClipboardTempFiles();\r
10535 }\r
10536 \r
10537 void\r
10538 BoardToTop()\r
10539 {\r
10540     if (IsIconic(hwndMain))\r
10541       ShowWindow(hwndMain, SW_RESTORE);\r
10542 \r
10543     SetActiveWindow(hwndMain);\r
10544 }\r
10545 \r
10546 /*\r
10547  * Prototypes for animation support routines\r
10548  */\r
10549 static void ScreenSquare(int column, int row, POINT * pt);\r
10550 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10551      POINT frames[], int * nFrames);\r
10552 \r
10553 \r
10554 #define kFactor 4\r
10555 \r
10556 void\r
10557 AnimateMove(board, fromX, fromY, toX, toY)\r
10558      Board board;\r
10559      int fromX;\r
10560      int fromY;\r
10561      int toX;\r
10562      int toY;\r
10563 {\r
10564   ChessSquare piece;\r
10565   POINT start, finish, mid;\r
10566   POINT frames[kFactor * 2 + 1];\r
10567   int nFrames, n;\r
10568 \r
10569   if (!appData.animate) return;\r
10570   if (doingSizing) return;\r
10571   if (fromY < 0 || fromX < 0) return;\r
10572   piece = board[fromY][fromX];\r
10573   if (piece >= EmptySquare) return;\r
10574 \r
10575   ScreenSquare(fromX, fromY, &start);\r
10576   ScreenSquare(toX, toY, &finish);\r
10577 \r
10578   /* All pieces except knights move in straight line */\r
10579   if (piece != WhiteKnight && piece != BlackKnight) {\r
10580     mid.x = start.x + (finish.x - start.x) / 2;\r
10581     mid.y = start.y + (finish.y - start.y) / 2;\r
10582   } else {\r
10583     /* Knight: make diagonal movement then straight */\r
10584     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10585        mid.x = start.x + (finish.x - start.x) / 2;\r
10586        mid.y = finish.y;\r
10587      } else {\r
10588        mid.x = finish.x;\r
10589        mid.y = start.y + (finish.y - start.y) / 2;\r
10590      }\r
10591   }\r
10592   \r
10593   /* Don't use as many frames for very short moves */\r
10594   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10595     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10596   else\r
10597     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10598 \r
10599   animInfo.from.x = fromX;\r
10600   animInfo.from.y = fromY;\r
10601   animInfo.to.x = toX;\r
10602   animInfo.to.y = toY;\r
10603   animInfo.lastpos = start;\r
10604   animInfo.piece = piece;\r
10605   for (n = 0; n < nFrames; n++) {\r
10606     animInfo.pos = frames[n];\r
10607     DrawPosition(FALSE, NULL);\r
10608     animInfo.lastpos = animInfo.pos;\r
10609     Sleep(appData.animSpeed);\r
10610   }\r
10611   animInfo.pos = finish;\r
10612   DrawPosition(FALSE, NULL);\r
10613   animInfo.piece = EmptySquare;\r
10614 }\r
10615 \r
10616 /*      Convert board position to corner of screen rect and color       */\r
10617 \r
10618 static void\r
10619 ScreenSquare(column, row, pt)\r
10620      int column; int row; POINT * pt;\r
10621 {\r
10622   if (flipView) {\r
10623     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10624     pt->y = lineGap + row * (squareSize + lineGap);\r
10625   } else {\r
10626     pt->x = lineGap + column * (squareSize + lineGap);\r
10627     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10628   }\r
10629 }\r
10630 \r
10631 /*      Generate a series of frame coords from start->mid->finish.\r
10632         The movement rate doubles until the half way point is\r
10633         reached, then halves back down to the final destination,\r
10634         which gives a nice slow in/out effect. The algorithmn\r
10635         may seem to generate too many intermediates for short\r
10636         moves, but remember that the purpose is to attract the\r
10637         viewers attention to the piece about to be moved and\r
10638         then to where it ends up. Too few frames would be less\r
10639         noticeable.                                             */\r
10640 \r
10641 static void\r
10642 Tween(start, mid, finish, factor, frames, nFrames)\r
10643      POINT * start; POINT * mid;\r
10644      POINT * finish; int factor;\r
10645      POINT frames[]; int * nFrames;\r
10646 {\r
10647   int n, fraction = 1, count = 0;\r
10648 \r
10649   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10650   for (n = 0; n < factor; n++)\r
10651     fraction *= 2;\r
10652   for (n = 0; n < factor; n++) {\r
10653     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10654     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10655     count ++;\r
10656     fraction = fraction / 2;\r
10657   }\r
10658   \r
10659   /* Midpoint */\r
10660   frames[count] = *mid;\r
10661   count ++;\r
10662   \r
10663   /* Slow out, stepping 1/2, then 1/4, ... */\r
10664   fraction = 2;\r
10665   for (n = 0; n < factor; n++) {\r
10666     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10667     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10668     count ++;\r
10669     fraction = fraction * 2;\r
10670   }\r
10671   *nFrames = count;\r
10672 }\r
10673 \r
10674 void\r
10675 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10676 {\r
10677 #if 0\r
10678     char buf[256];\r
10679 \r
10680     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10681         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10682 \r
10683     OutputDebugString( buf );\r
10684 #endif\r
10685 \r
10686     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10687 \r
10688     EvalGraphSet( first, last, current, pvInfoList );\r
10689 }\r
10690 \r
10691 void SetProgramStats( FrontEndProgramStats * stats )\r
10692 {\r
10693 #if 0\r
10694     char buf[1024];\r
10695 \r
10696     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10697         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10698 \r
10699     OutputDebugString( buf );\r
10700 #endif\r
10701 \r
10702     EngineOutputUpdate( stats );\r
10703 }\r
10704 ///