small change to mousewheel support; two changes in window behaviour
[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 \r
61 #include <stdio.h>\r
62 #include <stdlib.h>\r
63 #include <time.h>\r
64 #include <malloc.h>\r
65 #include <sys/stat.h>\r
66 #include <fcntl.h>\r
67 #include <math.h>\r
68 #include <commdlg.h>\r
69 #include <dlgs.h>\r
70 #include <richedit.h>\r
71 #include <mmsystem.h>\r
72 \r
73 #if __GNUC__\r
74 #include <errno.h>\r
75 #include <string.h>\r
76 #endif\r
77 \r
78 #include "common.h"\r
79 #include "winboard.h"\r
80 #include "frontend.h"\r
81 #include "backend.h"\r
82 #include "moves.h"\r
83 #include "wclipbrd.h"\r
84 #include "wgamelist.h"\r
85 #include "wedittags.h"\r
86 #include "woptions.h"\r
87 #include "wsockerr.h"\r
88 #include "defaults.h"\r
89 \r
90 #include "wsnap.h"\r
91 \r
92 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
93 \r
94   int myrandom(void);\r
95   void mysrandom(unsigned int seed);\r
96 \r
97 extern int whiteFlag, blackFlag;\r
98 Boolean flipClock = FALSE;\r
99 \r
100 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
101 \r
102 typedef struct {\r
103   ChessSquare piece;  \r
104   POINT pos;      /* window coordinates of current pos */\r
105   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
106   POINT from;     /* board coordinates of the piece's orig pos */\r
107   POINT to;       /* board coordinates of the piece's new pos */\r
108 } AnimInfo;\r
109 \r
110 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
111 \r
112 typedef struct {\r
113   POINT start;    /* window coordinates of start pos */\r
114   POINT pos;      /* window coordinates of current pos */\r
115   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
116   POINT from;     /* board coordinates of the piece's orig pos */\r
117 } DragInfo;\r
118 \r
119 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT sq[2];    /* board coordinates of from, to squares */\r
123 } HighlightInfo;\r
124 \r
125 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
126 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
127 \r
128 /* Window class names */\r
129 char szAppName[] = "WinBoard";\r
130 char szConsoleName[] = "WBConsole";\r
131 \r
132 /* Title bar text */\r
133 char szTitle[] = "WinBoard";\r
134 char szConsoleTitle[] = "ICS Interaction";\r
135 \r
136 char *programName;\r
137 char *settingsFileName;\r
138 BOOLEAN saveSettingsOnExit;\r
139 char installDir[MSG_SIZ];\r
140 \r
141 BoardSize boardSize;\r
142 BOOLEAN chessProgram;\r
143 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
144 static int squareSize, lineGap, minorSize;\r
145 static int winWidth, winHeight;\r
146 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
147 static int logoHeight = 0;\r
148 static char messageText[MESSAGE_TEXT_MAX];\r
149 static int clockTimerEvent = 0;\r
150 static int loadGameTimerEvent = 0;\r
151 static int analysisTimerEvent = 0;\r
152 static DelayedEventCallback delayedTimerCallback;\r
153 static int delayedTimerEvent = 0;\r
154 static int buttonCount = 2;\r
155 char *icsTextMenuString;\r
156 char *icsNames;\r
157 char *firstChessProgramNames;\r
158 char *secondChessProgramNames;\r
159 \r
160 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
161 \r
162 #define PALETTESIZE 256\r
163 \r
164 HINSTANCE hInst;          /* current instance */\r
165 HWND hwndMain = NULL;        /* root window*/\r
166 HWND hwndConsole = NULL;\r
167 BOOLEAN alwaysOnTop = FALSE;\r
168 RECT boardRect;\r
169 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
170   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
171 HPALETTE hPal;\r
172 ColorClass currentColorClass;\r
173 \r
174 HWND hCommPort = NULL;    /* currently open comm port */\r
175 static HWND hwndPause;    /* pause button */\r
176 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
177 static HBRUSH lightSquareBrush, darkSquareBrush,\r
178   blackSquareBrush, /* [HGM] for band between board and holdings */\r
179   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
180 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
181 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
182 static HPEN gridPen = NULL;\r
183 static HPEN highlightPen = NULL;\r
184 static HPEN premovePen = NULL;\r
185 static NPLOGPALETTE pLogPal;\r
186 static BOOL paletteChanged = FALSE;\r
187 static HICON iconWhite, iconBlack, iconCurrent;\r
188 static int doingSizing = FALSE;\r
189 static int lastSizing = 0;\r
190 static int prevStderrPort;\r
191 \r
192 /* [AS] Support for background textures */\r
193 #define BACK_TEXTURE_MODE_DISABLED      0\r
194 #define BACK_TEXTURE_MODE_PLAIN         1\r
195 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
196 \r
197 static HBITMAP liteBackTexture = NULL;\r
198 static HBITMAP darkBackTexture = NULL;\r
199 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
200 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
201 static int backTextureSquareSize = 0;\r
202 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
203 \r
204 #if __GNUC__ && !defined(_winmajor)\r
205 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
206 #else\r
207 #define oldDialog (_winmajor < 4)\r
208 #endif\r
209 \r
210 char *defaultTextAttribs[] = \r
211 {\r
212   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
213   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
214   COLOR_NONE\r
215 };\r
216 \r
217 typedef struct {\r
218   char *name;\r
219   int squareSize;\r
220   int lineGap;\r
221   int smallLayout;\r
222   int tinyLayout;\r
223   int cliWidth, cliHeight;\r
224 } SizeInfo;\r
225 \r
226 SizeInfo sizeInfo[] = \r
227 {\r
228   { "tiny",     21, 0, 1, 1, 0, 0 },\r
229   { "teeny",    25, 1, 1, 1, 0, 0 },\r
230   { "dinky",    29, 1, 1, 1, 0, 0 },\r
231   { "petite",   33, 1, 1, 1, 0, 0 },\r
232   { "slim",     37, 2, 1, 0, 0, 0 },\r
233   { "small",    40, 2, 1, 0, 0, 0 },\r
234   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
235   { "middling", 49, 2, 0, 0, 0, 0 },\r
236   { "average",  54, 2, 0, 0, 0, 0 },\r
237   { "moderate", 58, 3, 0, 0, 0, 0 },\r
238   { "medium",   64, 3, 0, 0, 0, 0 },\r
239   { "bulky",    72, 3, 0, 0, 0, 0 },\r
240   { "large",    80, 3, 0, 0, 0, 0 },\r
241   { "big",      87, 3, 0, 0, 0, 0 },\r
242   { "huge",     95, 3, 0, 0, 0, 0 },\r
243   { "giant",    108, 3, 0, 0, 0, 0 },\r
244   { "colossal", 116, 4, 0, 0, 0, 0 },\r
245   { "titanic",  129, 4, 0, 0, 0, 0 },\r
246   { NULL, 0, 0, 0, 0, 0, 0 }\r
247 };\r
248 \r
249 #define MF(x) {x, {0, }, {0, }, 0}\r
250 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
251 {\r
252   { 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
253   { 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
254   { 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
255   { 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
256   { 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
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266   { 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
267   { 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
268   { 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
269   { 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
270 };\r
271 \r
272 MyFont *font[NUM_SIZES][NUM_FONTS];\r
273 \r
274 typedef struct {\r
275   char *label;\r
276   int id;\r
277   HWND hwnd;\r
278   WNDPROC wndproc;\r
279 } MyButtonDesc;\r
280 \r
281 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
282 #define N_BUTTONS 5\r
283 \r
284 MyButtonDesc buttonDesc[N_BUTTONS] =\r
285 {\r
286   {"<<", IDM_ToStart, NULL, NULL},\r
287   {"<", IDM_Backward, NULL, NULL},\r
288   {"P", IDM_Pause, NULL, NULL},\r
289   {">", IDM_Forward, NULL, NULL},\r
290   {">>", IDM_ToEnd, NULL, NULL},\r
291 };\r
292 \r
293 int tinyLayout = 0, smallLayout = 0;\r
294 #define MENU_BAR_ITEMS 6\r
295 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
296   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
297   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
298 };\r
299 \r
300 \r
301 MySound sounds[(int)NSoundClasses];\r
302 MyTextAttribs textAttribs[(int)NColorClasses];\r
303 \r
304 MyColorizeAttribs colorizeAttribs[] = {\r
305   { (COLORREF)0, 0, "Shout Text" },\r
306   { (COLORREF)0, 0, "SShout/CShout" },\r
307   { (COLORREF)0, 0, "Channel 1 Text" },\r
308   { (COLORREF)0, 0, "Channel Text" },\r
309   { (COLORREF)0, 0, "Kibitz Text" },\r
310   { (COLORREF)0, 0, "Tell Text" },\r
311   { (COLORREF)0, 0, "Challenge Text" },\r
312   { (COLORREF)0, 0, "Request Text" },\r
313   { (COLORREF)0, 0, "Seek Text" },\r
314   { (COLORREF)0, 0, "Normal Text" },\r
315   { (COLORREF)0, 0, "None" }\r
316 };\r
317 \r
318 \r
319 \r
320 static char *commentTitle;\r
321 static char *commentText;\r
322 static int commentIndex;\r
323 static Boolean editComment = FALSE;\r
324 HWND commentDialog = NULL;\r
325 BOOLEAN commentDialogUp = FALSE;\r
326 static int commentX, commentY, commentH, commentW;\r
327 \r
328 static char *analysisTitle;\r
329 static char *analysisText;\r
330 HWND analysisDialog = NULL;\r
331 BOOLEAN analysisDialogUp = FALSE;\r
332 static int analysisX, analysisY, analysisH, analysisW;\r
333 \r
334 char errorTitle[MSG_SIZ];\r
335 char errorMessage[2*MSG_SIZ];\r
336 HWND errorDialog = NULL;\r
337 BOOLEAN moveErrorMessageUp = FALSE;\r
338 BOOLEAN consoleEcho = TRUE;\r
339 CHARFORMAT consoleCF;\r
340 COLORREF consoleBackgroundColor;\r
341 \r
342 char *programVersion;\r
343 \r
344 #define CPReal 1\r
345 #define CPComm 2\r
346 #define CPSock 3\r
347 #define CPRcmd 4\r
348 typedef int CPKind;\r
349 \r
350 typedef struct {\r
351   CPKind kind;\r
352   HANDLE hProcess;\r
353   DWORD pid;\r
354   HANDLE hTo;\r
355   HANDLE hFrom;\r
356   SOCKET sock;\r
357   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
358 } ChildProc;\r
359 \r
360 #define INPUT_SOURCE_BUF_SIZE 4096\r
361 \r
362 typedef struct _InputSource {\r
363   CPKind kind;\r
364   HANDLE hFile;\r
365   SOCKET sock;\r
366   int lineByLine;\r
367   HANDLE hThread;\r
368   DWORD id;\r
369   char buf[INPUT_SOURCE_BUF_SIZE];\r
370   char *next;\r
371   DWORD count;\r
372   int error;\r
373   InputCallback func;\r
374   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
375   VOIDSTAR closure;\r
376 } InputSource;\r
377 \r
378 InputSource *consoleInputSource;\r
379 \r
380 DCB dcb;\r
381 \r
382 /* forward */\r
383 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
384 VOID ConsoleCreate();\r
385 LRESULT CALLBACK\r
386   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
387 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
388 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
389 VOID ParseCommSettings(char *arg, DCB *dcb);\r
390 LRESULT CALLBACK\r
391   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
392 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
393 void ParseIcsTextMenu(char *icsTextMenuString);\r
394 VOID PopUpMoveDialog(char firstchar);\r
395 VOID PopUpNameDialog(char firstchar);\r
396 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
397 \r
398 /* [AS] */\r
399 int NewGameFRC();\r
400 int GameListOptions();\r
401 \r
402 HWND moveHistoryDialog = NULL;\r
403 BOOLEAN moveHistoryDialogUp = FALSE;\r
404 \r
405 WindowPlacement wpMoveHistory;\r
406 \r
407 HWND evalGraphDialog = NULL;\r
408 BOOLEAN evalGraphDialogUp = FALSE;\r
409 \r
410 WindowPlacement wpEvalGraph;\r
411 \r
412 HWND engineOutputDialog = NULL;\r
413 BOOLEAN engineOutputDialogUp = FALSE;\r
414 \r
415 WindowPlacement wpEngineOutput;\r
416 \r
417 VOID MoveHistoryPopUp();\r
418 VOID MoveHistoryPopDown();\r
419 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
420 BOOL MoveHistoryIsUp();\r
421 \r
422 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
423 VOID EvalGraphPopUp();\r
424 VOID EvalGraphPopDown();\r
425 BOOL EvalGraphIsUp();\r
426 \r
427 VOID EngineOutputPopUp();\r
428 VOID EngineOutputPopDown();\r
429 BOOL EngineOutputIsUp();\r
430 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
431 \r
432 VOID GothicPopUp(char *title, VariantClass variant);\r
433 /*\r
434  * Setting "frozen" should disable all user input other than deleting\r
435  * the window.  We do this while engines are initializing themselves.\r
436  */\r
437 static int frozen = 0;\r
438 static int oldMenuItemState[MENU_BAR_ITEMS];\r
439 void FreezeUI()\r
440 {\r
441   HMENU hmenu;\r
442   int i;\r
443 \r
444   if (frozen) return;\r
445   frozen = 1;\r
446   hmenu = GetMenu(hwndMain);\r
447   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
448     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
449   }\r
450   DrawMenuBar(hwndMain);\r
451 }\r
452 \r
453 /* Undo a FreezeUI */\r
454 void ThawUI()\r
455 {\r
456   HMENU hmenu;\r
457   int i;\r
458 \r
459   if (!frozen) return;\r
460   frozen = 0;\r
461   hmenu = GetMenu(hwndMain);\r
462   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
463     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
464   }\r
465   DrawMenuBar(hwndMain);\r
466 }\r
467 \r
468 /*---------------------------------------------------------------------------*\\r
469  *\r
470  * WinMain\r
471  *\r
472 \*---------------------------------------------------------------------------*/\r
473 \r
474 int APIENTRY\r
475 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
476         LPSTR lpCmdLine, int nCmdShow)\r
477 {\r
478   MSG msg;\r
479   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
480 \r
481   debugFP = stderr;\r
482 \r
483   LoadLibrary("RICHED32.DLL");\r
484   consoleCF.cbSize = sizeof(CHARFORMAT);\r
485 \r
486   if (!InitApplication(hInstance)) {\r
487     return (FALSE);\r
488   }\r
489   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
490     return (FALSE);\r
491   }\r
492 \r
493   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
494   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
495   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
496 \r
497   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
498 \r
499   while (GetMessage(&msg, /* message structure */\r
500                     NULL, /* handle of window receiving the message */\r
501                     0,    /* lowest message to examine */\r
502                     0))   /* highest message to examine */\r
503     {\r
504       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
505           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
506           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
507           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
508           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
509           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
510           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
511           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
512           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
513           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
514         TranslateMessage(&msg); /* Translates virtual key codes */\r
515         DispatchMessage(&msg);  /* Dispatches message to window */\r
516       }\r
517     }\r
518 \r
519 \r
520   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
521 }\r
522 \r
523 /*---------------------------------------------------------------------------*\\r
524  *\r
525  * Initialization functions\r
526  *\r
527 \*---------------------------------------------------------------------------*/\r
528 \r
529 BOOL\r
530 InitApplication(HINSTANCE hInstance)\r
531 {\r
532   WNDCLASS wc;\r
533 \r
534   /* Fill in window class structure with parameters that describe the */\r
535   /* main window. */\r
536 \r
537   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
538   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
539   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
540   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
541   wc.hInstance     = hInstance;         /* Owner of this class */\r
542   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
543   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
544   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
545   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
546   wc.lpszClassName = szAppName;                 /* Name to register as */\r
547 \r
548   /* Register the window class and return success/failure code. */\r
549   if (!RegisterClass(&wc)) return FALSE;\r
550 \r
551   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
552   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
553   wc.cbClsExtra    = 0;\r
554   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
555   wc.hInstance     = hInstance;\r
556   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
557   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
558   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
559   wc.lpszMenuName  = NULL;\r
560   wc.lpszClassName = szConsoleName;\r
561 \r
562   if (!RegisterClass(&wc)) return FALSE;\r
563   return TRUE;\r
564 }\r
565 \r
566 \r
567 /* Set by InitInstance, used by EnsureOnScreen */\r
568 int screenHeight, screenWidth;\r
569 \r
570 void\r
571 EnsureOnScreen(int *x, int *y)\r
572 {\r
573 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
574   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
575   if (*x > screenWidth - 32) *x = 0;\r
576   if (*y > screenHeight - 32) *y = 0;\r
577   if (*x < 0) *x = 0;\r
578   if (*y < 0) *y = 0;\r
579 //  if (*x < 10) *x = 10;\r
580 //  if (*y < gap) *y = gap;\r
581 }\r
582 \r
583 BOOL\r
584 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
585 {\r
586   HWND hwnd; /* Main window handle. */\r
587   int ibs;\r
588   WINDOWPLACEMENT wp;\r
589   char *filepart;\r
590 \r
591   hInst = hInstance;    /* Store instance handle in our global variable */\r
592 \r
593   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
594     *filepart = NULLCHAR;\r
595   } else {\r
596     GetCurrentDirectory(MSG_SIZ, installDir);\r
597   }\r
598   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
599   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
600   if (appData.debugMode) {\r
601     debugFP = fopen(appData.nameOfDebugFile, "w");\r
602     setbuf(debugFP, NULL);\r
603   }\r
604 \r
605   InitBackEnd1();\r
606 \r
607 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
608 //  InitEngineUCI( installDir, &second );\r
609 \r
610   /* Create a main window for this application instance. */\r
611   hwnd = CreateWindow(szAppName, szTitle,\r
612                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
613                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
614                       NULL, NULL, hInstance, NULL);\r
615   hwndMain = hwnd;\r
616 \r
617   /* If window could not be created, return "failure" */\r
618   if (!hwnd) {\r
619     return (FALSE);\r
620   }\r
621 \r
622   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
623   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
624       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
625 \r
626       if (first.programLogo == NULL && appData.debugMode) {\r
627           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
628       }\r
629   } else if(appData.autoLogo) {\r
630       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
631         char buf[MSG_SIZ];\r
632         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
633         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
634       }\r
635   }\r
636 \r
637   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
638       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
639 \r
640       if (second.programLogo == NULL && appData.debugMode) {\r
641           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
642       }\r
643   } else if(appData.autoLogo) {\r
644       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
645         char buf[MSG_SIZ];\r
646         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
647         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
648       }\r
649   }\r
650 \r
651   iconWhite = LoadIcon(hInstance, "icon_white");\r
652   iconBlack = LoadIcon(hInstance, "icon_black");\r
653   iconCurrent = iconWhite;\r
654   InitDrawingColors();\r
655   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
656   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
657   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
658     /* Compute window size for each board size, and use the largest\r
659        size that fits on this screen as the default. */\r
660     InitDrawingSizes((BoardSize)ibs, 0);\r
661     if (boardSize == (BoardSize)-1 &&\r
662         winHeight <= screenHeight\r
663            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
664         && winWidth <= screenWidth) {\r
665       boardSize = (BoardSize)ibs;\r
666     }\r
667   }\r
668 \r
669   InitDrawingSizes(boardSize, 0);\r
670   InitMenuChecks();\r
671   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
672 \r
673   /* [AS] Load textures if specified */\r
674   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
675   \r
676   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
677       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
678       liteBackTextureMode = appData.liteBackTextureMode;\r
679 \r
680       if (liteBackTexture == NULL && appData.debugMode) {\r
681           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
682       }\r
683   }\r
684   \r
685   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
686       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
687       darkBackTextureMode = appData.darkBackTextureMode;\r
688 \r
689       if (darkBackTexture == NULL && appData.debugMode) {\r
690           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
691       }\r
692   }\r
693 \r
694   mysrandom( (unsigned) time(NULL) );\r
695 \r
696   /* [AS] Restore layout */\r
697   if( wpMoveHistory.visible ) {\r
698       MoveHistoryPopUp();\r
699   }\r
700 \r
701   if( wpEvalGraph.visible ) {\r
702       EvalGraphPopUp();\r
703   }\r
704 \r
705   if( wpEngineOutput.visible ) {\r
706       EngineOutputPopUp();\r
707   }\r
708 \r
709   InitBackEnd2();\r
710 \r
711   /* Make the window visible; update its client area; and return "success" */\r
712   EnsureOnScreen(&boardX, &boardY);\r
713   wp.length = sizeof(WINDOWPLACEMENT);\r
714   wp.flags = 0;\r
715   wp.showCmd = nCmdShow;\r
716   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
717   wp.rcNormalPosition.left = boardX;\r
718   wp.rcNormalPosition.right = boardX + winWidth;\r
719   wp.rcNormalPosition.top = boardY;\r
720   wp.rcNormalPosition.bottom = boardY + winHeight;\r
721   SetWindowPlacement(hwndMain, &wp);\r
722 \r
723   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
724                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
725 \r
726 #if 0\r
727   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
728   if( gameInfo.variant != VariantFischeRandom ) {\r
729       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
730   }\r
731 #endif\r
732   if (hwndConsole) {\r
733 #if AOT_CONSOLE\r
734     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
735                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
736 #endif\r
737     ShowWindow(hwndConsole, nCmdShow);\r
738   }\r
739   UpdateWindow(hwnd);\r
740 \r
741   return TRUE;\r
742 \r
743 }\r
744 \r
745 \r
746 typedef enum {\r
747   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
748   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
749   ArgSettingsFilename\r
750 } ArgType;\r
751 \r
752 typedef struct {\r
753   char *argName;\r
754   ArgType argType;\r
755   /***\r
756   union {\r
757     String *pString;       // ArgString\r
758     int *pInt;             // ArgInt\r
759     float *pFloat;         // ArgFloat\r
760     Boolean *pBoolean;     // ArgBoolean\r
761     COLORREF *pColor;      // ArgColor\r
762     ColorClass cc;         // ArgAttribs\r
763     String *pFilename;     // ArgFilename\r
764     BoardSize *pBoardSize; // ArgBoardSize\r
765     int whichFont;         // ArgFont\r
766     DCB *pDCB;             // ArgCommSettings\r
767     String *pFilename;     // ArgSettingsFilename\r
768   } argLoc;\r
769   ***/\r
770   LPVOID argLoc;\r
771   BOOL save;\r
772 } ArgDescriptor;\r
773 \r
774 int junk;\r
775 ArgDescriptor argDescriptors[] = {\r
776   /* positional arguments */\r
777   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
778   { "", ArgNone, NULL },\r
779   /* keyword arguments */\r
780   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
781   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
782   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
783   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
784   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
785   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
786   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
787   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
788   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
789   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
790   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
791   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
792   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
793   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
794   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
795   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
796   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
797   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
798     FALSE },\r
799   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
800     FALSE },\r
801   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
802     FALSE },\r
803   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
804   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
805     FALSE },\r
806   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
807   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
808   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
809   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
810   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
811   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
812   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
813   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
814   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
815   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
816   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
817   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
818   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
819   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
820   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
821   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
822   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
823   /*!!bitmapDirectory?*/\r
824   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
825   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
826   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
827   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
828   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
829   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
830   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
831   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
832   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
833   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
834   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
835   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
836   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
837   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
838   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
839   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
840   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
841   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
842   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
843   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
844   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
845   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
846   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
847   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
848   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
849   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
850   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
851   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
852   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
853   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
854   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
855   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
856   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
857   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
858   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
859   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
860   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
861   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
862   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
863   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
864   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
865   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
866   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
867   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
868   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
869   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
870   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
871   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
872   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
873   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
874   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
875   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
876   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
877   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
878   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
879   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
880   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
881   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
882   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
883   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
884   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
885   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
886   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
887   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
888   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
889   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
890   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
891   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
892   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
893   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
894   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
895   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
896   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
897   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
898   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
899   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
900   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
901   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
902   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
903   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
904   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
905   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
906   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
907   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
908   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
909   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
910   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
911   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
912   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
913   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
914   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
915   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
916   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
917   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
918     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
919   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
920   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
921   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
922   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
923   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
924   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
925   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
926   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
927     TRUE }, /* must come after all fonts */\r
928   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
929   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
930     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
931   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
932   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
933   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
934   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
935   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
936   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
937   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
938   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
939   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
940   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
941   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
942   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
943   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
944   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
945   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
946   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
947   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
948   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
949   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
950   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
951   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
952   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
953   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
954   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
955   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
956   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
957   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
958   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
959 #if 0\r
960   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
961   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
962 #endif\r
963   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
964   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
965   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
966   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
967   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
968   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
969   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
970   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
971   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
972   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
973   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
974   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
975   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
976   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
977   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
978   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
979   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
980   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
981   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
982   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
983   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
984   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
985   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
986   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
987   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
988   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
989   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
990   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
991   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
992   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
993   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
994   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
995   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
996   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
997   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
998   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
999   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1000   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1001   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1002   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1003   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1004   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1005   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1006   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1007   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1008   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1009   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1010   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1011   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1012   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1013   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1014   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1015   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1016   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1017   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1018   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1019   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1020   { "highlightLastMove", ArgBoolean,\r
1021     (LPVOID) &appData.highlightLastMove, TRUE },\r
1022   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1023   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1024   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1025   { "highlightDragging", ArgBoolean,\r
1026     (LPVOID) &appData.highlightDragging, TRUE },\r
1027   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1028   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1029   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1030   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1031   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1032   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1033   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1034   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1035   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1036   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1037   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1038   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1039   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1040   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1041   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1042   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1043   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1044   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1045   { "soundShout", ArgFilename,\r
1046     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1047   { "soundSShout", ArgFilename,\r
1048     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1049   { "soundChannel1", ArgFilename,\r
1050     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1051   { "soundChannel", ArgFilename,\r
1052     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1053   { "soundKibitz", ArgFilename,\r
1054     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1055   { "soundTell", ArgFilename,\r
1056     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1057   { "soundChallenge", ArgFilename,\r
1058     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1059   { "soundRequest", ArgFilename,\r
1060     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1061   { "soundSeek", ArgFilename,\r
1062     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1063   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1064   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1065   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1066   { "soundIcsLoss", ArgFilename, \r
1067     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1068   { "soundIcsDraw", ArgFilename, \r
1069     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1070   { "soundIcsUnfinished", ArgFilename, \r
1071     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1072   { "soundIcsAlarm", ArgFilename, \r
1073     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1074   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1075   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1076   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1077   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1078   { "reuseChessPrograms", ArgBoolean,\r
1079     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1080   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1081   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1082   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1083   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1084   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1085   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1086   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1087   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1088   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1089   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1090   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1091   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1092   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1093   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1094   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1095   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1096   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1097   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1098   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1099   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1100   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1101   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1102   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1103   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1104   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1105   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1106   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1107   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1108   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1109   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1110   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1111   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1112   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1113   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1114   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1115   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1116   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1117     TRUE },\r
1118   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1119     TRUE },\r
1120   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1121   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1122   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1123   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1124   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1125   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1126   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1127   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1128   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1129   /* [AS] New features */\r
1130   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1131   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1132   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1133   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1134   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1135   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1136   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1137   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1138   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1139   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1140   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1141   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1142   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1143   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1144   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1145   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1146   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1147   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1148   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1149   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1150   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1151   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1152   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1153   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1154   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1155   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1156   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1157   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1158   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1159   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1160   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1161   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1162   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1163   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1164   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1165   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1166   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1167   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1168   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1169   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1170   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1171   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1172   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1173   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1174   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1175   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1176   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1177   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1178   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1179   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1180 \r
1181   /* [AS] Layout stuff */\r
1182   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1183   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1184   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1185   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1186   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1187 \r
1188   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1189   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1190   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1191   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1192   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1193 \r
1194   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1195   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1196   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1197   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1198   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1199 \r
1200   /* [HGM] board-size, adjudication and misc. options */\r
1201   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1202   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1203   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1204   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1205   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1206   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1207   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1208   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1209   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1210   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1211   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1212   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1213   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1214   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1215   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1216   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1217   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1218   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1219   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1220   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1221   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1222   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1223   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1224   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1225   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1226   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1227   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1228   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1229   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1230 \r
1231 #ifdef ZIPPY\r
1232   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1233   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1234   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1235   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1236   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1237   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1238   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1239   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1240   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1241   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1242   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1243   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1244   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1245     FALSE },\r
1246   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1247   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1248   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1249   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1250   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1251   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1252   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1253     FALSE },\r
1254   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1255   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1256   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1257   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1258   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1259   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1260   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1261   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1262   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1263   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1264   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1265   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1266   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1267   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1268   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1269   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1270   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1271   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1272 #endif\r
1273   /* [HGM] options for broadcasting and time odds */\r
1274   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1275   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1276   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1277   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1278   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1279   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1280   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1281   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1282   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1283   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1284   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1285   { NULL, ArgNone, NULL, FALSE }\r
1286 };\r
1287 \r
1288 \r
1289 /* Kludge for indirection files on command line */\r
1290 char* lastIndirectionFilename;\r
1291 ArgDescriptor argDescriptorIndirection =\r
1292 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1293 \r
1294 \r
1295 VOID\r
1296 ExitArgError(char *msg, char *badArg)\r
1297 {\r
1298   char buf[MSG_SIZ];\r
1299 \r
1300   sprintf(buf, "%s %s", msg, badArg);\r
1301   DisplayFatalError(buf, 0, 2);\r
1302   exit(2);\r
1303 }\r
1304 \r
1305 /* Command line font name parser.  NULL name means do nothing.\r
1306    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1307    For backward compatibility, syntax without the colon is also\r
1308    accepted, but font names with digits in them won't work in that case.\r
1309 */\r
1310 VOID\r
1311 ParseFontName(char *name, MyFontParams *mfp)\r
1312 {\r
1313   char *p, *q;\r
1314   if (name == NULL) return;\r
1315   p = name;\r
1316   q = strchr(p, ':');\r
1317   if (q) {\r
1318     if (q - p >= sizeof(mfp->faceName))\r
1319       ExitArgError("Font name too long:", name);\r
1320     memcpy(mfp->faceName, p, q - p);\r
1321     mfp->faceName[q - p] = NULLCHAR;\r
1322     p = q + 1;\r
1323   } else {\r
1324     q = mfp->faceName;\r
1325     while (*p && !isdigit(*p)) {\r
1326       *q++ = *p++;\r
1327       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1328         ExitArgError("Font name too long:", name);\r
1329     }\r
1330     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1331     *q = NULLCHAR;\r
1332   }\r
1333   if (!*p) ExitArgError("Font point size missing:", name);\r
1334   mfp->pointSize = (float) atof(p);\r
1335   mfp->bold = (strchr(p, 'b') != NULL);\r
1336   mfp->italic = (strchr(p, 'i') != NULL);\r
1337   mfp->underline = (strchr(p, 'u') != NULL);\r
1338   mfp->strikeout = (strchr(p, 's') != NULL);\r
1339 }\r
1340 \r
1341 /* Color name parser.\r
1342    X version accepts X color names, but this one\r
1343    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1344 COLORREF\r
1345 ParseColorName(char *name)\r
1346 {\r
1347   int red, green, blue, count;\r
1348   char buf[MSG_SIZ];\r
1349 \r
1350   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1351   if (count != 3) {\r
1352     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1353       &red, &green, &blue);\r
1354   }\r
1355   if (count != 3) {\r
1356     sprintf(buf, "Can't parse color name %s", name);\r
1357     DisplayError(buf, 0);\r
1358     return RGB(0, 0, 0);\r
1359   }\r
1360   return PALETTERGB(red, green, blue);\r
1361 }\r
1362 \r
1363 \r
1364 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1365 {\r
1366   char *e = argValue;\r
1367   int eff = 0;\r
1368 \r
1369   while (*e) {\r
1370     if (*e == 'b')      eff |= CFE_BOLD;\r
1371     else if (*e == 'i') eff |= CFE_ITALIC;\r
1372     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1373     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1374     else if (*e == '#' || isdigit(*e)) break;\r
1375     e++;\r
1376   }\r
1377   *effects = eff;\r
1378   *color   = ParseColorName(e);\r
1379 }\r
1380 \r
1381 \r
1382 BoardSize\r
1383 ParseBoardSize(char *name)\r
1384 {\r
1385   BoardSize bs = SizeTiny;\r
1386   while (sizeInfo[bs].name != NULL) {\r
1387     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1388     bs++;\r
1389   }\r
1390   ExitArgError("Unrecognized board size value", name);\r
1391   return bs; /* not reached */\r
1392 }\r
1393 \r
1394 \r
1395 char\r
1396 StringGet(void *getClosure)\r
1397 {\r
1398   char **p = (char **) getClosure;\r
1399   return *((*p)++);\r
1400 }\r
1401 \r
1402 char\r
1403 FileGet(void *getClosure)\r
1404 {\r
1405   int c;\r
1406   FILE* f = (FILE*) getClosure;\r
1407 \r
1408   c = getc(f);\r
1409   if (c == EOF)\r
1410     return NULLCHAR;\r
1411   else\r
1412     return (char) c;\r
1413 }\r
1414 \r
1415 /* Parse settings file named "name". If file found, return the\r
1416    full name in fullname and return TRUE; else return FALSE */\r
1417 BOOLEAN\r
1418 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1419 {\r
1420   char *dummy;\r
1421   FILE *f;\r
1422 \r
1423   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1424     f = fopen(fullname, "r");\r
1425     if (f != NULL) {\r
1426       ParseArgs(FileGet, f);\r
1427       fclose(f);\r
1428       return TRUE;\r
1429     }\r
1430   }\r
1431   return FALSE;\r
1432 }\r
1433 \r
1434 VOID\r
1435 ParseArgs(GetFunc get, void *cl)\r
1436 {\r
1437   char argName[ARG_MAX];\r
1438   char argValue[ARG_MAX];\r
1439   ArgDescriptor *ad;\r
1440   char start;\r
1441   char *q;\r
1442   int i, octval;\r
1443   char ch;\r
1444   int posarg = 0;\r
1445 \r
1446   ch = get(cl);\r
1447   for (;;) {\r
1448     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1449     if (ch == NULLCHAR) break;\r
1450     if (ch == ';') {\r
1451       /* Comment to end of line */\r
1452       ch = get(cl);\r
1453       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1454       continue;\r
1455     } else if (ch == '/' || ch == '-') {\r
1456       /* Switch */\r
1457       q = argName;\r
1458       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1459              ch != '\n' && ch != '\t') {\r
1460         *q++ = ch;\r
1461         ch = get(cl);\r
1462       }\r
1463       *q = NULLCHAR;\r
1464 \r
1465       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1466         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1467 \r
1468       if (ad->argName == NULL)\r
1469         ExitArgError("Unrecognized argument", argName);\r
1470 \r
1471     } else if (ch == '@') {\r
1472       /* Indirection file */\r
1473       ad = &argDescriptorIndirection;\r
1474       ch = get(cl);\r
1475     } else {\r
1476       /* Positional argument */\r
1477       ad = &argDescriptors[posarg++];\r
1478       strcpy(argName, ad->argName);\r
1479     }\r
1480 \r
1481     if (ad->argType == ArgTrue) {\r
1482       *(Boolean *) ad->argLoc = TRUE;\r
1483       continue;\r
1484     }\r
1485     if (ad->argType == ArgFalse) {\r
1486       *(Boolean *) ad->argLoc = FALSE;\r
1487       continue;\r
1488     }\r
1489 \r
1490     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1491     if (ch == NULLCHAR || ch == '\n') {\r
1492       ExitArgError("No value provided for argument", argName);\r
1493     }\r
1494     q = argValue;\r
1495     if (ch == '{') {\r
1496       // Quoting with { }.  No characters have to (or can) be escaped.\r
1497       // Thus the string cannot contain a '}' character.\r
1498       start = ch;\r
1499       ch = get(cl);\r
1500       while (start) {\r
1501         switch (ch) {\r
1502         case NULLCHAR:\r
1503           start = NULLCHAR;\r
1504           break;\r
1505           \r
1506         case '}':\r
1507           ch = get(cl);\r
1508           start = NULLCHAR;\r
1509           break;\r
1510 \r
1511         default:\r
1512           *q++ = ch;\r
1513           ch = get(cl);\r
1514           break;\r
1515         }\r
1516       }   \r
1517     } else if (ch == '\'' || ch == '"') {\r
1518       // Quoting with ' ' or " ", with \ as escape character.\r
1519       // Inconvenient for long strings that may contain Windows filenames.\r
1520       start = ch;\r
1521       ch = get(cl);\r
1522       while (start) {\r
1523         switch (ch) {\r
1524         case NULLCHAR:\r
1525           start = NULLCHAR;\r
1526           break;\r
1527 \r
1528         default:\r
1529         not_special:\r
1530           *q++ = ch;\r
1531           ch = get(cl);\r
1532           break;\r
1533 \r
1534         case '\'':\r
1535         case '\"':\r
1536           if (ch == start) {\r
1537             ch = get(cl);\r
1538             start = NULLCHAR;\r
1539             break;\r
1540           } else {\r
1541             goto not_special;\r
1542           }\r
1543 \r
1544         case '\\':\r
1545           if (ad->argType == ArgFilename\r
1546               || ad->argType == ArgSettingsFilename) {\r
1547               goto not_special;\r
1548           }\r
1549           ch = get(cl);\r
1550           switch (ch) {\r
1551           case NULLCHAR:\r
1552             ExitArgError("Incomplete \\ escape in value for", argName);\r
1553             break;\r
1554           case 'n':\r
1555             *q++ = '\n';\r
1556             ch = get(cl);\r
1557             break;\r
1558           case 'r':\r
1559             *q++ = '\r';\r
1560             ch = get(cl);\r
1561             break;\r
1562           case 't':\r
1563             *q++ = '\t';\r
1564             ch = get(cl);\r
1565             break;\r
1566           case 'b':\r
1567             *q++ = '\b';\r
1568             ch = get(cl);\r
1569             break;\r
1570           case 'f':\r
1571             *q++ = '\f';\r
1572             ch = get(cl);\r
1573             break;\r
1574           default:\r
1575             octval = 0;\r
1576             for (i = 0; i < 3; i++) {\r
1577               if (ch >= '0' && ch <= '7') {\r
1578                 octval = octval*8 + (ch - '0');\r
1579                 ch = get(cl);\r
1580               } else {\r
1581                 break;\r
1582               }\r
1583             }\r
1584             if (i > 0) {\r
1585               *q++ = (char) octval;\r
1586             } else {\r
1587               *q++ = ch;\r
1588               ch = get(cl);\r
1589             }\r
1590             break;\r
1591           }\r
1592           break;\r
1593         }\r
1594       }\r
1595     } else {\r
1596       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1597         *q++ = ch;\r
1598         ch = get(cl);\r
1599       }\r
1600     }\r
1601     *q = NULLCHAR;\r
1602 \r
1603     switch (ad->argType) {\r
1604     case ArgInt:\r
1605       *(int *) ad->argLoc = atoi(argValue);\r
1606       break;\r
1607 \r
1608     case ArgFloat:\r
1609       *(float *) ad->argLoc = (float) atof(argValue);\r
1610       break;\r
1611 \r
1612     case ArgString:\r
1613     case ArgFilename:\r
1614       *(char **) ad->argLoc = strdup(argValue);\r
1615       break;\r
1616 \r
1617     case ArgSettingsFilename:\r
1618       {\r
1619         char fullname[MSG_SIZ];\r
1620         if (ParseSettingsFile(argValue, fullname)) {\r
1621           if (ad->argLoc != NULL) {\r
1622             *(char **) ad->argLoc = strdup(fullname);\r
1623           }\r
1624         } else {\r
1625           if (ad->argLoc != NULL) {\r
1626           } else {\r
1627             ExitArgError("Failed to open indirection file", argValue);\r
1628           }\r
1629         }\r
1630       }\r
1631       break;\r
1632 \r
1633     case ArgBoolean:\r
1634       switch (argValue[0]) {\r
1635       case 't':\r
1636       case 'T':\r
1637         *(Boolean *) ad->argLoc = TRUE;\r
1638         break;\r
1639       case 'f':\r
1640       case 'F':\r
1641         *(Boolean *) ad->argLoc = FALSE;\r
1642         break;\r
1643       default:\r
1644         ExitArgError("Unrecognized boolean argument value", argValue);\r
1645         break;\r
1646       }\r
1647       break;\r
1648 \r
1649     case ArgColor:\r
1650       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1651       break;\r
1652 \r
1653     case ArgAttribs: {\r
1654       ColorClass cc = (ColorClass)ad->argLoc;\r
1655       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1656       }\r
1657       break;\r
1658       \r
1659     case ArgBoardSize:\r
1660       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1661       break;\r
1662 \r
1663     case ArgFont:\r
1664       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1665       break;\r
1666 \r
1667     case ArgCommSettings:\r
1668       ParseCommSettings(argValue, &dcb);\r
1669       break;\r
1670 \r
1671     case ArgNone:\r
1672       ExitArgError("Unrecognized argument", argValue);\r
1673       break;\r
1674     }\r
1675   }\r
1676 }\r
1677 \r
1678 VOID\r
1679 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1680 {\r
1681   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1682   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1683   DeleteDC(hdc);\r
1684   lf->lfWidth = 0;\r
1685   lf->lfEscapement = 0;\r
1686   lf->lfOrientation = 0;\r
1687   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1688   lf->lfItalic = mfp->italic;\r
1689   lf->lfUnderline = mfp->underline;\r
1690   lf->lfStrikeOut = mfp->strikeout;\r
1691   lf->lfCharSet = DEFAULT_CHARSET;\r
1692   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1693   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1694   lf->lfQuality = DEFAULT_QUALITY;\r
1695   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1696   strcpy(lf->lfFaceName, mfp->faceName);\r
1697 }\r
1698 \r
1699 VOID\r
1700 CreateFontInMF(MyFont *mf)\r
1701 {\r
1702   LFfromMFP(&mf->lf, &mf->mfp);\r
1703   if (mf->hf) DeleteObject(mf->hf);\r
1704   mf->hf = CreateFontIndirect(&mf->lf);\r
1705 }\r
1706 \r
1707 VOID\r
1708 SetDefaultTextAttribs()\r
1709 {\r
1710   ColorClass cc;\r
1711   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1712     ParseAttribs(&textAttribs[cc].color, \r
1713                  &textAttribs[cc].effects, \r
1714                  defaultTextAttribs[cc]);\r
1715   }\r
1716 }\r
1717 \r
1718 VOID\r
1719 SetDefaultSounds()\r
1720 {\r
1721   ColorClass cc;\r
1722   SoundClass sc;\r
1723   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1724     textAttribs[cc].sound.name = strdup("");\r
1725     textAttribs[cc].sound.data = NULL;\r
1726   }\r
1727   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1728     sounds[sc].name = strdup("");\r
1729     sounds[sc].data = NULL;\r
1730   }\r
1731   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1732 }\r
1733 \r
1734 VOID\r
1735 LoadAllSounds()\r
1736 {\r
1737   ColorClass cc;\r
1738   SoundClass sc;\r
1739   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1740     MyLoadSound(&textAttribs[cc].sound);\r
1741   }\r
1742   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1743     MyLoadSound(&sounds[sc]);\r
1744   }\r
1745 }\r
1746 \r
1747 VOID\r
1748 InitAppData(LPSTR lpCmdLine)\r
1749 {\r
1750   int i, j;\r
1751   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1752   char *dummy, *p;\r
1753 \r
1754   programName = szAppName;\r
1755 \r
1756   /* Initialize to defaults */\r
1757   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1758   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1759   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1760   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1761   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1762   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1763   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1764   SetDefaultTextAttribs();\r
1765   SetDefaultSounds();\r
1766   appData.movesPerSession = MOVES_PER_SESSION;\r
1767   appData.initString = INIT_STRING;\r
1768   appData.secondInitString = INIT_STRING;\r
1769   appData.firstComputerString = COMPUTER_STRING;\r
1770   appData.secondComputerString = COMPUTER_STRING;\r
1771   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1772   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1773   appData.firstPlaysBlack = FALSE;\r
1774   appData.noChessProgram = FALSE;\r
1775   chessProgram = FALSE;\r
1776   appData.firstHost = FIRST_HOST;\r
1777   appData.secondHost = SECOND_HOST;\r
1778   appData.firstDirectory = FIRST_DIRECTORY;\r
1779   appData.secondDirectory = SECOND_DIRECTORY;\r
1780   appData.bitmapDirectory = "";\r
1781   appData.remoteShell = REMOTE_SHELL;\r
1782   appData.remoteUser = "";\r
1783   appData.timeDelay = TIME_DELAY;\r
1784   appData.timeControl = TIME_CONTROL;\r
1785   appData.timeIncrement = TIME_INCREMENT;\r
1786   appData.icsActive = FALSE;\r
1787   appData.icsHost = "";\r
1788   appData.icsPort = ICS_PORT;\r
1789   appData.icsCommPort = ICS_COMM_PORT;\r
1790   appData.icsLogon = ICS_LOGON;\r
1791   appData.icsHelper = "";\r
1792   appData.useTelnet = FALSE;\r
1793   appData.telnetProgram = TELNET_PROGRAM;\r
1794   appData.gateway = "";\r
1795   appData.loadGameFile = "";\r
1796   appData.loadGameIndex = 0;\r
1797   appData.saveGameFile = "";\r
1798   appData.autoSaveGames = FALSE;\r
1799   appData.loadPositionFile = "";\r
1800   appData.loadPositionIndex = 1;\r
1801   appData.savePositionFile = "";\r
1802   appData.matchMode = FALSE;\r
1803   appData.matchGames = 0;\r
1804   appData.monoMode = FALSE;\r
1805   appData.debugMode = FALSE;\r
1806   appData.clockMode = TRUE;\r
1807   boardSize = (BoardSize) -1; /* determine by screen size */\r
1808   appData.Iconic = FALSE; /*unused*/\r
1809   appData.searchTime = "";\r
1810   appData.searchDepth = 0;\r
1811   appData.showCoords = FALSE;\r
1812   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1813   appData.autoCallFlag = FALSE;\r
1814   appData.flipView = FALSE;\r
1815   appData.autoFlipView = TRUE;\r
1816   appData.cmailGameName = "";\r
1817   appData.alwaysPromoteToQueen = FALSE;\r
1818   appData.oldSaveStyle = FALSE;\r
1819   appData.quietPlay = FALSE;\r
1820   appData.showThinking = FALSE;\r
1821   appData.ponderNextMove = TRUE;\r
1822   appData.periodicUpdates = TRUE;\r
1823   appData.popupExitMessage = TRUE;\r
1824   appData.popupMoveErrors = FALSE;\r
1825   appData.autoObserve = FALSE;\r
1826   appData.autoComment = FALSE;\r
1827   appData.animate = TRUE;\r
1828   appData.animSpeed = 10;\r
1829   appData.animateDragging = TRUE;\r
1830   appData.highlightLastMove = TRUE;\r
1831   appData.getMoveList = TRUE;\r
1832   appData.testLegality = TRUE;\r
1833   appData.premove = TRUE;\r
1834   appData.premoveWhite = FALSE;\r
1835   appData.premoveWhiteText = "";\r
1836   appData.premoveBlack = FALSE;\r
1837   appData.premoveBlackText = "";\r
1838   appData.icsAlarm = TRUE;\r
1839   appData.icsAlarmTime = 5000;\r
1840   appData.autoRaiseBoard = TRUE;\r
1841   appData.localLineEditing = TRUE;\r
1842   appData.colorize = TRUE;\r
1843   appData.reuseFirst = TRUE;\r
1844   appData.reuseSecond = TRUE;\r
1845   appData.blindfold = FALSE;\r
1846   appData.icsEngineAnalyze = FALSE;\r
1847   dcb.DCBlength = sizeof(DCB);\r
1848   dcb.BaudRate = 9600;\r
1849   dcb.fBinary = TRUE;\r
1850   dcb.fParity = FALSE;\r
1851   dcb.fOutxCtsFlow = FALSE;\r
1852   dcb.fOutxDsrFlow = FALSE;\r
1853   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1854   dcb.fDsrSensitivity = FALSE;\r
1855   dcb.fTXContinueOnXoff = TRUE;\r
1856   dcb.fOutX = FALSE;\r
1857   dcb.fInX = FALSE;\r
1858   dcb.fNull = FALSE;\r
1859   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1860   dcb.fAbortOnError = FALSE;\r
1861   /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */\r
1862   #if (defined(_MSC_VER) && _MSC_VER <= 1200) \r
1863         //dcb.wReserved = 0;\r
1864   #else\r
1865     dcb.wReserved = 0;\r
1866   #endif\r
1867   dcb.ByteSize = 7;\r
1868   dcb.Parity = SPACEPARITY;\r
1869   dcb.StopBits = ONESTOPBIT;\r
1870   settingsFileName = SETTINGS_FILE;\r
1871   saveSettingsOnExit = TRUE;\r
1872   boardX = CW_USEDEFAULT;\r
1873   boardY = CW_USEDEFAULT;\r
1874   consoleX = CW_USEDEFAULT; \r
1875   consoleY = CW_USEDEFAULT; \r
1876   consoleW = CW_USEDEFAULT;\r
1877   consoleH = CW_USEDEFAULT;\r
1878   analysisX = CW_USEDEFAULT; \r
1879   analysisY = CW_USEDEFAULT; \r
1880   analysisW = CW_USEDEFAULT;\r
1881   analysisH = CW_USEDEFAULT;\r
1882   commentX = CW_USEDEFAULT; \r
1883   commentY = CW_USEDEFAULT; \r
1884   commentW = CW_USEDEFAULT;\r
1885   commentH = CW_USEDEFAULT;\r
1886   editTagsX = CW_USEDEFAULT; \r
1887   editTagsY = CW_USEDEFAULT; \r
1888   editTagsW = CW_USEDEFAULT;\r
1889   editTagsH = CW_USEDEFAULT;\r
1890   gameListX = CW_USEDEFAULT; \r
1891   gameListY = CW_USEDEFAULT; \r
1892   gameListW = CW_USEDEFAULT;\r
1893   gameListH = CW_USEDEFAULT;\r
1894   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1895   icsNames = ICS_NAMES;\r
1896   firstChessProgramNames = FCP_NAMES;\r
1897   secondChessProgramNames = SCP_NAMES;\r
1898   appData.initialMode = "";\r
1899   appData.variant = "normal";\r
1900   appData.firstProtocolVersion = PROTOVER;\r
1901   appData.secondProtocolVersion = PROTOVER;\r
1902   appData.showButtonBar = TRUE;\r
1903 \r
1904    /* [AS] New properties (see comments in header file) */\r
1905   appData.firstScoreIsAbsolute = FALSE;\r
1906   appData.secondScoreIsAbsolute = FALSE;\r
1907   appData.saveExtendedInfoInPGN = FALSE;\r
1908   appData.hideThinkingFromHuman = FALSE;\r
1909   appData.liteBackTextureFile = "";\r
1910   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1911   appData.darkBackTextureFile = "";\r
1912   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1913   appData.renderPiecesWithFont = "";\r
1914   appData.fontToPieceTable = "";\r
1915   appData.fontBackColorWhite = 0;\r
1916   appData.fontForeColorWhite = 0;\r
1917   appData.fontBackColorBlack = 0;\r
1918   appData.fontForeColorBlack = 0;\r
1919   appData.fontPieceSize = 80;\r
1920   appData.overrideLineGap = 1;\r
1921   appData.adjudicateLossThreshold = 0;\r
1922   appData.delayBeforeQuit = 0;\r
1923   appData.delayAfterQuit = 0;\r
1924   appData.nameOfDebugFile = "winboard.debug";\r
1925   appData.pgnEventHeader = "Computer Chess Game";\r
1926   appData.defaultFrcPosition = -1;\r
1927   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1928   appData.saveOutOfBookInfo = TRUE;\r
1929   appData.showEvalInMoveHistory = TRUE;\r
1930   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1931   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1932   appData.highlightMoveWithArrow = FALSE;\r
1933   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1934   appData.useStickyWindows = TRUE;\r
1935   appData.adjudicateDrawMoves = 0;\r
1936   appData.autoDisplayComment = TRUE;\r
1937   appData.autoDisplayTags = TRUE;\r
1938   appData.firstIsUCI = FALSE;\r
1939   appData.secondIsUCI = FALSE;\r
1940   appData.firstHasOwnBookUCI = TRUE;\r
1941   appData.secondHasOwnBookUCI = TRUE;\r
1942   appData.polyglotDir = "";\r
1943   appData.usePolyglotBook = FALSE;\r
1944   appData.polyglotBook = "";\r
1945   appData.defaultHashSize = 64;\r
1946   appData.defaultCacheSizeEGTB = 4;\r
1947   appData.defaultPathEGTB = "c:\\egtb";\r
1948   appData.firstOptions = "";\r
1949   appData.secondOptions = "";\r
1950 \r
1951   InitWindowPlacement( &wpMoveHistory );\r
1952   InitWindowPlacement( &wpEvalGraph );\r
1953   InitWindowPlacement( &wpEngineOutput );\r
1954 \r
1955   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1956   appData.NrFiles      = -1;\r
1957   appData.NrRanks      = -1;\r
1958   appData.holdingsSize = -1;\r
1959   appData.testClaims   = FALSE;\r
1960   appData.checkMates   = FALSE;\r
1961   appData.materialDraws= FALSE;\r
1962   appData.trivialDraws = FALSE;\r
1963   appData.ruleMoves    = 51;\r
1964   appData.drawRepeats  = 6;\r
1965   appData.matchPause   = 10000;\r
1966   appData.alphaRank    = FALSE;\r
1967   appData.allWhite     = FALSE;\r
1968   appData.upsideDown   = FALSE;\r
1969   appData.serverPause  = 15;\r
1970   appData.serverMovesName   = NULL;\r
1971   appData.suppressLoadMoves = FALSE;\r
1972   appData.firstTimeOdds  = 1;\r
1973   appData.secondTimeOdds = 1;\r
1974   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1975   appData.secondAccumulateTC = 1;\r
1976   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1977   appData.secondNPS = -1;\r
1978   appData.engineComments = 1;\r
1979   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1980   appData.egtFormats = "";\r
1981 \r
1982 #ifdef ZIPPY\r
1983   appData.zippyTalk = ZIPPY_TALK;\r
1984   appData.zippyPlay = ZIPPY_PLAY;\r
1985   appData.zippyLines = ZIPPY_LINES;\r
1986   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1987   appData.zippyPassword = ZIPPY_PASSWORD;\r
1988   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1989   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1990   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1991   appData.zippyUseI = ZIPPY_USE_I;\r
1992   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1993   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1994   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1995   appData.zippyGameStart = ZIPPY_GAME_START;\r
1996   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1997   appData.zippyAbort = ZIPPY_ABORT;\r
1998   appData.zippyVariants = ZIPPY_VARIANTS;\r
1999   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2000   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2001 #endif\r
2002 \r
2003   /* Point font array elements to structures and\r
2004      parse default font names */\r
2005   for (i=0; i<NUM_FONTS; i++) {\r
2006     for (j=0; j<NUM_SIZES; j++) {\r
2007       font[j][i] = &fontRec[j][i];\r
2008       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2009     }\r
2010   }\r
2011   \r
2012   /* Parse default settings file if any */\r
2013   if (ParseSettingsFile(settingsFileName, buf)) {\r
2014     settingsFileName = strdup(buf);\r
2015   }\r
2016 \r
2017   /* Parse command line */\r
2018   ParseArgs(StringGet, &lpCmdLine);\r
2019 \r
2020   /* [HGM] make sure board size is acceptable */\r
2021   if(appData.NrFiles > BOARD_SIZE ||\r
2022      appData.NrRanks > BOARD_SIZE   )\r
2023       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2024 \r
2025   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2026    * with options from the command line, we now make an even higher priority\r
2027    * overrule by WB options attached to the engine command line. This so that\r
2028    * tournament managers can use WB options (such as /timeOdds) that follow\r
2029    * the engines.\r
2030    */\r
2031   if(appData.firstChessProgram != NULL) {\r
2032       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2033       static char *f = "first";\r
2034       char buf[MSG_SIZ], *q = buf;\r
2035       if(p != NULL) { // engine command line contains WinBoard options\r
2036           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2037           ParseArgs(StringGet, &q);\r
2038           p[-1] = 0; // cut them offengine command line\r
2039       }\r
2040   }\r
2041   // now do same for second chess program\r
2042   if(appData.secondChessProgram != NULL) {\r
2043       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2044       static char *s = "second";\r
2045       char buf[MSG_SIZ], *q = buf;\r
2046       if(p != NULL) { // engine command line contains WinBoard options\r
2047           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2048           ParseArgs(StringGet, &q);\r
2049           p[-1] = 0; // cut them offengine command line\r
2050       }\r
2051   }\r
2052 \r
2053 \r
2054   /* Propagate options that affect others */\r
2055   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2056   if (appData.icsActive || appData.noChessProgram) {\r
2057      chessProgram = FALSE;  /* not local chess program mode */\r
2058   }\r
2059 \r
2060   /* Open startup dialog if needed */\r
2061   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2062       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2063       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2064                         *appData.secondChessProgram == NULLCHAR))) {\r
2065     FARPROC lpProc;\r
2066     \r
2067     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2068     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2069     FreeProcInstance(lpProc);\r
2070   }\r
2071 \r
2072   /* Make sure save files land in the right (?) directory */\r
2073   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2074     appData.saveGameFile = strdup(buf);\r
2075   }\r
2076   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2077     appData.savePositionFile = strdup(buf);\r
2078   }\r
2079 \r
2080   /* Finish initialization for fonts and sounds */\r
2081   for (i=0; i<NUM_FONTS; i++) {\r
2082     for (j=0; j<NUM_SIZES; j++) {\r
2083       CreateFontInMF(font[j][i]);\r
2084     }\r
2085   }\r
2086   /* xboard, and older WinBoards, controlled the move sound with the\r
2087      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2088      always turn the option on (so that the backend will call us),\r
2089      then let the user turn the sound off by setting it to silence if\r
2090      desired.  To accommodate old winboard.ini files saved by old\r
2091      versions of WinBoard, we also turn off the sound if the option\r
2092      was initially set to false. */\r
2093   if (!appData.ringBellAfterMoves) {\r
2094     sounds[(int)SoundMove].name = strdup("");\r
2095     appData.ringBellAfterMoves = TRUE;\r
2096   }\r
2097   GetCurrentDirectory(MSG_SIZ, currDir);\r
2098   SetCurrentDirectory(installDir);\r
2099   LoadAllSounds();\r
2100   SetCurrentDirectory(currDir);\r
2101 \r
2102   p = icsTextMenuString;\r
2103   if (p[0] == '@') {\r
2104     FILE* f = fopen(p + 1, "r");\r
2105     if (f == NULL) {\r
2106       DisplayFatalError(p + 1, errno, 2);\r
2107       return;\r
2108     }\r
2109     i = fread(buf, 1, sizeof(buf)-1, f);\r
2110     fclose(f);\r
2111     buf[i] = NULLCHAR;\r
2112     p = buf;\r
2113   }\r
2114   ParseIcsTextMenu(strdup(p));\r
2115 }\r
2116 \r
2117 \r
2118 VOID\r
2119 InitMenuChecks()\r
2120 {\r
2121   HMENU hmenu = GetMenu(hwndMain);\r
2122 \r
2123   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2124                         MF_BYCOMMAND|((appData.icsActive &&\r
2125                                        *appData.icsCommPort != NULLCHAR) ?\r
2126                                       MF_ENABLED : MF_GRAYED));\r
2127   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2128                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2129                                      MF_CHECKED : MF_UNCHECKED));\r
2130 }\r
2131 \r
2132 \r
2133 VOID\r
2134 SaveSettings(char* name)\r
2135 {\r
2136   FILE *f;\r
2137   ArgDescriptor *ad;\r
2138   WINDOWPLACEMENT wp;\r
2139   char dir[MSG_SIZ];\r
2140 \r
2141   if (!hwndMain) return;\r
2142 \r
2143   GetCurrentDirectory(MSG_SIZ, dir);\r
2144   SetCurrentDirectory(installDir);\r
2145   f = fopen(name, "w");\r
2146   SetCurrentDirectory(dir);\r
2147   if (f == NULL) {\r
2148     DisplayError(name, errno);\r
2149     return;\r
2150   }\r
2151   fprintf(f, ";\n");\r
2152   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2153   fprintf(f, ";\n");\r
2154   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2155   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2156   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2157   fprintf(f, ";\n");\r
2158 \r
2159   wp.length = sizeof(WINDOWPLACEMENT);\r
2160   GetWindowPlacement(hwndMain, &wp);\r
2161   boardX = wp.rcNormalPosition.left;\r
2162   boardY = wp.rcNormalPosition.top;\r
2163 \r
2164   if (hwndConsole) {\r
2165     GetWindowPlacement(hwndConsole, &wp);\r
2166     consoleX = wp.rcNormalPosition.left;\r
2167     consoleY = wp.rcNormalPosition.top;\r
2168     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2169     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2170   }\r
2171 \r
2172   if (analysisDialog) {\r
2173     GetWindowPlacement(analysisDialog, &wp);\r
2174     analysisX = wp.rcNormalPosition.left;\r
2175     analysisY = wp.rcNormalPosition.top;\r
2176     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2177     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2178   }\r
2179 \r
2180   if (commentDialog) {\r
2181     GetWindowPlacement(commentDialog, &wp);\r
2182     commentX = wp.rcNormalPosition.left;\r
2183     commentY = wp.rcNormalPosition.top;\r
2184     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2185     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2186   }\r
2187 \r
2188   if (editTagsDialog) {\r
2189     GetWindowPlacement(editTagsDialog, &wp);\r
2190     editTagsX = wp.rcNormalPosition.left;\r
2191     editTagsY = wp.rcNormalPosition.top;\r
2192     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2193     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2194   }\r
2195 \r
2196   if (gameListDialog) {\r
2197     GetWindowPlacement(gameListDialog, &wp);\r
2198     gameListX = wp.rcNormalPosition.left;\r
2199     gameListY = wp.rcNormalPosition.top;\r
2200     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2201     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2202   }\r
2203 \r
2204   /* [AS] Move history */\r
2205   wpMoveHistory.visible = MoveHistoryIsUp();\r
2206   \r
2207   if( moveHistoryDialog ) {\r
2208     GetWindowPlacement(moveHistoryDialog, &wp);\r
2209     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2210     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2211     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2212     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2213   }\r
2214 \r
2215   /* [AS] Eval graph */\r
2216   wpEvalGraph.visible = EvalGraphIsUp();\r
2217 \r
2218   if( evalGraphDialog ) {\r
2219     GetWindowPlacement(evalGraphDialog, &wp);\r
2220     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2221     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2222     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2223     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2224   }\r
2225 \r
2226   /* [AS] Engine output */\r
2227   wpEngineOutput.visible = EngineOutputIsUp();\r
2228 \r
2229   if( engineOutputDialog ) {\r
2230     GetWindowPlacement(engineOutputDialog, &wp);\r
2231     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2232     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2233     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2234     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2235   }\r
2236 \r
2237   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2238     if (!ad->save) continue;\r
2239     switch (ad->argType) {\r
2240     case ArgString:\r
2241       {\r
2242         char *p = *(char **)ad->argLoc;\r
2243         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2244           /* Quote multiline values or \-containing values\r
2245              with { } if possible */\r
2246           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2247         } else {\r
2248           /* Else quote with " " */\r
2249           fprintf(f, "/%s=\"", ad->argName);\r
2250           while (*p) {\r
2251             if (*p == '\n') fprintf(f, "\n");\r
2252             else if (*p == '\r') fprintf(f, "\\r");\r
2253             else if (*p == '\t') fprintf(f, "\\t");\r
2254             else if (*p == '\b') fprintf(f, "\\b");\r
2255             else if (*p == '\f') fprintf(f, "\\f");\r
2256             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2257             else if (*p == '\"') fprintf(f, "\\\"");\r
2258             else if (*p == '\\') fprintf(f, "\\\\");\r
2259             else putc(*p, f);\r
2260             p++;\r
2261           }\r
2262           fprintf(f, "\"\n");\r
2263         }\r
2264       }\r
2265       break;\r
2266     case ArgInt:\r
2267       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2268       break;\r
2269     case ArgFloat:\r
2270       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2271       break;\r
2272     case ArgBoolean:\r
2273       fprintf(f, "/%s=%s\n", ad->argName, \r
2274         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2275       break;\r
2276     case ArgTrue:\r
2277       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2278       break;\r
2279     case ArgFalse:\r
2280       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2281       break;\r
2282     case ArgColor:\r
2283       {\r
2284         COLORREF color = *(COLORREF *)ad->argLoc;\r
2285         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2286           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2287       }\r
2288       break;\r
2289     case ArgAttribs:\r
2290       {\r
2291         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2292         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2293           (ta->effects & CFE_BOLD) ? "b" : "",\r
2294           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2295           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2296           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2297           (ta->effects) ? " " : "",\r
2298           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2299       }\r
2300       break;\r
2301     case ArgFilename:\r
2302       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2303         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2304       } else {\r
2305         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2306       }\r
2307       break;\r
2308     case ArgBoardSize:\r
2309       fprintf(f, "/%s=%s\n", ad->argName,\r
2310               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2311       break;\r
2312     case ArgFont:\r
2313       {\r
2314         int bs;\r
2315         for (bs=0; bs<NUM_SIZES; bs++) {\r
2316           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2317           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2318           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2319             ad->argName, mfp->faceName, mfp->pointSize,\r
2320             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2321             mfp->bold ? "b" : "",\r
2322             mfp->italic ? "i" : "",\r
2323             mfp->underline ? "u" : "",\r
2324             mfp->strikeout ? "s" : "");\r
2325         }\r
2326       }\r
2327       break;\r
2328     case ArgCommSettings:\r
2329       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2330     }\r
2331   }\r
2332   fclose(f);\r
2333 }\r
2334 \r
2335 \r
2336 \r
2337 /*---------------------------------------------------------------------------*\\r
2338  *\r
2339  * GDI board drawing routines\r
2340  *\r
2341 \*---------------------------------------------------------------------------*/\r
2342 \r
2343 /* [AS] Draw square using background texture */\r
2344 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2345 {\r
2346     XFORM   x;\r
2347 \r
2348     if( mode == 0 ) {\r
2349         return; /* Should never happen! */\r
2350     }\r
2351 \r
2352     SetGraphicsMode( dst, GM_ADVANCED );\r
2353 \r
2354     switch( mode ) {\r
2355     case 1:\r
2356         /* Identity */\r
2357         break;\r
2358     case 2:\r
2359         /* X reflection */\r
2360         x.eM11 = -1.0;\r
2361         x.eM12 = 0;\r
2362         x.eM21 = 0;\r
2363         x.eM22 = 1.0;\r
2364         x.eDx = (FLOAT) dw + dx - 1;\r
2365         x.eDy = 0;\r
2366         dx = 0;\r
2367         SetWorldTransform( dst, &x );\r
2368         break;\r
2369     case 3:\r
2370         /* Y reflection */\r
2371         x.eM11 = 1.0;\r
2372         x.eM12 = 0;\r
2373         x.eM21 = 0;\r
2374         x.eM22 = -1.0;\r
2375         x.eDx = 0;\r
2376         x.eDy = (FLOAT) dh + dy - 1;\r
2377         dy = 0;\r
2378         SetWorldTransform( dst, &x );\r
2379         break;\r
2380     case 4:\r
2381         /* X/Y flip */\r
2382         x.eM11 = 0;\r
2383         x.eM12 = 1.0;\r
2384         x.eM21 = 1.0;\r
2385         x.eM22 = 0;\r
2386         x.eDx = (FLOAT) dx;\r
2387         x.eDy = (FLOAT) dy;\r
2388         dx = 0;\r
2389         dy = 0;\r
2390         SetWorldTransform( dst, &x );\r
2391         break;\r
2392     }\r
2393 \r
2394     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2395 \r
2396     x.eM11 = 1.0;\r
2397     x.eM12 = 0;\r
2398     x.eM21 = 0;\r
2399     x.eM22 = 1.0;\r
2400     x.eDx = 0;\r
2401     x.eDy = 0;\r
2402     SetWorldTransform( dst, &x );\r
2403 \r
2404     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2405 }\r
2406 \r
2407 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2408 enum {\r
2409     PM_WP = (int) WhitePawn, \r
2410     PM_WN = (int) WhiteKnight, \r
2411     PM_WB = (int) WhiteBishop, \r
2412     PM_WR = (int) WhiteRook, \r
2413     PM_WQ = (int) WhiteQueen, \r
2414     PM_WF = (int) WhiteFerz, \r
2415     PM_WW = (int) WhiteWazir, \r
2416     PM_WE = (int) WhiteAlfil, \r
2417     PM_WM = (int) WhiteMan, \r
2418     PM_WO = (int) WhiteCannon, \r
2419     PM_WU = (int) WhiteUnicorn, \r
2420     PM_WH = (int) WhiteNightrider, \r
2421     PM_WA = (int) WhiteAngel, \r
2422     PM_WC = (int) WhiteMarshall, \r
2423     PM_WAB = (int) WhiteCardinal, \r
2424     PM_WD = (int) WhiteDragon, \r
2425     PM_WL = (int) WhiteLance, \r
2426     PM_WS = (int) WhiteCobra, \r
2427     PM_WV = (int) WhiteFalcon, \r
2428     PM_WSG = (int) WhiteSilver, \r
2429     PM_WG = (int) WhiteGrasshopper, \r
2430     PM_WK = (int) WhiteKing,\r
2431     PM_BP = (int) BlackPawn, \r
2432     PM_BN = (int) BlackKnight, \r
2433     PM_BB = (int) BlackBishop, \r
2434     PM_BR = (int) BlackRook, \r
2435     PM_BQ = (int) BlackQueen, \r
2436     PM_BF = (int) BlackFerz, \r
2437     PM_BW = (int) BlackWazir, \r
2438     PM_BE = (int) BlackAlfil, \r
2439     PM_BM = (int) BlackMan,\r
2440     PM_BO = (int) BlackCannon, \r
2441     PM_BU = (int) BlackUnicorn, \r
2442     PM_BH = (int) BlackNightrider, \r
2443     PM_BA = (int) BlackAngel, \r
2444     PM_BC = (int) BlackMarshall, \r
2445     PM_BG = (int) BlackGrasshopper, \r
2446     PM_BAB = (int) BlackCardinal,\r
2447     PM_BD = (int) BlackDragon,\r
2448     PM_BL = (int) BlackLance,\r
2449     PM_BS = (int) BlackCobra,\r
2450     PM_BV = (int) BlackFalcon,\r
2451     PM_BSG = (int) BlackSilver,\r
2452     PM_BK = (int) BlackKing\r
2453 };\r
2454 \r
2455 static HFONT hPieceFont = NULL;\r
2456 static HBITMAP hPieceMask[(int) EmptySquare];\r
2457 static HBITMAP hPieceFace[(int) EmptySquare];\r
2458 static int fontBitmapSquareSize = 0;\r
2459 static char pieceToFontChar[(int) EmptySquare] =\r
2460                               { 'p', 'n', 'b', 'r', 'q', \r
2461                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2462                       'k', 'o', 'm', 'v', 't', 'w', \r
2463                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2464                                                               'l' };\r
2465 \r
2466 extern BOOL SetCharTable( char *table, const char * map );\r
2467 /* [HGM] moved to backend.c */\r
2468 \r
2469 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2470 {\r
2471     HBRUSH hbrush;\r
2472     BYTE r1 = GetRValue( color );\r
2473     BYTE g1 = GetGValue( color );\r
2474     BYTE b1 = GetBValue( color );\r
2475     BYTE r2 = r1 / 2;\r
2476     BYTE g2 = g1 / 2;\r
2477     BYTE b2 = b1 / 2;\r
2478     RECT rc;\r
2479 \r
2480     /* Create a uniform background first */\r
2481     hbrush = CreateSolidBrush( color );\r
2482     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2483     FillRect( hdc, &rc, hbrush );\r
2484     DeleteObject( hbrush );\r
2485     \r
2486     if( mode == 1 ) {\r
2487         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2488         int steps = squareSize / 2;\r
2489         int i;\r
2490 \r
2491         for( i=0; i<steps; i++ ) {\r
2492             BYTE r = r1 - (r1-r2) * i / steps;\r
2493             BYTE g = g1 - (g1-g2) * i / steps;\r
2494             BYTE b = b1 - (b1-b2) * i / steps;\r
2495 \r
2496             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2497             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2498             FillRect( hdc, &rc, hbrush );\r
2499             DeleteObject(hbrush);\r
2500         }\r
2501     }\r
2502     else if( mode == 2 ) {\r
2503         /* Diagonal gradient, good more or less for every piece */\r
2504         POINT triangle[3];\r
2505         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2506         HBRUSH hbrush_old;\r
2507         int steps = squareSize;\r
2508         int i;\r
2509 \r
2510         triangle[0].x = squareSize - steps;\r
2511         triangle[0].y = squareSize;\r
2512         triangle[1].x = squareSize;\r
2513         triangle[1].y = squareSize;\r
2514         triangle[2].x = squareSize;\r
2515         triangle[2].y = squareSize - steps;\r
2516 \r
2517         for( i=0; i<steps; i++ ) {\r
2518             BYTE r = r1 - (r1-r2) * i / steps;\r
2519             BYTE g = g1 - (g1-g2) * i / steps;\r
2520             BYTE b = b1 - (b1-b2) * i / steps;\r
2521 \r
2522             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2523             hbrush_old = SelectObject( hdc, hbrush );\r
2524             Polygon( hdc, triangle, 3 );\r
2525             SelectObject( hdc, hbrush_old );\r
2526             DeleteObject(hbrush);\r
2527             triangle[0].x++;\r
2528             triangle[2].y++;\r
2529         }\r
2530 \r
2531         SelectObject( hdc, hpen );\r
2532     }\r
2533 }\r
2534 \r
2535 /*\r
2536     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2537     seems to work ok. The main problem here is to find the "inside" of a chess\r
2538     piece: follow the steps as explained below.\r
2539 */\r
2540 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2541 {\r
2542     HBITMAP hbm;\r
2543     HBITMAP hbm_old;\r
2544     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2545     RECT rc;\r
2546     SIZE sz;\r
2547     POINT pt;\r
2548     int backColor = whitePieceColor; \r
2549     int foreColor = blackPieceColor;\r
2550     \r
2551     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2552         backColor = appData.fontBackColorWhite;\r
2553         foreColor = appData.fontForeColorWhite;\r
2554     }\r
2555     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2556         backColor = appData.fontBackColorBlack;\r
2557         foreColor = appData.fontForeColorBlack;\r
2558     }\r
2559 \r
2560     /* Mask */\r
2561     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2562 \r
2563     hbm_old = SelectObject( hdc, hbm );\r
2564 \r
2565     rc.left = 0;\r
2566     rc.top = 0;\r
2567     rc.right = squareSize;\r
2568     rc.bottom = squareSize;\r
2569 \r
2570     /* Step 1: background is now black */\r
2571     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2572 \r
2573     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2574 \r
2575     pt.x = (squareSize - sz.cx) / 2;\r
2576     pt.y = (squareSize - sz.cy) / 2;\r
2577 \r
2578     SetBkMode( hdc, TRANSPARENT );\r
2579     SetTextColor( hdc, chroma );\r
2580     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2581     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2582 \r
2583     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2584     /* Step 3: the area outside the piece is filled with white */\r
2585 //    FloodFill( hdc, 0, 0, chroma );\r
2586     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2587     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2588     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2589     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2590     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2591     /* \r
2592         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2593         but if the start point is not inside the piece we're lost!\r
2594         There should be a better way to do this... if we could create a region or path\r
2595         from the fill operation we would be fine for example.\r
2596     */\r
2597 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2598     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2599 \r
2600     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2601         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2602         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2603 \r
2604         SelectObject( dc2, bm2 );\r
2605         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2606         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2607         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2608         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2609         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2610 \r
2611         DeleteDC( dc2 );\r
2612         DeleteObject( bm2 );\r
2613     }\r
2614 \r
2615     SetTextColor( hdc, 0 );\r
2616     /* \r
2617         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2618         draw the piece again in black for safety.\r
2619     */\r
2620     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2621 \r
2622     SelectObject( hdc, hbm_old );\r
2623 \r
2624     if( hPieceMask[index] != NULL ) {\r
2625         DeleteObject( hPieceMask[index] );\r
2626     }\r
2627 \r
2628     hPieceMask[index] = hbm;\r
2629 \r
2630     /* Face */\r
2631     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2632 \r
2633     SelectObject( hdc, hbm );\r
2634 \r
2635     {\r
2636         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2637         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2638         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2639 \r
2640         SelectObject( dc1, hPieceMask[index] );\r
2641         SelectObject( dc2, bm2 );\r
2642         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2643         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2644         \r
2645         /* \r
2646             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2647             the piece background and deletes (makes transparent) the rest.\r
2648             Thanks to that mask, we are free to paint the background with the greates\r
2649             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2650             We use this, to make gradients and give the pieces a "roundish" look.\r
2651         */\r
2652         SetPieceBackground( hdc, backColor, 2 );\r
2653         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2654 \r
2655         DeleteDC( dc2 );\r
2656         DeleteDC( dc1 );\r
2657         DeleteObject( bm2 );\r
2658     }\r
2659 \r
2660     SetTextColor( hdc, foreColor );\r
2661     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2662 \r
2663     SelectObject( hdc, hbm_old );\r
2664 \r
2665     if( hPieceFace[index] != NULL ) {\r
2666         DeleteObject( hPieceFace[index] );\r
2667     }\r
2668 \r
2669     hPieceFace[index] = hbm;\r
2670 }\r
2671 \r
2672 static int TranslatePieceToFontPiece( int piece )\r
2673 {\r
2674     switch( piece ) {\r
2675     case BlackPawn:\r
2676         return PM_BP;\r
2677     case BlackKnight:\r
2678         return PM_BN;\r
2679     case BlackBishop:\r
2680         return PM_BB;\r
2681     case BlackRook:\r
2682         return PM_BR;\r
2683     case BlackQueen:\r
2684         return PM_BQ;\r
2685     case BlackKing:\r
2686         return PM_BK;\r
2687     case WhitePawn:\r
2688         return PM_WP;\r
2689     case WhiteKnight:\r
2690         return PM_WN;\r
2691     case WhiteBishop:\r
2692         return PM_WB;\r
2693     case WhiteRook:\r
2694         return PM_WR;\r
2695     case WhiteQueen:\r
2696         return PM_WQ;\r
2697     case WhiteKing:\r
2698         return PM_WK;\r
2699 \r
2700     case BlackAngel:\r
2701         return PM_BA;\r
2702     case BlackMarshall:\r
2703         return PM_BC;\r
2704     case BlackFerz:\r
2705         return PM_BF;\r
2706     case BlackNightrider:\r
2707         return PM_BH;\r
2708     case BlackAlfil:\r
2709         return PM_BE;\r
2710     case BlackWazir:\r
2711         return PM_BW;\r
2712     case BlackUnicorn:\r
2713         return PM_BU;\r
2714     case BlackCannon:\r
2715         return PM_BO;\r
2716     case BlackGrasshopper:\r
2717         return PM_BG;\r
2718     case BlackMan:\r
2719         return PM_BM;\r
2720     case BlackSilver:\r
2721         return PM_BSG;\r
2722     case BlackLance:\r
2723         return PM_BL;\r
2724     case BlackFalcon:\r
2725         return PM_BV;\r
2726     case BlackCobra:\r
2727         return PM_BS;\r
2728     case BlackCardinal:\r
2729         return PM_BAB;\r
2730     case BlackDragon:\r
2731         return PM_BD;\r
2732 \r
2733     case WhiteAngel:\r
2734         return PM_WA;\r
2735     case WhiteMarshall:\r
2736         return PM_WC;\r
2737     case WhiteFerz:\r
2738         return PM_WF;\r
2739     case WhiteNightrider:\r
2740         return PM_WH;\r
2741     case WhiteAlfil:\r
2742         return PM_WE;\r
2743     case WhiteWazir:\r
2744         return PM_WW;\r
2745     case WhiteUnicorn:\r
2746         return PM_WU;\r
2747     case WhiteCannon:\r
2748         return PM_WO;\r
2749     case WhiteGrasshopper:\r
2750         return PM_WG;\r
2751     case WhiteMan:\r
2752         return PM_WM;\r
2753     case WhiteSilver:\r
2754         return PM_WSG;\r
2755     case WhiteLance:\r
2756         return PM_WL;\r
2757     case WhiteFalcon:\r
2758         return PM_WV;\r
2759     case WhiteCobra:\r
2760         return PM_WS;\r
2761     case WhiteCardinal:\r
2762         return PM_WAB;\r
2763     case WhiteDragon:\r
2764         return PM_WD;\r
2765     }\r
2766 \r
2767     return 0;\r
2768 }\r
2769 \r
2770 void CreatePiecesFromFont()\r
2771 {\r
2772     LOGFONT lf;\r
2773     HDC hdc_window = NULL;\r
2774     HDC hdc = NULL;\r
2775     HFONT hfont_old;\r
2776     int fontHeight;\r
2777     int i;\r
2778 \r
2779     if( fontBitmapSquareSize < 0 ) {\r
2780         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2781         return;\r
2782     }\r
2783 \r
2784     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2785         fontBitmapSquareSize = -1;\r
2786         return;\r
2787     }\r
2788 \r
2789     if( fontBitmapSquareSize != squareSize ) {\r
2790         hdc_window = GetDC( hwndMain );\r
2791         hdc = CreateCompatibleDC( hdc_window );\r
2792 \r
2793         if( hPieceFont != NULL ) {\r
2794             DeleteObject( hPieceFont );\r
2795         }\r
2796         else {\r
2797             for( i=0; i<=(int)BlackKing; i++ ) {\r
2798                 hPieceMask[i] = NULL;\r
2799                 hPieceFace[i] = NULL;\r
2800             }\r
2801         }\r
2802 \r
2803         fontHeight = 75;\r
2804 \r
2805         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2806             fontHeight = appData.fontPieceSize;\r
2807         }\r
2808 \r
2809         fontHeight = (fontHeight * squareSize) / 100;\r
2810 \r
2811         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2812         lf.lfWidth = 0;\r
2813         lf.lfEscapement = 0;\r
2814         lf.lfOrientation = 0;\r
2815         lf.lfWeight = FW_NORMAL;\r
2816         lf.lfItalic = 0;\r
2817         lf.lfUnderline = 0;\r
2818         lf.lfStrikeOut = 0;\r
2819         lf.lfCharSet = DEFAULT_CHARSET;\r
2820         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2821         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2822         lf.lfQuality = PROOF_QUALITY;\r
2823         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2824         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2825         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2826 \r
2827         hPieceFont = CreateFontIndirect( &lf );\r
2828 \r
2829         if( hPieceFont == NULL ) {\r
2830             fontBitmapSquareSize = -2;\r
2831         }\r
2832         else {\r
2833             /* Setup font-to-piece character table */\r
2834             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2835                 /* No (or wrong) global settings, try to detect the font */\r
2836                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2837                     /* Alpha */\r
2838                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2839                 }\r
2840                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2841                     /* DiagramTT* family */\r
2842                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2843                 }\r
2844                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2845                     /* Fairy symbols */\r
2846                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2847                 }\r
2848                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2849                     /* Good Companion (Some characters get warped as literal :-( */\r
2850                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2851                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2852                     SetCharTable(pieceToFontChar, s);\r
2853                 }\r
2854                 else {\r
2855                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2856                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2857                 }\r
2858             }\r
2859 \r
2860             /* Create bitmaps */\r
2861             hfont_old = SelectObject( hdc, hPieceFont );\r
2862 #if 0\r
2863             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2864             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2865             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2866             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2867             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2868             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2869             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2870             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2871             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2872             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2873             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2874             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2875 \r
2876             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2877             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2878             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2879             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2880             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2881             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2882             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2883             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2884             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2885             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2897             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2898             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2899             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2900             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2901             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2902             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2903             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2904             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2905             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2906             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2907             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2908 #else\r
2909             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2910                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2911                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2912 #endif\r
2913             SelectObject( hdc, hfont_old );\r
2914 \r
2915             fontBitmapSquareSize = squareSize;\r
2916         }\r
2917     }\r
2918 \r
2919     if( hdc != NULL ) {\r
2920         DeleteDC( hdc );\r
2921     }\r
2922 \r
2923     if( hdc_window != NULL ) {\r
2924         ReleaseDC( hwndMain, hdc_window );\r
2925     }\r
2926 }\r
2927 \r
2928 HBITMAP\r
2929 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2930 {\r
2931   char name[128];\r
2932 \r
2933   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2934   if (gameInfo.event &&\r
2935       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2936       strcmp(name, "k80s") == 0) {\r
2937     strcpy(name, "tim");\r
2938   }\r
2939   return LoadBitmap(hinst, name);\r
2940 }\r
2941 \r
2942 \r
2943 /* Insert a color into the program's logical palette\r
2944    structure.  This code assumes the given color is\r
2945    the result of the RGB or PALETTERGB macro, and it\r
2946    knows how those macros work (which is documented).\r
2947 */\r
2948 VOID\r
2949 InsertInPalette(COLORREF color)\r
2950 {\r
2951   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2952 \r
2953   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2954     DisplayFatalError("Too many colors", 0, 1);\r
2955     pLogPal->palNumEntries--;\r
2956     return;\r
2957   }\r
2958 \r
2959   pe->peFlags = (char) 0;\r
2960   pe->peRed = (char) (0xFF & color);\r
2961   pe->peGreen = (char) (0xFF & (color >> 8));\r
2962   pe->peBlue = (char) (0xFF & (color >> 16));\r
2963   return;\r
2964 }\r
2965 \r
2966 \r
2967 VOID\r
2968 InitDrawingColors()\r
2969 {\r
2970   if (pLogPal == NULL) {\r
2971     /* Allocate enough memory for a logical palette with\r
2972      * PALETTESIZE entries and set the size and version fields\r
2973      * of the logical palette structure.\r
2974      */\r
2975     pLogPal = (NPLOGPALETTE)\r
2976       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2977                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2978     pLogPal->palVersion    = 0x300;\r
2979   }\r
2980   pLogPal->palNumEntries = 0;\r
2981 \r
2982   InsertInPalette(lightSquareColor);\r
2983   InsertInPalette(darkSquareColor);\r
2984   InsertInPalette(whitePieceColor);\r
2985   InsertInPalette(blackPieceColor);\r
2986   InsertInPalette(highlightSquareColor);\r
2987   InsertInPalette(premoveHighlightColor);\r
2988 \r
2989   /*  create a logical color palette according the information\r
2990    *  in the LOGPALETTE structure.\r
2991    */\r
2992   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2993 \r
2994   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2995   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2996   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2997   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2998   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2999   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3000 \r
3001   /* [AS] Force rendering of the font-based pieces */\r
3002   if( fontBitmapSquareSize > 0 ) {\r
3003     fontBitmapSquareSize = 0;\r
3004   }\r
3005 }\r
3006 \r
3007 \r
3008 int\r
3009 BoardWidth(int boardSize, int n)\r
3010 { /* [HGM] argument n added to allow different width and height */\r
3011   int lineGap = sizeInfo[boardSize].lineGap;\r
3012 \r
3013   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3014       lineGap = appData.overrideLineGap;\r
3015   }\r
3016 \r
3017   return (n + 1) * lineGap +\r
3018           n * sizeInfo[boardSize].squareSize;\r
3019 }\r
3020 \r
3021 /* Respond to board resize by dragging edge */\r
3022 VOID\r
3023 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3024 {\r
3025   BoardSize newSize = NUM_SIZES - 1;\r
3026   static int recurse = 0;\r
3027   if (IsIconic(hwndMain)) return;\r
3028   if (recurse > 0) return;\r
3029   recurse++;\r
3030   while (newSize > 0) {\r
3031         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3032         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3033            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3034     newSize--;\r
3035   } \r
3036   boardSize = newSize;\r
3037   InitDrawingSizes(boardSize, flags);\r
3038   recurse--;\r
3039 }\r
3040 \r
3041 \r
3042 \r
3043 VOID\r
3044 InitDrawingSizes(BoardSize boardSize, int flags)\r
3045 {\r
3046   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3047   ChessSquare piece;\r
3048   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3049   HDC hdc;\r
3050   SIZE clockSize, messageSize;\r
3051   HFONT oldFont;\r
3052   char buf[MSG_SIZ];\r
3053   char *str;\r
3054   HMENU hmenu = GetMenu(hwndMain);\r
3055   RECT crect, wrect;\r
3056   int offby;\r
3057   LOGBRUSH logbrush;\r
3058 \r
3059   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3060   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3061 \r
3062   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3063   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3064 \r
3065   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3066   smallLayout = sizeInfo[boardSize].smallLayout;\r
3067   squareSize = sizeInfo[boardSize].squareSize;\r
3068   lineGap = sizeInfo[boardSize].lineGap;\r
3069   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3070 \r
3071   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3072       lineGap = appData.overrideLineGap;\r
3073   }\r
3074 \r
3075   if (tinyLayout != oldTinyLayout) {\r
3076     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3077     if (tinyLayout) {\r
3078       style &= ~WS_SYSMENU;\r
3079       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3080                  "&Minimize\tCtrl+F4");\r
3081     } else {\r
3082       style |= WS_SYSMENU;\r
3083       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3084     }\r
3085     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3086 \r
3087     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3088       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3089         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3090     }\r
3091     DrawMenuBar(hwndMain);\r
3092   }\r
3093 \r
3094   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3095   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3096 \r
3097   /* Get text area sizes */\r
3098   hdc = GetDC(hwndMain);\r
3099   if (appData.clockMode) {\r
3100     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3101   } else {\r
3102     sprintf(buf, "White");\r
3103   }\r
3104   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3105   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3106   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3107   str = "We only care about the height here";\r
3108   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3109   SelectObject(hdc, oldFont);\r
3110   ReleaseDC(hwndMain, hdc);\r
3111 \r
3112   /* Compute where everything goes */\r
3113   if(first.programLogo || second.programLogo) {\r
3114         /* [HGM] logo: if either logo is on, reserve space for it */\r
3115         logoHeight =  2*clockSize.cy;\r
3116         leftLogoRect.left   = OUTER_MARGIN;\r
3117         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3118         leftLogoRect.top    = OUTER_MARGIN;\r
3119         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3120 \r
3121         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3122         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3123         rightLogoRect.top    = OUTER_MARGIN;\r
3124         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3125 \r
3126 \r
3127     blackRect.left = leftLogoRect.right;\r
3128     blackRect.right = rightLogoRect.left;\r
3129     blackRect.top = OUTER_MARGIN;\r
3130     blackRect.bottom = blackRect.top + clockSize.cy;\r
3131 \r
3132     whiteRect.left = blackRect.left ;\r
3133     whiteRect.right = blackRect.right;\r
3134     whiteRect.top = blackRect.bottom;\r
3135     whiteRect.bottom = leftLogoRect.bottom;\r
3136   } else {\r
3137     whiteRect.left = OUTER_MARGIN;\r
3138     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3139     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3140     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3141 \r
3142     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3143     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3144     blackRect.top = whiteRect.top;\r
3145     blackRect.bottom = whiteRect.bottom;\r
3146   }\r
3147 \r
3148   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3149   if (appData.showButtonBar) {\r
3150     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3151       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3152   } else {\r
3153     messageRect.right = OUTER_MARGIN + boardWidth;\r
3154   }\r
3155   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3156   messageRect.bottom = messageRect.top + messageSize.cy;\r
3157 \r
3158   boardRect.left = OUTER_MARGIN;\r
3159   boardRect.right = boardRect.left + boardWidth;\r
3160   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3161   boardRect.bottom = boardRect.top + boardHeight;\r
3162 \r
3163   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3164   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3165   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3166   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3167   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3168     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3169   GetWindowRect(hwndMain, &wrect);\r
3170   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3171                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3172   /* compensate if menu bar wrapped */\r
3173   GetClientRect(hwndMain, &crect);\r
3174   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3175   winHeight += offby;\r
3176   switch (flags) {\r
3177   case WMSZ_TOPLEFT:\r
3178     SetWindowPos(hwndMain, NULL, \r
3179                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3180                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3181     break;\r
3182 \r
3183   case WMSZ_TOPRIGHT:\r
3184   case WMSZ_TOP:\r
3185     SetWindowPos(hwndMain, NULL, \r
3186                  wrect.left, wrect.bottom - winHeight, \r
3187                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3188     break;\r
3189 \r
3190   case WMSZ_BOTTOMLEFT:\r
3191   case WMSZ_LEFT:\r
3192     SetWindowPos(hwndMain, NULL, \r
3193                  wrect.right - winWidth, wrect.top, \r
3194                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3195     break;\r
3196 \r
3197   case WMSZ_BOTTOMRIGHT:\r
3198   case WMSZ_BOTTOM:\r
3199   case WMSZ_RIGHT:\r
3200   default:\r
3201     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3202                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3203     break;\r
3204   }\r
3205 \r
3206   hwndPause = NULL;\r
3207   for (i = 0; i < N_BUTTONS; i++) {\r
3208     if (buttonDesc[i].hwnd != NULL) {\r
3209       DestroyWindow(buttonDesc[i].hwnd);\r
3210       buttonDesc[i].hwnd = NULL;\r
3211     }\r
3212     if (appData.showButtonBar) {\r
3213       buttonDesc[i].hwnd =\r
3214         CreateWindow("BUTTON", buttonDesc[i].label,\r
3215                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3216                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3217                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3218                      (HMENU) buttonDesc[i].id,\r
3219                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3220       if (tinyLayout) {\r
3221         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3222                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3223                     MAKELPARAM(FALSE, 0));\r
3224       }\r
3225       if (buttonDesc[i].id == IDM_Pause)\r
3226         hwndPause = buttonDesc[i].hwnd;\r
3227       buttonDesc[i].wndproc = (WNDPROC)\r
3228         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3229     }\r
3230   }\r
3231   if (gridPen != NULL) DeleteObject(gridPen);\r
3232   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3233   if (premovePen != NULL) DeleteObject(premovePen);\r
3234   if (lineGap != 0) {\r
3235     logbrush.lbStyle = BS_SOLID;\r
3236     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3237     gridPen =\r
3238       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3239                    lineGap, &logbrush, 0, NULL);\r
3240     logbrush.lbColor = highlightSquareColor;\r
3241     highlightPen =\r
3242       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3243                    lineGap, &logbrush, 0, NULL);\r
3244 \r
3245     logbrush.lbColor = premoveHighlightColor; \r
3246     premovePen =\r
3247       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3248                    lineGap, &logbrush, 0, NULL);\r
3249 \r
3250     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3251     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3252       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3253       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3254         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3255       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3256         BOARD_WIDTH * (squareSize + lineGap);\r
3257         lineGap / 2 + (i * (squareSize + lineGap));\r
3258       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3259     }\r
3260     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3261       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3262       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3263         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3264         lineGap / 2 + (i * (squareSize + lineGap));\r
3265       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3266         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3267       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3268     }\r
3269   }\r
3270 \r
3271   /* [HGM] Licensing requirement */\r
3272 #ifdef GOTHIC\r
3273   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3274 #endif\r
3275 #ifdef FALCON\r
3276   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3277 #endif\r
3278   GothicPopUp( "", VariantNormal);\r
3279 \r
3280 \r
3281 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3282   oldBoardSize = boardSize;\r
3283   oldTinyLayout = tinyLayout;\r
3284 \r
3285   /* Load piece bitmaps for this board size */\r
3286   for (i=0; i<=2; i++) {\r
3287     for (piece = WhitePawn;\r
3288          (int) piece < (int) BlackPawn;\r
3289          piece = (ChessSquare) ((int) piece + 1)) {\r
3290       if (pieceBitmap[i][piece] != NULL)\r
3291         DeleteObject(pieceBitmap[i][piece]);\r
3292     }\r
3293   }\r
3294 \r
3295   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3296   // Orthodox Chess pieces\r
3297   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3298   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3299   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3300   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3301   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3302   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3303   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3304   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3305   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3306   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3307   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3308   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3309   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3310   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3311   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3312   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3313     // in Shogi, Hijack the unused Queen for Lance\r
3314     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3315     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3316     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3317   } else {\r
3318     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3319     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3320     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3321   }\r
3322 \r
3323   if(squareSize <= 72 && squareSize >= 33) { \r
3324     /* A & C are available in most sizes now */\r
3325     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3326       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3327       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3328       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3329       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3330       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3331       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3332       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3333       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3334       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3335       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3336       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3337       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3338     } else { // Smirf-like\r
3339       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3340       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3341       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3342     }\r
3343     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3344       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3345       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3346       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3347     } else { // WinBoard standard\r
3348       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3349       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3350       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3351     }\r
3352   }\r
3353 \r
3354 \r
3355   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3356     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3357     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3358     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3359     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3360     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3361     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3362     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3363     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3364     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3365     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3366     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3367     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3368     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3369     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3370     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3371     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3372     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3373     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3374     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3375     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3376     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3377     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3378     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3379     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3380     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3381     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3382     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3383     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3384     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3385     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3386 \r
3387     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3388       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3389       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3390       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3391       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3392       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3393       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3394       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3395       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3396       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3397       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3398       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3399       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3400     } else {\r
3401       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3402       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3403       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3404       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3405       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3406       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3407       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3408       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3409       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3410       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3411       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3412       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3413     }\r
3414 \r
3415   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3416     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3417     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3418     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3419     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3420     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3421     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3422     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3423     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3424     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3425     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3426     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3427     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3428     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3429     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3430   }\r
3431 \r
3432 \r
3433   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3434   /* special Shogi support in this size */\r
3435   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3436       for (piece = WhitePawn;\r
3437            (int) piece < (int) BlackPawn;\r
3438            piece = (ChessSquare) ((int) piece + 1)) {\r
3439         if (pieceBitmap[i][piece] != NULL)\r
3440           DeleteObject(pieceBitmap[i][piece]);\r
3441       }\r
3442     }\r
3443   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3444   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3445   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3446   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3447   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3448   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3449   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3450   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3451   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3452   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3453   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3454   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3455   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3456   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3457   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3458   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3459   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3460   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3461   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3462   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3463   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3464   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3465   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3466   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3467   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3468   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3469   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3470   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3471   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3472   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3473   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3474   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3475   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3476   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3477   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3478   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3479   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3480   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3481   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3482   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3483   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3484   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3485   minorSize = 0;\r
3486   }\r
3487 }\r
3488 \r
3489 HBITMAP\r
3490 PieceBitmap(ChessSquare p, int kind)\r
3491 {\r
3492   if ((int) p >= (int) BlackPawn)\r
3493     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3494 \r
3495   return pieceBitmap[kind][(int) p];\r
3496 }\r
3497 \r
3498 /***************************************************************/\r
3499 \r
3500 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3501 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3502 /*\r
3503 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3504 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3505 */\r
3506 \r
3507 VOID\r
3508 SquareToPos(int row, int column, int * x, int * y)\r
3509 {\r
3510   if (flipView) {\r
3511     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3512     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3513   } else {\r
3514     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3515     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3516   }\r
3517 }\r
3518 \r
3519 VOID\r
3520 DrawCoordsOnDC(HDC hdc)\r
3521 {\r
3522   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
3523   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
3524   char str[2] = { NULLCHAR, NULLCHAR };\r
3525   int oldMode, oldAlign, x, y, start, i;\r
3526   HFONT oldFont;\r
3527   HBRUSH oldBrush;\r
3528 \r
3529   if (!appData.showCoords)\r
3530     return;\r
3531 \r
3532   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3533 \r
3534   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3535   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3536   oldAlign = GetTextAlign(hdc);\r
3537   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3538 \r
3539   y = boardRect.top + lineGap;\r
3540   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3541 \r
3542   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3543   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3544     str[0] = files[start + i];\r
3545     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3546     y += squareSize + lineGap;\r
3547   }\r
3548 \r
3549   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3550 \r
3551   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3552   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3553     str[0] = ranks[start + i];\r
3554     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3555     x += squareSize + lineGap;\r
3556   }    \r
3557 \r
3558   SelectObject(hdc, oldBrush);\r
3559   SetBkMode(hdc, oldMode);\r
3560   SetTextAlign(hdc, oldAlign);\r
3561   SelectObject(hdc, oldFont);\r
3562 }\r
3563 \r
3564 VOID\r
3565 DrawGridOnDC(HDC hdc)\r
3566 {\r
3567   HPEN oldPen;\r
3568  \r
3569   if (lineGap != 0) {\r
3570     oldPen = SelectObject(hdc, gridPen);\r
3571     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3572     SelectObject(hdc, oldPen);\r
3573   }\r
3574 }\r
3575 \r
3576 #define HIGHLIGHT_PEN 0\r
3577 #define PREMOVE_PEN   1\r
3578 \r
3579 VOID\r
3580 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3581 {\r
3582   int x1, y1;\r
3583   HPEN oldPen, hPen;\r
3584   if (lineGap == 0) return;\r
3585   if (flipView) {\r
3586     x1 = boardRect.left +\r
3587       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3588     y1 = boardRect.top +\r
3589       lineGap/2 + y * (squareSize + lineGap);\r
3590   } else {\r
3591     x1 = boardRect.left +\r
3592       lineGap/2 + x * (squareSize + lineGap);\r
3593     y1 = boardRect.top +\r
3594       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3595   }\r
3596   hPen = pen ? premovePen : highlightPen;\r
3597   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3598   MoveToEx(hdc, x1, y1, NULL);\r
3599   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3600   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3601   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3602   LineTo(hdc, x1, y1);\r
3603   SelectObject(hdc, oldPen);\r
3604 }\r
3605 \r
3606 VOID\r
3607 DrawHighlightsOnDC(HDC hdc)\r
3608 {\r
3609   int i;\r
3610   for (i=0; i<2; i++) {\r
3611     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3612       DrawHighlightOnDC(hdc, TRUE,\r
3613                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3614                         HIGHLIGHT_PEN);\r
3615   }\r
3616   for (i=0; i<2; i++) {\r
3617     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3618         premoveHighlightInfo.sq[i].y >= 0) {\r
3619         DrawHighlightOnDC(hdc, TRUE,\r
3620                           premoveHighlightInfo.sq[i].x, \r
3621                           premoveHighlightInfo.sq[i].y,\r
3622                           PREMOVE_PEN);\r
3623     }\r
3624   }\r
3625 }\r
3626 \r
3627 /* Note: sqcolor is used only in monoMode */\r
3628 /* Note that this code is largely duplicated in woptions.c,\r
3629    function DrawSampleSquare, so that needs to be updated too */\r
3630 VOID\r
3631 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3632 {\r
3633   HBITMAP oldBitmap;\r
3634   HBRUSH oldBrush;\r
3635   int tmpSize;\r
3636 \r
3637   if (appData.blindfold) return;\r
3638 \r
3639   /* [AS] Use font-based pieces if needed */\r
3640   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3641     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3642     CreatePiecesFromFont();\r
3643 \r
3644     if( fontBitmapSquareSize == squareSize ) {\r
3645         int index = TranslatePieceToFontPiece(piece);\r
3646 \r
3647         SelectObject( tmphdc, hPieceMask[ index ] );\r
3648 \r
3649         BitBlt( hdc,\r
3650             x, y,\r
3651             squareSize, squareSize,\r
3652             tmphdc,\r
3653             0, 0,\r
3654             SRCAND );\r
3655 \r
3656         SelectObject( tmphdc, hPieceFace[ index ] );\r
3657 \r
3658         BitBlt( hdc,\r
3659             x, y,\r
3660             squareSize, squareSize,\r
3661             tmphdc,\r
3662             0, 0,\r
3663             SRCPAINT );\r
3664 \r
3665         return;\r
3666     }\r
3667   }\r
3668 \r
3669   if (appData.monoMode) {\r
3670     SelectObject(tmphdc, PieceBitmap(piece, \r
3671       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3672     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3673            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3674   } else {\r
3675     tmpSize = squareSize;\r
3676     if(minorSize &&\r
3677         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3678          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3679       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3680       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3681       x += (squareSize - minorSize)>>1;\r
3682       y += squareSize - minorSize - 2;\r
3683       tmpSize = minorSize;\r
3684     }\r
3685     if (color || appData.allWhite ) {\r
3686       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3687       if( color )\r
3688               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3689       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3690       if(appData.upsideDown && color==flipView)\r
3691         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3692       else\r
3693         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3694 #if 0\r
3695       /* Use black piece color for outline of white pieces */\r
3696       /* Not sure this looks really good (though xboard does it).\r
3697          Maybe better to have another selectable color, default black */\r
3698       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3699       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3700       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3701 #else\r
3702       /* Use black for outline of white pieces */\r
3703       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3704       if(appData.upsideDown && color==flipView)\r
3705         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3706       else\r
3707         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3708 #endif\r
3709     } else {\r
3710 #if 0\r
3711       /* Use white piece color for details of black pieces */\r
3712       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3713          WHITE_PIECE ones aren't always the right shape. */\r
3714       /* Not sure this looks really good (though xboard does it).\r
3715          Maybe better to have another selectable color, default medium gray? */\r
3716       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3717       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3718       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3719       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3720       SelectObject(hdc, blackPieceBrush);\r
3721       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3722 #else\r
3723       /* Use square color for details of black pieces */\r
3724       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3725       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3726       if(appData.upsideDown && !flipView)\r
3727         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3728       else\r
3729         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3730 #endif\r
3731     }\r
3732     SelectObject(hdc, oldBrush);\r
3733     SelectObject(tmphdc, oldBitmap);\r
3734   }\r
3735 }\r
3736 \r
3737 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3738 int GetBackTextureMode( int algo )\r
3739 {\r
3740     int result = BACK_TEXTURE_MODE_DISABLED;\r
3741 \r
3742     switch( algo ) \r
3743     {\r
3744         case BACK_TEXTURE_MODE_PLAIN:\r
3745             result = 1; /* Always use identity map */\r
3746             break;\r
3747         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3748             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3749             break;\r
3750     }\r
3751 \r
3752     return result;\r
3753 }\r
3754 \r
3755 /* \r
3756     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3757     to handle redraws cleanly (as random numbers would always be different).\r
3758 */\r
3759 VOID RebuildTextureSquareInfo()\r
3760 {\r
3761     BITMAP bi;\r
3762     int lite_w = 0;\r
3763     int lite_h = 0;\r
3764     int dark_w = 0;\r
3765     int dark_h = 0;\r
3766     int row;\r
3767     int col;\r
3768 \r
3769     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3770 \r
3771     if( liteBackTexture != NULL ) {\r
3772         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3773             lite_w = bi.bmWidth;\r
3774             lite_h = bi.bmHeight;\r
3775         }\r
3776     }\r
3777 \r
3778     if( darkBackTexture != NULL ) {\r
3779         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3780             dark_w = bi.bmWidth;\r
3781             dark_h = bi.bmHeight;\r
3782         }\r
3783     }\r
3784 \r
3785     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3786         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3787             if( (col + row) & 1 ) {\r
3788                 /* Lite square */\r
3789                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3790                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3791                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3792                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3793                 }\r
3794             }\r
3795             else {\r
3796                 /* Dark square */\r
3797                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3798                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3799                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3800                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3801                 }\r
3802             }\r
3803         }\r
3804     }\r
3805 }\r
3806 \r
3807 /* [AS] Arrow highlighting support */\r
3808 \r
3809 static int A_WIDTH = 5; /* Width of arrow body */\r
3810 \r
3811 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3812 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3813 \r
3814 static double Sqr( double x )\r
3815 {\r
3816     return x*x;\r
3817 }\r
3818 \r
3819 static int Round( double x )\r
3820 {\r
3821     return (int) (x + 0.5);\r
3822 }\r
3823 \r
3824 /* Draw an arrow between two points using current settings */\r
3825 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3826 {\r
3827     POINT arrow[7];\r
3828     double dx, dy, j, k, x, y;\r
3829 \r
3830     if( d_x == s_x ) {\r
3831         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3832 \r
3833         arrow[0].x = s_x + A_WIDTH;\r
3834         arrow[0].y = s_y;\r
3835 \r
3836         arrow[1].x = s_x + A_WIDTH;\r
3837         arrow[1].y = d_y - h;\r
3838 \r
3839         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3840         arrow[2].y = d_y - h;\r
3841 \r
3842         arrow[3].x = d_x;\r
3843         arrow[3].y = d_y;\r
3844 \r
3845         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3846         arrow[4].y = d_y - h;\r
3847 \r
3848         arrow[5].x = s_x - A_WIDTH;\r
3849         arrow[5].y = d_y - h;\r
3850 \r
3851         arrow[6].x = s_x - A_WIDTH;\r
3852         arrow[6].y = s_y;\r
3853     }\r
3854     else if( d_y == s_y ) {\r
3855         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3856 \r
3857         arrow[0].x = s_x;\r
3858         arrow[0].y = s_y + A_WIDTH;\r
3859 \r
3860         arrow[1].x = d_x - w;\r
3861         arrow[1].y = s_y + A_WIDTH;\r
3862 \r
3863         arrow[2].x = d_x - w;\r
3864         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3865 \r
3866         arrow[3].x = d_x;\r
3867         arrow[3].y = d_y;\r
3868 \r
3869         arrow[4].x = d_x - w;\r
3870         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3871 \r
3872         arrow[5].x = d_x - w;\r
3873         arrow[5].y = s_y - A_WIDTH;\r
3874 \r
3875         arrow[6].x = s_x;\r
3876         arrow[6].y = s_y - A_WIDTH;\r
3877     }\r
3878     else {\r
3879         /* [AS] Needed a lot of paper for this! :-) */\r
3880         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3881         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3882   \r
3883         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3884 \r
3885         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3886 \r
3887         x = s_x;\r
3888         y = s_y;\r
3889 \r
3890         arrow[0].x = Round(x - j);\r
3891         arrow[0].y = Round(y + j*dx);\r
3892 \r
3893         arrow[1].x = Round(x + j);\r
3894         arrow[1].y = Round(y - j*dx);\r
3895 \r
3896         if( d_x > s_x ) {\r
3897             x = (double) d_x - k;\r
3898             y = (double) d_y - k*dy;\r
3899         }\r
3900         else {\r
3901             x = (double) d_x + k;\r
3902             y = (double) d_y + k*dy;\r
3903         }\r
3904 \r
3905         arrow[2].x = Round(x + j);\r
3906         arrow[2].y = Round(y - j*dx);\r
3907 \r
3908         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3909         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3910 \r
3911         arrow[4].x = d_x;\r
3912         arrow[4].y = d_y;\r
3913 \r
3914         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3915         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3916 \r
3917         arrow[6].x = Round(x - j);\r
3918         arrow[6].y = Round(y + j*dx);\r
3919     }\r
3920 \r
3921     Polygon( hdc, arrow, 7 );\r
3922 }\r
3923 \r
3924 /* [AS] Draw an arrow between two squares */\r
3925 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3926 {\r
3927     int s_x, s_y, d_x, d_y;\r
3928     HPEN hpen;\r
3929     HPEN holdpen;\r
3930     HBRUSH hbrush;\r
3931     HBRUSH holdbrush;\r
3932     LOGBRUSH stLB;\r
3933 \r
3934     if( s_col == d_col && s_row == d_row ) {\r
3935         return;\r
3936     }\r
3937 \r
3938     /* Get source and destination points */\r
3939     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3940     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3941 \r
3942     if( d_y > s_y ) {\r
3943         d_y += squareSize / 4;\r
3944     }\r
3945     else if( d_y < s_y ) {\r
3946         d_y += 3 * squareSize / 4;\r
3947     }\r
3948     else {\r
3949         d_y += squareSize / 2;\r
3950     }\r
3951 \r
3952     if( d_x > s_x ) {\r
3953         d_x += squareSize / 4;\r
3954     }\r
3955     else if( d_x < s_x ) {\r
3956         d_x += 3 * squareSize / 4;\r
3957     }\r
3958     else {\r
3959         d_x += squareSize / 2;\r
3960     }\r
3961 \r
3962     s_x += squareSize / 2;\r
3963     s_y += squareSize / 2;\r
3964 \r
3965     /* Adjust width */\r
3966     A_WIDTH = squareSize / 14;\r
3967 \r
3968     /* Draw */\r
3969     stLB.lbStyle = BS_SOLID;\r
3970     stLB.lbColor = appData.highlightArrowColor;\r
3971     stLB.lbHatch = 0;\r
3972 \r
3973     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3974     holdpen = SelectObject( hdc, hpen );\r
3975     hbrush = CreateBrushIndirect( &stLB );\r
3976     holdbrush = SelectObject( hdc, hbrush );\r
3977 \r
3978     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3979 \r
3980     SelectObject( hdc, holdpen );\r
3981     SelectObject( hdc, holdbrush );\r
3982     DeleteObject( hpen );\r
3983     DeleteObject( hbrush );\r
3984 }\r
3985 \r
3986 BOOL HasHighlightInfo()\r
3987 {\r
3988     BOOL result = FALSE;\r
3989 \r
3990     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3991         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3992     {\r
3993         result = TRUE;\r
3994     }\r
3995 \r
3996     return result;\r
3997 }\r
3998 \r
3999 BOOL IsDrawArrowEnabled()\r
4000 {\r
4001     BOOL result = FALSE;\r
4002 \r
4003     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4004         result = TRUE;\r
4005     }\r
4006 \r
4007     return result;\r
4008 }\r
4009 \r
4010 VOID DrawArrowHighlight( HDC hdc )\r
4011 {\r
4012     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4013         DrawArrowBetweenSquares( hdc,\r
4014             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4015             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4016     }\r
4017 }\r
4018 \r
4019 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4020 {\r
4021     HRGN result = NULL;\r
4022 \r
4023     if( HasHighlightInfo() ) {\r
4024         int x1, y1, x2, y2;\r
4025         int sx, sy, dx, dy;\r
4026 \r
4027         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4028         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4029 \r
4030         sx = MIN( x1, x2 );\r
4031         sy = MIN( y1, y2 );\r
4032         dx = MAX( x1, x2 ) + squareSize;\r
4033         dy = MAX( y1, y2 ) + squareSize;\r
4034 \r
4035         result = CreateRectRgn( sx, sy, dx, dy );\r
4036     }\r
4037 \r
4038     return result;\r
4039 }\r
4040 \r
4041 /*\r
4042     Warning: this function modifies the behavior of several other functions. \r
4043     \r
4044     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4045     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4046     repaint is scattered all over the place, which is not good for features such as\r
4047     "arrow highlighting" that require a full repaint of the board.\r
4048 \r
4049     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4050     user interaction, when speed is not so important) but especially to avoid errors\r
4051     in the displayed graphics.\r
4052 \r
4053     In such patched places, I always try refer to this function so there is a single\r
4054     place to maintain knowledge.\r
4055     \r
4056     To restore the original behavior, just return FALSE unconditionally.\r
4057 */\r
4058 BOOL IsFullRepaintPreferrable()\r
4059 {\r
4060     BOOL result = FALSE;\r
4061 \r
4062     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4063         /* Arrow may appear on the board */\r
4064         result = TRUE;\r
4065     }\r
4066 \r
4067     return result;\r
4068 }\r
4069 \r
4070 /* \r
4071     This function is called by DrawPosition to know whether a full repaint must\r
4072     be forced or not.\r
4073 \r
4074     Only DrawPosition may directly call this function, which makes use of \r
4075     some state information. Other function should call DrawPosition specifying \r
4076     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4077 */\r
4078 BOOL DrawPositionNeedsFullRepaint()\r
4079 {\r
4080     BOOL result = FALSE;\r
4081 \r
4082     /* \r
4083         Probably a slightly better policy would be to trigger a full repaint\r
4084         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4085         but animation is fast enough that it's difficult to notice.\r
4086     */\r
4087     if( animInfo.piece == EmptySquare ) {\r
4088         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4089             result = TRUE;\r
4090         }\r
4091     }\r
4092 \r
4093     return result;\r
4094 }\r
4095 \r
4096 VOID\r
4097 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4098 {\r
4099   int row, column, x, y, square_color, piece_color;\r
4100   ChessSquare piece;\r
4101   HBRUSH oldBrush;\r
4102   HDC texture_hdc = NULL;\r
4103 \r
4104   /* [AS] Initialize background textures if needed */\r
4105   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4106       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4107       if( backTextureSquareSize != squareSize \r
4108        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4109           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4110           backTextureSquareSize = squareSize;\r
4111           RebuildTextureSquareInfo();\r
4112       }\r
4113 \r
4114       texture_hdc = CreateCompatibleDC( hdc );\r
4115   }\r
4116 \r
4117   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4118     for (column = 0; column < BOARD_WIDTH; column++) {\r
4119   \r
4120       SquareToPos(row, column, &x, &y);\r
4121 \r
4122       piece = board[row][column];\r
4123 \r
4124       square_color = ((column + row) % 2) == 1;\r
4125       if( gameInfo.variant == VariantXiangqi ) {\r
4126           square_color = !InPalace(row, column);\r
4127           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4128           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4129       }\r
4130       piece_color = (int) piece < (int) BlackPawn;\r
4131 \r
4132 \r
4133       /* [HGM] holdings file: light square or black */\r
4134       if(column == BOARD_LEFT-2) {\r
4135             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4136                 square_color = 1;\r
4137             else {\r
4138                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4139                 continue;\r
4140             }\r
4141       } else\r
4142       if(column == BOARD_RGHT + 1 ) {\r
4143             if( row < gameInfo.holdingsSize )\r
4144                 square_color = 1;\r
4145             else {\r
4146                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4147                 continue;\r
4148             }\r
4149       }\r
4150       if(column == BOARD_LEFT-1 ) /* left align */\r
4151             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4152       else if( column == BOARD_RGHT) /* right align */\r
4153             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4154       else\r
4155       if (appData.monoMode) {\r
4156         if (piece == EmptySquare) {\r
4157           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4158                  square_color ? WHITENESS : BLACKNESS);\r
4159         } else {\r
4160           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4161         }\r
4162       } \r
4163       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4164           /* [AS] Draw the square using a texture bitmap */\r
4165           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4166           int r = row, c = column; // [HGM] do not flip board in flipView\r
4167           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4168 \r
4169           DrawTile( x, y, \r
4170               squareSize, squareSize, \r
4171               hdc, \r
4172               texture_hdc,\r
4173               backTextureSquareInfo[r][c].mode,\r
4174               backTextureSquareInfo[r][c].x,\r
4175               backTextureSquareInfo[r][c].y );\r
4176 \r
4177           SelectObject( texture_hdc, hbm );\r
4178 \r
4179           if (piece != EmptySquare) {\r
4180               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4181           }\r
4182       }\r
4183       else {\r
4184         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4185 \r
4186         oldBrush = SelectObject(hdc, brush );\r
4187         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4188         SelectObject(hdc, oldBrush);\r
4189         if (piece != EmptySquare)\r
4190           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4191       }\r
4192     }\r
4193   }\r
4194 \r
4195   if( texture_hdc != NULL ) {\r
4196     DeleteDC( texture_hdc );\r
4197   }\r
4198 }\r
4199 \r
4200 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4201 void fputDW(FILE *f, int x)\r
4202 {\r
4203         fputc(x     & 255, f);\r
4204         fputc(x>>8  & 255, f);\r
4205         fputc(x>>16 & 255, f);\r
4206         fputc(x>>24 & 255, f);\r
4207 }\r
4208 \r
4209 #define MAX_CLIPS 200   /* more than enough */\r
4210 \r
4211 VOID\r
4212 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4213 {\r
4214   HBITMAP bufferBitmap;\r
4215   BITMAP bi;\r
4216   RECT Rect;\r
4217   HDC tmphdc;\r
4218   HBITMAP hbm;\r
4219   int w = 100, h = 50;\r
4220 \r
4221   if(cps->programLogo == NULL) return;\r
4222 //  GetClientRect(hwndMain, &Rect);\r
4223 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4224 //                                      Rect.bottom-Rect.top+1);\r
4225   tmphdc = CreateCompatibleDC(hdc);\r
4226   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4227   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4228             w = bi.bmWidth;\r
4229             h = bi.bmHeight;\r
4230   }\r
4231   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4232                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4233   SelectObject(tmphdc, hbm);\r
4234   DeleteDC(tmphdc);\r
4235 }\r
4236 \r
4237 VOID\r
4238 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4239 {\r
4240   static Board lastReq, lastDrawn;\r
4241   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4242   static int lastDrawnFlipView = 0;\r
4243   static int lastReqValid = 0, lastDrawnValid = 0;\r
4244   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4245   HDC tmphdc;\r
4246   HDC hdcmem;\r
4247   HBITMAP bufferBitmap;\r
4248   HBITMAP oldBitmap;\r
4249   RECT Rect;\r
4250   HRGN clips[MAX_CLIPS];\r
4251   ChessSquare dragged_piece = EmptySquare;\r
4252 \r
4253   /* I'm undecided on this - this function figures out whether a full\r
4254    * repaint is necessary on its own, so there's no real reason to have the\r
4255    * caller tell it that.  I think this can safely be set to FALSE - but\r
4256    * if we trust the callers not to request full repaints unnessesarily, then\r
4257    * we could skip some clipping work.  In other words, only request a full\r
4258    * redraw when the majority of pieces have changed positions (ie. flip, \r
4259    * gamestart and similar)  --Hawk\r
4260    */\r
4261   Boolean fullrepaint = repaint;\r
4262 \r
4263   if( DrawPositionNeedsFullRepaint() ) {\r
4264       fullrepaint = TRUE;\r
4265   }\r
4266 \r
4267 #if 0\r
4268   if( fullrepaint ) {\r
4269       static int repaint_count = 0;\r
4270       char buf[128];\r
4271 \r
4272       repaint_count++;\r
4273       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4274       OutputDebugString( buf );\r
4275   }\r
4276 #endif\r
4277 \r
4278   if (board == NULL) {\r
4279     if (!lastReqValid) {\r
4280       return;\r
4281     }\r
4282     board = lastReq;\r
4283   } else {\r
4284     CopyBoard(lastReq, board);\r
4285     lastReqValid = 1;\r
4286   }\r
4287 \r
4288   if (doingSizing) {\r
4289     return;\r
4290   }\r
4291 \r
4292   if (IsIconic(hwndMain)) {\r
4293     return;\r
4294   }\r
4295 \r
4296   if (hdc == NULL) {\r
4297     hdc = GetDC(hwndMain);\r
4298     if (!appData.monoMode) {\r
4299       SelectPalette(hdc, hPal, FALSE);\r
4300       RealizePalette(hdc);\r
4301     }\r
4302     releaseDC = TRUE;\r
4303   } else {\r
4304     releaseDC = FALSE;\r
4305   }\r
4306 \r
4307 #if 0\r
4308   fprintf(debugFP, "*******************************\n"\r
4309                    "repaint = %s\n"\r
4310                    "dragInfo.from (%d,%d)\n"\r
4311                    "dragInfo.start (%d,%d)\n"\r
4312                    "dragInfo.pos (%d,%d)\n"\r
4313                    "dragInfo.lastpos (%d,%d)\n", \r
4314                     repaint ? "TRUE" : "FALSE",\r
4315                     dragInfo.from.x, dragInfo.from.y, \r
4316                     dragInfo.start.x, dragInfo.start.y,\r
4317                     dragInfo.pos.x, dragInfo.pos.y,\r
4318                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4319   fprintf(debugFP, "prev:  ");\r
4320   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4321     for (column = 0; column < BOARD_WIDTH; column++) {\r
4322       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4323     }\r
4324   }\r
4325   fprintf(debugFP, "\n");\r
4326   fprintf(debugFP, "board: ");\r
4327   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4328     for (column = 0; column < BOARD_WIDTH; column++) {\r
4329       fprintf(debugFP, "%d ", board[row][column]);\r
4330     }\r
4331   }\r
4332   fprintf(debugFP, "\n");\r
4333   fflush(debugFP);\r
4334 #endif\r
4335 \r
4336   /* Create some work-DCs */\r
4337   hdcmem = CreateCompatibleDC(hdc);\r
4338   tmphdc = CreateCompatibleDC(hdc);\r
4339 \r
4340   /* If dragging is in progress, we temporarely remove the piece */\r
4341   /* [HGM] or temporarily decrease count if stacked              */\r
4342   /*       !! Moved to before board compare !!                   */\r
4343   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4344     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4345     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4346             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4347         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4348     } else \r
4349     if(dragInfo.from.x == BOARD_RGHT+1) {\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         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4354   }\r
4355 \r
4356   /* Figure out which squares need updating by comparing the \r
4357    * newest board with the last drawn board and checking if\r
4358    * flipping has changed.\r
4359    */\r
4360   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4361     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4362       for (column = 0; column < BOARD_WIDTH; column++) {\r
4363         if (lastDrawn[row][column] != board[row][column]) {\r
4364           SquareToPos(row, column, &x, &y);\r
4365           clips[num_clips++] =\r
4366             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4367         }\r
4368       }\r
4369     }\r
4370     for (i=0; i<2; i++) {\r
4371       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4372           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4373         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4374             lastDrawnHighlight.sq[i].y >= 0) {\r
4375           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4376                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4377           clips[num_clips++] =\r
4378             CreateRectRgn(x - lineGap, y - lineGap, \r
4379                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4380         }\r
4381         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4382           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4383           clips[num_clips++] =\r
4384             CreateRectRgn(x - lineGap, y - lineGap, \r
4385                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4386         }\r
4387       }\r
4388     }\r
4389     for (i=0; i<2; i++) {\r
4390       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4391           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4392         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4393             lastDrawnPremove.sq[i].y >= 0) {\r
4394           SquareToPos(lastDrawnPremove.sq[i].y,\r
4395                       lastDrawnPremove.sq[i].x, &x, &y);\r
4396           clips[num_clips++] =\r
4397             CreateRectRgn(x - lineGap, y - lineGap, \r
4398                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4399         }\r
4400         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4401             premoveHighlightInfo.sq[i].y >= 0) {\r
4402           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4403                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4404           clips[num_clips++] =\r
4405             CreateRectRgn(x - lineGap, y - lineGap, \r
4406                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4407         }\r
4408       }\r
4409     }\r
4410   } else {\r
4411     fullrepaint = TRUE;\r
4412   }\r
4413 \r
4414   /* Create a buffer bitmap - this is the actual bitmap\r
4415    * being written to.  When all the work is done, we can\r
4416    * copy it to the real DC (the screen).  This avoids\r
4417    * the problems with flickering.\r
4418    */\r
4419   GetClientRect(hwndMain, &Rect);\r
4420   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4421                                         Rect.bottom-Rect.top+1);\r
4422   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4423   if (!appData.monoMode) {\r
4424     SelectPalette(hdcmem, hPal, FALSE);\r
4425   }\r
4426 \r
4427   /* Create clips for dragging */\r
4428   if (!fullrepaint) {\r
4429     if (dragInfo.from.x >= 0) {\r
4430       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4431       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4432     }\r
4433     if (dragInfo.start.x >= 0) {\r
4434       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4435       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4436     }\r
4437     if (dragInfo.pos.x >= 0) {\r
4438       x = dragInfo.pos.x - squareSize / 2;\r
4439       y = dragInfo.pos.y - squareSize / 2;\r
4440       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4441     }\r
4442     if (dragInfo.lastpos.x >= 0) {\r
4443       x = dragInfo.lastpos.x - squareSize / 2;\r
4444       y = dragInfo.lastpos.y - squareSize / 2;\r
4445       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4446     }\r
4447   }\r
4448 \r
4449   /* Are we animating a move?  \r
4450    * If so, \r
4451    *   - remove the piece from the board (temporarely)\r
4452    *   - calculate the clipping region\r
4453    */\r
4454   if (!fullrepaint) {\r
4455     if (animInfo.piece != EmptySquare) {\r
4456       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4457       x = boardRect.left + animInfo.lastpos.x;\r
4458       y = boardRect.top + animInfo.lastpos.y;\r
4459       x2 = boardRect.left + animInfo.pos.x;\r
4460       y2 = boardRect.top + animInfo.pos.y;\r
4461       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4462       /* Slight kludge.  The real problem is that after AnimateMove is\r
4463          done, the position on the screen does not match lastDrawn.\r
4464          This currently causes trouble only on e.p. captures in\r
4465          atomic, where the piece moves to an empty square and then\r
4466          explodes.  The old and new positions both had an empty square\r
4467          at the destination, but animation has drawn a piece there and\r
4468          we have to remember to erase it. */\r
4469       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4470     }\r
4471   }\r
4472 \r
4473   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4474   if (num_clips == 0)\r
4475     fullrepaint = TRUE;\r
4476 \r
4477   /* Set clipping on the memory DC */\r
4478   if (!fullrepaint) {\r
4479     SelectClipRgn(hdcmem, clips[0]);\r
4480     for (x = 1; x < num_clips; x++) {\r
4481       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4482         abort();  // this should never ever happen!\r
4483     }\r
4484   }\r
4485 \r
4486   /* Do all the drawing to the memory DC */\r
4487   DrawGridOnDC(hdcmem);\r
4488   DrawHighlightsOnDC(hdcmem);\r
4489   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4490 \r
4491   if(logoHeight) {\r
4492         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4493         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4494   }\r
4495 \r
4496   if( appData.highlightMoveWithArrow ) {\r
4497     DrawArrowHighlight(hdcmem);\r
4498   }\r
4499 \r
4500   DrawCoordsOnDC(hdcmem);\r
4501 \r
4502   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4503                  /* to make sure lastDrawn contains what is actually drawn */\r
4504 \r
4505   /* Put the dragged piece back into place and draw it (out of place!) */\r
4506     if (dragged_piece != EmptySquare) {\r
4507     /* [HGM] or restack */\r
4508     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4509                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4510     else\r
4511     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4512                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4513     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4514     x = dragInfo.pos.x - squareSize / 2;\r
4515     y = dragInfo.pos.y - squareSize / 2;\r
4516     DrawPieceOnDC(hdcmem, dragged_piece,\r
4517                   ((int) dragged_piece < (int) BlackPawn), \r
4518                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4519   }   \r
4520   \r
4521   /* Put the animated piece back into place and draw it */\r
4522   if (animInfo.piece != EmptySquare) {\r
4523     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4524     x = boardRect.left + animInfo.pos.x;\r
4525     y = boardRect.top + animInfo.pos.y;\r
4526     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4527                   ((int) animInfo.piece < (int) BlackPawn),\r
4528                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4529   }\r
4530 \r
4531   /* Release the bufferBitmap by selecting in the old bitmap \r
4532    * and delete the memory DC\r
4533    */\r
4534   SelectObject(hdcmem, oldBitmap);\r
4535   DeleteDC(hdcmem);\r
4536 \r
4537   /* Set clipping on the target DC */\r
4538   if (!fullrepaint) {\r
4539     SelectClipRgn(hdc, clips[0]);\r
4540     for (x = 1; x < num_clips; x++) {\r
4541       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4542         abort();   // this should never ever happen!\r
4543     } \r
4544   }\r
4545 \r
4546   /* Copy the new bitmap onto the screen in one go.\r
4547    * This way we avoid any flickering\r
4548    */\r
4549   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4550   BitBlt(hdc, boardRect.left, boardRect.top,\r
4551          boardRect.right - boardRect.left,\r
4552          boardRect.bottom - boardRect.top,\r
4553          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4554   if(saveDiagFlag) { \r
4555     BITMAP b; int i, j, m, w, wb, fac=0; char pData[1000000]; \r
4556     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4557 \r
4558     GetObject(bufferBitmap, sizeof(b), &b);\r
4559     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4560         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4561         bih.biWidth = b.bmWidth;\r
4562         bih.biHeight = b.bmHeight;\r
4563         bih.biPlanes = 1;\r
4564         bih.biBitCount = b.bmBitsPixel;\r
4565         bih.biCompression = 0;\r
4566         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4567         bih.biXPelsPerMeter = 0;\r
4568         bih.biYPelsPerMeter = 0;\r
4569         bih.biClrUsed = 0;\r
4570         bih.biClrImportant = 0;\r
4571 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4572 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4573         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4574 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4575 \r
4576 #if 1\r
4577         wb = b.bmWidthBytes;\r
4578         // count colors\r
4579         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4580                 int k = ((int*) pData)[i];\r
4581                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4582                 if(j >= 16) break;\r
4583                 color[j] = k;\r
4584                 if(j >= nrColors) nrColors = j+1;\r
4585         }\r
4586         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4587                 INT p = 0;\r
4588                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4589                     for(w=0; w<(wb>>2); w+=2) {\r
4590                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4591                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4592                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4593                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4594                         pData[p++] = m | j<<4;\r
4595                     }\r
4596                     while(p&3) pData[p++] = 0;\r
4597                 }\r
4598                 fac = 3;\r
4599                 wb = (wb+31>>5)<<2;\r
4600         }\r
4601         // write BITMAPFILEHEADER\r
4602         fprintf(diagFile, "BM");\r
4603         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4604         fputDW(diagFile, 0);\r
4605         fputDW(diagFile, 0x36 + (fac?64:0));\r
4606         // write BITMAPINFOHEADER\r
4607         fputDW(diagFile, 40);\r
4608         fputDW(diagFile, b.bmWidth);\r
4609         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4610         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4611         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4612         fputDW(diagFile, 0);\r
4613         fputDW(diagFile, 0);\r
4614         fputDW(diagFile, 0);\r
4615         fputDW(diagFile, 0);\r
4616         fputDW(diagFile, 0);\r
4617         fputDW(diagFile, 0);\r
4618         // write color table\r
4619         if(fac)\r
4620         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4621         // write bitmap data\r
4622         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4623                 fputc(pData[i], diagFile);\r
4624 #endif\r
4625      }\r
4626   }\r
4627 \r
4628   SelectObject(tmphdc, oldBitmap);\r
4629 \r
4630   /* Massive cleanup */\r
4631   for (x = 0; x < num_clips; x++)\r
4632     DeleteObject(clips[x]);\r
4633 \r
4634   DeleteDC(tmphdc);\r
4635   DeleteObject(bufferBitmap);\r
4636 \r
4637   if (releaseDC) \r
4638     ReleaseDC(hwndMain, hdc);\r
4639   \r
4640   if (lastDrawnFlipView != flipView) {\r
4641     if (flipView)\r
4642       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4643     else\r
4644       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4645   }\r
4646 \r
4647 /*  CopyBoard(lastDrawn, board);*/\r
4648   lastDrawnHighlight = highlightInfo;\r
4649   lastDrawnPremove   = premoveHighlightInfo;\r
4650   lastDrawnFlipView = flipView;\r
4651   lastDrawnValid = 1;\r
4652 }\r
4653 \r
4654 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4655 int\r
4656 SaveDiagram(f)\r
4657      FILE *f;\r
4658 {\r
4659     time_t tm;\r
4660     char *fen;\r
4661 \r
4662     saveDiagFlag = 1; diagFile = f;\r
4663     HDCDrawPosition(NULL, TRUE, NULL);\r
4664 \r
4665     saveDiagFlag = 0;\r
4666 \r
4667 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4668     \r
4669     fclose(f);\r
4670     return TRUE;\r
4671 }\r
4672 \r
4673 \r
4674 /*---------------------------------------------------------------------------*\\r
4675 | CLIENT PAINT PROCEDURE\r
4676 |   This is the main event-handler for the WM_PAINT message.\r
4677 |\r
4678 \*---------------------------------------------------------------------------*/\r
4679 VOID\r
4680 PaintProc(HWND hwnd)\r
4681 {\r
4682   HDC         hdc;\r
4683   PAINTSTRUCT ps;\r
4684   HFONT       oldFont;\r
4685 \r
4686   if(hdc = BeginPaint(hwnd, &ps)) {\r
4687     if (IsIconic(hwnd)) {\r
4688       DrawIcon(hdc, 2, 2, iconCurrent);\r
4689     } else {\r
4690       if (!appData.monoMode) {\r
4691         SelectPalette(hdc, hPal, FALSE);\r
4692         RealizePalette(hdc);\r
4693       }\r
4694       HDCDrawPosition(hdc, 1, NULL);\r
4695       oldFont =\r
4696         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4697       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4698                  ETO_CLIPPED|ETO_OPAQUE,\r
4699                  &messageRect, messageText, strlen(messageText), NULL);\r
4700       SelectObject(hdc, oldFont);\r
4701       DisplayBothClocks();\r
4702     }\r
4703     EndPaint(hwnd,&ps);\r
4704   }\r
4705 \r
4706   return;\r
4707 }\r
4708 \r
4709 \r
4710 /*\r
4711  * If the user selects on a border boundary, return -1; if off the board,\r
4712  *   return -2.  Otherwise map the event coordinate to the square.\r
4713  * The offset boardRect.left or boardRect.top must already have been\r
4714  *   subtracted from x.\r
4715  */\r
4716 int\r
4717 EventToSquare(int x)\r
4718 {\r
4719   if (x <= 0)\r
4720     return -2;\r
4721   if (x < lineGap)\r
4722     return -1;\r
4723   x -= lineGap;\r
4724   if ((x % (squareSize + lineGap)) >= squareSize)\r
4725     return -1;\r
4726   x /= (squareSize + lineGap);\r
4727   if (x >= BOARD_SIZE)\r
4728     return -2;\r
4729   return x;\r
4730 }\r
4731 \r
4732 typedef struct {\r
4733   char piece;\r
4734   int command;\r
4735   char* name;\r
4736 } DropEnable;\r
4737 \r
4738 DropEnable dropEnables[] = {\r
4739   { 'P', DP_Pawn, "Pawn" },\r
4740   { 'N', DP_Knight, "Knight" },\r
4741   { 'B', DP_Bishop, "Bishop" },\r
4742   { 'R', DP_Rook, "Rook" },\r
4743   { 'Q', DP_Queen, "Queen" },\r
4744 };\r
4745 \r
4746 VOID\r
4747 SetupDropMenu(HMENU hmenu)\r
4748 {\r
4749   int i, count, enable;\r
4750   char *p;\r
4751   extern char white_holding[], black_holding[];\r
4752   char item[MSG_SIZ];\r
4753 \r
4754   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4755     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4756                dropEnables[i].piece);\r
4757     count = 0;\r
4758     while (p && *p++ == dropEnables[i].piece) count++;\r
4759     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4760     enable = count > 0 || !appData.testLegality\r
4761       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4762                       && !appData.icsActive);\r
4763     ModifyMenu(hmenu, dropEnables[i].command,\r
4764                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4765                dropEnables[i].command, item);\r
4766   }\r
4767 }\r
4768 \r
4769 static int fromX = -1, fromY = -1, toX, toY;\r
4770 \r
4771 /* Event handler for mouse messages */\r
4772 VOID\r
4773 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4774 {\r
4775   int x, y;\r
4776   POINT pt;\r
4777   static int recursive = 0;\r
4778   HMENU hmenu;\r
4779   BOOLEAN needsRedraw = FALSE;\r
4780   BOOLEAN saveAnimate;\r
4781   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4782   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4783   ChessMove moveType;\r
4784 \r
4785   if (recursive) {\r
4786     if (message == WM_MBUTTONUP) {\r
4787       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4788          to the middle button: we simulate pressing the left button too!\r
4789          */\r
4790       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4791       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4792     }\r
4793     return;\r
4794   }\r
4795   recursive++;\r
4796   \r
4797   pt.x = LOWORD(lParam);\r
4798   pt.y = HIWORD(lParam);\r
4799   x = EventToSquare(pt.x - boardRect.left);\r
4800   y = EventToSquare(pt.y - boardRect.top);\r
4801   if (!flipView && y >= 0) {\r
4802     y = BOARD_HEIGHT - 1 - y;\r
4803   }\r
4804   if (flipView && x >= 0) {\r
4805     x = BOARD_WIDTH - 1 - x;\r
4806   }\r
4807 \r
4808   switch (message) {\r
4809   case WM_LBUTTONDOWN:\r
4810     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4811         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4812         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4813         if(gameInfo.holdingsWidth && \r
4814                 (WhiteOnMove(currentMove) \r
4815                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4816                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4817             // click in right holdings, for determining promotion piece\r
4818             ChessSquare p = boards[currentMove][y][x];\r
4819             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4820             if(p != EmptySquare) {\r
4821                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4822                 fromX = fromY = -1;\r
4823                 break;\r
4824             }\r
4825         }\r
4826         DrawPosition(FALSE, boards[currentMove]);\r
4827         break;\r
4828     }\r
4829     ErrorPopDown();\r
4830     sameAgain = FALSE;\r
4831     if (y == -2) {\r
4832       /* Downclick vertically off board; check if on clock */\r
4833       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4834         if (gameMode == EditPosition) {\r
4835           SetWhiteToPlayEvent();\r
4836         } else if (gameMode == IcsPlayingBlack ||\r
4837                    gameMode == MachinePlaysWhite) {\r
4838           CallFlagEvent();\r
4839         } else if (gameMode == EditGame) {\r
4840           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4841         }\r
4842       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4843         if (gameMode == EditPosition) {\r
4844           SetBlackToPlayEvent();\r
4845         } else if (gameMode == IcsPlayingWhite ||\r
4846                    gameMode == MachinePlaysBlack) {\r
4847           CallFlagEvent();\r
4848         } else if (gameMode == EditGame) {\r
4849           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4850         }\r
4851       }\r
4852       if (!appData.highlightLastMove) {\r
4853         ClearHighlights();\r
4854         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4855       }\r
4856       fromX = fromY = -1;\r
4857       dragInfo.start.x = dragInfo.start.y = -1;\r
4858       dragInfo.from = dragInfo.start;\r
4859       break;\r
4860     } else if (x < 0 || y < 0\r
4861       /* [HGM] block clicks between board and holdings */\r
4862               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4863               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4864               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4865         /* EditPosition, empty square, or different color piece;\r
4866            click-click move is possible */\r
4867                                ) {\r
4868       break;\r
4869     } else if (fromX == x && fromY == y) {\r
4870       /* Downclick on same square again */\r
4871       ClearHighlights();\r
4872       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4873       sameAgain = TRUE;  \r
4874     } else if (fromX != -1 &&\r
4875                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4876                                                                         ) {\r
4877       /* Downclick on different square. */\r
4878       /* [HGM] if on holdings file, should count as new first click ! */\r
4879       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4880         toX = x;\r
4881         toY = y;\r
4882         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4883            to make sure move is legal before showing promotion popup */\r
4884         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4885         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4886                 fromX = fromY = -1; \r
4887                 ClearHighlights();\r
4888                 DrawPosition(FALSE, boards[currentMove]);\r
4889                 break; \r
4890         } else \r
4891         if(moveType != ImpossibleMove) {\r
4892           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4893           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4894              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4895               appData.alwaysPromoteToQueen) {\r
4896                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4897                   if (!appData.highlightLastMove) {\r
4898                       ClearHighlights();\r
4899                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4900                   }\r
4901           } else\r
4902           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4903                   SetHighlights(fromX, fromY, toX, toY);\r
4904                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4905                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4906                      If promotion to Q is legal, all are legal! */\r
4907                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4908                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4909                     // kludge to temporarily execute move on display, wthout promotng yet\r
4910                     promotionChoice = TRUE;\r
4911                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4912                     boards[currentMove][toY][toX] = p;\r
4913                     DrawPosition(FALSE, boards[currentMove]);\r
4914                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4915                     boards[currentMove][toY][toX] = q;\r
4916                   } else\r
4917                   PromotionPopup(hwnd);\r
4918           } else {       /* not a promotion */\r
4919              if (appData.animate || appData.highlightLastMove) {\r
4920                  SetHighlights(fromX, fromY, toX, toY);\r
4921              } else {\r
4922                  ClearHighlights();\r
4923              }\r
4924              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4925              fromX = fromY = -1;\r
4926              if (appData.animate && !appData.highlightLastMove) {\r
4927                   ClearHighlights();\r
4928                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4929              }\r
4930           }\r
4931           break;\r
4932         }\r
4933         if (gotPremove) {\r
4934             /* [HGM] it seemed that braces were missing here */\r
4935             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4936             fromX = fromY = -1;\r
4937             break;\r
4938         }\r
4939       }\r
4940       ClearHighlights();\r
4941       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4942     }\r
4943     /* First downclick, or restart on a square with same color piece */\r
4944     if (!frozen && OKToStartUserMove(x, y)) {\r
4945       fromX = x;\r
4946       fromY = y;\r
4947       dragInfo.lastpos = pt;\r
4948       dragInfo.from.x = fromX;\r
4949       dragInfo.from.y = fromY;\r
4950       dragInfo.start = dragInfo.from;\r
4951       SetCapture(hwndMain);\r
4952     } else {\r
4953       fromX = fromY = -1;\r
4954       dragInfo.start.x = dragInfo.start.y = -1;\r
4955       dragInfo.from = dragInfo.start;\r
4956       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4957     }\r
4958     break;\r
4959 \r
4960   case WM_LBUTTONUP:\r
4961     ReleaseCapture();\r
4962     if (fromX == -1) break;\r
4963     if (x == fromX && y == fromY) {\r
4964       dragInfo.from.x = dragInfo.from.y = -1;\r
4965       /* Upclick on same square */\r
4966       if (sameAgain) {\r
4967         /* Clicked same square twice: abort click-click move */\r
4968         fromX = fromY = -1;\r
4969         gotPremove = 0;\r
4970         ClearPremoveHighlights();\r
4971       } else {\r
4972         /* First square clicked: start click-click move */\r
4973         SetHighlights(fromX, fromY, -1, -1);\r
4974       }\r
4975       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4976     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4977       /* Errant click; ignore */\r
4978       break;\r
4979     } else {\r
4980       /* Finish drag move. */\r
4981     if (appData.debugMode) {\r
4982         fprintf(debugFP, "release\n");\r
4983     }\r
4984       dragInfo.from.x = dragInfo.from.y = -1;\r
4985       toX = x;\r
4986       toY = y;\r
4987       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4988       appData.animate = appData.animate && !appData.animateDragging;\r
4989       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4990       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4991                 fromX = fromY = -1; \r
4992                 ClearHighlights();\r
4993                 DrawPosition(FALSE, boards[currentMove]);\r
4994                 break; \r
4995       } else \r
4996       if(moveType != ImpossibleMove) {\r
4997           /* [HGM] use move type to determine if move is promotion.\r
4998              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4999           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5000              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5001               appData.alwaysPromoteToQueen) \r
5002                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5003           else \r
5004           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5005                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5006                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5007                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5008                     // kludge to temporarily execute move on display, wthout promotng yet\r
5009                     promotionChoice = TRUE;\r
5010                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5011                     boards[currentMove][toY][toX] = p;\r
5012                     DrawPosition(FALSE, boards[currentMove]);\r
5013                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5014                     boards[currentMove][toY][toX] = q;\r
5015                     break;\r
5016                   } else\r
5017                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5018         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5019       }\r
5020       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5021       appData.animate = saveAnimate;\r
5022       fromX = fromY = -1;\r
5023       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5024         ClearHighlights();\r
5025       }\r
5026       if (appData.animate || appData.animateDragging ||\r
5027           appData.highlightDragging || gotPremove) {\r
5028         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5029       }\r
5030     }\r
5031     dragInfo.start.x = dragInfo.start.y = -1; \r
5032     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5033     break;\r
5034 \r
5035   case WM_MOUSEMOVE:\r
5036     if ((appData.animateDragging || appData.highlightDragging)\r
5037         && (wParam & MK_LBUTTON)\r
5038         && dragInfo.from.x >= 0) \r
5039     {\r
5040       BOOL full_repaint = FALSE;\r
5041 \r
5042       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5043       if (appData.animateDragging) {\r
5044         dragInfo.pos = pt;\r
5045       }\r
5046       if (appData.highlightDragging) {\r
5047         SetHighlights(fromX, fromY, x, y);\r
5048         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5049             full_repaint = TRUE;\r
5050         }\r
5051       }\r
5052       \r
5053       DrawPosition( full_repaint, NULL);\r
5054       \r
5055       dragInfo.lastpos = dragInfo.pos;\r
5056     }\r
5057     break;\r
5058 \r
5059   case WM_MOUSEWHEEL: // [DM]\r
5060     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5061        /* Mouse Wheel is being rolled forward\r
5062         * Play moves forward\r
5063         */\r
5064        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5065                 if(lastDir == 1) ForwardEvent(); else lastDir = 1; // [HGM] suppress first event in each direction\r
5066        /* Mouse Wheel is being rolled backward\r
5067         * Play moves backward\r
5068         */\r
5069        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5070                 if(lastDir == -1) BackwardEvent(); else lastDir = -1;\r
5071     }\r
5072     break;\r
5073 \r
5074   case WM_MBUTTONDOWN:\r
5075   case WM_RBUTTONDOWN:\r
5076     ErrorPopDown();\r
5077     ReleaseCapture();\r
5078     fromX = fromY = -1;\r
5079     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5080     dragInfo.start.x = dragInfo.start.y = -1;\r
5081     dragInfo.from = dragInfo.start;\r
5082     dragInfo.lastpos = dragInfo.pos;\r
5083     if (appData.highlightDragging) {\r
5084       ClearHighlights();\r
5085     }\r
5086     if(y == -2) {\r
5087       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5088       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5089           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5090       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5091           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5092       }\r
5093     }\r
5094     DrawPosition(TRUE, NULL);\r
5095 \r
5096     switch (gameMode) {\r
5097     case EditPosition:\r
5098     case IcsExamining:\r
5099       if (x < 0 || y < 0) break;\r
5100       fromX = x;\r
5101       fromY = y;\r
5102       if (message == WM_MBUTTONDOWN) {\r
5103         buttonCount = 3;  /* even if system didn't think so */\r
5104         if (wParam & MK_SHIFT) \r
5105           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5106         else\r
5107           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5108       } else { /* message == WM_RBUTTONDOWN */\r
5109 #if 0\r
5110         if (buttonCount == 3) {\r
5111           if (wParam & MK_SHIFT) \r
5112             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5113           else\r
5114             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5115         } else {\r
5116           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5117         }\r
5118 #else\r
5119         /* Just have one menu, on the right button.  Windows users don't\r
5120            think to try the middle one, and sometimes other software steals\r
5121            it, or it doesn't really exist. */\r
5122         if(gameInfo.variant != VariantShogi)\r
5123             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5124         else\r
5125             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5126 #endif\r
5127       }\r
5128       break;\r
5129     case IcsPlayingWhite:\r
5130     case IcsPlayingBlack:\r
5131     case EditGame:\r
5132     case MachinePlaysWhite:\r
5133     case MachinePlaysBlack:\r
5134       if (appData.testLegality &&\r
5135           gameInfo.variant != VariantBughouse &&\r
5136           gameInfo.variant != VariantCrazyhouse) break;\r
5137       if (x < 0 || y < 0) break;\r
5138       fromX = x;\r
5139       fromY = y;\r
5140       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5141       SetupDropMenu(hmenu);\r
5142       MenuPopup(hwnd, pt, hmenu, -1);\r
5143       break;\r
5144     default:\r
5145       break;\r
5146     }\r
5147     break;\r
5148   }\r
5149 \r
5150   recursive--;\r
5151 }\r
5152 \r
5153 /* Preprocess messages for buttons in main window */\r
5154 LRESULT CALLBACK\r
5155 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5156 {\r
5157   int id = GetWindowLong(hwnd, GWL_ID);\r
5158   int i, dir;\r
5159 \r
5160   for (i=0; i<N_BUTTONS; i++) {\r
5161     if (buttonDesc[i].id == id) break;\r
5162   }\r
5163   if (i == N_BUTTONS) return 0;\r
5164   switch (message) {\r
5165   case WM_KEYDOWN:\r
5166     switch (wParam) {\r
5167     case VK_LEFT:\r
5168     case VK_RIGHT:\r
5169       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5170       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5171       return TRUE;\r
5172     }\r
5173     break;\r
5174   case WM_CHAR:\r
5175     switch (wParam) {\r
5176     case '\r':\r
5177       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5178       return TRUE;\r
5179     case '\t':\r
5180       if (appData.icsActive) {\r
5181         if (GetKeyState(VK_SHIFT) < 0) {\r
5182           /* shifted */\r
5183           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5184           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5185           SetFocus(h);\r
5186         } else {\r
5187           /* unshifted */\r
5188           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5189           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5190           SetFocus(h);\r
5191         }\r
5192         return TRUE;\r
5193       }\r
5194       break;\r
5195     default:\r
5196       if (appData.icsActive) {\r
5197         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5198         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5199         SetFocus(h);\r
5200         SendMessage(h, WM_CHAR, wParam, lParam);\r
5201         return TRUE;\r
5202       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5203         PopUpMoveDialog((char)wParam);\r
5204       }\r
5205       break;\r
5206     }\r
5207     break;\r
5208   }\r
5209   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5210 }\r
5211 \r
5212 /* Process messages for Promotion dialog box */\r
5213 LRESULT CALLBACK\r
5214 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5215 {\r
5216   char promoChar;\r
5217 \r
5218   switch (message) {\r
5219   case WM_INITDIALOG: /* message: initialize dialog box */\r
5220     /* Center the dialog over the application window */\r
5221     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5222     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5223       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5224        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5225                SW_SHOW : SW_HIDE);\r
5226     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5227     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5228        (PieceToChar(WhiteAngel) >= 'A' &&\r
5229         PieceToChar(WhiteAngel) != '~' ||\r
5230         PieceToChar(BlackAngel) >= 'A' &&\r
5231         PieceToChar(BlackAngel) != '~'   ) ?\r
5232                SW_SHOW : SW_HIDE);\r
5233     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5234        (PieceToChar(WhiteMarshall) >= 'A' &&\r
5235         PieceToChar(WhiteMarshall) != '~' ||\r
5236         PieceToChar(BlackMarshall) >= 'A' &&\r
5237         PieceToChar(BlackMarshall) != '~'   ) ?\r
5238                SW_SHOW : SW_HIDE);\r
5239     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5240     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5241        gameInfo.variant != VariantShogi ?\r
5242                SW_SHOW : SW_HIDE);\r
5243     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5244        gameInfo.variant != VariantShogi ?\r
5245                SW_SHOW : SW_HIDE);\r
5246     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5247        gameInfo.variant == VariantShogi ?\r
5248                SW_SHOW : SW_HIDE);\r
5249     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5250        gameInfo.variant == VariantShogi ?\r
5251                SW_SHOW : SW_HIDE);\r
5252     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5253        gameInfo.variant == VariantSuper ?\r
5254                SW_SHOW : SW_HIDE);\r
5255     return TRUE;\r
5256 \r
5257   case WM_COMMAND: /* message: received a command */\r
5258     switch (LOWORD(wParam)) {\r
5259     case IDCANCEL:\r
5260       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5261       ClearHighlights();\r
5262       DrawPosition(FALSE, NULL);\r
5263       return TRUE;\r
5264     case PB_King:\r
5265       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5266       break;\r
5267     case PB_Queen:\r
5268       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5269       break;\r
5270     case PB_Rook:\r
5271       promoChar = PieceToChar(BlackRook);\r
5272       break;\r
5273     case PB_Bishop:\r
5274       promoChar = PieceToChar(BlackBishop);\r
5275       break;\r
5276     case PB_Chancellor:\r
5277       promoChar = PieceToChar(BlackMarshall);\r
5278       break;\r
5279     case PB_Archbishop:\r
5280       promoChar = PieceToChar(BlackAngel);\r
5281       break;\r
5282     case PB_Knight:\r
5283       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5284       break;\r
5285     default:\r
5286       return FALSE;\r
5287     }\r
5288     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5289     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5290        only show the popup when we are already sure the move is valid or\r
5291        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5292        will figure out it is a promotion from the promoChar. */\r
5293     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5294     if (!appData.highlightLastMove) {\r
5295       ClearHighlights();\r
5296       DrawPosition(FALSE, NULL);\r
5297     }\r
5298     return TRUE;\r
5299   }\r
5300   return FALSE;\r
5301 }\r
5302 \r
5303 /* Pop up promotion dialog */\r
5304 VOID\r
5305 PromotionPopup(HWND hwnd)\r
5306 {\r
5307   FARPROC lpProc;\r
5308 \r
5309   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5310   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5311     hwnd, (DLGPROC)lpProc);\r
5312   FreeProcInstance(lpProc);\r
5313 }\r
5314 \r
5315 /* Toggle ShowThinking */\r
5316 VOID\r
5317 ToggleShowThinking()\r
5318 {\r
5319   appData.showThinking = !appData.showThinking;\r
5320   ShowThinkingEvent();\r
5321 }\r
5322 \r
5323 VOID\r
5324 LoadGameDialog(HWND hwnd, char* title)\r
5325 {\r
5326   UINT number = 0;\r
5327   FILE *f;\r
5328   char fileTitle[MSG_SIZ];\r
5329   f = OpenFileDialog(hwnd, "rb", "",\r
5330                      appData.oldSaveStyle ? "gam" : "pgn",\r
5331                      GAME_FILT,\r
5332                      title, &number, fileTitle, NULL);\r
5333   if (f != NULL) {\r
5334     cmailMsgLoaded = FALSE;\r
5335     if (number == 0) {\r
5336       int error = GameListBuild(f);\r
5337       if (error) {\r
5338         DisplayError("Cannot build game list", error);\r
5339       } else if (!ListEmpty(&gameList) &&\r
5340                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5341         GameListPopUp(f, fileTitle);\r
5342         return;\r
5343       }\r
5344       GameListDestroy();\r
5345       number = 1;\r
5346     }\r
5347     LoadGame(f, number, fileTitle, FALSE);\r
5348   }\r
5349 }\r
5350 \r
5351 VOID\r
5352 ChangedConsoleFont()\r
5353 {\r
5354   CHARFORMAT cfmt;\r
5355   CHARRANGE tmpsel, sel;\r
5356   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5357   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5358   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5359   PARAFORMAT paraf;\r
5360 \r
5361   cfmt.cbSize = sizeof(CHARFORMAT);\r
5362   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5363   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5364   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5365    * size.  This was undocumented in the version of MSVC++ that I had\r
5366    * when I wrote the code, but is apparently documented now.\r
5367    */\r
5368   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5369   cfmt.bCharSet = f->lf.lfCharSet;\r
5370   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5371   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5372   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5373   /* Why are the following seemingly needed too? */\r
5374   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5375   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5376   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5377   tmpsel.cpMin = 0;\r
5378   tmpsel.cpMax = -1; /*999999?*/\r
5379   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5380   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5381   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5382    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5383    */\r
5384   paraf.cbSize = sizeof(paraf);\r
5385   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5386   paraf.dxStartIndent = 0;\r
5387   paraf.dxOffset = WRAP_INDENT;\r
5388   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5389   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5390 }\r
5391 \r
5392 /*---------------------------------------------------------------------------*\\r
5393  *\r
5394  * Window Proc for main window\r
5395  *\r
5396 \*---------------------------------------------------------------------------*/\r
5397 \r
5398 /* Process messages for main window, etc. */\r
5399 LRESULT CALLBACK\r
5400 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5401 {\r
5402   FARPROC lpProc;\r
5403   int wmId, wmEvent;\r
5404   char *defName;\r
5405   FILE *f;\r
5406   UINT number;\r
5407   char fileTitle[MSG_SIZ];\r
5408   char buf[MSG_SIZ];\r
5409   static SnapData sd;\r
5410 \r
5411   switch (message) {\r
5412 \r
5413   case WM_PAINT: /* message: repaint portion of window */\r
5414     PaintProc(hwnd);\r
5415     break;\r
5416 \r
5417   case WM_ERASEBKGND:\r
5418     if (IsIconic(hwnd)) {\r
5419       /* Cheat; change the message */\r
5420       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5421     } else {\r
5422       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5423     }\r
5424     break;\r
5425 \r
5426   case WM_LBUTTONDOWN:\r
5427   case WM_MBUTTONDOWN:\r
5428   case WM_RBUTTONDOWN:\r
5429   case WM_LBUTTONUP:\r
5430   case WM_MBUTTONUP:\r
5431   case WM_RBUTTONUP:\r
5432   case WM_MOUSEMOVE:\r
5433   case WM_MOUSEWHEEL:\r
5434     MouseEvent(hwnd, message, wParam, lParam);\r
5435     break;\r
5436 \r
5437   case WM_CHAR:\r
5438     \r
5439     if (appData.icsActive) {\r
5440       if (wParam == '\t') {\r
5441         if (GetKeyState(VK_SHIFT) < 0) {\r
5442           /* shifted */\r
5443           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5444           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5445           SetFocus(h);\r
5446         } else {\r
5447           /* unshifted */\r
5448           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5449           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5450           SetFocus(h);\r
5451         }\r
5452       } else {\r
5453         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5454         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5455         SetFocus(h);\r
5456         SendMessage(h, message, wParam, lParam);\r
5457       }\r
5458     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5459       PopUpMoveDialog((char)wParam);\r
5460     }\r
5461     break;\r
5462 \r
5463   case WM_PALETTECHANGED:\r
5464     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5465       int nnew;\r
5466       HDC hdc = GetDC(hwndMain);\r
5467       SelectPalette(hdc, hPal, TRUE);\r
5468       nnew = RealizePalette(hdc);\r
5469       if (nnew > 0) {\r
5470         paletteChanged = TRUE;\r
5471 #if 0\r
5472         UpdateColors(hdc);\r
5473 #else\r
5474         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5475 #endif\r
5476       }\r
5477       ReleaseDC(hwnd, hdc);\r
5478     }\r
5479     break;\r
5480 \r
5481   case WM_QUERYNEWPALETTE:\r
5482     if (!appData.monoMode /*&& paletteChanged*/) {\r
5483       int nnew;\r
5484       HDC hdc = GetDC(hwndMain);\r
5485       paletteChanged = FALSE;\r
5486       SelectPalette(hdc, hPal, FALSE);\r
5487       nnew = RealizePalette(hdc);\r
5488       if (nnew > 0) {\r
5489         InvalidateRect(hwnd, &boardRect, FALSE);\r
5490       }\r
5491       ReleaseDC(hwnd, hdc);\r
5492       return TRUE;\r
5493     }\r
5494     return FALSE;\r
5495 \r
5496   case WM_COMMAND: /* message: command from application menu */\r
5497     wmId    = LOWORD(wParam);\r
5498     wmEvent = HIWORD(wParam);\r
5499 \r
5500     switch (wmId) {\r
5501     case IDM_NewGame:\r
5502       ResetGameEvent();\r
5503       AnalysisPopDown();\r
5504       break;\r
5505 \r
5506     case IDM_NewGameFRC:\r
5507       if( NewGameFRC() == 0 ) {\r
5508         ResetGameEvent();\r
5509         AnalysisPopDown();\r
5510       }\r
5511       break;\r
5512 \r
5513     case IDM_NewVariant:\r
5514       NewVariantPopup(hwnd);\r
5515       break;\r
5516 \r
5517     case IDM_LoadGame:\r
5518       LoadGameDialog(hwnd, "Load Game from File");\r
5519       break;\r
5520 \r
5521     case IDM_LoadNextGame:\r
5522       ReloadGame(1);\r
5523       break;\r
5524 \r
5525     case IDM_LoadPrevGame:\r
5526       ReloadGame(-1);\r
5527       break;\r
5528 \r
5529     case IDM_ReloadGame:\r
5530       ReloadGame(0);\r
5531       break;\r
5532 \r
5533     case IDM_LoadPosition:\r
5534       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5535         Reset(FALSE, TRUE);\r
5536       }\r
5537       number = 1;\r
5538       f = OpenFileDialog(hwnd, "rb", "",\r
5539                          appData.oldSaveStyle ? "pos" : "fen",\r
5540                          POSITION_FILT,\r
5541                          "Load Position from File", &number, fileTitle, NULL);\r
5542       if (f != NULL) {\r
5543         LoadPosition(f, number, fileTitle);\r
5544       }\r
5545       break;\r
5546 \r
5547     case IDM_LoadNextPosition:\r
5548       ReloadPosition(1);\r
5549       break;\r
5550 \r
5551     case IDM_LoadPrevPosition:\r
5552       ReloadPosition(-1);\r
5553       break;\r
5554 \r
5555     case IDM_ReloadPosition:\r
5556       ReloadPosition(0);\r
5557       break;\r
5558 \r
5559     case IDM_SaveGame:\r
5560       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5561       f = OpenFileDialog(hwnd, "a", defName,\r
5562                          appData.oldSaveStyle ? "gam" : "pgn",\r
5563                          GAME_FILT,\r
5564                          "Save Game to File", NULL, fileTitle, NULL);\r
5565       if (f != NULL) {\r
5566         SaveGame(f, 0, "");\r
5567       }\r
5568       break;\r
5569 \r
5570     case IDM_SavePosition:\r
5571       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5572       f = OpenFileDialog(hwnd, "a", defName,\r
5573                          appData.oldSaveStyle ? "pos" : "fen",\r
5574                          POSITION_FILT,\r
5575                          "Save Position to File", NULL, fileTitle, NULL);\r
5576       if (f != NULL) {\r
5577         SavePosition(f, 0, "");\r
5578       }\r
5579       break;\r
5580 \r
5581     case IDM_SaveDiagram:\r
5582       defName = "diagram";\r
5583       f = OpenFileDialog(hwnd, "wb", defName,\r
5584                          "bmp",\r
5585                          DIAGRAM_FILT,\r
5586                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5587       if (f != NULL) {\r
5588         SaveDiagram(f);\r
5589       }\r
5590       break;\r
5591 \r
5592     case IDM_CopyGame:\r
5593       CopyGameToClipboard();\r
5594       break;\r
5595 \r
5596     case IDM_PasteGame:\r
5597       PasteGameFromClipboard();\r
5598       break;\r
5599 \r
5600     case IDM_CopyGameListToClipboard:\r
5601       CopyGameListToClipboard();\r
5602       break;\r
5603 \r
5604     /* [AS] Autodetect FEN or PGN data */\r
5605     case IDM_PasteAny:\r
5606       PasteGameOrFENFromClipboard();\r
5607       break;\r
5608 \r
5609     /* [AS] Move history */\r
5610     case IDM_ShowMoveHistory:\r
5611         if( MoveHistoryIsUp() ) {\r
5612             MoveHistoryPopDown();\r
5613         }\r
5614         else {\r
5615             MoveHistoryPopUp();\r
5616         }\r
5617         break;\r
5618 \r
5619     /* [AS] Eval graph */\r
5620     case IDM_ShowEvalGraph:\r
5621         if( EvalGraphIsUp() ) {\r
5622             EvalGraphPopDown();\r
5623         }\r
5624         else {\r
5625             EvalGraphPopUp();\r
5626         }\r
5627         break;\r
5628 \r
5629     /* [AS] Engine output */\r
5630     case IDM_ShowEngineOutput:\r
5631         if( EngineOutputIsUp() ) {\r
5632             EngineOutputPopDown();\r
5633         }\r
5634         else {\r
5635             EngineOutputPopUp();\r
5636         }\r
5637         break;\r
5638 \r
5639     /* [AS] User adjudication */\r
5640     case IDM_UserAdjudication_White:\r
5641         UserAdjudicationEvent( +1 );\r
5642         break;\r
5643 \r
5644     case IDM_UserAdjudication_Black:\r
5645         UserAdjudicationEvent( -1 );\r
5646         break;\r
5647 \r
5648     case IDM_UserAdjudication_Draw:\r
5649         UserAdjudicationEvent( 0 );\r
5650         break;\r
5651 \r
5652     /* [AS] Game list options dialog */\r
5653     case IDM_GameListOptions:\r
5654       GameListOptions();\r
5655       break;\r
5656 \r
5657     case IDM_CopyPosition:\r
5658       CopyFENToClipboard();\r
5659       break;\r
5660 \r
5661     case IDM_PastePosition:\r
5662       PasteFENFromClipboard();\r
5663       break;\r
5664 \r
5665     case IDM_MailMove:\r
5666       MailMoveEvent();\r
5667       break;\r
5668 \r
5669     case IDM_ReloadCMailMsg:\r
5670       Reset(TRUE, TRUE);\r
5671       ReloadCmailMsgEvent(FALSE);\r
5672       break;\r
5673 \r
5674     case IDM_Minimize:\r
5675       ShowWindow(hwnd, SW_MINIMIZE);\r
5676       break;\r
5677 \r
5678     case IDM_Exit:\r
5679       ExitEvent(0);\r
5680       break;\r
5681 \r
5682     case IDM_MachineWhite:\r
5683       MachineWhiteEvent();\r
5684       /*\r
5685        * refresh the tags dialog only if it's visible\r
5686        */\r
5687       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5688           char *tags;\r
5689           tags = PGNTags(&gameInfo);\r
5690           TagsPopUp(tags, CmailMsg());\r
5691           free(tags);\r
5692       }\r
5693       break;\r
5694 \r
5695     case IDM_MachineBlack:\r
5696       MachineBlackEvent();\r
5697       /*\r
5698        * refresh the tags dialog only if it's visible\r
5699        */\r
5700       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5701           char *tags;\r
5702           tags = PGNTags(&gameInfo);\r
5703           TagsPopUp(tags, CmailMsg());\r
5704           free(tags);\r
5705       }\r
5706       break;\r
5707 \r
5708     case IDM_TwoMachines:\r
5709       TwoMachinesEvent();\r
5710       /*\r
5711        * refresh the tags dialog only if it's visible\r
5712        */\r
5713       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5714           char *tags;\r
5715           tags = PGNTags(&gameInfo);\r
5716           TagsPopUp(tags, CmailMsg());\r
5717           free(tags);\r
5718       }\r
5719       break;\r
5720 \r
5721     case IDM_AnalysisMode:\r
5722       if (!first.analysisSupport) {\r
5723         sprintf(buf, "%s does not support analysis", first.tidy);\r
5724         DisplayError(buf, 0);\r
5725       } else {\r
5726         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5727         if (appData.icsActive) {\r
5728                if (gameMode != IcsObserving) {\r
5729                        sprintf(buf, "You are not observing a game");\r
5730                        DisplayError(buf, 0);\r
5731                        /* secure check */\r
5732                        if (appData.icsEngineAnalyze) {\r
5733                                if (appData.debugMode) \r
5734                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5735                                ExitAnalyzeMode();\r
5736                                ModeHighlight();\r
5737                                break;\r
5738                        }\r
5739                        break;\r
5740                } else {\r
5741                        /* if enable, user want disable icsEngineAnalyze */\r
5742                        if (appData.icsEngineAnalyze) {\r
5743                                ExitAnalyzeMode();\r
5744                                ModeHighlight();\r
5745                                break;\r
5746                        }\r
5747                        appData.icsEngineAnalyze = TRUE;\r
5748                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5749                }\r
5750         } \r
5751         if (!appData.showThinking) ToggleShowThinking();\r
5752         AnalyzeModeEvent();\r
5753       }\r
5754       break;\r
5755 \r
5756     case IDM_AnalyzeFile:\r
5757       if (!first.analysisSupport) {\r
5758         char buf[MSG_SIZ];\r
5759         sprintf(buf, "%s does not support analysis", first.tidy);\r
5760         DisplayError(buf, 0);\r
5761       } else {\r
5762         if (!appData.showThinking) ToggleShowThinking();\r
5763         AnalyzeFileEvent();\r
5764         LoadGameDialog(hwnd, "Analyze Game from File");\r
5765         AnalysisPeriodicEvent(1);\r
5766       }\r
5767       break;\r
5768 \r
5769     case IDM_IcsClient:\r
5770       IcsClientEvent();\r
5771       break;\r
5772 \r
5773     case IDM_EditGame:\r
5774       EditGameEvent();\r
5775       break;\r
5776 \r
5777     case IDM_EditPosition:\r
5778       EditPositionEvent();\r
5779       break;\r
5780 \r
5781     case IDM_Training:\r
5782       TrainingEvent();\r
5783       break;\r
5784 \r
5785     case IDM_ShowGameList:\r
5786       ShowGameListProc();\r
5787       break;\r
5788 \r
5789     case IDM_EditTags:\r
5790       EditTagsProc();\r
5791       break;\r
5792 \r
5793     case IDM_EditComment:\r
5794       if (commentDialogUp && editComment) {\r
5795         CommentPopDown();\r
5796       } else {\r
5797         EditCommentEvent();\r
5798       }\r
5799       break;\r
5800 \r
5801     case IDM_Pause:\r
5802       PauseEvent();\r
5803       break;\r
5804 \r
5805     case IDM_Accept:\r
5806       AcceptEvent();\r
5807       break;\r
5808 \r
5809     case IDM_Decline:\r
5810       DeclineEvent();\r
5811       break;\r
5812 \r
5813     case IDM_Rematch:\r
5814       RematchEvent();\r
5815       break;\r
5816 \r
5817     case IDM_CallFlag:\r
5818       CallFlagEvent();\r
5819       break;\r
5820 \r
5821     case IDM_Draw:\r
5822       DrawEvent();\r
5823       break;\r
5824 \r
5825     case IDM_Adjourn:\r
5826       AdjournEvent();\r
5827       break;\r
5828 \r
5829     case IDM_Abort:\r
5830       AbortEvent();\r
5831       break;\r
5832 \r
5833     case IDM_Resign:\r
5834       ResignEvent();\r
5835       break;\r
5836 \r
5837     case IDM_StopObserving:\r
5838       StopObservingEvent();\r
5839       break;\r
5840 \r
5841     case IDM_StopExamining:\r
5842       StopExaminingEvent();\r
5843       break;\r
5844 \r
5845     case IDM_TypeInMove:\r
5846       PopUpMoveDialog('\000');\r
5847       break;\r
5848 \r
5849     case IDM_TypeInName:\r
5850       PopUpNameDialog('\000');\r
5851       break;\r
5852 \r
5853     case IDM_Backward:\r
5854       BackwardEvent();\r
5855       SetFocus(hwndMain);\r
5856       break;\r
5857 \r
5858     case IDM_Forward:\r
5859       ForwardEvent();\r
5860       SetFocus(hwndMain);\r
5861       break;\r
5862 \r
5863     case IDM_ToStart:\r
5864       ToStartEvent();\r
5865       SetFocus(hwndMain);\r
5866       break;\r
5867 \r
5868     case IDM_ToEnd:\r
5869       ToEndEvent();\r
5870       SetFocus(hwndMain);\r
5871       break;\r
5872 \r
5873     case IDM_Revert:\r
5874       RevertEvent();\r
5875       break;\r
5876 \r
5877     case IDM_TruncateGame:\r
5878       TruncateGameEvent();\r
5879       break;\r
5880 \r
5881     case IDM_MoveNow:\r
5882       MoveNowEvent();\r
5883       break;\r
5884 \r
5885     case IDM_RetractMove:\r
5886       RetractMoveEvent();\r
5887       break;\r
5888 \r
5889     case IDM_FlipView:\r
5890       flipView = !flipView;\r
5891       DrawPosition(FALSE, NULL);\r
5892       break;\r
5893 \r
5894     case IDM_FlipClock:\r
5895       flipClock = !flipClock;\r
5896       DisplayBothClocks();\r
5897       break;\r
5898 \r
5899     case IDM_GeneralOptions:\r
5900       GeneralOptionsPopup(hwnd);\r
5901       DrawPosition(TRUE, NULL);\r
5902       break;\r
5903 \r
5904     case IDM_BoardOptions:\r
5905       BoardOptionsPopup(hwnd);\r
5906       break;\r
5907 \r
5908     case IDM_EnginePlayOptions:\r
5909       EnginePlayOptionsPopup(hwnd);\r
5910       break;\r
5911 \r
5912     case IDM_OptionsUCI:\r
5913       UciOptionsPopup(hwnd);\r
5914       break;\r
5915 \r
5916     case IDM_IcsOptions:\r
5917       IcsOptionsPopup(hwnd);\r
5918       break;\r
5919 \r
5920     case IDM_Fonts:\r
5921       FontsOptionsPopup(hwnd);\r
5922       break;\r
5923 \r
5924     case IDM_Sounds:\r
5925       SoundOptionsPopup(hwnd);\r
5926       break;\r
5927 \r
5928     case IDM_CommPort:\r
5929       CommPortOptionsPopup(hwnd);\r
5930       break;\r
5931 \r
5932     case IDM_LoadOptions:\r
5933       LoadOptionsPopup(hwnd);\r
5934       break;\r
5935 \r
5936     case IDM_SaveOptions:\r
5937       SaveOptionsPopup(hwnd);\r
5938       break;\r
5939 \r
5940     case IDM_TimeControl:\r
5941       TimeControlOptionsPopup(hwnd);\r
5942       break;\r
5943 \r
5944     case IDM_SaveSettings:\r
5945       SaveSettings(settingsFileName);\r
5946       break;\r
5947 \r
5948     case IDM_SaveSettingsOnExit:\r
5949       saveSettingsOnExit = !saveSettingsOnExit;\r
5950       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5951                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5952                                          MF_CHECKED : MF_UNCHECKED));\r
5953       break;\r
5954 \r
5955     case IDM_Hint:\r
5956       HintEvent();\r
5957       break;\r
5958 \r
5959     case IDM_Book:\r
5960       BookEvent();\r
5961       break;\r
5962 \r
5963     case IDM_AboutGame:\r
5964       AboutGameEvent();\r
5965       break;\r
5966 \r
5967     case IDM_Debug:\r
5968       appData.debugMode = !appData.debugMode;\r
5969       if (appData.debugMode) {\r
5970         char dir[MSG_SIZ];\r
5971         GetCurrentDirectory(MSG_SIZ, dir);\r
5972         SetCurrentDirectory(installDir);\r
5973         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5974         SetCurrentDirectory(dir);\r
5975         setbuf(debugFP, NULL);\r
5976       } else {\r
5977         fclose(debugFP);\r
5978         debugFP = NULL;\r
5979       }\r
5980       break;\r
5981 \r
5982     case IDM_HELPCONTENTS:\r
5983       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5984         MessageBox (GetFocus(),\r
5985                     "Unable to activate help",\r
5986                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5987       }\r
5988       break;\r
5989 \r
5990     case IDM_HELPSEARCH:\r
5991       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5992         MessageBox (GetFocus(),\r
5993                     "Unable to activate help",\r
5994                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5995       }\r
5996       break;\r
5997 \r
5998     case IDM_HELPHELP:\r
5999       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6000         MessageBox (GetFocus(),\r
6001                     "Unable to activate help",\r
6002                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6003       }\r
6004       break;\r
6005 \r
6006     case IDM_ABOUT:\r
6007       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6008       DialogBox(hInst, \r
6009         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6010         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6011       FreeProcInstance(lpProc);\r
6012       break;\r
6013 \r
6014     case IDM_DirectCommand1:\r
6015       AskQuestionEvent("Direct Command",\r
6016                        "Send to chess program:", "", "1");\r
6017       break;\r
6018     case IDM_DirectCommand2:\r
6019       AskQuestionEvent("Direct Command",\r
6020                        "Send to second chess program:", "", "2");\r
6021       break;\r
6022 \r
6023     case EP_WhitePawn:\r
6024       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6025       fromX = fromY = -1;\r
6026       break;\r
6027 \r
6028     case EP_WhiteKnight:\r
6029       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6030       fromX = fromY = -1;\r
6031       break;\r
6032 \r
6033     case EP_WhiteBishop:\r
6034       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6035       fromX = fromY = -1;\r
6036       break;\r
6037 \r
6038     case EP_WhiteRook:\r
6039       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6040       fromX = fromY = -1;\r
6041       break;\r
6042 \r
6043     case EP_WhiteQueen:\r
6044       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6045       fromX = fromY = -1;\r
6046       break;\r
6047 \r
6048     case EP_WhiteFerz:\r
6049       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6050       fromX = fromY = -1;\r
6051       break;\r
6052 \r
6053     case EP_WhiteWazir:\r
6054       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6055       fromX = fromY = -1;\r
6056       break;\r
6057 \r
6058     case EP_WhiteAlfil:\r
6059       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6060       fromX = fromY = -1;\r
6061       break;\r
6062 \r
6063     case EP_WhiteCannon:\r
6064       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6065       fromX = fromY = -1;\r
6066       break;\r
6067 \r
6068     case EP_WhiteCardinal:\r
6069       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6070       fromX = fromY = -1;\r
6071       break;\r
6072 \r
6073     case EP_WhiteMarshall:\r
6074       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6075       fromX = fromY = -1;\r
6076       break;\r
6077 \r
6078     case EP_WhiteKing:\r
6079       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6080       fromX = fromY = -1;\r
6081       break;\r
6082 \r
6083     case EP_BlackPawn:\r
6084       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6085       fromX = fromY = -1;\r
6086       break;\r
6087 \r
6088     case EP_BlackKnight:\r
6089       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6090       fromX = fromY = -1;\r
6091       break;\r
6092 \r
6093     case EP_BlackBishop:\r
6094       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6095       fromX = fromY = -1;\r
6096       break;\r
6097 \r
6098     case EP_BlackRook:\r
6099       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6100       fromX = fromY = -1;\r
6101       break;\r
6102 \r
6103     case EP_BlackQueen:\r
6104       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6105       fromX = fromY = -1;\r
6106       break;\r
6107 \r
6108     case EP_BlackFerz:\r
6109       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6110       fromX = fromY = -1;\r
6111       break;\r
6112 \r
6113     case EP_BlackWazir:\r
6114       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6115       fromX = fromY = -1;\r
6116       break;\r
6117 \r
6118     case EP_BlackAlfil:\r
6119       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6120       fromX = fromY = -1;\r
6121       break;\r
6122 \r
6123     case EP_BlackCannon:\r
6124       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6125       fromX = fromY = -1;\r
6126       break;\r
6127 \r
6128     case EP_BlackCardinal:\r
6129       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6130       fromX = fromY = -1;\r
6131       break;\r
6132 \r
6133     case EP_BlackMarshall:\r
6134       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6135       fromX = fromY = -1;\r
6136       break;\r
6137 \r
6138     case EP_BlackKing:\r
6139       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6140       fromX = fromY = -1;\r
6141       break;\r
6142 \r
6143     case EP_EmptySquare:\r
6144       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6145       fromX = fromY = -1;\r
6146       break;\r
6147 \r
6148     case EP_ClearBoard:\r
6149       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6150       fromX = fromY = -1;\r
6151       break;\r
6152 \r
6153     case EP_White:\r
6154       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6155       fromX = fromY = -1;\r
6156       break;\r
6157 \r
6158     case EP_Black:\r
6159       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6160       fromX = fromY = -1;\r
6161       break;\r
6162 \r
6163     case EP_Promote:\r
6164       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6165       fromX = fromY = -1;\r
6166       break;\r
6167 \r
6168     case EP_Demote:\r
6169       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6170       fromX = fromY = -1;\r
6171       break;\r
6172 \r
6173     case DP_Pawn:\r
6174       DropMenuEvent(WhitePawn, fromX, fromY);\r
6175       fromX = fromY = -1;\r
6176       break;\r
6177 \r
6178     case DP_Knight:\r
6179       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6180       fromX = fromY = -1;\r
6181       break;\r
6182 \r
6183     case DP_Bishop:\r
6184       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6185       fromX = fromY = -1;\r
6186       break;\r
6187 \r
6188     case DP_Rook:\r
6189       DropMenuEvent(WhiteRook, fromX, fromY);\r
6190       fromX = fromY = -1;\r
6191       break;\r
6192 \r
6193     case DP_Queen:\r
6194       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6195       fromX = fromY = -1;\r
6196       break;\r
6197 \r
6198     default:\r
6199       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6200     }\r
6201     break;\r
6202 \r
6203   case WM_TIMER:\r
6204     switch (wParam) {\r
6205     case CLOCK_TIMER_ID:\r
6206       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6207       clockTimerEvent = 0;\r
6208       DecrementClocks(); /* call into back end */\r
6209       break;\r
6210     case LOAD_GAME_TIMER_ID:\r
6211       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6212       loadGameTimerEvent = 0;\r
6213       AutoPlayGameLoop(); /* call into back end */\r
6214       break;\r
6215     case ANALYSIS_TIMER_ID:\r
6216       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6217                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6218         AnalysisPeriodicEvent(0);\r
6219       } else {\r
6220         KillTimer(hwnd, analysisTimerEvent);\r
6221         analysisTimerEvent = 0;\r
6222       }\r
6223       break;\r
6224     case DELAYED_TIMER_ID:\r
6225       KillTimer(hwnd, delayedTimerEvent);\r
6226       delayedTimerEvent = 0;\r
6227       delayedTimerCallback();\r
6228       break;\r
6229     }\r
6230     break;\r
6231 \r
6232   case WM_USER_Input:\r
6233     InputEvent(hwnd, message, wParam, lParam);\r
6234     break;\r
6235 \r
6236   /* [AS] Also move "attached" child windows */\r
6237   case WM_WINDOWPOSCHANGING:\r
6238     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6239         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6240 \r
6241         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6242             /* Window is moving */\r
6243             RECT rcMain;\r
6244 \r
6245             GetWindowRect( hwnd, &rcMain );\r
6246             \r
6247             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6248             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6249             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6250         }\r
6251     }\r
6252     break;\r
6253 \r
6254   /* [AS] Snapping */\r
6255   case WM_ENTERSIZEMOVE:\r
6256     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6257     if (hwnd == hwndMain) {\r
6258       doingSizing = TRUE;\r
6259       lastSizing = 0;\r
6260     }\r
6261     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6262     break;\r
6263 \r
6264   case WM_SIZING:\r
6265     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6266     if (hwnd == hwndMain) {\r
6267       lastSizing = wParam;\r
6268     }\r
6269     break;\r
6270 \r
6271   case WM_MOVING:\r
6272     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6273       return OnMoving( &sd, hwnd, wParam, lParam );\r
6274 \r
6275   case WM_EXITSIZEMOVE:\r
6276     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6277     if (hwnd == hwndMain) {\r
6278       RECT client;\r
6279       doingSizing = FALSE;\r
6280       InvalidateRect(hwnd, &boardRect, FALSE);\r
6281       GetClientRect(hwnd, &client);\r
6282       ResizeBoard(client.right, client.bottom, lastSizing);\r
6283       lastSizing = 0;\r
6284       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6285     }\r
6286     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6287     break;\r
6288 \r
6289   case WM_DESTROY: /* message: window being destroyed */\r
6290     PostQuitMessage(0);\r
6291     break;\r
6292 \r
6293   case WM_CLOSE:\r
6294     if (hwnd == hwndMain) {\r
6295       ExitEvent(0);\r
6296     }\r
6297     break;\r
6298 \r
6299   default:      /* Passes it on if unprocessed */\r
6300     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6301   }\r
6302   return 0;\r
6303 }\r
6304 \r
6305 /*---------------------------------------------------------------------------*\\r
6306  *\r
6307  * Misc utility routines\r
6308  *\r
6309 \*---------------------------------------------------------------------------*/\r
6310 \r
6311 /*\r
6312  * Decent random number generator, at least not as bad as Windows\r
6313  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6314  */\r
6315 unsigned int randstate;\r
6316 \r
6317 int\r
6318 myrandom(void)\r
6319 {\r
6320   randstate = randstate * 1664525 + 1013904223;\r
6321   return (int) randstate & 0x7fffffff;\r
6322 }\r
6323 \r
6324 void\r
6325 mysrandom(unsigned int seed)\r
6326 {\r
6327   randstate = seed;\r
6328 }\r
6329 \r
6330 \r
6331 /* \r
6332  * returns TRUE if user selects a different color, FALSE otherwise \r
6333  */\r
6334 \r
6335 BOOL\r
6336 ChangeColor(HWND hwnd, COLORREF *which)\r
6337 {\r
6338   static BOOL firstTime = TRUE;\r
6339   static DWORD customColors[16];\r
6340   CHOOSECOLOR cc;\r
6341   COLORREF newcolor;\r
6342   int i;\r
6343   ColorClass ccl;\r
6344 \r
6345   if (firstTime) {\r
6346     /* Make initial colors in use available as custom colors */\r
6347     /* Should we put the compiled-in defaults here instead? */\r
6348     i = 0;\r
6349     customColors[i++] = lightSquareColor & 0xffffff;\r
6350     customColors[i++] = darkSquareColor & 0xffffff;\r
6351     customColors[i++] = whitePieceColor & 0xffffff;\r
6352     customColors[i++] = blackPieceColor & 0xffffff;\r
6353     customColors[i++] = highlightSquareColor & 0xffffff;\r
6354     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6355 \r
6356     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6357       customColors[i++] = textAttribs[ccl].color;\r
6358     }\r
6359     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6360     firstTime = FALSE;\r
6361   }\r
6362 \r
6363   cc.lStructSize = sizeof(cc);\r
6364   cc.hwndOwner = hwnd;\r
6365   cc.hInstance = NULL;\r
6366   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6367   cc.lpCustColors = (LPDWORD) customColors;\r
6368   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6369 \r
6370   if (!ChooseColor(&cc)) return FALSE;\r
6371 \r
6372   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6373   if (newcolor == *which) return FALSE;\r
6374   *which = newcolor;\r
6375   return TRUE;\r
6376 \r
6377   /*\r
6378   InitDrawingColors();\r
6379   InvalidateRect(hwnd, &boardRect, FALSE);\r
6380   */\r
6381 }\r
6382 \r
6383 BOOLEAN\r
6384 MyLoadSound(MySound *ms)\r
6385 {\r
6386   BOOL ok = FALSE;\r
6387   struct stat st;\r
6388   FILE *f;\r
6389 \r
6390   if (ms->data) free(ms->data);\r
6391   ms->data = NULL;\r
6392 \r
6393   switch (ms->name[0]) {\r
6394   case NULLCHAR:\r
6395     /* Silence */\r
6396     ok = TRUE;\r
6397     break;\r
6398   case '$':\r
6399     /* System sound from Control Panel.  Don't preload here. */\r
6400     ok = TRUE;\r
6401     break;\r
6402   case '!':\r
6403     if (ms->name[1] == NULLCHAR) {\r
6404       /* "!" alone = silence */\r
6405       ok = TRUE;\r
6406     } else {\r
6407       /* Builtin wave resource.  Error if not found. */\r
6408       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6409       if (h == NULL) break;\r
6410       ms->data = (void *)LoadResource(hInst, h);\r
6411       if (h == NULL) break;\r
6412       ok = TRUE;\r
6413     }\r
6414     break;\r
6415   default:\r
6416     /* .wav file.  Error if not found. */\r
6417     f = fopen(ms->name, "rb");\r
6418     if (f == NULL) break;\r
6419     if (fstat(fileno(f), &st) < 0) break;\r
6420     ms->data = malloc(st.st_size);\r
6421     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6422     fclose(f);\r
6423     ok = TRUE;\r
6424     break;\r
6425   }\r
6426   if (!ok) {\r
6427     char buf[MSG_SIZ];\r
6428     sprintf(buf, "Error loading sound %s", ms->name);\r
6429     DisplayError(buf, GetLastError());\r
6430   }\r
6431   return ok;\r
6432 }\r
6433 \r
6434 BOOLEAN\r
6435 MyPlaySound(MySound *ms)\r
6436 {\r
6437   BOOLEAN ok = FALSE;\r
6438   switch (ms->name[0]) {\r
6439   case NULLCHAR:\r
6440     /* Silence */\r
6441     ok = TRUE;\r
6442     break;\r
6443   case '$':\r
6444     /* System sound from Control Panel (deprecated feature).\r
6445        "$" alone or an unset sound name gets default beep (still in use). */\r
6446     if (ms->name[1]) {\r
6447       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6448     }\r
6449     if (!ok) ok = MessageBeep(MB_OK);\r
6450     break; \r
6451   case '!':\r
6452     /* Builtin wave resource, or "!" alone for silence */\r
6453     if (ms->name[1]) {\r
6454       if (ms->data == NULL) return FALSE;\r
6455       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6456     } else {\r
6457       ok = TRUE;\r
6458     }\r
6459     break;\r
6460   default:\r
6461     /* .wav file.  Error if not found. */\r
6462     if (ms->data == NULL) return FALSE;\r
6463     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6464     break;\r
6465   }\r
6466   /* Don't print an error: this can happen innocently if the sound driver\r
6467      is busy; for instance, if another instance of WinBoard is playing\r
6468      a sound at about the same time. */\r
6469 #if 0\r
6470   if (!ok) {\r
6471     char buf[MSG_SIZ];\r
6472     sprintf(buf, "Error playing sound %s", ms->name);\r
6473     DisplayError(buf, GetLastError());\r
6474   }\r
6475 #endif\r
6476   return ok;\r
6477 }\r
6478 \r
6479 \r
6480 LRESULT CALLBACK\r
6481 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6482 {\r
6483   BOOL ok;\r
6484   OPENFILENAME *ofn;\r
6485   static UINT *number; /* gross that this is static */\r
6486 \r
6487   switch (message) {\r
6488   case WM_INITDIALOG: /* message: initialize dialog box */\r
6489     /* Center the dialog over the application window */\r
6490     ofn = (OPENFILENAME *) lParam;\r
6491     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6492       number = (UINT *) ofn->lCustData;\r
6493       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6494     } else {\r
6495       number = NULL;\r
6496     }\r
6497     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6498     return FALSE;  /* Allow for further processing */\r
6499 \r
6500   case WM_COMMAND:\r
6501     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6502       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6503     }\r
6504     return FALSE;  /* Allow for further processing */\r
6505   }\r
6506   return FALSE;\r
6507 }\r
6508 \r
6509 UINT APIENTRY\r
6510 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6511 {\r
6512   static UINT *number;\r
6513   OPENFILENAME *ofname;\r
6514   OFNOTIFY *ofnot;\r
6515   switch (uiMsg) {\r
6516   case WM_INITDIALOG:\r
6517     ofname = (OPENFILENAME *)lParam;\r
6518     number = (UINT *)(ofname->lCustData);\r
6519     break;\r
6520   case WM_NOTIFY:\r
6521     ofnot = (OFNOTIFY *)lParam;\r
6522     if (ofnot->hdr.code == CDN_FILEOK) {\r
6523       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6524     }\r
6525     break;\r
6526   }\r
6527   return 0;\r
6528 }\r
6529 \r
6530 \r
6531 FILE *\r
6532 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6533                char *nameFilt, char *dlgTitle, UINT *number,\r
6534                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6535 {\r
6536   OPENFILENAME openFileName;\r
6537   char buf1[MSG_SIZ];\r
6538   FILE *f;\r
6539 \r
6540   if (fileName == NULL) fileName = buf1;\r
6541   if (defName == NULL) {\r
6542     strcpy(fileName, "*.");\r
6543     strcat(fileName, defExt);\r
6544   } else {\r
6545     strcpy(fileName, defName);\r
6546   }\r
6547   if (fileTitle) strcpy(fileTitle, "");\r
6548   if (number) *number = 0;\r
6549 \r
6550   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6551   openFileName.hwndOwner         = hwnd;\r
6552   openFileName.hInstance         = (HANDLE) hInst;\r
6553   openFileName.lpstrFilter       = nameFilt;\r
6554   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6555   openFileName.nMaxCustFilter    = 0L;\r
6556   openFileName.nFilterIndex      = 1L;\r
6557   openFileName.lpstrFile         = fileName;\r
6558   openFileName.nMaxFile          = MSG_SIZ;\r
6559   openFileName.lpstrFileTitle    = fileTitle;\r
6560   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6561   openFileName.lpstrInitialDir   = NULL;\r
6562   openFileName.lpstrTitle        = dlgTitle;\r
6563   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6564     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6565     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6566     | (oldDialog ? 0 : OFN_EXPLORER);\r
6567   openFileName.nFileOffset       = 0;\r
6568   openFileName.nFileExtension    = 0;\r
6569   openFileName.lpstrDefExt       = defExt;\r
6570   openFileName.lCustData         = (LONG) number;\r
6571   openFileName.lpfnHook          = oldDialog ?\r
6572     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6573   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6574 \r
6575   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6576                         GetOpenFileName(&openFileName)) {\r
6577     /* open the file */\r
6578     f = fopen(openFileName.lpstrFile, write);\r
6579     if (f == NULL) {\r
6580       MessageBox(hwnd, "File open failed", NULL,\r
6581                  MB_OK|MB_ICONEXCLAMATION);\r
6582       return NULL;\r
6583     }\r
6584   } else {\r
6585     int err = CommDlgExtendedError();\r
6586     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6587     return FALSE;\r
6588   }\r
6589   return f;\r
6590 }\r
6591 \r
6592 \r
6593 \r
6594 VOID APIENTRY\r
6595 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6596 {\r
6597   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6598 \r
6599   /*\r
6600    * Get the first pop-up menu in the menu template. This is the\r
6601    * menu that TrackPopupMenu displays.\r
6602    */\r
6603   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6604 \r
6605   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6606 \r
6607   /*\r
6608    * TrackPopup uses screen coordinates, so convert the\r
6609    * coordinates of the mouse click to screen coordinates.\r
6610    */\r
6611   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6612 \r
6613   /* Draw and track the floating pop-up menu. */\r
6614   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6615                  pt.x, pt.y, 0, hwnd, NULL);\r
6616 \r
6617   /* Destroy the menu.*/\r
6618   DestroyMenu(hmenu);\r
6619 }\r
6620    \r
6621 typedef struct {\r
6622   HWND hDlg, hText;\r
6623   int sizeX, sizeY, newSizeX, newSizeY;\r
6624   HDWP hdwp;\r
6625 } ResizeEditPlusButtonsClosure;\r
6626 \r
6627 BOOL CALLBACK\r
6628 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6629 {\r
6630   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6631   RECT rect;\r
6632   POINT pt;\r
6633 \r
6634   if (hChild == cl->hText) return TRUE;\r
6635   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6636   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6637   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6638   ScreenToClient(cl->hDlg, &pt);\r
6639   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6640     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6641   return TRUE;\r
6642 }\r
6643 \r
6644 /* Resize a dialog that has a (rich) edit field filling most of\r
6645    the top, with a row of buttons below */\r
6646 VOID\r
6647 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6648 {\r
6649   RECT rectText;\r
6650   int newTextHeight, newTextWidth;\r
6651   ResizeEditPlusButtonsClosure cl;\r
6652   \r
6653   /*if (IsIconic(hDlg)) return;*/\r
6654   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6655   \r
6656   cl.hdwp = BeginDeferWindowPos(8);\r
6657 \r
6658   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6659   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6660   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6661   if (newTextHeight < 0) {\r
6662     newSizeY += -newTextHeight;\r
6663     newTextHeight = 0;\r
6664   }\r
6665   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6666     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6667 \r
6668   cl.hDlg = hDlg;\r
6669   cl.hText = hText;\r
6670   cl.sizeX = sizeX;\r
6671   cl.sizeY = sizeY;\r
6672   cl.newSizeX = newSizeX;\r
6673   cl.newSizeY = newSizeY;\r
6674   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6675 \r
6676   EndDeferWindowPos(cl.hdwp);\r
6677 }\r
6678 \r
6679 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6680 {\r
6681     RECT    rChild, rParent;\r
6682     int     wChild, hChild, wParent, hParent;\r
6683     int     wScreen, hScreen, xNew, yNew;\r
6684     HDC     hdc;\r
6685 \r
6686     /* Get the Height and Width of the child window */\r
6687     GetWindowRect (hwndChild, &rChild);\r
6688     wChild = rChild.right - rChild.left;\r
6689     hChild = rChild.bottom - rChild.top;\r
6690 \r
6691     /* Get the Height and Width of the parent window */\r
6692     GetWindowRect (hwndParent, &rParent);\r
6693     wParent = rParent.right - rParent.left;\r
6694     hParent = rParent.bottom - rParent.top;\r
6695 \r
6696     /* Get the display limits */\r
6697     hdc = GetDC (hwndChild);\r
6698     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6699     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6700     ReleaseDC(hwndChild, hdc);\r
6701 \r
6702     /* Calculate new X position, then adjust for screen */\r
6703     xNew = rParent.left + ((wParent - wChild) /2);\r
6704     if (xNew < 0) {\r
6705         xNew = 0;\r
6706     } else if ((xNew+wChild) > wScreen) {\r
6707         xNew = wScreen - wChild;\r
6708     }\r
6709 \r
6710     /* Calculate new Y position, then adjust for screen */\r
6711     if( mode == 0 ) {\r
6712         yNew = rParent.top  + ((hParent - hChild) /2);\r
6713     }\r
6714     else {\r
6715         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6716     }\r
6717 \r
6718     if (yNew < 0) {\r
6719         yNew = 0;\r
6720     } else if ((yNew+hChild) > hScreen) {\r
6721         yNew = hScreen - hChild;\r
6722     }\r
6723 \r
6724     /* Set it, and return */\r
6725     return SetWindowPos (hwndChild, NULL,\r
6726                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6727 }\r
6728 \r
6729 /* Center one window over another */\r
6730 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6731 {\r
6732     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6733 }\r
6734 \r
6735 /*---------------------------------------------------------------------------*\\r
6736  *\r
6737  * Startup Dialog functions\r
6738  *\r
6739 \*---------------------------------------------------------------------------*/\r
6740 void\r
6741 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6742 {\r
6743   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6744 \r
6745   while (*cd != NULL) {\r
6746     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6747     cd++;\r
6748   }\r
6749 }\r
6750 \r
6751 void\r
6752 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6753 {\r
6754   char buf1[ARG_MAX];\r
6755   int len;\r
6756 \r
6757   if (str[0] == '@') {\r
6758     FILE* f = fopen(str + 1, "r");\r
6759     if (f == NULL) {\r
6760       DisplayFatalError(str + 1, errno, 2);\r
6761       return;\r
6762     }\r
6763     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6764     fclose(f);\r
6765     buf1[len] = NULLCHAR;\r
6766     str = buf1;\r
6767   }\r
6768 \r
6769   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6770 \r
6771   for (;;) {\r
6772     char buf[MSG_SIZ];\r
6773     char *end = strchr(str, '\n');\r
6774     if (end == NULL) return;\r
6775     memcpy(buf, str, end - str);\r
6776     buf[end - str] = NULLCHAR;\r
6777     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6778     str = end + 1;\r
6779   }\r
6780 }\r
6781 \r
6782 void\r
6783 SetStartupDialogEnables(HWND hDlg)\r
6784 {\r
6785   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6786     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6787     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6788   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6789     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6790   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6791     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6792   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6793     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6794   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6795     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6796     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6797     IsDlgButtonChecked(hDlg, OPT_View));\r
6798 }\r
6799 \r
6800 char *\r
6801 QuoteForFilename(char *filename)\r
6802 {\r
6803   int dquote, space;\r
6804   dquote = strchr(filename, '"') != NULL;\r
6805   space = strchr(filename, ' ') != NULL;\r
6806   if (dquote || space) {\r
6807     if (dquote) {\r
6808       return "'";\r
6809     } else {\r
6810       return "\"";\r
6811     }\r
6812   } else {\r
6813     return "";\r
6814   }\r
6815 }\r
6816 \r
6817 VOID\r
6818 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6819 {\r
6820   char buf[MSG_SIZ];\r
6821   char *q;\r
6822 \r
6823   InitComboStringsFromOption(hwndCombo, nthnames);\r
6824   q = QuoteForFilename(nthcp);\r
6825   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6826   if (*nthdir != NULLCHAR) {\r
6827     q = QuoteForFilename(nthdir);\r
6828     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6829   }\r
6830   if (*nthcp == NULLCHAR) {\r
6831     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6832   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6833     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6834     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6835   }\r
6836 }\r
6837 \r
6838 LRESULT CALLBACK\r
6839 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6840 {\r
6841   char buf[MSG_SIZ];\r
6842   HANDLE hwndCombo;\r
6843   char *p;\r
6844 \r
6845   switch (message) {\r
6846   case WM_INITDIALOG:\r
6847     /* Center the dialog */\r
6848     CenterWindow (hDlg, GetDesktopWindow());\r
6849     /* Initialize the dialog items */\r
6850     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6851                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6852                   firstChessProgramNames);\r
6853     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6854                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6855                   secondChessProgramNames);\r
6856     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6857     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6858     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6859     if (*appData.icsHelper != NULLCHAR) {\r
6860       char *q = QuoteForFilename(appData.icsHelper);\r
6861       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6862     }\r
6863     if (*appData.icsHost == NULLCHAR) {\r
6864       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6865       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6866     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6867       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6868       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6869     }\r
6870 \r
6871     if (appData.icsActive) {\r
6872       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6873     }\r
6874     else if (appData.noChessProgram) {\r
6875       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6876     }\r
6877     else {\r
6878       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6879     }\r
6880 \r
6881     SetStartupDialogEnables(hDlg);\r
6882     return TRUE;\r
6883 \r
6884   case WM_COMMAND:\r
6885     switch (LOWORD(wParam)) {\r
6886     case IDOK:\r
6887       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6888         strcpy(buf, "/fcp=");\r
6889         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6890         p = buf;\r
6891         ParseArgs(StringGet, &p);\r
6892         strcpy(buf, "/scp=");\r
6893         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6894         p = buf;\r
6895         ParseArgs(StringGet, &p);\r
6896         appData.noChessProgram = FALSE;\r
6897         appData.icsActive = FALSE;\r
6898       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6899         strcpy(buf, "/ics /icshost=");\r
6900         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6901         p = buf;\r
6902         ParseArgs(StringGet, &p);\r
6903         if (appData.zippyPlay) {\r
6904           strcpy(buf, "/fcp=");\r
6905           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6906           p = buf;\r
6907           ParseArgs(StringGet, &p);\r
6908         }\r
6909       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6910         appData.noChessProgram = TRUE;\r
6911         appData.icsActive = FALSE;\r
6912       } else {\r
6913         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6914                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6915         return TRUE;\r
6916       }\r
6917       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6918         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6919         p = buf;\r
6920         ParseArgs(StringGet, &p);\r
6921       }\r
6922       EndDialog(hDlg, TRUE);\r
6923       return TRUE;\r
6924 \r
6925     case IDCANCEL:\r
6926       ExitEvent(0);\r
6927       return TRUE;\r
6928 \r
6929     case IDM_HELPCONTENTS:\r
6930       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6931         MessageBox (GetFocus(),\r
6932                     "Unable to activate help",\r
6933                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6934       }\r
6935       break;\r
6936 \r
6937     default:\r
6938       SetStartupDialogEnables(hDlg);\r
6939       break;\r
6940     }\r
6941     break;\r
6942   }\r
6943   return FALSE;\r
6944 }\r
6945 \r
6946 /*---------------------------------------------------------------------------*\\r
6947  *\r
6948  * About box dialog functions\r
6949  *\r
6950 \*---------------------------------------------------------------------------*/\r
6951 \r
6952 /* Process messages for "About" dialog box */\r
6953 LRESULT CALLBACK\r
6954 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6955 {\r
6956   switch (message) {\r
6957   case WM_INITDIALOG: /* message: initialize dialog box */\r
6958     /* Center the dialog over the application window */\r
6959     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6960     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6961     return (TRUE);\r
6962 \r
6963   case WM_COMMAND: /* message: received a command */\r
6964     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6965         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6966       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6967       return (TRUE);\r
6968     }\r
6969     break;\r
6970   }\r
6971   return (FALSE);\r
6972 }\r
6973 \r
6974 /*---------------------------------------------------------------------------*\\r
6975  *\r
6976  * Comment Dialog functions\r
6977  *\r
6978 \*---------------------------------------------------------------------------*/\r
6979 \r
6980 LRESULT CALLBACK\r
6981 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6982 {\r
6983   static HANDLE hwndText = NULL;\r
6984   int len, newSizeX, newSizeY, flags;\r
6985   static int sizeX, sizeY;\r
6986   char *str;\r
6987   RECT rect;\r
6988   MINMAXINFO *mmi;\r
6989 \r
6990   switch (message) {\r
6991   case WM_INITDIALOG: /* message: initialize dialog box */\r
6992     /* Initialize the dialog items */\r
6993     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6994     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6995     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6996     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6997     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6998     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6999     SetWindowText(hDlg, commentTitle);\r
7000     if (editComment) {\r
7001       SetFocus(hwndText);\r
7002     } else {\r
7003       SetFocus(GetDlgItem(hDlg, IDOK));\r
7004     }\r
7005     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7006                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7007                 MAKELPARAM(FALSE, 0));\r
7008     /* Size and position the dialog */\r
7009     if (!commentDialog) {\r
7010       commentDialog = hDlg;\r
7011       flags = SWP_NOZORDER;\r
7012       GetClientRect(hDlg, &rect);\r
7013       sizeX = rect.right;\r
7014       sizeY = rect.bottom;\r
7015       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7016           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7017         WINDOWPLACEMENT wp;\r
7018         EnsureOnScreen(&commentX, &commentY);\r
7019         wp.length = sizeof(WINDOWPLACEMENT);\r
7020         wp.flags = 0;\r
7021         wp.showCmd = SW_SHOW;\r
7022         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7023         wp.rcNormalPosition.left = commentX;\r
7024         wp.rcNormalPosition.right = commentX + commentW;\r
7025         wp.rcNormalPosition.top = commentY;\r
7026         wp.rcNormalPosition.bottom = commentY + commentH;\r
7027         SetWindowPlacement(hDlg, &wp);\r
7028 \r
7029         GetClientRect(hDlg, &rect);\r
7030         newSizeX = rect.right;\r
7031         newSizeY = rect.bottom;\r
7032         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7033                               newSizeX, newSizeY);\r
7034         sizeX = newSizeX;\r
7035         sizeY = newSizeY;\r
7036       }\r
7037     }\r
7038     return FALSE;\r
7039 \r
7040   case WM_COMMAND: /* message: received a command */\r
7041     switch (LOWORD(wParam)) {\r
7042     case IDOK:\r
7043       if (editComment) {\r
7044         char *p, *q;\r
7045         /* Read changed options from the dialog box */\r
7046         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7047         len = GetWindowTextLength(hwndText);\r
7048         str = (char *) malloc(len + 1);\r
7049         GetWindowText(hwndText, str, len + 1);\r
7050         p = q = str;\r
7051         while (*q) {\r
7052           if (*q == '\r')\r
7053             q++;\r
7054           else\r
7055             *p++ = *q++;\r
7056         }\r
7057         *p = NULLCHAR;\r
7058         ReplaceComment(commentIndex, str);\r
7059         free(str);\r
7060       }\r
7061       CommentPopDown();\r
7062       return TRUE;\r
7063 \r
7064     case IDCANCEL:\r
7065     case OPT_CancelComment:\r
7066       CommentPopDown();\r
7067       return TRUE;\r
7068 \r
7069     case OPT_ClearComment:\r
7070       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7071       break;\r
7072 \r
7073     case OPT_EditComment:\r
7074       EditCommentEvent();\r
7075       return TRUE;\r
7076 \r
7077     default:\r
7078       break;\r
7079     }\r
7080     break;\r
7081 \r
7082   case WM_SIZE:\r
7083     newSizeX = LOWORD(lParam);\r
7084     newSizeY = HIWORD(lParam);\r
7085     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7086     sizeX = newSizeX;\r
7087     sizeY = newSizeY;\r
7088     break;\r
7089 \r
7090   case WM_GETMINMAXINFO:\r
7091     /* Prevent resizing window too small */\r
7092     mmi = (MINMAXINFO *) lParam;\r
7093     mmi->ptMinTrackSize.x = 100;\r
7094     mmi->ptMinTrackSize.y = 100;\r
7095     break;\r
7096   }\r
7097   return FALSE;\r
7098 }\r
7099 \r
7100 VOID\r
7101 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7102 {\r
7103   FARPROC lpProc;\r
7104   char *p, *q;\r
7105 \r
7106   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7107 \r
7108   if (str == NULL) str = "";\r
7109   p = (char *) malloc(2 * strlen(str) + 2);\r
7110   q = p;\r
7111   while (*str) {\r
7112     if (*str == '\n') *q++ = '\r';\r
7113     *q++ = *str++;\r
7114   }\r
7115   *q = NULLCHAR;\r
7116   if (commentText != NULL) free(commentText);\r
7117 \r
7118   commentIndex = index;\r
7119   commentTitle = title;\r
7120   commentText = p;\r
7121   editComment = edit;\r
7122 \r
7123   if (commentDialog) {\r
7124     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7125     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7126   } else {\r
7127     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7128     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7129                  hwndMain, (DLGPROC)lpProc);\r
7130     FreeProcInstance(lpProc);\r
7131   }\r
7132   commentDialogUp = TRUE;\r
7133 }\r
7134 \r
7135 \r
7136 /*---------------------------------------------------------------------------*\\r
7137  *\r
7138  * Type-in move dialog functions\r
7139  * \r
7140 \*---------------------------------------------------------------------------*/\r
7141 \r
7142 LRESULT CALLBACK\r
7143 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7144 {\r
7145   char move[MSG_SIZ];\r
7146   HWND hInput;\r
7147   ChessMove moveType;\r
7148   int fromX, fromY, toX, toY;\r
7149   char promoChar;\r
7150 \r
7151   switch (message) {\r
7152   case WM_INITDIALOG:\r
7153     move[0] = (char) lParam;\r
7154     move[1] = NULLCHAR;\r
7155     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7156     hInput = GetDlgItem(hDlg, OPT_Move);\r
7157     SetWindowText(hInput, move);\r
7158     SetFocus(hInput);\r
7159     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7160     return FALSE;\r
7161 \r
7162   case WM_COMMAND:\r
7163     switch (LOWORD(wParam)) {\r
7164     case IDOK:\r
7165       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7166         gameMode != Training) {\r
7167         DisplayMoveError("Displayed move is not current");\r
7168       } else {\r
7169         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7170         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7171           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7172           if (gameMode != Training)\r
7173               forwardMostMove = currentMove;\r
7174           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7175         } else {\r
7176           DisplayMoveError("Could not parse move");\r
7177         }\r
7178       }\r
7179       EndDialog(hDlg, TRUE);\r
7180       return TRUE;\r
7181     case IDCANCEL:\r
7182       EndDialog(hDlg, FALSE);\r
7183       return TRUE;\r
7184     default:\r
7185       break;\r
7186     }\r
7187     break;\r
7188   }\r
7189   return FALSE;\r
7190 }\r
7191 \r
7192 VOID\r
7193 PopUpMoveDialog(char firstchar)\r
7194 {\r
7195     FARPROC lpProc;\r
7196     \r
7197     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7198         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7199         gameMode == AnalyzeMode || gameMode == EditGame || \r
7200         gameMode == EditPosition || gameMode == IcsExamining ||\r
7201         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7202         gameMode == Training) {\r
7203       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7204       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7205         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7206       FreeProcInstance(lpProc);\r
7207     }\r
7208 }\r
7209 \r
7210 /*---------------------------------------------------------------------------*\\r
7211  *\r
7212  * Type-in name dialog functions\r
7213  * \r
7214 \*---------------------------------------------------------------------------*/\r
7215 \r
7216 LRESULT CALLBACK\r
7217 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7218 {\r
7219   char move[MSG_SIZ];\r
7220   HWND hInput;\r
7221 \r
7222   switch (message) {\r
7223   case WM_INITDIALOG:\r
7224     move[0] = (char) lParam;\r
7225     move[1] = NULLCHAR;\r
7226     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7227     hInput = GetDlgItem(hDlg, OPT_Name);\r
7228     SetWindowText(hInput, move);\r
7229     SetFocus(hInput);\r
7230     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7231     return FALSE;\r
7232 \r
7233   case WM_COMMAND:\r
7234     switch (LOWORD(wParam)) {\r
7235     case IDOK:\r
7236       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7237       appData.userName = strdup(move);\r
7238 \r
7239       EndDialog(hDlg, TRUE);\r
7240       return TRUE;\r
7241     case IDCANCEL:\r
7242       EndDialog(hDlg, FALSE);\r
7243       return TRUE;\r
7244     default:\r
7245       break;\r
7246     }\r
7247     break;\r
7248   }\r
7249   return FALSE;\r
7250 }\r
7251 \r
7252 VOID\r
7253 PopUpNameDialog(char firstchar)\r
7254 {\r
7255     FARPROC lpProc;\r
7256     \r
7257       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7258       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7259         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7260       FreeProcInstance(lpProc);\r
7261 }\r
7262 \r
7263 /*---------------------------------------------------------------------------*\\r
7264  *\r
7265  *  Error dialogs\r
7266  * \r
7267 \*---------------------------------------------------------------------------*/\r
7268 \r
7269 /* Nonmodal error box */\r
7270 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7271                              WPARAM wParam, LPARAM lParam);\r
7272 \r
7273 VOID\r
7274 ErrorPopUp(char *title, char *content)\r
7275 {\r
7276   FARPROC lpProc;\r
7277   char *p, *q;\r
7278   BOOLEAN modal = hwndMain == NULL;\r
7279 \r
7280   p = content;\r
7281   q = errorMessage;\r
7282   while (*p) {\r
7283     if (*p == '\n') {\r
7284       if (modal) {\r
7285         *q++ = ' ';\r
7286         p++;\r
7287       } else {\r
7288         *q++ = '\r';\r
7289         *q++ = *p++;\r
7290       }\r
7291     } else {\r
7292       *q++ = *p++;\r
7293     }\r
7294   }\r
7295   *q = NULLCHAR;\r
7296   strncpy(errorTitle, title, sizeof(errorTitle));\r
7297   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7298   \r
7299   if (modal) {\r
7300     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7301   } else {\r
7302     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7303     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7304                  hwndMain, (DLGPROC)lpProc);\r
7305     FreeProcInstance(lpProc);\r
7306   }\r
7307 }\r
7308 \r
7309 VOID\r
7310 ErrorPopDown()\r
7311 {\r
7312   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7313   if (errorDialog == NULL) return;\r
7314   DestroyWindow(errorDialog);\r
7315   errorDialog = NULL;\r
7316 }\r
7317 \r
7318 LRESULT CALLBACK\r
7319 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7320 {\r
7321   HANDLE hwndText;\r
7322   RECT rChild;\r
7323 \r
7324   switch (message) {\r
7325   case WM_INITDIALOG:\r
7326     GetWindowRect(hDlg, &rChild);\r
7327 \r
7328     /*\r
7329     SetWindowPos(hDlg, NULL, rChild.left,\r
7330       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7331       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7332     */\r
7333 \r
7334     /* \r
7335         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7336         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7337         and it doesn't work when you resize the dialog.\r
7338         For now, just give it a default position.\r
7339     */\r
7340     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7341 \r
7342     errorDialog = hDlg;\r
7343     SetWindowText(hDlg, errorTitle);\r
7344     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7345     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7346     return FALSE;\r
7347 \r
7348   case WM_COMMAND:\r
7349     switch (LOWORD(wParam)) {\r
7350     case IDOK:\r
7351     case IDCANCEL:\r
7352       if (errorDialog == hDlg) errorDialog = NULL;\r
7353       DestroyWindow(hDlg);\r
7354       return TRUE;\r
7355 \r
7356     default:\r
7357       break;\r
7358     }\r
7359     break;\r
7360   }\r
7361   return FALSE;\r
7362 }\r
7363 \r
7364 #ifdef GOTHIC\r
7365 HWND gothicDialog = NULL;\r
7366 \r
7367 LRESULT CALLBACK\r
7368 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7369 {\r
7370   HANDLE hwndText;\r
7371   RECT rChild;\r
7372   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7373 \r
7374   switch (message) {\r
7375   case WM_INITDIALOG:\r
7376     GetWindowRect(hDlg, &rChild);\r
7377 \r
7378     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7379                                                              SWP_NOZORDER);\r
7380 \r
7381     /* \r
7382         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7383         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7384         and it doesn't work when you resize the dialog.\r
7385         For now, just give it a default position.\r
7386     */\r
7387     gothicDialog = hDlg;\r
7388     SetWindowText(hDlg, errorTitle);\r
7389     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7390     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7391     return FALSE;\r
7392 \r
7393   case WM_COMMAND:\r
7394     switch (LOWORD(wParam)) {\r
7395     case IDOK:\r
7396     case IDCANCEL:\r
7397       if (errorDialog == hDlg) errorDialog = NULL;\r
7398       DestroyWindow(hDlg);\r
7399       return TRUE;\r
7400 \r
7401     default:\r
7402       break;\r
7403     }\r
7404     break;\r
7405   }\r
7406   return FALSE;\r
7407 }\r
7408 \r
7409 VOID\r
7410 GothicPopUp(char *title, VariantClass variant)\r
7411 {\r
7412   FARPROC lpProc;\r
7413   char *p, *q;\r
7414   BOOLEAN modal = hwndMain == NULL;\r
7415   static char *lastTitle;\r
7416 \r
7417   strncpy(errorTitle, title, sizeof(errorTitle));\r
7418   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7419 \r
7420   if(lastTitle != title && gothicDialog != NULL) {\r
7421     DestroyWindow(gothicDialog);\r
7422     gothicDialog = NULL;\r
7423   }\r
7424   if(variant != VariantNormal && gothicDialog == NULL) {\r
7425     title = lastTitle;\r
7426     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7427     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7428                  hwndMain, (DLGPROC)lpProc);\r
7429     FreeProcInstance(lpProc);\r
7430   }\r
7431 }\r
7432 #endif\r
7433 \r
7434 /*---------------------------------------------------------------------------*\\r
7435  *\r
7436  *  Ics Interaction console functions\r
7437  *\r
7438 \*---------------------------------------------------------------------------*/\r
7439 \r
7440 #define HISTORY_SIZE 64\r
7441 static char *history[HISTORY_SIZE];\r
7442 int histIn = 0, histP = 0;\r
7443 \r
7444 VOID\r
7445 SaveInHistory(char *cmd)\r
7446 {\r
7447   if (history[histIn] != NULL) {\r
7448     free(history[histIn]);\r
7449     history[histIn] = NULL;\r
7450   }\r
7451   if (*cmd == NULLCHAR) return;\r
7452   history[histIn] = StrSave(cmd);\r
7453   histIn = (histIn + 1) % HISTORY_SIZE;\r
7454   if (history[histIn] != NULL) {\r
7455     free(history[histIn]);\r
7456     history[histIn] = NULL;\r
7457   }\r
7458   histP = histIn;\r
7459 }\r
7460 \r
7461 char *\r
7462 PrevInHistory(char *cmd)\r
7463 {\r
7464   int newhp;\r
7465   if (histP == histIn) {\r
7466     if (history[histIn] != NULL) free(history[histIn]);\r
7467     history[histIn] = StrSave(cmd);\r
7468   }\r
7469   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7470   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7471   histP = newhp;\r
7472   return history[histP];\r
7473 }\r
7474 \r
7475 char *\r
7476 NextInHistory()\r
7477 {\r
7478   if (histP == histIn) return NULL;\r
7479   histP = (histP + 1) % HISTORY_SIZE;\r
7480   return history[histP];\r
7481 }\r
7482 \r
7483 typedef struct {\r
7484   char *item;\r
7485   char *command;\r
7486   BOOLEAN getname;\r
7487   BOOLEAN immediate;\r
7488 } IcsTextMenuEntry;\r
7489 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7490 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7491 \r
7492 void\r
7493 ParseIcsTextMenu(char *icsTextMenuString)\r
7494 {\r
7495   int flags = 0;\r
7496   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7497   char *p = icsTextMenuString;\r
7498   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7499     free(e->item);\r
7500     e->item = NULL;\r
7501     if (e->command != NULL) {\r
7502       free(e->command);\r
7503       e->command = NULL;\r
7504     }\r
7505     e++;\r
7506   }\r
7507   e = icsTextMenuEntry;\r
7508   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7509     if (*p == ';' || *p == '\n') {\r
7510       e->item = strdup("-");\r
7511       e->command = NULL;\r
7512       p++;\r
7513     } else if (*p == '-') {\r
7514       e->item = strdup("-");\r
7515       e->command = NULL;\r
7516       p++;\r
7517       if (*p) p++;\r
7518     } else {\r
7519       char *q, *r, *s, *t;\r
7520       char c;\r
7521       q = strchr(p, ',');\r
7522       if (q == NULL) break;\r
7523       *q = NULLCHAR;\r
7524       r = strchr(q + 1, ',');\r
7525       if (r == NULL) break;\r
7526       *r = NULLCHAR;\r
7527       s = strchr(r + 1, ',');\r
7528       if (s == NULL) break;\r
7529       *s = NULLCHAR;\r
7530       c = ';';\r
7531       t = strchr(s + 1, c);\r
7532       if (t == NULL) {\r
7533         c = '\n';\r
7534         t = strchr(s + 1, c);\r
7535       }\r
7536       if (t != NULL) *t = NULLCHAR;\r
7537       e->item = strdup(p);\r
7538       e->command = strdup(q + 1);\r
7539       e->getname = *(r + 1) != '0';\r
7540       e->immediate = *(s + 1) != '0';\r
7541       *q = ',';\r
7542       *r = ',';\r
7543       *s = ',';\r
7544       if (t == NULL) break;\r
7545       *t = c;\r
7546       p = t + 1;\r
7547     }\r
7548     e++;\r
7549   } \r
7550 }\r
7551 \r
7552 HMENU\r
7553 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7554 {\r
7555   HMENU hmenu, h;\r
7556   int i = 0;\r
7557   hmenu = LoadMenu(hInst, "TextMenu");\r
7558   h = GetSubMenu(hmenu, 0);\r
7559   while (e->item) {\r
7560     if (strcmp(e->item, "-") == 0) {\r
7561       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7562     } else {\r
7563       if (e->item[0] == '|') {\r
7564         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7565                    IDM_CommandX + i, &e->item[1]);\r
7566       } else {\r
7567         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7568       }\r
7569     }\r
7570     e++;\r
7571     i++;\r
7572   } \r
7573   return hmenu;\r
7574 }\r
7575 \r
7576 WNDPROC consoleTextWindowProc;\r
7577 \r
7578 void\r
7579 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7580 {\r
7581   char buf[MSG_SIZ], name[MSG_SIZ];\r
7582   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7583   CHARRANGE sel;\r
7584 \r
7585   if (!getname) {\r
7586     SetWindowText(hInput, command);\r
7587     if (immediate) {\r
7588       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7589     } else {\r
7590       sel.cpMin = 999999;\r
7591       sel.cpMax = 999999;\r
7592       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7593       SetFocus(hInput);\r
7594     }\r
7595     return;\r
7596   }    \r
7597   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7598   if (sel.cpMin == sel.cpMax) {\r
7599     /* Expand to surrounding word */\r
7600     TEXTRANGE tr;\r
7601     do {\r
7602       tr.chrg.cpMax = sel.cpMin;\r
7603       tr.chrg.cpMin = --sel.cpMin;\r
7604       if (sel.cpMin < 0) break;\r
7605       tr.lpstrText = name;\r
7606       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7607     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7608     sel.cpMin++;\r
7609 \r
7610     do {\r
7611       tr.chrg.cpMin = sel.cpMax;\r
7612       tr.chrg.cpMax = ++sel.cpMax;\r
7613       tr.lpstrText = name;\r
7614       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7615     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7616     sel.cpMax--;\r
7617 \r
7618     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7619       MessageBeep(MB_ICONEXCLAMATION);\r
7620       return;\r
7621     }\r
7622     tr.chrg = sel;\r
7623     tr.lpstrText = name;\r
7624     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7625   } else {\r
7626     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7627       MessageBeep(MB_ICONEXCLAMATION);\r
7628       return;\r
7629     }\r
7630     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7631   }\r
7632   if (immediate) {\r
7633     sprintf(buf, "%s %s", command, name);\r
7634     SetWindowText(hInput, buf);\r
7635     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7636   } else {\r
7637     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7638     SetWindowText(hInput, buf);\r
7639     sel.cpMin = 999999;\r
7640     sel.cpMax = 999999;\r
7641     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7642     SetFocus(hInput);\r
7643   }\r
7644 }\r
7645 \r
7646 LRESULT CALLBACK \r
7647 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7648 {\r
7649   HWND hInput;\r
7650   CHARRANGE sel;\r
7651 \r
7652   switch (message) {\r
7653   case WM_KEYDOWN:\r
7654     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7655     switch (wParam) {\r
7656     case VK_PRIOR:\r
7657       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7658       return 0;\r
7659     case VK_NEXT:\r
7660       sel.cpMin = 999999;\r
7661       sel.cpMax = 999999;\r
7662       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7663       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7664       return 0;\r
7665     }\r
7666     break;\r
7667   case WM_CHAR:\r
7668     if (wParam == '\t') {\r
7669       if (GetKeyState(VK_SHIFT) < 0) {\r
7670         /* shifted */\r
7671         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7672         if (buttonDesc[0].hwnd) {\r
7673           SetFocus(buttonDesc[0].hwnd);\r
7674         } else {\r
7675           SetFocus(hwndMain);\r
7676         }\r
7677       } else {\r
7678         /* unshifted */\r
7679         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7680       }\r
7681     } else {\r
7682       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7683       SetFocus(hInput);\r
7684       SendMessage(hInput, message, wParam, lParam);\r
7685     }\r
7686     return 0;\r
7687   case WM_PASTE:\r
7688     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7689     SetFocus(hInput);\r
7690     return SendMessage(hInput, message, wParam, lParam);\r
7691   case WM_MBUTTONDOWN:\r
7692     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7693   case WM_RBUTTONDOWN:\r
7694     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7695       /* Move selection here if it was empty */\r
7696       POINT pt;\r
7697       pt.x = LOWORD(lParam);\r
7698       pt.y = HIWORD(lParam);\r
7699       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7700       if (sel.cpMin == sel.cpMax) {\r
7701         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7702         sel.cpMax = sel.cpMin;\r
7703         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7704       }\r
7705       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7706     }\r
7707     return 0;\r
7708   case WM_RBUTTONUP:\r
7709     if (GetKeyState(VK_SHIFT) & ~1) {\r
7710       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7711         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7712     } else {\r
7713       POINT pt;\r
7714       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7715       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7716       if (sel.cpMin == sel.cpMax) {\r
7717         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7718         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7719       }\r
7720       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7721         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7722       }\r
7723       pt.x = LOWORD(lParam);\r
7724       pt.y = HIWORD(lParam);\r
7725       MenuPopup(hwnd, pt, hmenu, -1);\r
7726     }\r
7727     return 0;\r
7728   case WM_COMMAND:\r
7729     switch (LOWORD(wParam)) {\r
7730     case IDM_QuickPaste:\r
7731       {\r
7732         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7733         if (sel.cpMin == sel.cpMax) {\r
7734           MessageBeep(MB_ICONEXCLAMATION);\r
7735           return 0;\r
7736         }\r
7737         SendMessage(hwnd, WM_COPY, 0, 0);\r
7738         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7739         SendMessage(hInput, WM_PASTE, 0, 0);\r
7740         SetFocus(hInput);\r
7741         return 0;\r
7742       }\r
7743     case IDM_Cut:\r
7744       SendMessage(hwnd, WM_CUT, 0, 0);\r
7745       return 0;\r
7746     case IDM_Paste:\r
7747       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7748       return 0;\r
7749     case IDM_Copy:\r
7750       SendMessage(hwnd, WM_COPY, 0, 0);\r
7751       return 0;\r
7752     default:\r
7753       {\r
7754         int i = LOWORD(wParam) - IDM_CommandX;\r
7755         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7756             icsTextMenuEntry[i].command != NULL) {\r
7757           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7758                    icsTextMenuEntry[i].getname,\r
7759                    icsTextMenuEntry[i].immediate);\r
7760           return 0;\r
7761         }\r
7762       }\r
7763       break;\r
7764     }\r
7765     break;\r
7766   }\r
7767   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7768 }\r
7769 \r
7770 WNDPROC consoleInputWindowProc;\r
7771 \r
7772 LRESULT CALLBACK\r
7773 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7774 {\r
7775   char buf[MSG_SIZ];\r
7776   char *p;\r
7777   static BOOL sendNextChar = FALSE;\r
7778   static BOOL quoteNextChar = FALSE;\r
7779   InputSource *is = consoleInputSource;\r
7780   CHARFORMAT cf;\r
7781   CHARRANGE sel;\r
7782 \r
7783   switch (message) {\r
7784   case WM_CHAR:\r
7785     if (!appData.localLineEditing || sendNextChar) {\r
7786       is->buf[0] = (CHAR) wParam;\r
7787       is->count = 1;\r
7788       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7789       sendNextChar = FALSE;\r
7790       return 0;\r
7791     }\r
7792     if (quoteNextChar) {\r
7793       buf[0] = (char) wParam;\r
7794       buf[1] = NULLCHAR;\r
7795       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7796       quoteNextChar = FALSE;\r
7797       return 0;\r
7798     }\r
7799     switch (wParam) {\r
7800     case '\r':   /* Enter key */\r
7801       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7802       if (consoleEcho) SaveInHistory(is->buf);\r
7803       is->buf[is->count++] = '\n';\r
7804       is->buf[is->count] = NULLCHAR;\r
7805       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7806       if (consoleEcho) {\r
7807         ConsoleOutput(is->buf, is->count, TRUE);\r
7808       } else if (appData.localLineEditing) {\r
7809         ConsoleOutput("\n", 1, TRUE);\r
7810       }\r
7811       /* fall thru */\r
7812     case '\033': /* Escape key */\r
7813       SetWindowText(hwnd, "");\r
7814       cf.cbSize = sizeof(CHARFORMAT);\r
7815       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7816       if (consoleEcho) {\r
7817         cf.crTextColor = textAttribs[ColorNormal].color;\r
7818       } else {\r
7819         cf.crTextColor = COLOR_ECHOOFF;\r
7820       }\r
7821       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7822       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7823       return 0;\r
7824     case '\t':   /* Tab key */\r
7825       if (GetKeyState(VK_SHIFT) < 0) {\r
7826         /* shifted */\r
7827         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7828       } else {\r
7829         /* unshifted */\r
7830         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7831         if (buttonDesc[0].hwnd) {\r
7832           SetFocus(buttonDesc[0].hwnd);\r
7833         } else {\r
7834           SetFocus(hwndMain);\r
7835         }\r
7836       }\r
7837       return 0;\r
7838     case '\023': /* Ctrl+S */\r
7839       sendNextChar = TRUE;\r
7840       return 0;\r
7841     case '\021': /* Ctrl+Q */\r
7842       quoteNextChar = TRUE;\r
7843       return 0;\r
7844     default:\r
7845       break;\r
7846     }\r
7847     break;\r
7848   case WM_KEYDOWN:\r
7849     switch (wParam) {\r
7850     case VK_UP:\r
7851       GetWindowText(hwnd, buf, MSG_SIZ);\r
7852       p = PrevInHistory(buf);\r
7853       if (p != NULL) {\r
7854         SetWindowText(hwnd, p);\r
7855         sel.cpMin = 999999;\r
7856         sel.cpMax = 999999;\r
7857         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7858         return 0;\r
7859       }\r
7860       break;\r
7861     case VK_DOWN:\r
7862       p = NextInHistory();\r
7863       if (p != NULL) {\r
7864         SetWindowText(hwnd, p);\r
7865         sel.cpMin = 999999;\r
7866         sel.cpMax = 999999;\r
7867         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7868         return 0;\r
7869       }\r
7870       break;\r
7871     case VK_HOME:\r
7872     case VK_END:\r
7873       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7874       /* fall thru */\r
7875     case VK_PRIOR:\r
7876     case VK_NEXT:\r
7877       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7878       return 0;\r
7879     }\r
7880     break;\r
7881   case WM_MBUTTONDOWN:\r
7882     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7883       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7884     break;\r
7885   case WM_RBUTTONUP:\r
7886     if (GetKeyState(VK_SHIFT) & ~1) {\r
7887       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7888         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7889     } else {\r
7890       POINT pt;\r
7891       HMENU hmenu;\r
7892       hmenu = LoadMenu(hInst, "InputMenu");\r
7893       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7894       if (sel.cpMin == sel.cpMax) {\r
7895         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7896         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7897       }\r
7898       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7899         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7900       }\r
7901       pt.x = LOWORD(lParam);\r
7902       pt.y = HIWORD(lParam);\r
7903       MenuPopup(hwnd, pt, hmenu, -1);\r
7904     }\r
7905     return 0;\r
7906   case WM_COMMAND:\r
7907     switch (LOWORD(wParam)) { \r
7908     case IDM_Undo:\r
7909       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7910       return 0;\r
7911     case IDM_SelectAll:\r
7912       sel.cpMin = 0;\r
7913       sel.cpMax = -1; /*999999?*/\r
7914       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7915       return 0;\r
7916     case IDM_Cut:\r
7917       SendMessage(hwnd, WM_CUT, 0, 0);\r
7918       return 0;\r
7919     case IDM_Paste:\r
7920       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7921       return 0;\r
7922     case IDM_Copy:\r
7923       SendMessage(hwnd, WM_COPY, 0, 0);\r
7924       return 0;\r
7925     }\r
7926     break;\r
7927   }\r
7928   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7929 }\r
7930 \r
7931 #define CO_MAX  100000\r
7932 #define CO_TRIM   1000\r
7933 \r
7934 LRESULT CALLBACK\r
7935 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7936 {\r
7937   static SnapData sd;\r
7938   static HWND hText, hInput, hFocus;\r
7939   InputSource *is = consoleInputSource;\r
7940   RECT rect;\r
7941   static int sizeX, sizeY;\r
7942   int newSizeX, newSizeY;\r
7943   MINMAXINFO *mmi;\r
7944 \r
7945   switch (message) {\r
7946   case WM_INITDIALOG: /* message: initialize dialog box */\r
7947     hwndConsole = hDlg;\r
7948     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7949     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7950     SetFocus(hInput);\r
7951     consoleTextWindowProc = (WNDPROC)\r
7952       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7953     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7954     consoleInputWindowProc = (WNDPROC)\r
7955       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7956     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7957     Colorize(ColorNormal, TRUE);\r
7958     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7959     ChangedConsoleFont();\r
7960     GetClientRect(hDlg, &rect);\r
7961     sizeX = rect.right;\r
7962     sizeY = rect.bottom;\r
7963     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7964         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7965       WINDOWPLACEMENT wp;\r
7966       EnsureOnScreen(&consoleX, &consoleY);\r
7967       wp.length = sizeof(WINDOWPLACEMENT);\r
7968       wp.flags = 0;\r
7969       wp.showCmd = SW_SHOW;\r
7970       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7971       wp.rcNormalPosition.left = consoleX;\r
7972       wp.rcNormalPosition.right = consoleX + consoleW;\r
7973       wp.rcNormalPosition.top = consoleY;\r
7974       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7975       SetWindowPlacement(hDlg, &wp);\r
7976     }\r
7977 #if 0 \r
7978    // [HGM] Chessknight's change 2004-07-13\r
7979    else { /* Determine Defaults */\r
7980        WINDOWPLACEMENT wp;\r
7981        consoleX = winWidth + 1;\r
7982        consoleY = boardY;\r
7983        consoleW = screenWidth -  winWidth;\r
7984        consoleH = winHeight;\r
7985        EnsureOnScreen(&consoleX, &consoleY);\r
7986        wp.length = sizeof(WINDOWPLACEMENT);\r
7987        wp.flags = 0;\r
7988        wp.showCmd = SW_SHOW;\r
7989        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7990        wp.rcNormalPosition.left = consoleX;\r
7991        wp.rcNormalPosition.right = consoleX + consoleW;\r
7992        wp.rcNormalPosition.top = consoleY;\r
7993        wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7994        SetWindowPlacement(hDlg, &wp);\r
7995     }\r
7996 #endif\r
7997     return FALSE;\r
7998 \r
7999   case WM_SETFOCUS:\r
8000     SetFocus(hInput);\r
8001     return 0;\r
8002 \r
8003   case WM_CLOSE:\r
8004     ExitEvent(0);\r
8005     /* not reached */\r
8006     break;\r
8007 \r
8008   case WM_SIZE:\r
8009     if (IsIconic(hDlg)) break;\r
8010     newSizeX = LOWORD(lParam);\r
8011     newSizeY = HIWORD(lParam);\r
8012     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8013       RECT rectText, rectInput;\r
8014       POINT pt;\r
8015       int newTextHeight, newTextWidth;\r
8016       GetWindowRect(hText, &rectText);\r
8017       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8018       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8019       if (newTextHeight < 0) {\r
8020         newSizeY += -newTextHeight;\r
8021         newTextHeight = 0;\r
8022       }\r
8023       SetWindowPos(hText, NULL, 0, 0,\r
8024         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8025       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8026       pt.x = rectInput.left;\r
8027       pt.y = rectInput.top + newSizeY - sizeY;\r
8028       ScreenToClient(hDlg, &pt);\r
8029       SetWindowPos(hInput, NULL, \r
8030         pt.x, pt.y, /* needs client coords */   \r
8031         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8032         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8033     }\r
8034     sizeX = newSizeX;\r
8035     sizeY = newSizeY;\r
8036     break;\r
8037 \r
8038   case WM_GETMINMAXINFO:\r
8039     /* Prevent resizing window too small */\r
8040     mmi = (MINMAXINFO *) lParam;\r
8041     mmi->ptMinTrackSize.x = 100;\r
8042     mmi->ptMinTrackSize.y = 100;\r
8043     break;\r
8044 \r
8045   /* [AS] Snapping */\r
8046   case WM_ENTERSIZEMOVE:\r
8047     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8048 \r
8049   case WM_SIZING:\r
8050     return OnSizing( &sd, hDlg, wParam, lParam );\r
8051 \r
8052   case WM_MOVING:\r
8053     return OnMoving( &sd, hDlg, wParam, lParam );\r
8054 \r
8055   case WM_EXITSIZEMOVE:\r
8056     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8057   }\r
8058 \r
8059   return DefWindowProc(hDlg, message, wParam, lParam);\r
8060 }\r
8061 \r
8062 \r
8063 VOID\r
8064 ConsoleCreate()\r
8065 {\r
8066   HWND hCons;\r
8067   if (hwndConsole) return;\r
8068   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8069   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8070 }\r
8071 \r
8072 \r
8073 VOID\r
8074 ConsoleOutput(char* data, int length, int forceVisible)\r
8075 {\r
8076   HWND hText;\r
8077   int trim, exlen;\r
8078   char *p, *q;\r
8079   char buf[CO_MAX+1];\r
8080   POINT pEnd;\r
8081   RECT rect;\r
8082   static int delayLF = 0;\r
8083   CHARRANGE savesel, sel;\r
8084 \r
8085   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8086   p = data;\r
8087   q = buf;\r
8088   if (delayLF) {\r
8089     *q++ = '\r';\r
8090     *q++ = '\n';\r
8091     delayLF = 0;\r
8092   }\r
8093   while (length--) {\r
8094     if (*p == '\n') {\r
8095       if (*++p) {\r
8096         *q++ = '\r';\r
8097         *q++ = '\n';\r
8098       } else {\r
8099         delayLF = 1;\r
8100       }\r
8101     } else if (*p == '\007') {\r
8102        MyPlaySound(&sounds[(int)SoundBell]);\r
8103        p++;\r
8104     } else {\r
8105       *q++ = *p++;\r
8106     }\r
8107   }\r
8108   *q = NULLCHAR;\r
8109   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8110   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8111   /* Save current selection */\r
8112   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8113   exlen = GetWindowTextLength(hText);\r
8114   /* Find out whether current end of text is visible */\r
8115   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8116   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8117   /* Trim existing text if it's too long */\r
8118   if (exlen + (q - buf) > CO_MAX) {\r
8119     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8120     sel.cpMin = 0;\r
8121     sel.cpMax = trim;\r
8122     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8123     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8124     exlen -= trim;\r
8125     savesel.cpMin -= trim;\r
8126     savesel.cpMax -= trim;\r
8127     if (exlen < 0) exlen = 0;\r
8128     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8129     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8130   }\r
8131   /* Append the new text */\r
8132   sel.cpMin = exlen;\r
8133   sel.cpMax = exlen;\r
8134   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8135   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8136   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8137   if (forceVisible || exlen == 0 ||\r
8138       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8139        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8140     /* Scroll to make new end of text visible if old end of text\r
8141        was visible or new text is an echo of user typein */\r
8142     sel.cpMin = 9999999;\r
8143     sel.cpMax = 9999999;\r
8144     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8145     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8146     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8147     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8148   }\r
8149   if (savesel.cpMax == exlen || forceVisible) {\r
8150     /* Move insert point to new end of text if it was at the old\r
8151        end of text or if the new text is an echo of user typein */\r
8152     sel.cpMin = 9999999;\r
8153     sel.cpMax = 9999999;\r
8154     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8155   } else {\r
8156     /* Restore previous selection */\r
8157     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8158   }\r
8159   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8160 }\r
8161 \r
8162 /*---------*/\r
8163 \r
8164 \r
8165 void\r
8166 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8167 {\r
8168   char buf[100];\r
8169   char *str;\r
8170   COLORREF oldFg, oldBg;\r
8171   HFONT oldFont;\r
8172   RECT rect;\r
8173 \r
8174   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8175 \r
8176   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8177   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8178   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8179 \r
8180   rect.left = x;\r
8181   rect.right = x + squareSize;\r
8182   rect.top  = y;\r
8183   rect.bottom = y + squareSize;\r
8184   str = buf;\r
8185 \r
8186   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8187                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8188              y, ETO_CLIPPED|ETO_OPAQUE,\r
8189              &rect, str, strlen(str), NULL);\r
8190 \r
8191   (void) SetTextColor(hdc, oldFg);\r
8192   (void) SetBkColor(hdc, oldBg);\r
8193   (void) SelectObject(hdc, oldFont);\r
8194 }\r
8195 \r
8196 void\r
8197 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8198               RECT *rect, char *color, char *flagFell)\r
8199 {\r
8200   char buf[100];\r
8201   char *str;\r
8202   COLORREF oldFg, oldBg;\r
8203   HFONT oldFont;\r
8204 \r
8205   if (appData.clockMode) {\r
8206     if (tinyLayout)\r
8207       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8208     else\r
8209       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8210     str = buf;\r
8211   } else {\r
8212     str = color;\r
8213   }\r
8214 \r
8215   if (highlight) {\r
8216     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8217     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8218   } else {\r
8219     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8220     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8221   }\r
8222   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8223 \r
8224   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8225              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8226              rect, str, strlen(str), NULL);\r
8227 \r
8228   (void) SetTextColor(hdc, oldFg);\r
8229   (void) SetBkColor(hdc, oldBg);\r
8230   (void) SelectObject(hdc, oldFont);\r
8231 }\r
8232 \r
8233 \r
8234 int\r
8235 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8236            OVERLAPPED *ovl)\r
8237 {\r
8238   int ok, err;\r
8239 \r
8240   /* [AS]  */\r
8241   if( count <= 0 ) {\r
8242     if (appData.debugMode) {\r
8243       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8244     }\r
8245 \r
8246     return ERROR_INVALID_USER_BUFFER;\r
8247   }\r
8248 \r
8249   ResetEvent(ovl->hEvent);\r
8250   ovl->Offset = ovl->OffsetHigh = 0;\r
8251   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8252   if (ok) {\r
8253     err = NO_ERROR;\r
8254   } else {\r
8255     err = GetLastError();\r
8256     if (err == ERROR_IO_PENDING) {\r
8257       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8258       if (ok)\r
8259         err = NO_ERROR;\r
8260       else\r
8261         err = GetLastError();\r
8262     }\r
8263   }\r
8264   return err;\r
8265 }\r
8266 \r
8267 int\r
8268 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8269             OVERLAPPED *ovl)\r
8270 {\r
8271   int ok, err;\r
8272 \r
8273   ResetEvent(ovl->hEvent);\r
8274   ovl->Offset = ovl->OffsetHigh = 0;\r
8275   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8276   if (ok) {\r
8277     err = NO_ERROR;\r
8278   } else {\r
8279     err = GetLastError();\r
8280     if (err == ERROR_IO_PENDING) {\r
8281       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8282       if (ok)\r
8283         err = NO_ERROR;\r
8284       else\r
8285         err = GetLastError();\r
8286     }\r
8287   }\r
8288   return err;\r
8289 }\r
8290 \r
8291 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8292 void CheckForInputBufferFull( InputSource * is )\r
8293 {\r
8294     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8295         /* Look for end of line */\r
8296         char * p = is->buf;\r
8297         \r
8298         while( p < is->next && *p != '\n' ) {\r
8299             p++;\r
8300         }\r
8301 \r
8302         if( p >= is->next ) {\r
8303             if (appData.debugMode) {\r
8304                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
8305             }\r
8306 \r
8307             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8308             is->count = (DWORD) -1;\r
8309             is->next = is->buf;\r
8310         }\r
8311     }\r
8312 }\r
8313 \r
8314 DWORD\r
8315 InputThread(LPVOID arg)\r
8316 {\r
8317   InputSource *is;\r
8318   OVERLAPPED ovl;\r
8319 \r
8320   is = (InputSource *) arg;\r
8321   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8322   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8323   while (is->hThread != NULL) {\r
8324     is->error = DoReadFile(is->hFile, is->next,\r
8325                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8326                            &is->count, &ovl);\r
8327     if (is->error == NO_ERROR) {\r
8328       is->next += is->count;\r
8329     } else {\r
8330       if (is->error == ERROR_BROKEN_PIPE) {\r
8331         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8332         is->count = 0;\r
8333       } else {\r
8334         is->count = (DWORD) -1;\r
8335         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8336         break; \r
8337       }\r
8338     }\r
8339 \r
8340     CheckForInputBufferFull( is );\r
8341 \r
8342     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8343 \r
8344     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8345 \r
8346     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8347   }\r
8348 \r
8349   CloseHandle(ovl.hEvent);\r
8350   CloseHandle(is->hFile);\r
8351 \r
8352   if (appData.debugMode) {\r
8353     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
8354   }\r
8355 \r
8356   return 0;\r
8357 }\r
8358 \r
8359 \r
8360 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8361 DWORD\r
8362 NonOvlInputThread(LPVOID arg)\r
8363 {\r
8364   InputSource *is;\r
8365   char *p, *q;\r
8366   int i;\r
8367   char prev;\r
8368 \r
8369   is = (InputSource *) arg;\r
8370   while (is->hThread != NULL) {\r
8371     is->error = ReadFile(is->hFile, is->next,\r
8372                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8373                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8374     if (is->error == NO_ERROR) {\r
8375       /* Change CRLF to LF */\r
8376       if (is->next > is->buf) {\r
8377         p = is->next - 1;\r
8378         i = is->count + 1;\r
8379       } else {\r
8380         p = is->next;\r
8381         i = is->count;\r
8382       }\r
8383       q = p;\r
8384       prev = NULLCHAR;\r
8385       while (i > 0) {\r
8386         if (prev == '\r' && *p == '\n') {\r
8387           *(q-1) = '\n';\r
8388           is->count--;\r
8389         } else { \r
8390           *q++ = *p;\r
8391         }\r
8392         prev = *p++;\r
8393         i--;\r
8394       }\r
8395       *q = NULLCHAR;\r
8396       is->next = q;\r
8397     } else {\r
8398       if (is->error == ERROR_BROKEN_PIPE) {\r
8399         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8400         is->count = 0; \r
8401       } else {\r
8402         is->count = (DWORD) -1;\r
8403       }\r
8404     }\r
8405 \r
8406     CheckForInputBufferFull( is );\r
8407 \r
8408     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8409 \r
8410     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8411 \r
8412     if (is->count < 0) break;  /* Quit on error */\r
8413   }\r
8414   CloseHandle(is->hFile);\r
8415   return 0;\r
8416 }\r
8417 \r
8418 DWORD\r
8419 SocketInputThread(LPVOID arg)\r
8420 {\r
8421   InputSource *is;\r
8422 \r
8423   is = (InputSource *) arg;\r
8424   while (is->hThread != NULL) {\r
8425     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8426     if ((int)is->count == SOCKET_ERROR) {\r
8427       is->count = (DWORD) -1;\r
8428       is->error = WSAGetLastError();\r
8429     } else {\r
8430       is->error = NO_ERROR;\r
8431       is->next += is->count;\r
8432       if (is->count == 0 && is->second == is) {\r
8433         /* End of file on stderr; quit with no message */\r
8434         break;\r
8435       }\r
8436     }\r
8437     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8438 \r
8439     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8440 \r
8441     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8442   }\r
8443   return 0;\r
8444 }\r
8445 \r
8446 VOID\r
8447 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8448 {\r
8449   InputSource *is;\r
8450 \r
8451   is = (InputSource *) lParam;\r
8452   if (is->lineByLine) {\r
8453     /* Feed in lines one by one */\r
8454     char *p = is->buf;\r
8455     char *q = p;\r
8456     while (q < is->next) {\r
8457       if (*q++ == '\n') {\r
8458         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8459         p = q;\r
8460       }\r
8461     }\r
8462     \r
8463     /* Move any partial line to the start of the buffer */\r
8464     q = is->buf;\r
8465     while (p < is->next) {\r
8466       *q++ = *p++;\r
8467     }\r
8468     is->next = q;\r
8469 \r
8470     if (is->error != NO_ERROR || is->count == 0) {\r
8471       /* Notify backend of the error.  Note: If there was a partial\r
8472          line at the end, it is not flushed through. */\r
8473       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8474     }\r
8475   } else {\r
8476     /* Feed in the whole chunk of input at once */\r
8477     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8478     is->next = is->buf;\r
8479   }\r
8480 }\r
8481 \r
8482 /*---------------------------------------------------------------------------*\\r
8483  *\r
8484  *  Menu enables. Used when setting various modes.\r
8485  *\r
8486 \*---------------------------------------------------------------------------*/\r
8487 \r
8488 typedef struct {\r
8489   int item;\r
8490   int flags;\r
8491 } Enables;\r
8492 \r
8493 VOID\r
8494 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8495 {\r
8496   while (enab->item > 0) {\r
8497     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8498     enab++;\r
8499   }\r
8500 }\r
8501 \r
8502 Enables gnuEnables[] = {\r
8503   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8504   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8505   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8506   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8507   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8510   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8511   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8512   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8513   { -1, -1 }\r
8514 };\r
8515 \r
8516 Enables icsEnables[] = {\r
8517   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8518   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8519   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8520   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8521   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8522   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8523   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8524   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8525   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8526   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8527   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8528   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8529   { -1, -1 }\r
8530 };\r
8531 \r
8532 #ifdef ZIPPY\r
8533 Enables zippyEnables[] = {\r
8534   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8535   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8536   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8537   { -1, -1 }\r
8538 };\r
8539 #endif\r
8540 \r
8541 Enables ncpEnables[] = {\r
8542   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8543   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8544   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8545   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8546   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8547   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8548   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8550   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8551   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8552   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8553   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8554   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8555   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8556   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8557   { -1, -1 }\r
8558 };\r
8559 \r
8560 Enables trainingOnEnables[] = {\r
8561   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8562   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8563   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8564   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8565   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8566   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8567   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8568   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8569   { -1, -1 }\r
8570 };\r
8571 \r
8572 Enables trainingOffEnables[] = {\r
8573   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8574   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8575   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8576   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8577   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8578   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8579   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8580   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8581   { -1, -1 }\r
8582 };\r
8583 \r
8584 /* These modify either ncpEnables or gnuEnables */\r
8585 Enables cmailEnables[] = {\r
8586   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8587   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8588   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8589   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8591   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8593   { -1, -1 }\r
8594 };\r
8595 \r
8596 Enables machineThinkingEnables[] = {\r
8597   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8598   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8609   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8611   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8612   { -1, -1 }\r
8613 };\r
8614 \r
8615 Enables userThinkingEnables[] = {\r
8616   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8617   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8618   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8619   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8620   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8621   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8622   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8623   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8624   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8625   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8626   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8627   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8628   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8629   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8630   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8631   { -1, -1 }\r
8632 };\r
8633 \r
8634 /*---------------------------------------------------------------------------*\\r
8635  *\r
8636  *  Front-end interface functions exported by XBoard.\r
8637  *  Functions appear in same order as prototypes in frontend.h.\r
8638  * \r
8639 \*---------------------------------------------------------------------------*/\r
8640 VOID\r
8641 ModeHighlight()\r
8642 {\r
8643   static UINT prevChecked = 0;\r
8644   static int prevPausing = 0;\r
8645   UINT nowChecked;\r
8646 \r
8647   if (pausing != prevPausing) {\r
8648     prevPausing = pausing;\r
8649     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8650                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8651     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8652   }\r
8653 \r
8654   switch (gameMode) {\r
8655   case BeginningOfGame:\r
8656     if (appData.icsActive)\r
8657       nowChecked = IDM_IcsClient;\r
8658     else if (appData.noChessProgram)\r
8659       nowChecked = IDM_EditGame;\r
8660     else\r
8661       nowChecked = IDM_MachineBlack;\r
8662     break;\r
8663   case MachinePlaysBlack:\r
8664     nowChecked = IDM_MachineBlack;\r
8665     break;\r
8666   case MachinePlaysWhite:\r
8667     nowChecked = IDM_MachineWhite;\r
8668     break;\r
8669   case TwoMachinesPlay:\r
8670     nowChecked = IDM_TwoMachines;\r
8671     break;\r
8672   case AnalyzeMode:\r
8673     nowChecked = IDM_AnalysisMode;\r
8674     break;\r
8675   case AnalyzeFile:\r
8676     nowChecked = IDM_AnalyzeFile;\r
8677     break;\r
8678   case EditGame:\r
8679     nowChecked = IDM_EditGame;\r
8680     break;\r
8681   case PlayFromGameFile:\r
8682     nowChecked = IDM_LoadGame;\r
8683     break;\r
8684   case EditPosition:\r
8685     nowChecked = IDM_EditPosition;\r
8686     break;\r
8687   case Training:\r
8688     nowChecked = IDM_Training;\r
8689     break;\r
8690   case IcsPlayingWhite:\r
8691   case IcsPlayingBlack:\r
8692   case IcsObserving:\r
8693   case IcsIdle:\r
8694     nowChecked = IDM_IcsClient;\r
8695     break;\r
8696   default:\r
8697   case EndOfGame:\r
8698     nowChecked = 0;\r
8699     break;\r
8700   }\r
8701   if (prevChecked != 0)\r
8702     (void) CheckMenuItem(GetMenu(hwndMain),\r
8703                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8704   if (nowChecked != 0)\r
8705     (void) CheckMenuItem(GetMenu(hwndMain),\r
8706                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8707 \r
8708   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8709     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8710                           MF_BYCOMMAND|MF_ENABLED);\r
8711   } else {\r
8712     (void) EnableMenuItem(GetMenu(hwndMain), \r
8713                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8714   }\r
8715 \r
8716   prevChecked = nowChecked;\r
8717 \r
8718   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8719   if (appData.icsActive) {\r
8720        if (appData.icsEngineAnalyze) {\r
8721                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8722                        MF_BYCOMMAND|MF_CHECKED);\r
8723        } else {\r
8724                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8725                        MF_BYCOMMAND|MF_UNCHECKED);\r
8726        }\r
8727   }\r
8728 }\r
8729 \r
8730 VOID\r
8731 SetICSMode()\r
8732 {\r
8733   HMENU hmenu = GetMenu(hwndMain);\r
8734   SetMenuEnables(hmenu, icsEnables);\r
8735   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8736     MF_BYPOSITION|MF_ENABLED);\r
8737 #ifdef ZIPPY\r
8738   if (appData.zippyPlay) {\r
8739     SetMenuEnables(hmenu, zippyEnables);\r
8740     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8741          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8742           MF_BYCOMMAND|MF_ENABLED);\r
8743   }\r
8744 #endif\r
8745 }\r
8746 \r
8747 VOID\r
8748 SetGNUMode()\r
8749 {\r
8750   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8751 }\r
8752 \r
8753 VOID\r
8754 SetNCPMode()\r
8755 {\r
8756   HMENU hmenu = GetMenu(hwndMain);\r
8757   SetMenuEnables(hmenu, ncpEnables);\r
8758   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8759     MF_BYPOSITION|MF_GRAYED);\r
8760     DrawMenuBar(hwndMain);\r
8761 }\r
8762 \r
8763 VOID\r
8764 SetCmailMode()\r
8765 {\r
8766   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8767 }\r
8768 \r
8769 VOID \r
8770 SetTrainingModeOn()\r
8771 {\r
8772   int i;\r
8773   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8774   for (i = 0; i < N_BUTTONS; i++) {\r
8775     if (buttonDesc[i].hwnd != NULL)\r
8776       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8777   }\r
8778   CommentPopDown();\r
8779 }\r
8780 \r
8781 VOID SetTrainingModeOff()\r
8782 {\r
8783   int i;\r
8784   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8785   for (i = 0; i < N_BUTTONS; i++) {\r
8786     if (buttonDesc[i].hwnd != NULL)\r
8787       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8788   }\r
8789 }\r
8790 \r
8791 \r
8792 VOID\r
8793 SetUserThinkingEnables()\r
8794 {\r
8795   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8796 }\r
8797 \r
8798 VOID\r
8799 SetMachineThinkingEnables()\r
8800 {\r
8801   HMENU hMenu = GetMenu(hwndMain);\r
8802   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8803 \r
8804   SetMenuEnables(hMenu, machineThinkingEnables);\r
8805 \r
8806   if (gameMode == MachinePlaysBlack) {\r
8807     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8808   } else if (gameMode == MachinePlaysWhite) {\r
8809     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8810   } else if (gameMode == TwoMachinesPlay) {\r
8811     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8812   }\r
8813 }\r
8814 \r
8815 \r
8816 VOID\r
8817 DisplayTitle(char *str)\r
8818 {\r
8819   char title[MSG_SIZ], *host;\r
8820   if (str[0] != NULLCHAR) {\r
8821     strcpy(title, str);\r
8822   } else if (appData.icsActive) {\r
8823     if (appData.icsCommPort[0] != NULLCHAR)\r
8824       host = "ICS";\r
8825     else \r
8826       host = appData.icsHost;\r
8827     sprintf(title, "%s: %s", szTitle, host);\r
8828   } else if (appData.noChessProgram) {\r
8829     strcpy(title, szTitle);\r
8830   } else {\r
8831     strcpy(title, szTitle);\r
8832     strcat(title, ": ");\r
8833     strcat(title, first.tidy);\r
8834   }\r
8835   SetWindowText(hwndMain, title);\r
8836 }\r
8837 \r
8838 \r
8839 VOID\r
8840 DisplayMessage(char *str1, char *str2)\r
8841 {\r
8842   HDC hdc;\r
8843   HFONT oldFont;\r
8844   int remain = MESSAGE_TEXT_MAX - 1;\r
8845   int len;\r
8846 \r
8847   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8848   messageText[0] = NULLCHAR;\r
8849   if (*str1) {\r
8850     len = strlen(str1);\r
8851     if (len > remain) len = remain;\r
8852     strncpy(messageText, str1, len);\r
8853     messageText[len] = NULLCHAR;\r
8854     remain -= len;\r
8855   }\r
8856   if (*str2 && remain >= 2) {\r
8857     if (*str1) {\r
8858       strcat(messageText, "  ");\r
8859       remain -= 2;\r
8860     }\r
8861     len = strlen(str2);\r
8862     if (len > remain) len = remain;\r
8863     strncat(messageText, str2, len);\r
8864   }\r
8865   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8866 \r
8867   if (IsIconic(hwndMain)) return;\r
8868   hdc = GetDC(hwndMain);\r
8869   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8870   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8871              &messageRect, messageText, strlen(messageText), NULL);\r
8872   (void) SelectObject(hdc, oldFont);\r
8873   (void) ReleaseDC(hwndMain, hdc);\r
8874 }\r
8875 \r
8876 VOID\r
8877 DisplayError(char *str, int error)\r
8878 {\r
8879   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8880   int len;\r
8881 \r
8882   if (error == 0) {\r
8883     strcpy(buf, str);\r
8884   } else {\r
8885     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8886                         NULL, error, LANG_NEUTRAL,\r
8887                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8888     if (len > 0) {\r
8889       sprintf(buf, "%s:\n%s", str, buf2);\r
8890     } else {\r
8891       ErrorMap *em = errmap;\r
8892       while (em->err != 0 && em->err != error) em++;\r
8893       if (em->err != 0) {\r
8894         sprintf(buf, "%s:\n%s", str, em->msg);\r
8895       } else {\r
8896         sprintf(buf, "%s:\nError code %d", str, error);\r
8897       }\r
8898     }\r
8899   }\r
8900   \r
8901   ErrorPopUp("Error", buf);\r
8902 }\r
8903 \r
8904 \r
8905 VOID\r
8906 DisplayMoveError(char *str)\r
8907 {\r
8908   fromX = fromY = -1;\r
8909   ClearHighlights();\r
8910   DrawPosition(FALSE, NULL);\r
8911   if (appData.popupMoveErrors) {\r
8912     ErrorPopUp("Error", str);\r
8913   } else {\r
8914     DisplayMessage(str, "");\r
8915     moveErrorMessageUp = TRUE;\r
8916   }\r
8917 }\r
8918 \r
8919 VOID\r
8920 DisplayFatalError(char *str, int error, int exitStatus)\r
8921 {\r
8922   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8923   int len;\r
8924   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8925 \r
8926   if (error != 0) {\r
8927     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8928                         NULL, error, LANG_NEUTRAL,\r
8929                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8930     if (len > 0) {\r
8931       sprintf(buf, "%s:\n%s", str, buf2);\r
8932     } else {\r
8933       ErrorMap *em = errmap;\r
8934       while (em->err != 0 && em->err != error) em++;\r
8935       if (em->err != 0) {\r
8936         sprintf(buf, "%s:\n%s", str, em->msg);\r
8937       } else {\r
8938         sprintf(buf, "%s:\nError code %d", str, error);\r
8939       }\r
8940     }\r
8941     str = buf;\r
8942   }\r
8943   if (appData.debugMode) {\r
8944     fprintf(debugFP, "%s: %s\n", label, str);\r
8945   }\r
8946   if (appData.popupExitMessage) {\r
8947     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8948                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8949   }\r
8950   ExitEvent(exitStatus);\r
8951 }\r
8952 \r
8953 \r
8954 VOID\r
8955 DisplayInformation(char *str)\r
8956 {\r
8957   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8958 }\r
8959 \r
8960 \r
8961 VOID\r
8962 DisplayNote(char *str)\r
8963 {\r
8964   ErrorPopUp("Note", str);\r
8965 }\r
8966 \r
8967 \r
8968 typedef struct {\r
8969   char *title, *question, *replyPrefix;\r
8970   ProcRef pr;\r
8971 } QuestionParams;\r
8972 \r
8973 LRESULT CALLBACK\r
8974 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8975 {\r
8976   static QuestionParams *qp;\r
8977   char reply[MSG_SIZ];\r
8978   int len, err;\r
8979 \r
8980   switch (message) {\r
8981   case WM_INITDIALOG:\r
8982     qp = (QuestionParams *) lParam;\r
8983     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8984     SetWindowText(hDlg, qp->title);\r
8985     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8986     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8987     return FALSE;\r
8988 \r
8989   case WM_COMMAND:\r
8990     switch (LOWORD(wParam)) {\r
8991     case IDOK:\r
8992       strcpy(reply, qp->replyPrefix);\r
8993       if (*reply) strcat(reply, " ");\r
8994       len = strlen(reply);\r
8995       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8996       strcat(reply, "\n");\r
8997       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8998       EndDialog(hDlg, TRUE);\r
8999       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9000       return TRUE;\r
9001     case IDCANCEL:\r
9002       EndDialog(hDlg, FALSE);\r
9003       return TRUE;\r
9004     default:\r
9005       break;\r
9006     }\r
9007     break;\r
9008   }\r
9009   return FALSE;\r
9010 }\r
9011 \r
9012 VOID\r
9013 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9014 {\r
9015     QuestionParams qp;\r
9016     FARPROC lpProc;\r
9017     \r
9018     qp.title = title;\r
9019     qp.question = question;\r
9020     qp.replyPrefix = replyPrefix;\r
9021     qp.pr = pr;\r
9022     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9023     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9024       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9025     FreeProcInstance(lpProc);\r
9026 }\r
9027 \r
9028 /* [AS] Pick FRC position */\r
9029 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9030 {\r
9031     static int * lpIndexFRC;\r
9032     BOOL index_is_ok;\r
9033     char buf[16];\r
9034 \r
9035     switch( message )\r
9036     {\r
9037     case WM_INITDIALOG:\r
9038         lpIndexFRC = (int *) lParam;\r
9039 \r
9040         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9041 \r
9042         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9043         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9044         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9045         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9046 \r
9047         break;\r
9048 \r
9049     case WM_COMMAND:\r
9050         switch( LOWORD(wParam) ) {\r
9051         case IDOK:\r
9052             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9053             EndDialog( hDlg, 0 );\r
9054             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9055             return TRUE;\r
9056         case IDCANCEL:\r
9057             EndDialog( hDlg, 1 );   \r
9058             return TRUE;\r
9059         case IDC_NFG_Edit:\r
9060             if( HIWORD(wParam) == EN_CHANGE ) {\r
9061                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9062 \r
9063                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9064             }\r
9065             return TRUE;\r
9066         case IDC_NFG_Random:\r
9067             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9068             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9069             return TRUE;\r
9070         }\r
9071 \r
9072         break;\r
9073     }\r
9074 \r
9075     return FALSE;\r
9076 }\r
9077 \r
9078 int NewGameFRC()\r
9079 {\r
9080     int result;\r
9081     int index = appData.defaultFrcPosition;\r
9082     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9083 \r
9084     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9085 \r
9086     if( result == 0 ) {\r
9087         appData.defaultFrcPosition = index;\r
9088     }\r
9089 \r
9090     return result;\r
9091 }\r
9092 \r
9093 /* [AS] Game list options */\r
9094 typedef struct {\r
9095     char id;\r
9096     char * name;\r
9097 } GLT_Item;\r
9098 \r
9099 static GLT_Item GLT_ItemInfo[] = {\r
9100     { GLT_EVENT,      "Event" },\r
9101     { GLT_SITE,       "Site" },\r
9102     { GLT_DATE,       "Date" },\r
9103     { GLT_ROUND,      "Round" },\r
9104     { GLT_PLAYERS,    "Players" },\r
9105     { GLT_RESULT,     "Result" },\r
9106     { GLT_WHITE_ELO,  "White Rating" },\r
9107     { GLT_BLACK_ELO,  "Black Rating" },\r
9108     { GLT_TIME_CONTROL,"Time Control" },\r
9109     { GLT_VARIANT,    "Variant" },\r
9110     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9111     { 0, 0 }\r
9112 };\r
9113 \r
9114 const char * GLT_FindItem( char id )\r
9115 {\r
9116     const char * result = 0;\r
9117 \r
9118     GLT_Item * list = GLT_ItemInfo;\r
9119 \r
9120     while( list->id != 0 ) {\r
9121         if( list->id == id ) {\r
9122             result = list->name;\r
9123             break;\r
9124         }\r
9125 \r
9126         list++;\r
9127     }\r
9128 \r
9129     return result;\r
9130 }\r
9131 \r
9132 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9133 {\r
9134     const char * name = GLT_FindItem( id );\r
9135 \r
9136     if( name != 0 ) {\r
9137         if( index >= 0 ) {\r
9138             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9139         }\r
9140         else {\r
9141             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9142         }\r
9143     }\r
9144 }\r
9145 \r
9146 void GLT_TagsToList( HWND hDlg, char * tags )\r
9147 {\r
9148     char * pc = tags;\r
9149 \r
9150     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9151 \r
9152     while( *pc ) {\r
9153         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9154         pc++;\r
9155     }\r
9156 \r
9157     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9158 \r
9159     pc = GLT_ALL_TAGS;\r
9160 \r
9161     while( *pc ) {\r
9162         if( strchr( tags, *pc ) == 0 ) {\r
9163             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9164         }\r
9165         pc++;\r
9166     }\r
9167 \r
9168     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9169 }\r
9170 \r
9171 char GLT_ListItemToTag( HWND hDlg, int index )\r
9172 {\r
9173     char result = '\0';\r
9174     char name[128];\r
9175 \r
9176     GLT_Item * list = GLT_ItemInfo;\r
9177 \r
9178     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9179         while( list->id != 0 ) {\r
9180             if( strcmp( list->name, name ) == 0 ) {\r
9181                 result = list->id;\r
9182                 break;\r
9183             }\r
9184 \r
9185             list++;\r
9186         }\r
9187     }\r
9188 \r
9189     return result;\r
9190 }\r
9191 \r
9192 void GLT_MoveSelection( HWND hDlg, int delta )\r
9193 {\r
9194     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9195     int idx2 = idx1 + delta;\r
9196     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9197 \r
9198     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9199         char buf[128];\r
9200 \r
9201         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9202         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9203         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9204         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9205     }\r
9206 }\r
9207 \r
9208 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9209 {\r
9210     static char glt[64];\r
9211     static char * lpUserGLT;\r
9212 \r
9213     switch( message )\r
9214     {\r
9215     case WM_INITDIALOG:\r
9216         lpUserGLT = (char *) lParam;\r
9217         \r
9218         strcpy( glt, lpUserGLT );\r
9219 \r
9220         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9221 \r
9222         /* Initialize list */\r
9223         GLT_TagsToList( hDlg, glt );\r
9224 \r
9225         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9226 \r
9227         break;\r
9228 \r
9229     case WM_COMMAND:\r
9230         switch( LOWORD(wParam) ) {\r
9231         case IDOK:\r
9232             {\r
9233                 char * pc = lpUserGLT;\r
9234                 int idx = 0;\r
9235                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9236                 char id;\r
9237 \r
9238                 do {\r
9239                     id = GLT_ListItemToTag( hDlg, idx );\r
9240 \r
9241                     *pc++ = id;\r
9242                     idx++;\r
9243                 } while( id != '\0' );\r
9244             }\r
9245             EndDialog( hDlg, 0 );\r
9246             return TRUE;\r
9247         case IDCANCEL:\r
9248             EndDialog( hDlg, 1 );\r
9249             return TRUE;\r
9250 \r
9251         case IDC_GLT_Default:\r
9252             strcpy( glt, GLT_DEFAULT_TAGS );\r
9253             GLT_TagsToList( hDlg, glt );\r
9254             return TRUE;\r
9255 \r
9256         case IDC_GLT_Restore:\r
9257             strcpy( glt, lpUserGLT );\r
9258             GLT_TagsToList( hDlg, glt );\r
9259             return TRUE;\r
9260 \r
9261         case IDC_GLT_Up:\r
9262             GLT_MoveSelection( hDlg, -1 );\r
9263             return TRUE;\r
9264 \r
9265         case IDC_GLT_Down:\r
9266             GLT_MoveSelection( hDlg, +1 );\r
9267             return TRUE;\r
9268         }\r
9269 \r
9270         break;\r
9271     }\r
9272 \r
9273     return FALSE;\r
9274 }\r
9275 \r
9276 int GameListOptions()\r
9277 {\r
9278     char glt[64];\r
9279     int result;\r
9280     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9281 \r
9282     strcpy( glt, appData.gameListTags );\r
9283 \r
9284     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9285 \r
9286     if( result == 0 ) {\r
9287         /* [AS] Memory leak here! */\r
9288         appData.gameListTags = strdup( glt ); \r
9289     }\r
9290 \r
9291     return result;\r
9292 }\r
9293 \r
9294 \r
9295 VOID\r
9296 DisplayIcsInteractionTitle(char *str)\r
9297 {\r
9298   char consoleTitle[MSG_SIZ];\r
9299 \r
9300   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9301   SetWindowText(hwndConsole, consoleTitle);\r
9302 }\r
9303 \r
9304 void\r
9305 DrawPosition(int fullRedraw, Board board)\r
9306 {\r
9307   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9308 }\r
9309 \r
9310 \r
9311 VOID\r
9312 ResetFrontEnd()\r
9313 {\r
9314   fromX = fromY = -1;\r
9315   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9316     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9317     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9318     dragInfo.lastpos = dragInfo.pos;\r
9319     dragInfo.start.x = dragInfo.start.y = -1;\r
9320     dragInfo.from = dragInfo.start;\r
9321     ReleaseCapture();\r
9322     DrawPosition(TRUE, NULL);\r
9323   }\r
9324 }\r
9325 \r
9326 \r
9327 VOID\r
9328 CommentPopUp(char *title, char *str)\r
9329 {\r
9330   HWND hwnd = GetActiveWindow();\r
9331   EitherCommentPopUp(0, title, str, FALSE);\r
9332   SetActiveWindow(hwnd);\r
9333 }\r
9334 \r
9335 VOID\r
9336 CommentPopDown(void)\r
9337 {\r
9338   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9339   if (commentDialog) {\r
9340     ShowWindow(commentDialog, SW_HIDE);\r
9341   }\r
9342   commentDialogUp = FALSE;\r
9343 }\r
9344 \r
9345 VOID\r
9346 EditCommentPopUp(int index, char *title, char *str)\r
9347 {\r
9348   EitherCommentPopUp(index, title, str, TRUE);\r
9349 }\r
9350 \r
9351 \r
9352 VOID\r
9353 RingBell()\r
9354 {\r
9355   MyPlaySound(&sounds[(int)SoundMove]);\r
9356 }\r
9357 \r
9358 VOID PlayIcsWinSound()\r
9359 {\r
9360   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9361 }\r
9362 \r
9363 VOID PlayIcsLossSound()\r
9364 {\r
9365   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9366 }\r
9367 \r
9368 VOID PlayIcsDrawSound()\r
9369 {\r
9370   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9371 }\r
9372 \r
9373 VOID PlayIcsUnfinishedSound()\r
9374 {\r
9375   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9376 }\r
9377 \r
9378 VOID\r
9379 PlayAlarmSound()\r
9380 {\r
9381   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9382 }\r
9383 \r
9384 \r
9385 VOID\r
9386 EchoOn()\r
9387 {\r
9388   HWND hInput;\r
9389   consoleEcho = TRUE;\r
9390   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9391   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9392   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9393 }\r
9394 \r
9395 \r
9396 VOID\r
9397 EchoOff()\r
9398 {\r
9399   CHARFORMAT cf;\r
9400   HWND hInput;\r
9401   consoleEcho = FALSE;\r
9402   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9403   /* This works OK: set text and background both to the same color */\r
9404   cf = consoleCF;\r
9405   cf.crTextColor = COLOR_ECHOOFF;\r
9406   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9407   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9408 }\r
9409 \r
9410 /* No Raw()...? */\r
9411 \r
9412 void Colorize(ColorClass cc, int continuation)\r
9413 {\r
9414   currentColorClass = cc;\r
9415   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9416   consoleCF.crTextColor = textAttribs[cc].color;\r
9417   consoleCF.dwEffects = textAttribs[cc].effects;\r
9418   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9419 }\r
9420 \r
9421 char *\r
9422 UserName()\r
9423 {\r
9424   static char buf[MSG_SIZ];\r
9425   DWORD bufsiz = MSG_SIZ;\r
9426 \r
9427   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9428         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9429   }\r
9430   if (!GetUserName(buf, &bufsiz)) {\r
9431     /*DisplayError("Error getting user name", GetLastError());*/\r
9432     strcpy(buf, "User");\r
9433   }\r
9434   return buf;\r
9435 }\r
9436 \r
9437 char *\r
9438 HostName()\r
9439 {\r
9440   static char buf[MSG_SIZ];\r
9441   DWORD bufsiz = MSG_SIZ;\r
9442 \r
9443   if (!GetComputerName(buf, &bufsiz)) {\r
9444     /*DisplayError("Error getting host name", GetLastError());*/\r
9445     strcpy(buf, "Unknown");\r
9446   }\r
9447   return buf;\r
9448 }\r
9449 \r
9450 \r
9451 int\r
9452 ClockTimerRunning()\r
9453 {\r
9454   return clockTimerEvent != 0;\r
9455 }\r
9456 \r
9457 int\r
9458 StopClockTimer()\r
9459 {\r
9460   if (clockTimerEvent == 0) return FALSE;\r
9461   KillTimer(hwndMain, clockTimerEvent);\r
9462   clockTimerEvent = 0;\r
9463   return TRUE;\r
9464 }\r
9465 \r
9466 void\r
9467 StartClockTimer(long millisec)\r
9468 {\r
9469   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9470                              (UINT) millisec, NULL);\r
9471 }\r
9472 \r
9473 void\r
9474 DisplayWhiteClock(long timeRemaining, int highlight)\r
9475 {\r
9476   HDC hdc;\r
9477   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9478 \r
9479   if(appData.noGUI) return;\r
9480   hdc = GetDC(hwndMain);\r
9481   if (!IsIconic(hwndMain)) {\r
9482     DisplayAClock(hdc, timeRemaining, highlight, \r
9483                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9484   }\r
9485   if (highlight && iconCurrent == iconBlack) {\r
9486     iconCurrent = iconWhite;\r
9487     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9488     if (IsIconic(hwndMain)) {\r
9489       DrawIcon(hdc, 2, 2, iconCurrent);\r
9490     }\r
9491   }\r
9492   (void) ReleaseDC(hwndMain, hdc);\r
9493   if (hwndConsole)\r
9494     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9495 }\r
9496 \r
9497 void\r
9498 DisplayBlackClock(long timeRemaining, int highlight)\r
9499 {\r
9500   HDC hdc;\r
9501   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9502 \r
9503   if(appData.noGUI) return;\r
9504   hdc = GetDC(hwndMain);\r
9505   if (!IsIconic(hwndMain)) {\r
9506     DisplayAClock(hdc, timeRemaining, highlight, \r
9507                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9508   }\r
9509   if (highlight && iconCurrent == iconWhite) {\r
9510     iconCurrent = iconBlack;\r
9511     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9512     if (IsIconic(hwndMain)) {\r
9513       DrawIcon(hdc, 2, 2, iconCurrent);\r
9514     }\r
9515   }\r
9516   (void) ReleaseDC(hwndMain, hdc);\r
9517   if (hwndConsole)\r
9518     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9519 }\r
9520 \r
9521 \r
9522 int\r
9523 LoadGameTimerRunning()\r
9524 {\r
9525   return loadGameTimerEvent != 0;\r
9526 }\r
9527 \r
9528 int\r
9529 StopLoadGameTimer()\r
9530 {\r
9531   if (loadGameTimerEvent == 0) return FALSE;\r
9532   KillTimer(hwndMain, loadGameTimerEvent);\r
9533   loadGameTimerEvent = 0;\r
9534   return TRUE;\r
9535 }\r
9536 \r
9537 void\r
9538 StartLoadGameTimer(long millisec)\r
9539 {\r
9540   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9541                                 (UINT) millisec, NULL);\r
9542 }\r
9543 \r
9544 void\r
9545 AutoSaveGame()\r
9546 {\r
9547   char *defName;\r
9548   FILE *f;\r
9549   char fileTitle[MSG_SIZ];\r
9550 \r
9551   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9552   f = OpenFileDialog(hwndMain, "a", defName,\r
9553                      appData.oldSaveStyle ? "gam" : "pgn",\r
9554                      GAME_FILT, \r
9555                      "Save Game to File", NULL, fileTitle, NULL);\r
9556   if (f != NULL) {\r
9557     SaveGame(f, 0, "");\r
9558     fclose(f);\r
9559   }\r
9560 }\r
9561 \r
9562 \r
9563 void\r
9564 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9565 {\r
9566   if (delayedTimerEvent != 0) {\r
9567     if (appData.debugMode) {\r
9568       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9569     }\r
9570     KillTimer(hwndMain, delayedTimerEvent);\r
9571     delayedTimerEvent = 0;\r
9572     delayedTimerCallback();\r
9573   }\r
9574   delayedTimerCallback = cb;\r
9575   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9576                                 (UINT) millisec, NULL);\r
9577 }\r
9578 \r
9579 DelayedEventCallback\r
9580 GetDelayedEvent()\r
9581 {\r
9582   if (delayedTimerEvent) {\r
9583     return delayedTimerCallback;\r
9584   } else {\r
9585     return NULL;\r
9586   }\r
9587 }\r
9588 \r
9589 void\r
9590 CancelDelayedEvent()\r
9591 {\r
9592   if (delayedTimerEvent) {\r
9593     KillTimer(hwndMain, delayedTimerEvent);\r
9594     delayedTimerEvent = 0;\r
9595   }\r
9596 }\r
9597 \r
9598 DWORD GetWin32Priority(int nice)\r
9599 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9600 /*\r
9601 REALTIME_PRIORITY_CLASS     0x00000100\r
9602 HIGH_PRIORITY_CLASS         0x00000080\r
9603 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9604 NORMAL_PRIORITY_CLASS       0x00000020\r
9605 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9606 IDLE_PRIORITY_CLASS         0x00000040\r
9607 */\r
9608         if (nice < -15) return 0x00000080;\r
9609         if (nice < 0)   return 0x00008000;\r
9610         if (nice == 0)  return 0x00000020;\r
9611         if (nice < 15)  return 0x00004000;\r
9612         return 0x00000040;\r
9613 }\r
9614 \r
9615 /* Start a child process running the given program.\r
9616    The process's standard output can be read from "from", and its\r
9617    standard input can be written to "to".\r
9618    Exit with fatal error if anything goes wrong.\r
9619    Returns an opaque pointer that can be used to destroy the process\r
9620    later.\r
9621 */\r
9622 int\r
9623 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9624 {\r
9625 #define BUFSIZE 4096\r
9626 \r
9627   HANDLE hChildStdinRd, hChildStdinWr,\r
9628     hChildStdoutRd, hChildStdoutWr;\r
9629   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9630   SECURITY_ATTRIBUTES saAttr;\r
9631   BOOL fSuccess;\r
9632   PROCESS_INFORMATION piProcInfo;\r
9633   STARTUPINFO siStartInfo;\r
9634   ChildProc *cp;\r
9635   char buf[MSG_SIZ];\r
9636   DWORD err;\r
9637 \r
9638   if (appData.debugMode) {\r
9639     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9640   }\r
9641 \r
9642   *pr = NoProc;\r
9643 \r
9644   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9645   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9646   saAttr.bInheritHandle = TRUE;\r
9647   saAttr.lpSecurityDescriptor = NULL;\r
9648 \r
9649   /*\r
9650    * The steps for redirecting child's STDOUT:\r
9651    *     1. Create anonymous pipe to be STDOUT for child.\r
9652    *     2. Create a noninheritable duplicate of read handle,\r
9653    *         and close the inheritable read handle.\r
9654    */\r
9655 \r
9656   /* Create a pipe for the child's STDOUT. */\r
9657   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9658     return GetLastError();\r
9659   }\r
9660 \r
9661   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9662   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9663                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9664                              FALSE,     /* not inherited */\r
9665                              DUPLICATE_SAME_ACCESS);\r
9666   if (! fSuccess) {\r
9667     return GetLastError();\r
9668   }\r
9669   CloseHandle(hChildStdoutRd);\r
9670 \r
9671   /*\r
9672    * The steps for redirecting child's STDIN:\r
9673    *     1. Create anonymous pipe to be STDIN for child.\r
9674    *     2. Create a noninheritable duplicate of write handle,\r
9675    *         and close the inheritable write handle.\r
9676    */\r
9677 \r
9678   /* Create a pipe for the child's STDIN. */\r
9679   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9680     return GetLastError();\r
9681   }\r
9682 \r
9683   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9684   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9685                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9686                              FALSE,     /* not inherited */\r
9687                              DUPLICATE_SAME_ACCESS);\r
9688   if (! fSuccess) {\r
9689     return GetLastError();\r
9690   }\r
9691   CloseHandle(hChildStdinWr);\r
9692 \r
9693   /* Arrange to (1) look in dir for the child .exe file, and\r
9694    * (2) have dir be the child's working directory.  Interpret\r
9695    * dir relative to the directory WinBoard loaded from. */\r
9696   GetCurrentDirectory(MSG_SIZ, buf);\r
9697   SetCurrentDirectory(installDir);\r
9698   SetCurrentDirectory(dir);\r
9699 \r
9700   /* Now create the child process. */\r
9701 \r
9702   siStartInfo.cb = sizeof(STARTUPINFO);\r
9703   siStartInfo.lpReserved = NULL;\r
9704   siStartInfo.lpDesktop = NULL;\r
9705   siStartInfo.lpTitle = NULL;\r
9706   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9707   siStartInfo.cbReserved2 = 0;\r
9708   siStartInfo.lpReserved2 = NULL;\r
9709   siStartInfo.hStdInput = hChildStdinRd;\r
9710   siStartInfo.hStdOutput = hChildStdoutWr;\r
9711   siStartInfo.hStdError = hChildStdoutWr;\r
9712 \r
9713   fSuccess = CreateProcess(NULL,\r
9714                            cmdLine,        /* command line */\r
9715                            NULL,           /* process security attributes */\r
9716                            NULL,           /* primary thread security attrs */\r
9717                            TRUE,           /* handles are inherited */\r
9718                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9719                            NULL,           /* use parent's environment */\r
9720                            NULL,\r
9721                            &siStartInfo, /* STARTUPINFO pointer */\r
9722                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9723 \r
9724   err = GetLastError();\r
9725   SetCurrentDirectory(buf); /* return to prev directory */\r
9726   if (! fSuccess) {\r
9727     return err;\r
9728   }\r
9729 \r
9730   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9731     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9732     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9733   }\r
9734 \r
9735   /* Close the handles we don't need in the parent */\r
9736   CloseHandle(piProcInfo.hThread);\r
9737   CloseHandle(hChildStdinRd);\r
9738   CloseHandle(hChildStdoutWr);\r
9739 \r
9740   /* Prepare return value */\r
9741   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9742   cp->kind = CPReal;\r
9743   cp->hProcess = piProcInfo.hProcess;\r
9744   cp->pid = piProcInfo.dwProcessId;\r
9745   cp->hFrom = hChildStdoutRdDup;\r
9746   cp->hTo = hChildStdinWrDup;\r
9747 \r
9748   *pr = (void *) cp;\r
9749 \r
9750   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9751      2000 where engines sometimes don't see the initial command(s)\r
9752      from WinBoard and hang.  I don't understand how that can happen,\r
9753      but the Sleep is harmless, so I've put it in.  Others have also\r
9754      reported what may be the same problem, so hopefully this will fix\r
9755      it for them too.  */\r
9756   Sleep(500);\r
9757 \r
9758   return NO_ERROR;\r
9759 }\r
9760 \r
9761 \r
9762 void\r
9763 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9764 {\r
9765   ChildProc *cp; int result;\r
9766 \r
9767   cp = (ChildProc *) pr;\r
9768   if (cp == NULL) return;\r
9769 \r
9770   switch (cp->kind) {\r
9771   case CPReal:\r
9772     /* TerminateProcess is considered harmful, so... */\r
9773     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9774     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9775     /* The following doesn't work because the chess program\r
9776        doesn't "have the same console" as WinBoard.  Maybe\r
9777        we could arrange for this even though neither WinBoard\r
9778        nor the chess program uses a console for stdio? */\r
9779     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9780 \r
9781     /* [AS] Special termination modes for misbehaving programs... */\r
9782     if( signal == 9 ) { \r
9783         result = TerminateProcess( cp->hProcess, 0 );\r
9784 \r
9785         if ( appData.debugMode) {\r
9786             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9787         }\r
9788     }\r
9789     else if( signal == 10 ) {\r
9790         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9791 \r
9792         if( dw != WAIT_OBJECT_0 ) {\r
9793             result = TerminateProcess( cp->hProcess, 0 );\r
9794 \r
9795             if ( appData.debugMode) {\r
9796                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9797             }\r
9798 \r
9799         }\r
9800     }\r
9801 \r
9802     CloseHandle(cp->hProcess);\r
9803     break;\r
9804 \r
9805   case CPComm:\r
9806     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9807     break;\r
9808 \r
9809   case CPSock:\r
9810     closesocket(cp->sock);\r
9811     WSACleanup();\r
9812     break;\r
9813 \r
9814   case CPRcmd:\r
9815     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9816     closesocket(cp->sock);\r
9817     closesocket(cp->sock2);\r
9818     WSACleanup();\r
9819     break;\r
9820   }\r
9821   free(cp);\r
9822 }\r
9823 \r
9824 void\r
9825 InterruptChildProcess(ProcRef pr)\r
9826 {\r
9827   ChildProc *cp;\r
9828 \r
9829   cp = (ChildProc *) pr;\r
9830   if (cp == NULL) return;\r
9831   switch (cp->kind) {\r
9832   case CPReal:\r
9833     /* The following doesn't work because the chess program\r
9834        doesn't "have the same console" as WinBoard.  Maybe\r
9835        we could arrange for this even though neither WinBoard\r
9836        nor the chess program uses a console for stdio */\r
9837     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9838     break;\r
9839 \r
9840   case CPComm:\r
9841   case CPSock:\r
9842     /* Can't interrupt */\r
9843     break;\r
9844 \r
9845   case CPRcmd:\r
9846     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9847     break;\r
9848   }\r
9849 }\r
9850 \r
9851 \r
9852 int\r
9853 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9854 {\r
9855   char cmdLine[MSG_SIZ];\r
9856 \r
9857   if (port[0] == NULLCHAR) {\r
9858     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9859   } else {\r
9860     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9861   }\r
9862   return StartChildProcess(cmdLine, "", pr);\r
9863 }\r
9864 \r
9865 \r
9866 /* Code to open TCP sockets */\r
9867 \r
9868 int\r
9869 OpenTCP(char *host, char *port, ProcRef *pr)\r
9870 {\r
9871   ChildProc *cp;\r
9872   int err;\r
9873   SOCKET s;\r
9874   struct sockaddr_in sa, mysa;\r
9875   struct hostent FAR *hp;\r
9876   unsigned short uport;\r
9877   WORD wVersionRequested;\r
9878   WSADATA wsaData;\r
9879 \r
9880   /* Initialize socket DLL */\r
9881   wVersionRequested = MAKEWORD(1, 1);\r
9882   err = WSAStartup(wVersionRequested, &wsaData);\r
9883   if (err != 0) return err;\r
9884 \r
9885   /* Make socket */\r
9886   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9887     err = WSAGetLastError();\r
9888     WSACleanup();\r
9889     return err;\r
9890   }\r
9891 \r
9892   /* Bind local address using (mostly) don't-care values.\r
9893    */\r
9894   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9895   mysa.sin_family = AF_INET;\r
9896   mysa.sin_addr.s_addr = INADDR_ANY;\r
9897   uport = (unsigned short) 0;\r
9898   mysa.sin_port = htons(uport);\r
9899   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9900       == SOCKET_ERROR) {\r
9901     err = WSAGetLastError();\r
9902     WSACleanup();\r
9903     return err;\r
9904   }\r
9905 \r
9906   /* Resolve remote host name */\r
9907   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9908   if (!(hp = gethostbyname(host))) {\r
9909     unsigned int b0, b1, b2, b3;\r
9910 \r
9911     err = WSAGetLastError();\r
9912 \r
9913     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9914       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9915       hp->h_addrtype = AF_INET;\r
9916       hp->h_length = 4;\r
9917       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9918       hp->h_addr_list[0] = (char *) malloc(4);\r
9919       hp->h_addr_list[0][0] = (char) b0;\r
9920       hp->h_addr_list[0][1] = (char) b1;\r
9921       hp->h_addr_list[0][2] = (char) b2;\r
9922       hp->h_addr_list[0][3] = (char) b3;\r
9923     } else {\r
9924       WSACleanup();\r
9925       return err;\r
9926     }\r
9927   }\r
9928   sa.sin_family = hp->h_addrtype;\r
9929   uport = (unsigned short) atoi(port);\r
9930   sa.sin_port = htons(uport);\r
9931   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9932 \r
9933   /* Make connection */\r
9934   if (connect(s, (struct sockaddr *) &sa,\r
9935               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9936     err = WSAGetLastError();\r
9937     WSACleanup();\r
9938     return err;\r
9939   }\r
9940 \r
9941   /* Prepare return value */\r
9942   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9943   cp->kind = CPSock;\r
9944   cp->sock = s;\r
9945   *pr = (ProcRef *) cp;\r
9946 \r
9947   return NO_ERROR;\r
9948 }\r
9949 \r
9950 int\r
9951 OpenCommPort(char *name, ProcRef *pr)\r
9952 {\r
9953   HANDLE h;\r
9954   COMMTIMEOUTS ct;\r
9955   ChildProc *cp;\r
9956   char fullname[MSG_SIZ];\r
9957 \r
9958   if (*name != '\\')\r
9959     sprintf(fullname, "\\\\.\\%s", name);\r
9960   else\r
9961     strcpy(fullname, name);\r
9962 \r
9963   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9964                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9965   if (h == (HANDLE) -1) {\r
9966     return GetLastError();\r
9967   }\r
9968   hCommPort = h;\r
9969 \r
9970   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9971 \r
9972   /* Accumulate characters until a 100ms pause, then parse */\r
9973   ct.ReadIntervalTimeout = 100;\r
9974   ct.ReadTotalTimeoutMultiplier = 0;\r
9975   ct.ReadTotalTimeoutConstant = 0;\r
9976   ct.WriteTotalTimeoutMultiplier = 0;\r
9977   ct.WriteTotalTimeoutConstant = 0;\r
9978   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9979 \r
9980   /* Prepare return value */\r
9981   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9982   cp->kind = CPComm;\r
9983   cp->hFrom = h;\r
9984   cp->hTo = h;\r
9985   *pr = (ProcRef *) cp;\r
9986 \r
9987   return NO_ERROR;\r
9988 }\r
9989 \r
9990 int\r
9991 OpenLoopback(ProcRef *pr)\r
9992 {\r
9993   DisplayFatalError("Not implemented", 0, 1);\r
9994   return NO_ERROR;\r
9995 }\r
9996 \r
9997 \r
9998 int\r
9999 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10000 {\r
10001   ChildProc *cp;\r
10002   int err;\r
10003   SOCKET s, s2, s3;\r
10004   struct sockaddr_in sa, mysa;\r
10005   struct hostent FAR *hp;\r
10006   unsigned short uport;\r
10007   WORD wVersionRequested;\r
10008   WSADATA wsaData;\r
10009   int fromPort;\r
10010   char stderrPortStr[MSG_SIZ];\r
10011 \r
10012   /* Initialize socket DLL */\r
10013   wVersionRequested = MAKEWORD(1, 1);\r
10014   err = WSAStartup(wVersionRequested, &wsaData);\r
10015   if (err != 0) return err;\r
10016 \r
10017   /* Resolve remote host name */\r
10018   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10019   if (!(hp = gethostbyname(host))) {\r
10020     unsigned int b0, b1, b2, b3;\r
10021 \r
10022     err = WSAGetLastError();\r
10023 \r
10024     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10025       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10026       hp->h_addrtype = AF_INET;\r
10027       hp->h_length = 4;\r
10028       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10029       hp->h_addr_list[0] = (char *) malloc(4);\r
10030       hp->h_addr_list[0][0] = (char) b0;\r
10031       hp->h_addr_list[0][1] = (char) b1;\r
10032       hp->h_addr_list[0][2] = (char) b2;\r
10033       hp->h_addr_list[0][3] = (char) b3;\r
10034     } else {\r
10035       WSACleanup();\r
10036       return err;\r
10037     }\r
10038   }\r
10039   sa.sin_family = hp->h_addrtype;\r
10040   uport = (unsigned short) 514;\r
10041   sa.sin_port = htons(uport);\r
10042   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10043 \r
10044   /* Bind local socket to unused "privileged" port address\r
10045    */\r
10046   s = INVALID_SOCKET;\r
10047   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10048   mysa.sin_family = AF_INET;\r
10049   mysa.sin_addr.s_addr = INADDR_ANY;\r
10050   for (fromPort = 1023;; fromPort--) {\r
10051     if (fromPort < 0) {\r
10052       WSACleanup();\r
10053       return WSAEADDRINUSE;\r
10054     }\r
10055     if (s == INVALID_SOCKET) {\r
10056       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10057         err = WSAGetLastError();\r
10058         WSACleanup();\r
10059         return err;\r
10060       }\r
10061     }\r
10062     uport = (unsigned short) fromPort;\r
10063     mysa.sin_port = htons(uport);\r
10064     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10065         == SOCKET_ERROR) {\r
10066       err = WSAGetLastError();\r
10067       if (err == WSAEADDRINUSE) continue;\r
10068       WSACleanup();\r
10069       return err;\r
10070     }\r
10071     if (connect(s, (struct sockaddr *) &sa,\r
10072       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10073       err = WSAGetLastError();\r
10074       if (err == WSAEADDRINUSE) {\r
10075         closesocket(s);\r
10076         s = -1;\r
10077         continue;\r
10078       }\r
10079       WSACleanup();\r
10080       return err;\r
10081     }\r
10082     break;\r
10083   }\r
10084 \r
10085   /* Bind stderr local socket to unused "privileged" port address\r
10086    */\r
10087   s2 = INVALID_SOCKET;\r
10088   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10089   mysa.sin_family = AF_INET;\r
10090   mysa.sin_addr.s_addr = INADDR_ANY;\r
10091   for (fromPort = 1023;; fromPort--) {\r
10092     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10093     if (fromPort < 0) {\r
10094       (void) closesocket(s);\r
10095       WSACleanup();\r
10096       return WSAEADDRINUSE;\r
10097     }\r
10098     if (s2 == INVALID_SOCKET) {\r
10099       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10100         err = WSAGetLastError();\r
10101         closesocket(s);\r
10102         WSACleanup();\r
10103         return err;\r
10104       }\r
10105     }\r
10106     uport = (unsigned short) fromPort;\r
10107     mysa.sin_port = htons(uport);\r
10108     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10109         == SOCKET_ERROR) {\r
10110       err = WSAGetLastError();\r
10111       if (err == WSAEADDRINUSE) continue;\r
10112       (void) closesocket(s);\r
10113       WSACleanup();\r
10114       return err;\r
10115     }\r
10116     if (listen(s2, 1) == SOCKET_ERROR) {\r
10117       err = WSAGetLastError();\r
10118       if (err == WSAEADDRINUSE) {\r
10119         closesocket(s2);\r
10120         s2 = INVALID_SOCKET;\r
10121         continue;\r
10122       }\r
10123       (void) closesocket(s);\r
10124       (void) closesocket(s2);\r
10125       WSACleanup();\r
10126       return err;\r
10127     }\r
10128     break;\r
10129   }\r
10130   prevStderrPort = fromPort; // remember port used\r
10131   sprintf(stderrPortStr, "%d", fromPort);\r
10132 \r
10133   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10134     err = WSAGetLastError();\r
10135     (void) closesocket(s);\r
10136     (void) closesocket(s2);\r
10137     WSACleanup();\r
10138     return err;\r
10139   }\r
10140 \r
10141   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10142     err = WSAGetLastError();\r
10143     (void) closesocket(s);\r
10144     (void) closesocket(s2);\r
10145     WSACleanup();\r
10146     return err;\r
10147   }\r
10148   if (*user == NULLCHAR) user = UserName();\r
10149   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10150     err = WSAGetLastError();\r
10151     (void) closesocket(s);\r
10152     (void) closesocket(s2);\r
10153     WSACleanup();\r
10154     return err;\r
10155   }\r
10156   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10157     err = WSAGetLastError();\r
10158     (void) closesocket(s);\r
10159     (void) closesocket(s2);\r
10160     WSACleanup();\r
10161     return err;\r
10162   }\r
10163 \r
10164   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10165     err = WSAGetLastError();\r
10166     (void) closesocket(s);\r
10167     (void) closesocket(s2);\r
10168     WSACleanup();\r
10169     return err;\r
10170   }\r
10171   (void) closesocket(s2);  /* Stop listening */\r
10172 \r
10173   /* Prepare return value */\r
10174   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10175   cp->kind = CPRcmd;\r
10176   cp->sock = s;\r
10177   cp->sock2 = s3;\r
10178   *pr = (ProcRef *) cp;\r
10179 \r
10180   return NO_ERROR;\r
10181 }\r
10182 \r
10183 \r
10184 InputSourceRef\r
10185 AddInputSource(ProcRef pr, int lineByLine,\r
10186                InputCallback func, VOIDSTAR closure)\r
10187 {\r
10188   InputSource *is, *is2 = NULL;\r
10189   ChildProc *cp = (ChildProc *) pr;\r
10190 \r
10191   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10192   is->lineByLine = lineByLine;\r
10193   is->func = func;\r
10194   is->closure = closure;\r
10195   is->second = NULL;\r
10196   is->next = is->buf;\r
10197   if (pr == NoProc) {\r
10198     is->kind = CPReal;\r
10199     consoleInputSource = is;\r
10200   } else {\r
10201     is->kind = cp->kind;\r
10202     /* \r
10203         [AS] Try to avoid a race condition if the thread is given control too early:\r
10204         we create all threads suspended so that the is->hThread variable can be\r
10205         safely assigned, then let the threads start with ResumeThread.\r
10206     */\r
10207     switch (cp->kind) {\r
10208     case CPReal:\r
10209       is->hFile = cp->hFrom;\r
10210       cp->hFrom = NULL; /* now owned by InputThread */\r
10211       is->hThread =\r
10212         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10213                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10214       break;\r
10215 \r
10216     case CPComm:\r
10217       is->hFile = cp->hFrom;\r
10218       cp->hFrom = NULL; /* now owned by InputThread */\r
10219       is->hThread =\r
10220         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10221                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10222       break;\r
10223 \r
10224     case CPSock:\r
10225       is->sock = cp->sock;\r
10226       is->hThread =\r
10227         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10228                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10229       break;\r
10230 \r
10231     case CPRcmd:\r
10232       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10233       *is2 = *is;\r
10234       is->sock = cp->sock;\r
10235       is->second = is2;\r
10236       is2->sock = cp->sock2;\r
10237       is2->second = is2;\r
10238       is->hThread =\r
10239         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10240                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10241       is2->hThread =\r
10242         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10243                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10244       break;\r
10245     }\r
10246 \r
10247     if( is->hThread != NULL ) {\r
10248         ResumeThread( is->hThread );\r
10249     }\r
10250 \r
10251     if( is2 != NULL && is2->hThread != NULL ) {\r
10252         ResumeThread( is2->hThread );\r
10253     }\r
10254   }\r
10255 \r
10256   return (InputSourceRef) is;\r
10257 }\r
10258 \r
10259 void\r
10260 RemoveInputSource(InputSourceRef isr)\r
10261 {\r
10262   InputSource *is;\r
10263 \r
10264   is = (InputSource *) isr;\r
10265   is->hThread = NULL;  /* tell thread to stop */\r
10266   CloseHandle(is->hThread);\r
10267   if (is->second != NULL) {\r
10268     is->second->hThread = NULL;\r
10269     CloseHandle(is->second->hThread);\r
10270   }\r
10271 }\r
10272 \r
10273 \r
10274 int\r
10275 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10276 {\r
10277   DWORD dOutCount;\r
10278   int outCount = SOCKET_ERROR;\r
10279   ChildProc *cp = (ChildProc *) pr;\r
10280   static OVERLAPPED ovl;\r
10281 \r
10282   if (pr == NoProc) {\r
10283     ConsoleOutput(message, count, FALSE);\r
10284     return count;\r
10285   } \r
10286 \r
10287   if (ovl.hEvent == NULL) {\r
10288     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10289   }\r
10290   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10291 \r
10292   switch (cp->kind) {\r
10293   case CPSock:\r
10294   case CPRcmd:\r
10295     outCount = send(cp->sock, message, count, 0);\r
10296     if (outCount == SOCKET_ERROR) {\r
10297       *outError = WSAGetLastError();\r
10298     } else {\r
10299       *outError = NO_ERROR;\r
10300     }\r
10301     break;\r
10302 \r
10303   case CPReal:\r
10304     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10305                   &dOutCount, NULL)) {\r
10306       *outError = NO_ERROR;\r
10307       outCount = (int) dOutCount;\r
10308     } else {\r
10309       *outError = GetLastError();\r
10310     }\r
10311     break;\r
10312 \r
10313   case CPComm:\r
10314     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10315                             &dOutCount, &ovl);\r
10316     if (*outError == NO_ERROR) {\r
10317       outCount = (int) dOutCount;\r
10318     }\r
10319     break;\r
10320   }\r
10321   return outCount;\r
10322 }\r
10323 \r
10324 int\r
10325 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10326                        long msdelay)\r
10327 {\r
10328   /* Ignore delay, not implemented for WinBoard */\r
10329   return OutputToProcess(pr, message, count, outError);\r
10330 }\r
10331 \r
10332 \r
10333 void\r
10334 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10335                         char *buf, int count, int error)\r
10336 {\r
10337   DisplayFatalError("Not implemented", 0, 1);\r
10338 }\r
10339 \r
10340 /* see wgamelist.c for Game List functions */\r
10341 /* see wedittags.c for Edit Tags functions */\r
10342 \r
10343 \r
10344 VOID\r
10345 ICSInitScript()\r
10346 {\r
10347   FILE *f;\r
10348   char buf[MSG_SIZ];\r
10349   char *dummy;\r
10350 \r
10351   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10352     f = fopen(buf, "r");\r
10353     if (f != NULL) {\r
10354       ProcessICSInitScript(f);\r
10355       fclose(f);\r
10356     }\r
10357   }\r
10358 }\r
10359 \r
10360 \r
10361 VOID\r
10362 StartAnalysisClock()\r
10363 {\r
10364   if (analysisTimerEvent) return;\r
10365   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10366                                         (UINT) 2000, NULL);\r
10367 }\r
10368 \r
10369 LRESULT CALLBACK\r
10370 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10371 {\r
10372   static HANDLE hwndText;\r
10373   RECT rect;\r
10374   static int sizeX, sizeY;\r
10375   int newSizeX, newSizeY, flags;\r
10376   MINMAXINFO *mmi;\r
10377 \r
10378   switch (message) {\r
10379   case WM_INITDIALOG: /* message: initialize dialog box */\r
10380     /* Initialize the dialog items */\r
10381     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10382     SetWindowText(hDlg, analysisTitle);\r
10383     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10384     /* Size and position the dialog */\r
10385     if (!analysisDialog) {\r
10386       analysisDialog = hDlg;\r
10387       flags = SWP_NOZORDER;\r
10388       GetClientRect(hDlg, &rect);\r
10389       sizeX = rect.right;\r
10390       sizeY = rect.bottom;\r
10391       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10392           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10393         WINDOWPLACEMENT wp;\r
10394         EnsureOnScreen(&analysisX, &analysisY);\r
10395         wp.length = sizeof(WINDOWPLACEMENT);\r
10396         wp.flags = 0;\r
10397         wp.showCmd = SW_SHOW;\r
10398         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10399         wp.rcNormalPosition.left = analysisX;\r
10400         wp.rcNormalPosition.right = analysisX + analysisW;\r
10401         wp.rcNormalPosition.top = analysisY;\r
10402         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10403         SetWindowPlacement(hDlg, &wp);\r
10404 \r
10405         GetClientRect(hDlg, &rect);\r
10406         newSizeX = rect.right;\r
10407         newSizeY = rect.bottom;\r
10408         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10409                               newSizeX, newSizeY);\r
10410         sizeX = newSizeX;\r
10411         sizeY = newSizeY;\r
10412       }\r
10413     }\r
10414     return FALSE;\r
10415 \r
10416   case WM_COMMAND: /* message: received a command */\r
10417     switch (LOWORD(wParam)) {\r
10418     case IDCANCEL:\r
10419       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10420           ExitAnalyzeMode();\r
10421           ModeHighlight();\r
10422           return TRUE;\r
10423       }\r
10424       EditGameEvent();\r
10425       return TRUE;\r
10426     default:\r
10427       break;\r
10428     }\r
10429     break;\r
10430 \r
10431   case WM_SIZE:\r
10432     newSizeX = LOWORD(lParam);\r
10433     newSizeY = HIWORD(lParam);\r
10434     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10435     sizeX = newSizeX;\r
10436     sizeY = newSizeY;\r
10437     break;\r
10438 \r
10439   case WM_GETMINMAXINFO:\r
10440     /* Prevent resizing window too small */\r
10441     mmi = (MINMAXINFO *) lParam;\r
10442     mmi->ptMinTrackSize.x = 100;\r
10443     mmi->ptMinTrackSize.y = 100;\r
10444     break;\r
10445   }\r
10446   return FALSE;\r
10447 }\r
10448 \r
10449 VOID\r
10450 AnalysisPopUp(char* title, char* str)\r
10451 {\r
10452   FARPROC lpProc;\r
10453   char *p, *q;\r
10454 \r
10455   /* [AS] */\r
10456   EngineOutputPopUp();\r
10457   return;\r
10458 \r
10459   if (str == NULL) str = "";\r
10460   p = (char *) malloc(2 * strlen(str) + 2);\r
10461   q = p;\r
10462   while (*str) {\r
10463     if (*str == '\n') *q++ = '\r';\r
10464     *q++ = *str++;\r
10465   }\r
10466   *q = NULLCHAR;\r
10467   if (analysisText != NULL) free(analysisText);\r
10468   analysisText = p;\r
10469 \r
10470   if (analysisDialog) {\r
10471     SetWindowText(analysisDialog, title);\r
10472     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10473     ShowWindow(analysisDialog, SW_SHOW);\r
10474   } else {\r
10475     analysisTitle = title;\r
10476     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10477     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10478                  hwndMain, (DLGPROC)lpProc);\r
10479     FreeProcInstance(lpProc);\r
10480   }\r
10481   analysisDialogUp = TRUE;  \r
10482 }\r
10483 \r
10484 VOID\r
10485 AnalysisPopDown()\r
10486 {\r
10487   if (analysisDialog) {\r
10488     ShowWindow(analysisDialog, SW_HIDE);\r
10489   }\r
10490   analysisDialogUp = FALSE;  \r
10491 }\r
10492 \r
10493 \r
10494 VOID\r
10495 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10496 {\r
10497   highlightInfo.sq[0].x = fromX;\r
10498   highlightInfo.sq[0].y = fromY;\r
10499   highlightInfo.sq[1].x = toX;\r
10500   highlightInfo.sq[1].y = toY;\r
10501 }\r
10502 \r
10503 VOID\r
10504 ClearHighlights()\r
10505 {\r
10506   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10507     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10508 }\r
10509 \r
10510 VOID\r
10511 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10512 {\r
10513   premoveHighlightInfo.sq[0].x = fromX;\r
10514   premoveHighlightInfo.sq[0].y = fromY;\r
10515   premoveHighlightInfo.sq[1].x = toX;\r
10516   premoveHighlightInfo.sq[1].y = toY;\r
10517 }\r
10518 \r
10519 VOID\r
10520 ClearPremoveHighlights()\r
10521 {\r
10522   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10523     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10524 }\r
10525 \r
10526 VOID\r
10527 ShutDownFrontEnd()\r
10528 {\r
10529   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10530   DeleteClipboardTempFiles();\r
10531 }\r
10532 \r
10533 void\r
10534 BoardToTop()\r
10535 {\r
10536     if (IsIconic(hwndMain))\r
10537       ShowWindow(hwndMain, SW_RESTORE);\r
10538 \r
10539     SetActiveWindow(hwndMain);\r
10540 }\r
10541 \r
10542 /*\r
10543  * Prototypes for animation support routines\r
10544  */\r
10545 static void ScreenSquare(int column, int row, POINT * pt);\r
10546 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10547      POINT frames[], int * nFrames);\r
10548 \r
10549 \r
10550 #define kFactor 4\r
10551 \r
10552 void\r
10553 AnimateMove(board, fromX, fromY, toX, toY)\r
10554      Board board;\r
10555      int fromX;\r
10556      int fromY;\r
10557      int toX;\r
10558      int toY;\r
10559 {\r
10560   ChessSquare piece;\r
10561   POINT start, finish, mid;\r
10562   POINT frames[kFactor * 2 + 1];\r
10563   int nFrames, n;\r
10564 \r
10565   if (!appData.animate) return;\r
10566   if (doingSizing) return;\r
10567   if (fromY < 0 || fromX < 0) return;\r
10568   piece = board[fromY][fromX];\r
10569   if (piece >= EmptySquare) return;\r
10570 \r
10571   ScreenSquare(fromX, fromY, &start);\r
10572   ScreenSquare(toX, toY, &finish);\r
10573 \r
10574   /* All pieces except knights move in straight line */\r
10575   if (piece != WhiteKnight && piece != BlackKnight) {\r
10576     mid.x = start.x + (finish.x - start.x) / 2;\r
10577     mid.y = start.y + (finish.y - start.y) / 2;\r
10578   } else {\r
10579     /* Knight: make diagonal movement then straight */\r
10580     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10581        mid.x = start.x + (finish.x - start.x) / 2;\r
10582        mid.y = finish.y;\r
10583      } else {\r
10584        mid.x = finish.x;\r
10585        mid.y = start.y + (finish.y - start.y) / 2;\r
10586      }\r
10587   }\r
10588   \r
10589   /* Don't use as many frames for very short moves */\r
10590   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10591     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10592   else\r
10593     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10594 \r
10595   animInfo.from.x = fromX;\r
10596   animInfo.from.y = fromY;\r
10597   animInfo.to.x = toX;\r
10598   animInfo.to.y = toY;\r
10599   animInfo.lastpos = start;\r
10600   animInfo.piece = piece;\r
10601   for (n = 0; n < nFrames; n++) {\r
10602     animInfo.pos = frames[n];\r
10603     DrawPosition(FALSE, NULL);\r
10604     animInfo.lastpos = animInfo.pos;\r
10605     Sleep(appData.animSpeed);\r
10606   }\r
10607   animInfo.pos = finish;\r
10608   DrawPosition(FALSE, NULL);\r
10609   animInfo.piece = EmptySquare;\r
10610 }\r
10611 \r
10612 /*      Convert board position to corner of screen rect and color       */\r
10613 \r
10614 static void\r
10615 ScreenSquare(column, row, pt)\r
10616      int column; int row; POINT * pt;\r
10617 {\r
10618   if (flipView) {\r
10619     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10620     pt->y = lineGap + row * (squareSize + lineGap);\r
10621   } else {\r
10622     pt->x = lineGap + column * (squareSize + lineGap);\r
10623     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10624   }\r
10625 }\r
10626 \r
10627 /*      Generate a series of frame coords from start->mid->finish.\r
10628         The movement rate doubles until the half way point is\r
10629         reached, then halves back down to the final destination,\r
10630         which gives a nice slow in/out effect. The algorithmn\r
10631         may seem to generate too many intermediates for short\r
10632         moves, but remember that the purpose is to attract the\r
10633         viewers attention to the piece about to be moved and\r
10634         then to where it ends up. Too few frames would be less\r
10635         noticeable.                                             */\r
10636 \r
10637 static void\r
10638 Tween(start, mid, finish, factor, frames, nFrames)\r
10639      POINT * start; POINT * mid;\r
10640      POINT * finish; int factor;\r
10641      POINT frames[]; int * nFrames;\r
10642 {\r
10643   int n, fraction = 1, count = 0;\r
10644 \r
10645   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10646   for (n = 0; n < factor; n++)\r
10647     fraction *= 2;\r
10648   for (n = 0; n < factor; n++) {\r
10649     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10650     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10651     count ++;\r
10652     fraction = fraction / 2;\r
10653   }\r
10654   \r
10655   /* Midpoint */\r
10656   frames[count] = *mid;\r
10657   count ++;\r
10658   \r
10659   /* Slow out, stepping 1/2, then 1/4, ... */\r
10660   fraction = 2;\r
10661   for (n = 0; n < factor; n++) {\r
10662     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10663     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10664     count ++;\r
10665     fraction = fraction * 2;\r
10666   }\r
10667   *nFrames = count;\r
10668 }\r
10669 \r
10670 void\r
10671 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10672 {\r
10673 #if 0\r
10674     char buf[256];\r
10675 \r
10676     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10677         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10678 \r
10679     OutputDebugString( buf );\r
10680 #endif\r
10681 \r
10682     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10683 \r
10684     EvalGraphSet( first, last, current, pvInfoList );\r
10685 }\r
10686 \r
10687 void SetProgramStats( FrontEndProgramStats * stats )\r
10688 {\r
10689 #if 0\r
10690     char buf[1024];\r
10691 \r
10692     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10693         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10694 \r
10695     OutputDebugString( buf );\r
10696 #endif\r
10697 \r
10698     EngineOutputUpdate( stats );\r
10699 }\r