Merge commit 'v4.3.16'
[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, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 Boolean flipClock = FALSE;\r
96 \r
97 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
98 \r
99 typedef struct {\r
100   ChessSquare piece;  \r
101   POINT pos;      /* window coordinates of current pos */\r
102   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
103   POINT from;     /* board coordinates of the piece's orig pos */\r
104   POINT to;       /* board coordinates of the piece's new pos */\r
105 } AnimInfo;\r
106 \r
107 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
108 \r
109 typedef struct {\r
110   POINT start;    /* window coordinates of start pos */\r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114 } DragInfo;\r
115 \r
116 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT sq[2];    /* board coordinates of from, to squares */\r
120 } HighlightInfo;\r
121 \r
122 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
123 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
124 \r
125 /* Window class names */\r
126 char szAppName[] = "WinBoard";\r
127 char szConsoleName[] = "WBConsole";\r
128 \r
129 /* Title bar text */\r
130 char szTitle[] = "WinBoard";\r
131 char szConsoleTitle[] = "ICS Interaction";\r
132 \r
133 char *programName;\r
134 char *settingsFileName;\r
135 BOOLEAN saveSettingsOnExit;\r
136 char installDir[MSG_SIZ];\r
137 \r
138 BoardSize boardSize;\r
139 BOOLEAN chessProgram;\r
140 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
141 static int squareSize, lineGap, minorSize;\r
142 static int winWidth, winHeight;\r
143 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
144 static int logoHeight = 0;\r
145 static char messageText[MESSAGE_TEXT_MAX];\r
146 static int clockTimerEvent = 0;\r
147 static int loadGameTimerEvent = 0;\r
148 static int analysisTimerEvent = 0;\r
149 static DelayedEventCallback delayedTimerCallback;\r
150 static int delayedTimerEvent = 0;\r
151 static int buttonCount = 2;\r
152 char *icsTextMenuString;\r
153 char *icsNames;\r
154 char *firstChessProgramNames;\r
155 char *secondChessProgramNames;\r
156 \r
157 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
158 \r
159 #define PALETTESIZE 256\r
160 \r
161 HINSTANCE hInst;          /* current instance */\r
162 HWND hwndMain = NULL;        /* root window*/\r
163 HWND hwndConsole = NULL;\r
164 BOOLEAN alwaysOnTop = FALSE;\r
165 RECT boardRect;\r
166 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
167   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
168 HPALETTE hPal;\r
169 ColorClass currentColorClass;\r
170 \r
171 HWND hCommPort = NULL;    /* currently open comm port */\r
172 static HWND hwndPause;    /* pause button */\r
173 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
174 static HBRUSH lightSquareBrush, darkSquareBrush,\r
175   blackSquareBrush, /* [HGM] for band between board and holdings */\r
176   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
177 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
178 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
179 static HPEN gridPen = NULL;\r
180 static HPEN highlightPen = NULL;\r
181 static HPEN premovePen = NULL;\r
182 static NPLOGPALETTE pLogPal;\r
183 static BOOL paletteChanged = FALSE;\r
184 static HICON iconWhite, iconBlack, iconCurrent;\r
185 static int doingSizing = FALSE;\r
186 static int lastSizing = 0;\r
187 static int prevStderrPort;\r
188 \r
189 /* [AS] Support for background textures */\r
190 #define BACK_TEXTURE_MODE_DISABLED      0\r
191 #define BACK_TEXTURE_MODE_PLAIN         1\r
192 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
193 \r
194 static HBITMAP liteBackTexture = NULL;\r
195 static HBITMAP darkBackTexture = NULL;\r
196 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
197 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
198 static int backTextureSquareSize = 0;\r
199 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
200 \r
201 #if __GNUC__ && !defined(_winmajor)\r
202 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
203 #else\r
204 #define oldDialog (_winmajor < 4)\r
205 #endif\r
206 \r
207 char *defaultTextAttribs[] = \r
208 {\r
209   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
210   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
211   COLOR_NONE\r
212 };\r
213 \r
214 typedef struct {\r
215   char *name;\r
216   int squareSize;\r
217   int lineGap;\r
218   int smallLayout;\r
219   int tinyLayout;\r
220   int cliWidth, cliHeight;\r
221 } SizeInfo;\r
222 \r
223 SizeInfo sizeInfo[] = \r
224 {\r
225   { "tiny",     21, 0, 1, 1, 0, 0 },\r
226   { "teeny",    25, 1, 1, 1, 0, 0 },\r
227   { "dinky",    29, 1, 1, 1, 0, 0 },\r
228   { "petite",   33, 1, 1, 1, 0, 0 },\r
229   { "slim",     37, 2, 1, 0, 0, 0 },\r
230   { "small",    40, 2, 1, 0, 0, 0 },\r
231   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
232   { "middling", 49, 2, 0, 0, 0, 0 },\r
233   { "average",  54, 2, 0, 0, 0, 0 },\r
234   { "moderate", 58, 3, 0, 0, 0, 0 },\r
235   { "medium",   64, 3, 0, 0, 0, 0 },\r
236   { "bulky",    72, 3, 0, 0, 0, 0 },\r
237   { "large",    80, 3, 0, 0, 0, 0 },\r
238   { "big",      87, 3, 0, 0, 0, 0 },\r
239   { "huge",     95, 3, 0, 0, 0, 0 },\r
240   { "giant",    108, 3, 0, 0, 0, 0 },\r
241   { "colossal", 116, 4, 0, 0, 0, 0 },\r
242   { "titanic",  129, 4, 0, 0, 0, 0 },\r
243   { NULL, 0, 0, 0, 0, 0, 0 }\r
244 };\r
245 \r
246 #define MF(x) {x, {0, }, {0, }, 0}\r
247 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
248 {\r
249   { 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
250   { 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
251   { 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
252   { 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
253   { 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
254   { 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
255   { 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
256   { 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
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266   { 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
267 };\r
268 \r
269 MyFont *font[NUM_SIZES][NUM_FONTS];\r
270 \r
271 typedef struct {\r
272   char *label;\r
273   int id;\r
274   HWND hwnd;\r
275   WNDPROC wndproc;\r
276 } MyButtonDesc;\r
277 \r
278 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
279 #define N_BUTTONS 5\r
280 \r
281 MyButtonDesc buttonDesc[N_BUTTONS] =\r
282 {\r
283   {"<<", IDM_ToStart, NULL, NULL},\r
284   {"<", IDM_Backward, NULL, NULL},\r
285   {"P", IDM_Pause, NULL, NULL},\r
286   {">", IDM_Forward, NULL, NULL},\r
287   {">>", IDM_ToEnd, NULL, NULL},\r
288 };\r
289 \r
290 int tinyLayout = 0, smallLayout = 0;\r
291 #define MENU_BAR_ITEMS 6\r
292 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
293   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
294   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
295 };\r
296 \r
297 \r
298 MySound sounds[(int)NSoundClasses];\r
299 MyTextAttribs textAttribs[(int)NColorClasses];\r
300 \r
301 MyColorizeAttribs colorizeAttribs[] = {\r
302   { (COLORREF)0, 0, "Shout Text" },\r
303   { (COLORREF)0, 0, "SShout/CShout" },\r
304   { (COLORREF)0, 0, "Channel 1 Text" },\r
305   { (COLORREF)0, 0, "Channel Text" },\r
306   { (COLORREF)0, 0, "Kibitz Text" },\r
307   { (COLORREF)0, 0, "Tell Text" },\r
308   { (COLORREF)0, 0, "Challenge Text" },\r
309   { (COLORREF)0, 0, "Request Text" },\r
310   { (COLORREF)0, 0, "Seek Text" },\r
311   { (COLORREF)0, 0, "Normal Text" },\r
312   { (COLORREF)0, 0, "None" }\r
313 };\r
314 \r
315 \r
316 \r
317 static char *commentTitle;\r
318 static char *commentText;\r
319 static int commentIndex;\r
320 static Boolean editComment = FALSE;\r
321 HWND commentDialog = NULL;\r
322 BOOLEAN commentDialogUp = FALSE;\r
323 static int commentX, commentY, commentH, commentW;\r
324 \r
325 static char *analysisTitle;\r
326 static char *analysisText;\r
327 HWND analysisDialog = NULL;\r
328 BOOLEAN analysisDialogUp = FALSE;\r
329 static int analysisX, analysisY, analysisH, analysisW;\r
330 \r
331 char errorTitle[MSG_SIZ];\r
332 char errorMessage[2*MSG_SIZ];\r
333 HWND errorDialog = NULL;\r
334 BOOLEAN moveErrorMessageUp = FALSE;\r
335 BOOLEAN consoleEcho = TRUE;\r
336 CHARFORMAT consoleCF;\r
337 COLORREF consoleBackgroundColor;\r
338 \r
339 char *programVersion;\r
340 \r
341 #define CPReal 1\r
342 #define CPComm 2\r
343 #define CPSock 3\r
344 #define CPRcmd 4\r
345 typedef int CPKind;\r
346 \r
347 typedef struct {\r
348   CPKind kind;\r
349   HANDLE hProcess;\r
350   DWORD pid;\r
351   HANDLE hTo;\r
352   HANDLE hFrom;\r
353   SOCKET sock;\r
354   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
355 } ChildProc;\r
356 \r
357 #define INPUT_SOURCE_BUF_SIZE 4096\r
358 \r
359 typedef struct _InputSource {\r
360   CPKind kind;\r
361   HANDLE hFile;\r
362   SOCKET sock;\r
363   int lineByLine;\r
364   HANDLE hThread;\r
365   DWORD id;\r
366   char buf[INPUT_SOURCE_BUF_SIZE];\r
367   char *next;\r
368   DWORD count;\r
369   int error;\r
370   InputCallback func;\r
371   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
372   VOIDSTAR closure;\r
373 } InputSource;\r
374 \r
375 InputSource *consoleInputSource;\r
376 \r
377 DCB dcb;\r
378 \r
379 /* forward */\r
380 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
381 VOID ConsoleCreate();\r
382 LRESULT CALLBACK\r
383   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
384 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
385 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
386 VOID ParseCommSettings(char *arg, DCB *dcb);\r
387 LRESULT CALLBACK\r
388   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
389 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
390 void ParseIcsTextMenu(char *icsTextMenuString);\r
391 VOID PopUpMoveDialog(char firstchar);\r
392 VOID PopUpNameDialog(char firstchar);\r
393 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
394 \r
395 /* [AS] */\r
396 int NewGameFRC();\r
397 int GameListOptions();\r
398 \r
399 HWND moveHistoryDialog = NULL;\r
400 BOOLEAN moveHistoryDialogUp = FALSE;\r
401 \r
402 WindowPlacement wpMoveHistory;\r
403 \r
404 HWND evalGraphDialog = NULL;\r
405 BOOLEAN evalGraphDialogUp = FALSE;\r
406 \r
407 WindowPlacement wpEvalGraph;\r
408 \r
409 HWND engineOutputDialog = NULL;\r
410 BOOLEAN engineOutputDialogUp = FALSE;\r
411 \r
412 WindowPlacement wpEngineOutput;\r
413 \r
414 VOID MoveHistoryPopUp();\r
415 VOID MoveHistoryPopDown();\r
416 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
417 BOOL MoveHistoryIsUp();\r
418 \r
419 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
420 VOID EvalGraphPopUp();\r
421 VOID EvalGraphPopDown();\r
422 BOOL EvalGraphIsUp();\r
423 \r
424 VOID EngineOutputPopUp();\r
425 VOID EngineOutputPopDown();\r
426 BOOL EngineOutputIsUp();\r
427 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
428 \r
429 VOID GothicPopUp(char *title, VariantClass variant);\r
430 /*\r
431  * Setting "frozen" should disable all user input other than deleting\r
432  * the window.  We do this while engines are initializing themselves.\r
433  */\r
434 static int frozen = 0;\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];\r
436 void FreezeUI()\r
437 {\r
438   HMENU hmenu;\r
439   int i;\r
440 \r
441   if (frozen) return;\r
442   frozen = 1;\r
443   hmenu = GetMenu(hwndMain);\r
444   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
445     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
446   }\r
447   DrawMenuBar(hwndMain);\r
448 }\r
449 \r
450 /* Undo a FreezeUI */\r
451 void ThawUI()\r
452 {\r
453   HMENU hmenu;\r
454   int i;\r
455 \r
456   if (!frozen) return;\r
457   frozen = 0;\r
458   hmenu = GetMenu(hwndMain);\r
459   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
460     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
461   }\r
462   DrawMenuBar(hwndMain);\r
463 }\r
464 \r
465 /*---------------------------------------------------------------------------*\\r
466  *\r
467  * WinMain\r
468  *\r
469 \*---------------------------------------------------------------------------*/\r
470 \r
471 int APIENTRY\r
472 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
473         LPSTR lpCmdLine, int nCmdShow)\r
474 {\r
475   MSG msg;\r
476   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
477 \r
478   debugFP = stderr;\r
479 \r
480   LoadLibrary("RICHED32.DLL");\r
481   consoleCF.cbSize = sizeof(CHARFORMAT);\r
482 \r
483   if (!InitApplication(hInstance)) {\r
484     return (FALSE);\r
485   }\r
486   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
487     return (FALSE);\r
488   }\r
489 \r
490   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
491   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
492   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
493 \r
494   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
495 \r
496   while (GetMessage(&msg, /* message structure */\r
497                     NULL, /* handle of window receiving the message */\r
498                     0,    /* lowest message to examine */\r
499                     0))   /* highest message to examine */\r
500     {\r
501       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
502           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
503           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
504           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
505           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
506           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
507           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
508           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
509           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
510           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
511         TranslateMessage(&msg); /* Translates virtual key codes */\r
512         DispatchMessage(&msg);  /* Dispatches message to window */\r
513       }\r
514     }\r
515 \r
516 \r
517   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
518 }\r
519 \r
520 /*---------------------------------------------------------------------------*\\r
521  *\r
522  * Initialization functions\r
523  *\r
524 \*---------------------------------------------------------------------------*/\r
525 \r
526 BOOL\r
527 InitApplication(HINSTANCE hInstance)\r
528 {\r
529   WNDCLASS wc;\r
530 \r
531   /* Fill in window class structure with parameters that describe the */\r
532   /* main window. */\r
533 \r
534   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
535   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
536   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
537   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
538   wc.hInstance     = hInstance;         /* Owner of this class */\r
539   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
540   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
541   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
542   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
543   wc.lpszClassName = szAppName;                 /* Name to register as */\r
544 \r
545   /* Register the window class and return success/failure code. */\r
546   if (!RegisterClass(&wc)) return FALSE;\r
547 \r
548   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
549   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
550   wc.cbClsExtra    = 0;\r
551   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
552   wc.hInstance     = hInstance;\r
553   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
554   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
555   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
556   wc.lpszMenuName  = NULL;\r
557   wc.lpszClassName = szConsoleName;\r
558 \r
559   if (!RegisterClass(&wc)) return FALSE;\r
560   return TRUE;\r
561 }\r
562 \r
563 \r
564 /* Set by InitInstance, used by EnsureOnScreen */\r
565 int screenHeight, screenWidth;\r
566 \r
567 void\r
568 EnsureOnScreen(int *x, int *y)\r
569 {\r
570   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
571   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
572   if (*x > screenWidth - 32) *x = 0;\r
573   if (*y > screenHeight - 32) *y = 0;\r
574   if (*x < 10) *x = 10;\r
575   if (*y < gap) *y = gap;\r
576 }\r
577 \r
578 BOOL\r
579 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
580 {\r
581   HWND hwnd; /* Main window handle. */\r
582   int ibs;\r
583   WINDOWPLACEMENT wp;\r
584   char *filepart;\r
585 \r
586   hInst = hInstance;    /* Store instance handle in our global variable */\r
587 \r
588   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
589     *filepart = NULLCHAR;\r
590   } else {\r
591     GetCurrentDirectory(MSG_SIZ, installDir);\r
592   }\r
593   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
594   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
595   if (appData.debugMode) {\r
596     debugFP = fopen(appData.nameOfDebugFile, "w");\r
597     setbuf(debugFP, NULL);\r
598   }\r
599 \r
600   InitBackEnd1();\r
601 \r
602 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
603 //  InitEngineUCI( installDir, &second );\r
604 \r
605   /* Create a main window for this application instance. */\r
606   hwnd = CreateWindow(szAppName, szTitle,\r
607                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
608                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
609                       NULL, NULL, hInstance, NULL);\r
610   hwndMain = hwnd;\r
611 \r
612   /* If window could not be created, return "failure" */\r
613   if (!hwnd) {\r
614     return (FALSE);\r
615   }\r
616 \r
617   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
618   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
619       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
620 \r
621       if (first.programLogo == NULL && appData.debugMode) {\r
622           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
623       }\r
624   } else if(appData.autoLogo) {\r
625       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
626         char buf[MSG_SIZ];\r
627         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
628         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
629       }\r
630   }\r
631 \r
632   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
633       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
634 \r
635       if (second.programLogo == NULL && appData.debugMode) {\r
636           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
637       }\r
638   } else if(appData.autoLogo) {\r
639       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
640         char buf[MSG_SIZ];\r
641         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
642         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
643       }\r
644   }\r
645 \r
646   iconWhite = LoadIcon(hInstance, "icon_white");\r
647   iconBlack = LoadIcon(hInstance, "icon_black");\r
648   iconCurrent = iconWhite;\r
649   InitDrawingColors();\r
650   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
651   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
652   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
653     /* Compute window size for each board size, and use the largest\r
654        size that fits on this screen as the default. */\r
655     InitDrawingSizes((BoardSize)ibs, 0);\r
656     if (boardSize == (BoardSize)-1 &&\r
657         winHeight <= screenHeight\r
658            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
659         && winWidth <= screenWidth) {\r
660       boardSize = (BoardSize)ibs;\r
661     }\r
662   }\r
663 \r
664   InitDrawingSizes(boardSize, 0);\r
665   InitMenuChecks();\r
666   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
667 \r
668   /* [AS] Load textures if specified */\r
669   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
670   \r
671   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
672       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
673       liteBackTextureMode = appData.liteBackTextureMode;\r
674 \r
675       if (liteBackTexture == NULL && appData.debugMode) {\r
676           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
677       }\r
678   }\r
679   \r
680   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
681       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
682       darkBackTextureMode = appData.darkBackTextureMode;\r
683 \r
684       if (darkBackTexture == NULL && appData.debugMode) {\r
685           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
686       }\r
687   }\r
688 \r
689   mysrandom( (unsigned) time(NULL) );\r
690 \r
691   /* [AS] Restore layout */\r
692   if( wpMoveHistory.visible ) {\r
693       MoveHistoryPopUp();\r
694   }\r
695 \r
696   if( wpEvalGraph.visible ) {\r
697       EvalGraphPopUp();\r
698   }\r
699 \r
700   if( wpEngineOutput.visible ) {\r
701       EngineOutputPopUp();\r
702   }\r
703 \r
704   InitBackEnd2();\r
705 \r
706   /* Make the window visible; update its client area; and return "success" */\r
707   EnsureOnScreen(&boardX, &boardY);\r
708   wp.length = sizeof(WINDOWPLACEMENT);\r
709   wp.flags = 0;\r
710   wp.showCmd = nCmdShow;\r
711   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
712   wp.rcNormalPosition.left = boardX;\r
713   wp.rcNormalPosition.right = boardX + winWidth;\r
714   wp.rcNormalPosition.top = boardY;\r
715   wp.rcNormalPosition.bottom = boardY + winHeight;\r
716   SetWindowPlacement(hwndMain, &wp);\r
717 \r
718   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
719                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
720 \r
721 #if 0\r
722   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
723   if( gameInfo.variant != VariantFischeRandom ) {\r
724       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
725   }\r
726 #endif\r
727   if (hwndConsole) {\r
728 #if AOT_CONSOLE\r
729     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
730                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
731 #endif\r
732     ShowWindow(hwndConsole, nCmdShow);\r
733   }\r
734   UpdateWindow(hwnd);\r
735 \r
736   return TRUE;\r
737 \r
738 }\r
739 \r
740 \r
741 typedef enum {\r
742   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
743   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
744   ArgSettingsFilename\r
745 } ArgType;\r
746 \r
747 typedef struct {\r
748   char *argName;\r
749   ArgType argType;\r
750   /***\r
751   union {\r
752     String *pString;       // ArgString\r
753     int *pInt;             // ArgInt\r
754     float *pFloat;         // ArgFloat\r
755     Boolean *pBoolean;     // ArgBoolean\r
756     COLORREF *pColor;      // ArgColor\r
757     ColorClass cc;         // ArgAttribs\r
758     String *pFilename;     // ArgFilename\r
759     BoardSize *pBoardSize; // ArgBoardSize\r
760     int whichFont;         // ArgFont\r
761     DCB *pDCB;             // ArgCommSettings\r
762     String *pFilename;     // ArgSettingsFilename\r
763   } argLoc;\r
764   ***/\r
765   LPVOID argLoc;\r
766   BOOL save;\r
767 } ArgDescriptor;\r
768 \r
769 int junk;\r
770 ArgDescriptor argDescriptors[] = {\r
771   /* positional arguments */\r
772   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
773   { "", ArgNone, NULL },\r
774   /* keyword arguments */\r
775   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
776   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
777   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
778   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
779   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
780   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
781   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
782   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
783   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
784   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
785   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
786   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
787   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
788   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
789   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
790   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
791   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
792   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
793     FALSE },\r
794   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
795     FALSE },\r
796   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
797     FALSE },\r
798   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
799   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
800     FALSE },\r
801   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
802   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
803   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
804   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
805   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
806   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
807   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
808   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
809   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
810   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
811   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
812   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
813   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
814   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
815   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
816   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
817   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
818   /*!!bitmapDirectory?*/\r
819   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
820   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
821   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
822   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
823   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
824   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
825   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
826   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
827   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
828   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
829   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
830   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
831   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
832   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
833   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
834   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
835   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
836   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
837   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
838   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
839   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
840   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
841   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
842   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
843   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
844   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
845   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
846   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
847   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
848   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
849   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
850   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
851   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
852   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
853   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
854   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
855   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
856   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
857   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
858   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
859   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
860   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
861   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
862   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
863   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
864   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
865   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
866   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
867   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
868   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
869   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
870   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
871   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
872   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
873   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
874   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
875   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
876   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
877   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
878   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
879   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
880   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
881   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
882   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
883   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
884   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
885   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
886   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
887   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
888   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
889   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
890   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
891   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
892   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
893   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
894   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
895   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
896   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
897   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
898   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
899   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
900   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
901   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
902   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
903   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
904   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
905   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
906   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
907   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
908   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
909   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
910   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
911   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
912   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
913     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
914   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
915   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
916   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
917   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
918   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
919   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
920   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
921   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
922     TRUE }, /* must come after all fonts */\r
923   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
924   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
925     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
926   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
927   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
928   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
929   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
930   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
931   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
932   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
933   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
934   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
935   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
936   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
937   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
938   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
939   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
940   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
941   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
942   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
943   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
944   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
945   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
946   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
947   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
948   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
949   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
950   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
951   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
952   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
953   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
954 #if 0\r
955   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
956   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
957 #endif\r
958   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
959   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
960   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
961   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
962   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
963   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
964   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
965   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
966   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
967   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
968   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
969   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
970   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
971   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
972   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
973   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
974   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
975   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
976   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
977   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
978   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
979   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
980   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
981   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
982   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
983   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
984   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
985   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
986   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
987   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
988   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
989   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
990   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
991   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
992   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
993   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
994   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
995   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
996   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
997   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
998   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
999   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1000   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1001   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1002   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1003   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1004   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1005   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1006   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1007   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1008   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1009   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1010   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1011   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1012   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1013   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1014   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1015   { "highlightLastMove", ArgBoolean,\r
1016     (LPVOID) &appData.highlightLastMove, TRUE },\r
1017   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1018   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1019   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1020   { "highlightDragging", ArgBoolean,\r
1021     (LPVOID) &appData.highlightDragging, TRUE },\r
1022   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1023   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1024   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1025   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1026   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1027   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1028   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1029   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1030   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1031   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1032   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1033   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1034   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1035   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1036   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1037   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1038   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1039   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1040   { "soundShout", ArgFilename,\r
1041     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1042   { "soundSShout", ArgFilename,\r
1043     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1044   { "soundChannel1", ArgFilename,\r
1045     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1046   { "soundChannel", ArgFilename,\r
1047     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1048   { "soundKibitz", ArgFilename,\r
1049     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1050   { "soundTell", ArgFilename,\r
1051     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1052   { "soundChallenge", ArgFilename,\r
1053     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1054   { "soundRequest", ArgFilename,\r
1055     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1056   { "soundSeek", ArgFilename,\r
1057     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1058   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1059   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1060   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1061   { "soundIcsLoss", ArgFilename, \r
1062     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1063   { "soundIcsDraw", ArgFilename, \r
1064     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1065   { "soundIcsUnfinished", ArgFilename, \r
1066     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1067   { "soundIcsAlarm", ArgFilename, \r
1068     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1069   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1070   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1071   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1072   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1073   { "reuseChessPrograms", ArgBoolean,\r
1074     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1075   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1076   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1077   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1078   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1079   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1080   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1081   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1082   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1083   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1084   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1085   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1086   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1087   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1088   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1089   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1090   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1091   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1092   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1093   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1094   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1095   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1096   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1097   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1098   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1099   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1100   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1101   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1102   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1103   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1104   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1105   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1106   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1107   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1108   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1109   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1110   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1111   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1112     TRUE },\r
1113   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1114     TRUE },\r
1115   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1116   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1117   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1118   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1119   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1120   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1121   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1122   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1123   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1124   /* [AS] New features */\r
1125   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1126   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1127   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1128   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1129   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1130   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1131   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1132   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1133   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1134   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1135   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1136   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1137   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1138   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1139   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1140   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1141   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1142   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1143   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1144   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1145   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1146   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1147   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1148   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1149   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1150   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1151   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1152   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1153   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1154   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1155   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1156   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1157   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1158   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1159   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1160   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1161   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1162   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1163   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1164   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1165   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1166   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1167   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1168   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1169   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1170   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1171   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1172   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1173 \r
1174   /* [AS] Layout stuff */\r
1175   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1176   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1177   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1178   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1179   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1180 \r
1181   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1182   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1183   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1184   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1185   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1186 \r
1187   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1188   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1189   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1190   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1191   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1192 \r
1193   /* [HGM] board-size, adjudication and misc. options */\r
1194   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1195   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1196   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1197   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1198   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1199   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1200   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1201   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1202   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1203   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1204   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1205   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1206   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1207   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1208   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1209   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1210   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1211   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1212   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1213   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1214   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1215   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1216   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1217   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1218   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1219   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1220   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1221 \r
1222 #ifdef ZIPPY\r
1223   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1224   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1225   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1226   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1227   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1228   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1229   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1230   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1231   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1232   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1233   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1234   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1235   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1236     FALSE },\r
1237   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1238   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1239   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1240   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1241   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1242   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1243   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1244     FALSE },\r
1245   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1246   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1247   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1248   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1249   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1250   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1251   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1252   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1253   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1254   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1255   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1256   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1257   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1258   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1259   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1260   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1261   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1262   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1263 #endif\r
1264   /* [HGM] options for broadcasting and time odds */\r
1265   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1266   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1267   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1268   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1269   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1270   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1271   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1272   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1273   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1274   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1275   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1276   { NULL, ArgNone, NULL, FALSE }\r
1277 };\r
1278 \r
1279 \r
1280 /* Kludge for indirection files on command line */\r
1281 char* lastIndirectionFilename;\r
1282 ArgDescriptor argDescriptorIndirection =\r
1283 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1284 \r
1285 \r
1286 VOID\r
1287 ExitArgError(char *msg, char *badArg)\r
1288 {\r
1289   char buf[MSG_SIZ];\r
1290 \r
1291   sprintf(buf, "%s %s", msg, badArg);\r
1292   DisplayFatalError(buf, 0, 2);\r
1293   exit(2);\r
1294 }\r
1295 \r
1296 /* Command line font name parser.  NULL name means do nothing.\r
1297    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1298    For backward compatibility, syntax without the colon is also\r
1299    accepted, but font names with digits in them won't work in that case.\r
1300 */\r
1301 VOID\r
1302 ParseFontName(char *name, MyFontParams *mfp)\r
1303 {\r
1304   char *p, *q;\r
1305   if (name == NULL) return;\r
1306   p = name;\r
1307   q = strchr(p, ':');\r
1308   if (q) {\r
1309     if (q - p >= sizeof(mfp->faceName))\r
1310       ExitArgError("Font name too long:", name);\r
1311     memcpy(mfp->faceName, p, q - p);\r
1312     mfp->faceName[q - p] = NULLCHAR;\r
1313     p = q + 1;\r
1314   } else {\r
1315     q = mfp->faceName;\r
1316     while (*p && !isdigit(*p)) {\r
1317       *q++ = *p++;\r
1318       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1319         ExitArgError("Font name too long:", name);\r
1320     }\r
1321     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1322     *q = NULLCHAR;\r
1323   }\r
1324   if (!*p) ExitArgError("Font point size missing:", name);\r
1325   mfp->pointSize = (float) atof(p);\r
1326   mfp->bold = (strchr(p, 'b') != NULL);\r
1327   mfp->italic = (strchr(p, 'i') != NULL);\r
1328   mfp->underline = (strchr(p, 'u') != NULL);\r
1329   mfp->strikeout = (strchr(p, 's') != NULL);\r
1330 }\r
1331 \r
1332 /* Color name parser.\r
1333    X version accepts X color names, but this one\r
1334    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1335 COLORREF\r
1336 ParseColorName(char *name)\r
1337 {\r
1338   int red, green, blue, count;\r
1339   char buf[MSG_SIZ];\r
1340 \r
1341   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1342   if (count != 3) {\r
1343     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1344       &red, &green, &blue);\r
1345   }\r
1346   if (count != 3) {\r
1347     sprintf(buf, "Can't parse color name %s", name);\r
1348     DisplayError(buf, 0);\r
1349     return RGB(0, 0, 0);\r
1350   }\r
1351   return PALETTERGB(red, green, blue);\r
1352 }\r
1353 \r
1354 \r
1355 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1356 {\r
1357   char *e = argValue;\r
1358   int eff = 0;\r
1359 \r
1360   while (*e) {\r
1361     if (*e == 'b')      eff |= CFE_BOLD;\r
1362     else if (*e == 'i') eff |= CFE_ITALIC;\r
1363     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1364     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1365     else if (*e == '#' || isdigit(*e)) break;\r
1366     e++;\r
1367   }\r
1368   *effects = eff;\r
1369   *color   = ParseColorName(e);\r
1370 }\r
1371 \r
1372 \r
1373 BoardSize\r
1374 ParseBoardSize(char *name)\r
1375 {\r
1376   BoardSize bs = SizeTiny;\r
1377   while (sizeInfo[bs].name != NULL) {\r
1378     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1379     bs++;\r
1380   }\r
1381   ExitArgError("Unrecognized board size value", name);\r
1382   return bs; /* not reached */\r
1383 }\r
1384 \r
1385 \r
1386 char\r
1387 StringGet(void *getClosure)\r
1388 {\r
1389   char **p = (char **) getClosure;\r
1390   return *((*p)++);\r
1391 }\r
1392 \r
1393 char\r
1394 FileGet(void *getClosure)\r
1395 {\r
1396   int c;\r
1397   FILE* f = (FILE*) getClosure;\r
1398 \r
1399   c = getc(f);\r
1400   if (c == EOF)\r
1401     return NULLCHAR;\r
1402   else\r
1403     return (char) c;\r
1404 }\r
1405 \r
1406 /* Parse settings file named "name". If file found, return the\r
1407    full name in fullname and return TRUE; else return FALSE */\r
1408 BOOLEAN\r
1409 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1410 {\r
1411   char *dummy;\r
1412   FILE *f;\r
1413 \r
1414   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1415     f = fopen(fullname, "r");\r
1416     if (f != NULL) {\r
1417       ParseArgs(FileGet, f);\r
1418       fclose(f);\r
1419       return TRUE;\r
1420     }\r
1421   }\r
1422   return FALSE;\r
1423 }\r
1424 \r
1425 VOID\r
1426 ParseArgs(GetFunc get, void *cl)\r
1427 {\r
1428   char argName[ARG_MAX];\r
1429   char argValue[ARG_MAX];\r
1430   ArgDescriptor *ad;\r
1431   char start;\r
1432   char *q;\r
1433   int i, octval;\r
1434   char ch;\r
1435   int posarg = 0;\r
1436 \r
1437   ch = get(cl);\r
1438   for (;;) {\r
1439     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1440     if (ch == NULLCHAR) break;\r
1441     if (ch == ';') {\r
1442       /* Comment to end of line */\r
1443       ch = get(cl);\r
1444       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1445       continue;\r
1446     } else if (ch == '/' || ch == '-') {\r
1447       /* Switch */\r
1448       q = argName;\r
1449       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1450              ch != '\n' && ch != '\t') {\r
1451         *q++ = ch;\r
1452         ch = get(cl);\r
1453       }\r
1454       *q = NULLCHAR;\r
1455 \r
1456       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1457         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1458 \r
1459       if (ad->argName == NULL)\r
1460         ExitArgError("Unrecognized argument", argName);\r
1461 \r
1462     } else if (ch == '@') {\r
1463       /* Indirection file */\r
1464       ad = &argDescriptorIndirection;\r
1465       ch = get(cl);\r
1466     } else {\r
1467       /* Positional argument */\r
1468       ad = &argDescriptors[posarg++];\r
1469       strcpy(argName, ad->argName);\r
1470     }\r
1471 \r
1472     if (ad->argType == ArgTrue) {\r
1473       *(Boolean *) ad->argLoc = TRUE;\r
1474       continue;\r
1475     }\r
1476     if (ad->argType == ArgFalse) {\r
1477       *(Boolean *) ad->argLoc = FALSE;\r
1478       continue;\r
1479     }\r
1480 \r
1481     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1482     if (ch == NULLCHAR || ch == '\n') {\r
1483       ExitArgError("No value provided for argument", argName);\r
1484     }\r
1485     q = argValue;\r
1486     if (ch == '{') {\r
1487       // Quoting with { }.  No characters have to (or can) be escaped.\r
1488       // Thus the string cannot contain a '}' character.\r
1489       start = ch;\r
1490       ch = get(cl);\r
1491       while (start) {\r
1492         switch (ch) {\r
1493         case NULLCHAR:\r
1494           start = NULLCHAR;\r
1495           break;\r
1496           \r
1497         case '}':\r
1498           ch = get(cl);\r
1499           start = NULLCHAR;\r
1500           break;\r
1501 \r
1502         default:\r
1503           *q++ = ch;\r
1504           ch = get(cl);\r
1505           break;\r
1506         }\r
1507       }   \r
1508     } else if (ch == '\'' || ch == '"') {\r
1509       // Quoting with ' ' or " ", with \ as escape character.\r
1510       // Inconvenient for long strings that may contain Windows filenames.\r
1511       start = ch;\r
1512       ch = get(cl);\r
1513       while (start) {\r
1514         switch (ch) {\r
1515         case NULLCHAR:\r
1516           start = NULLCHAR;\r
1517           break;\r
1518 \r
1519         default:\r
1520         not_special:\r
1521           *q++ = ch;\r
1522           ch = get(cl);\r
1523           break;\r
1524 \r
1525         case '\'':\r
1526         case '\"':\r
1527           if (ch == start) {\r
1528             ch = get(cl);\r
1529             start = NULLCHAR;\r
1530             break;\r
1531           } else {\r
1532             goto not_special;\r
1533           }\r
1534 \r
1535         case '\\':\r
1536           if (ad->argType == ArgFilename\r
1537               || ad->argType == ArgSettingsFilename) {\r
1538               goto not_special;\r
1539           }\r
1540           ch = get(cl);\r
1541           switch (ch) {\r
1542           case NULLCHAR:\r
1543             ExitArgError("Incomplete \\ escape in value for", argName);\r
1544             break;\r
1545           case 'n':\r
1546             *q++ = '\n';\r
1547             ch = get(cl);\r
1548             break;\r
1549           case 'r':\r
1550             *q++ = '\r';\r
1551             ch = get(cl);\r
1552             break;\r
1553           case 't':\r
1554             *q++ = '\t';\r
1555             ch = get(cl);\r
1556             break;\r
1557           case 'b':\r
1558             *q++ = '\b';\r
1559             ch = get(cl);\r
1560             break;\r
1561           case 'f':\r
1562             *q++ = '\f';\r
1563             ch = get(cl);\r
1564             break;\r
1565           default:\r
1566             octval = 0;\r
1567             for (i = 0; i < 3; i++) {\r
1568               if (ch >= '0' && ch <= '7') {\r
1569                 octval = octval*8 + (ch - '0');\r
1570                 ch = get(cl);\r
1571               } else {\r
1572                 break;\r
1573               }\r
1574             }\r
1575             if (i > 0) {\r
1576               *q++ = (char) octval;\r
1577             } else {\r
1578               *q++ = ch;\r
1579               ch = get(cl);\r
1580             }\r
1581             break;\r
1582           }\r
1583           break;\r
1584         }\r
1585       }\r
1586     } else {\r
1587       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1588         *q++ = ch;\r
1589         ch = get(cl);\r
1590       }\r
1591     }\r
1592     *q = NULLCHAR;\r
1593 \r
1594     switch (ad->argType) {\r
1595     case ArgInt:\r
1596       *(int *) ad->argLoc = atoi(argValue);\r
1597       break;\r
1598 \r
1599     case ArgFloat:\r
1600       *(float *) ad->argLoc = (float) atof(argValue);\r
1601       break;\r
1602 \r
1603     case ArgString:\r
1604     case ArgFilename:\r
1605       *(char **) ad->argLoc = strdup(argValue);\r
1606       break;\r
1607 \r
1608     case ArgSettingsFilename:\r
1609       {\r
1610         char fullname[MSG_SIZ];\r
1611         if (ParseSettingsFile(argValue, fullname)) {\r
1612           if (ad->argLoc != NULL) {\r
1613             *(char **) ad->argLoc = strdup(fullname);\r
1614           }\r
1615         } else {\r
1616           if (ad->argLoc != NULL) {\r
1617           } else {\r
1618             ExitArgError("Failed to open indirection file", argValue);\r
1619           }\r
1620         }\r
1621       }\r
1622       break;\r
1623 \r
1624     case ArgBoolean:\r
1625       switch (argValue[0]) {\r
1626       case 't':\r
1627       case 'T':\r
1628         *(Boolean *) ad->argLoc = TRUE;\r
1629         break;\r
1630       case 'f':\r
1631       case 'F':\r
1632         *(Boolean *) ad->argLoc = FALSE;\r
1633         break;\r
1634       default:\r
1635         ExitArgError("Unrecognized boolean argument value", argValue);\r
1636         break;\r
1637       }\r
1638       break;\r
1639 \r
1640     case ArgColor:\r
1641       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1642       break;\r
1643 \r
1644     case ArgAttribs: {\r
1645       ColorClass cc = (ColorClass)ad->argLoc;\r
1646       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1647       }\r
1648       break;\r
1649       \r
1650     case ArgBoardSize:\r
1651       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1652       break;\r
1653 \r
1654     case ArgFont:\r
1655       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1656       break;\r
1657 \r
1658     case ArgCommSettings:\r
1659       ParseCommSettings(argValue, &dcb);\r
1660       break;\r
1661 \r
1662     case ArgNone:\r
1663       ExitArgError("Unrecognized argument", argValue);\r
1664       break;\r
1665     }\r
1666   }\r
1667 }\r
1668 \r
1669 VOID\r
1670 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1671 {\r
1672   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1673   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1674   DeleteDC(hdc);\r
1675   lf->lfWidth = 0;\r
1676   lf->lfEscapement = 0;\r
1677   lf->lfOrientation = 0;\r
1678   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1679   lf->lfItalic = mfp->italic;\r
1680   lf->lfUnderline = mfp->underline;\r
1681   lf->lfStrikeOut = mfp->strikeout;\r
1682   lf->lfCharSet = DEFAULT_CHARSET;\r
1683   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1684   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1685   lf->lfQuality = DEFAULT_QUALITY;\r
1686   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1687   strcpy(lf->lfFaceName, mfp->faceName);\r
1688 }\r
1689 \r
1690 VOID\r
1691 CreateFontInMF(MyFont *mf)\r
1692 {\r
1693   LFfromMFP(&mf->lf, &mf->mfp);\r
1694   if (mf->hf) DeleteObject(mf->hf);\r
1695   mf->hf = CreateFontIndirect(&mf->lf);\r
1696 }\r
1697 \r
1698 VOID\r
1699 SetDefaultTextAttribs()\r
1700 {\r
1701   ColorClass cc;\r
1702   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1703     ParseAttribs(&textAttribs[cc].color, \r
1704                  &textAttribs[cc].effects, \r
1705                  defaultTextAttribs[cc]);\r
1706   }\r
1707 }\r
1708 \r
1709 VOID\r
1710 SetDefaultSounds()\r
1711 {\r
1712   ColorClass cc;\r
1713   SoundClass sc;\r
1714   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1715     textAttribs[cc].sound.name = strdup("");\r
1716     textAttribs[cc].sound.data = NULL;\r
1717   }\r
1718   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1719     sounds[sc].name = strdup("");\r
1720     sounds[sc].data = NULL;\r
1721   }\r
1722   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1723 }\r
1724 \r
1725 VOID\r
1726 LoadAllSounds()\r
1727 {\r
1728   ColorClass cc;\r
1729   SoundClass sc;\r
1730   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1731     MyLoadSound(&textAttribs[cc].sound);\r
1732   }\r
1733   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1734     MyLoadSound(&sounds[sc]);\r
1735   }\r
1736 }\r
1737 \r
1738 VOID\r
1739 InitAppData(LPSTR lpCmdLine)\r
1740 {\r
1741   int i, j;\r
1742   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1743   char *dummy, *p;\r
1744 \r
1745   programName = szAppName;\r
1746 \r
1747   /* Initialize to defaults */\r
1748   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1749   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1750   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1751   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1752   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1753   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1754   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1755   SetDefaultTextAttribs();\r
1756   SetDefaultSounds();\r
1757   appData.movesPerSession = MOVES_PER_SESSION;\r
1758   appData.initString = INIT_STRING;\r
1759   appData.secondInitString = INIT_STRING;\r
1760   appData.firstComputerString = COMPUTER_STRING;\r
1761   appData.secondComputerString = COMPUTER_STRING;\r
1762   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1763   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1764   appData.firstPlaysBlack = FALSE;\r
1765   appData.noChessProgram = FALSE;\r
1766   chessProgram = FALSE;\r
1767   appData.firstHost = FIRST_HOST;\r
1768   appData.secondHost = SECOND_HOST;\r
1769   appData.firstDirectory = FIRST_DIRECTORY;\r
1770   appData.secondDirectory = SECOND_DIRECTORY;\r
1771   appData.bitmapDirectory = "";\r
1772   appData.remoteShell = REMOTE_SHELL;\r
1773   appData.remoteUser = "";\r
1774   appData.timeDelay = TIME_DELAY;\r
1775   appData.timeControl = TIME_CONTROL;\r
1776   appData.timeIncrement = TIME_INCREMENT;\r
1777   appData.icsActive = FALSE;\r
1778   appData.icsHost = "";\r
1779   appData.icsPort = ICS_PORT;\r
1780   appData.icsCommPort = ICS_COMM_PORT;\r
1781   appData.icsLogon = ICS_LOGON;\r
1782   appData.icsHelper = "";\r
1783   appData.useTelnet = FALSE;\r
1784   appData.telnetProgram = TELNET_PROGRAM;\r
1785   appData.gateway = "";\r
1786   appData.loadGameFile = "";\r
1787   appData.loadGameIndex = 0;\r
1788   appData.saveGameFile = "";\r
1789   appData.autoSaveGames = FALSE;\r
1790   appData.loadPositionFile = "";\r
1791   appData.loadPositionIndex = 1;\r
1792   appData.savePositionFile = "";\r
1793   appData.matchMode = FALSE;\r
1794   appData.matchGames = 0;\r
1795   appData.monoMode = FALSE;\r
1796   appData.debugMode = FALSE;\r
1797   appData.clockMode = TRUE;\r
1798   boardSize = (BoardSize) -1; /* determine by screen size */\r
1799   appData.Iconic = FALSE; /*unused*/\r
1800   appData.searchTime = "";\r
1801   appData.searchDepth = 0;\r
1802   appData.showCoords = FALSE;\r
1803   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1804   appData.autoCallFlag = FALSE;\r
1805   appData.flipView = FALSE;\r
1806   appData.autoFlipView = TRUE;\r
1807   appData.cmailGameName = "";\r
1808   appData.alwaysPromoteToQueen = FALSE;\r
1809   appData.oldSaveStyle = FALSE;\r
1810   appData.quietPlay = FALSE;\r
1811   appData.showThinking = FALSE;\r
1812   appData.ponderNextMove = TRUE;\r
1813   appData.periodicUpdates = TRUE;\r
1814   appData.popupExitMessage = TRUE;\r
1815   appData.popupMoveErrors = FALSE;\r
1816   appData.autoObserve = FALSE;\r
1817   appData.autoComment = FALSE;\r
1818   appData.animate = TRUE;\r
1819   appData.animSpeed = 10;\r
1820   appData.animateDragging = TRUE;\r
1821   appData.highlightLastMove = TRUE;\r
1822   appData.getMoveList = TRUE;\r
1823   appData.testLegality = TRUE;\r
1824   appData.premove = TRUE;\r
1825   appData.premoveWhite = FALSE;\r
1826   appData.premoveWhiteText = "";\r
1827   appData.premoveBlack = FALSE;\r
1828   appData.premoveBlackText = "";\r
1829   appData.icsAlarm = TRUE;\r
1830   appData.icsAlarmTime = 5000;\r
1831   appData.autoRaiseBoard = TRUE;\r
1832   appData.localLineEditing = TRUE;\r
1833   appData.colorize = TRUE;\r
1834   appData.reuseFirst = TRUE;\r
1835   appData.reuseSecond = TRUE;\r
1836   appData.blindfold = FALSE;\r
1837   appData.icsEngineAnalyze = FALSE;\r
1838   dcb.DCBlength = sizeof(DCB);\r
1839   dcb.BaudRate = 9600;\r
1840   dcb.fBinary = TRUE;\r
1841   dcb.fParity = FALSE;\r
1842   dcb.fOutxCtsFlow = FALSE;\r
1843   dcb.fOutxDsrFlow = FALSE;\r
1844   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1845   dcb.fDsrSensitivity = FALSE;\r
1846   dcb.fTXContinueOnXoff = TRUE;\r
1847   dcb.fOutX = FALSE;\r
1848   dcb.fInX = FALSE;\r
1849   dcb.fNull = FALSE;\r
1850   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1851   dcb.fAbortOnError = FALSE;\r
1852   /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */\r
1853   #if (defined(_MSC_VER) && _MSC_VER <= 1200) \r
1854         //dcb.wReserved = 0;\r
1855   #else\r
1856     dcb.wReserved = 0;\r
1857   #endif\r
1858   dcb.ByteSize = 7;\r
1859   dcb.Parity = SPACEPARITY;\r
1860   dcb.StopBits = ONESTOPBIT;\r
1861   settingsFileName = SETTINGS_FILE;\r
1862   saveSettingsOnExit = TRUE;\r
1863   boardX = CW_USEDEFAULT;\r
1864   boardY = CW_USEDEFAULT;\r
1865   consoleX = CW_USEDEFAULT; \r
1866   consoleY = CW_USEDEFAULT; \r
1867   consoleW = CW_USEDEFAULT;\r
1868   consoleH = CW_USEDEFAULT;\r
1869   analysisX = CW_USEDEFAULT; \r
1870   analysisY = CW_USEDEFAULT; \r
1871   analysisW = CW_USEDEFAULT;\r
1872   analysisH = CW_USEDEFAULT;\r
1873   commentX = CW_USEDEFAULT; \r
1874   commentY = CW_USEDEFAULT; \r
1875   commentW = CW_USEDEFAULT;\r
1876   commentH = CW_USEDEFAULT;\r
1877   editTagsX = CW_USEDEFAULT; \r
1878   editTagsY = CW_USEDEFAULT; \r
1879   editTagsW = CW_USEDEFAULT;\r
1880   editTagsH = CW_USEDEFAULT;\r
1881   gameListX = CW_USEDEFAULT; \r
1882   gameListY = CW_USEDEFAULT; \r
1883   gameListW = CW_USEDEFAULT;\r
1884   gameListH = CW_USEDEFAULT;\r
1885   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1886   icsNames = ICS_NAMES;\r
1887   firstChessProgramNames = FCP_NAMES;\r
1888   secondChessProgramNames = SCP_NAMES;\r
1889   appData.initialMode = "";\r
1890   appData.variant = "normal";\r
1891   appData.firstProtocolVersion = PROTOVER;\r
1892   appData.secondProtocolVersion = PROTOVER;\r
1893   appData.showButtonBar = TRUE;\r
1894 \r
1895    /* [AS] New properties (see comments in header file) */\r
1896   appData.firstScoreIsAbsolute = FALSE;\r
1897   appData.secondScoreIsAbsolute = FALSE;\r
1898   appData.saveExtendedInfoInPGN = FALSE;\r
1899   appData.hideThinkingFromHuman = FALSE;\r
1900   appData.liteBackTextureFile = "";\r
1901   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1902   appData.darkBackTextureFile = "";\r
1903   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1904   appData.renderPiecesWithFont = "";\r
1905   appData.fontToPieceTable = "";\r
1906   appData.fontBackColorWhite = 0;\r
1907   appData.fontForeColorWhite = 0;\r
1908   appData.fontBackColorBlack = 0;\r
1909   appData.fontForeColorBlack = 0;\r
1910   appData.fontPieceSize = 80;\r
1911   appData.overrideLineGap = 1;\r
1912   appData.adjudicateLossThreshold = 0;\r
1913   appData.delayBeforeQuit = 0;\r
1914   appData.delayAfterQuit = 0;\r
1915   appData.nameOfDebugFile = "winboard.debug";\r
1916   appData.pgnEventHeader = "Computer Chess Game";\r
1917   appData.defaultFrcPosition = -1;\r
1918   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1919   appData.saveOutOfBookInfo = TRUE;\r
1920   appData.showEvalInMoveHistory = TRUE;\r
1921   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1922   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1923   appData.highlightMoveWithArrow = FALSE;\r
1924   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1925   appData.useStickyWindows = TRUE;\r
1926   appData.adjudicateDrawMoves = 0;\r
1927   appData.autoDisplayComment = TRUE;\r
1928   appData.autoDisplayTags = TRUE;\r
1929   appData.firstIsUCI = FALSE;\r
1930   appData.secondIsUCI = FALSE;\r
1931   appData.firstHasOwnBookUCI = TRUE;\r
1932   appData.secondHasOwnBookUCI = TRUE;\r
1933   appData.polyglotDir = "";\r
1934   appData.usePolyglotBook = FALSE;\r
1935   appData.polyglotBook = "";\r
1936   appData.defaultHashSize = 64;\r
1937   appData.defaultCacheSizeEGTB = 4;\r
1938   appData.defaultPathEGTB = "c:\\egtb";\r
1939 \r
1940   InitWindowPlacement( &wpMoveHistory );\r
1941   InitWindowPlacement( &wpEvalGraph );\r
1942   InitWindowPlacement( &wpEngineOutput );\r
1943 \r
1944   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1945   appData.NrFiles      = -1;\r
1946   appData.NrRanks      = -1;\r
1947   appData.holdingsSize = -1;\r
1948   appData.testClaims   = FALSE;\r
1949   appData.checkMates   = FALSE;\r
1950   appData.materialDraws= FALSE;\r
1951   appData.trivialDraws = FALSE;\r
1952   appData.ruleMoves    = 51;\r
1953   appData.drawRepeats  = 6;\r
1954   appData.matchPause   = 10000;\r
1955   appData.alphaRank    = FALSE;\r
1956   appData.allWhite     = FALSE;\r
1957   appData.upsideDown   = FALSE;\r
1958   appData.serverPause  = 15;\r
1959   appData.serverMovesName   = NULL;\r
1960   appData.suppressLoadMoves = FALSE;\r
1961   appData.firstTimeOdds  = 1;\r
1962   appData.secondTimeOdds = 1;\r
1963   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1964   appData.secondAccumulateTC = 1;\r
1965   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1966   appData.secondNPS = -1;\r
1967   appData.engineComments = 1;\r
1968   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1969   appData.egtFormats = "";\r
1970 \r
1971 #ifdef ZIPPY\r
1972   appData.zippyTalk = ZIPPY_TALK;\r
1973   appData.zippyPlay = ZIPPY_PLAY;\r
1974   appData.zippyLines = ZIPPY_LINES;\r
1975   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1976   appData.zippyPassword = ZIPPY_PASSWORD;\r
1977   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1978   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1979   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1980   appData.zippyUseI = ZIPPY_USE_I;\r
1981   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1982   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1983   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1984   appData.zippyGameStart = ZIPPY_GAME_START;\r
1985   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1986   appData.zippyAbort = ZIPPY_ABORT;\r
1987   appData.zippyVariants = ZIPPY_VARIANTS;\r
1988   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1989   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1990 #endif\r
1991 \r
1992   /* Point font array elements to structures and\r
1993      parse default font names */\r
1994   for (i=0; i<NUM_FONTS; i++) {\r
1995     for (j=0; j<NUM_SIZES; j++) {\r
1996       font[j][i] = &fontRec[j][i];\r
1997       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1998     }\r
1999   }\r
2000   \r
2001   /* Parse default settings file if any */\r
2002   if (ParseSettingsFile(settingsFileName, buf)) {\r
2003     settingsFileName = strdup(buf);\r
2004   }\r
2005 \r
2006   /* Parse command line */\r
2007   ParseArgs(StringGet, &lpCmdLine);\r
2008 \r
2009   /* [HGM] make sure board size is acceptable */\r
2010   if(appData.NrFiles > BOARD_SIZE ||\r
2011      appData.NrRanks > BOARD_SIZE   )\r
2012       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2013 \r
2014   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2015    * with options from the command line, we now make an even higher priority\r
2016    * overrule by WB options attached to the engine command line. This so that\r
2017    * tournament managers can use WB options (such as /timeOdds) that follow\r
2018    * the engines.\r
2019    */\r
2020   if(appData.firstChessProgram != NULL) {\r
2021       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2022       static char *f = "first";\r
2023       char buf[MSG_SIZ], *q = buf;\r
2024       if(p != NULL) { // engine command line contains WinBoard options\r
2025           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2026           ParseArgs(StringGet, &q);\r
2027           p[-1] = 0; // cut them offengine command line\r
2028       }\r
2029   }\r
2030   // now do same for second chess program\r
2031   if(appData.secondChessProgram != NULL) {\r
2032       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2033       static char *s = "second";\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, s, s, s, s, s, s, s, s, s, s); // 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 \r
2042 \r
2043   /* Propagate options that affect others */\r
2044   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2045   if (appData.icsActive || appData.noChessProgram) {\r
2046      chessProgram = FALSE;  /* not local chess program mode */\r
2047   }\r
2048 \r
2049   /* Open startup dialog if needed */\r
2050   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2051       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2052       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2053                         *appData.secondChessProgram == NULLCHAR))) {\r
2054     FARPROC lpProc;\r
2055     \r
2056     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2057     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2058     FreeProcInstance(lpProc);\r
2059   }\r
2060 \r
2061   /* Make sure save files land in the right (?) directory */\r
2062   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2063     appData.saveGameFile = strdup(buf);\r
2064   }\r
2065   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2066     appData.savePositionFile = strdup(buf);\r
2067   }\r
2068 \r
2069   /* Finish initialization for fonts and sounds */\r
2070   for (i=0; i<NUM_FONTS; i++) {\r
2071     for (j=0; j<NUM_SIZES; j++) {\r
2072       CreateFontInMF(font[j][i]);\r
2073     }\r
2074   }\r
2075   /* xboard, and older WinBoards, controlled the move sound with the\r
2076      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2077      always turn the option on (so that the backend will call us),\r
2078      then let the user turn the sound off by setting it to silence if\r
2079      desired.  To accommodate old winboard.ini files saved by old\r
2080      versions of WinBoard, we also turn off the sound if the option\r
2081      was initially set to false. */\r
2082   if (!appData.ringBellAfterMoves) {\r
2083     sounds[(int)SoundMove].name = strdup("");\r
2084     appData.ringBellAfterMoves = TRUE;\r
2085   }\r
2086   GetCurrentDirectory(MSG_SIZ, currDir);\r
2087   SetCurrentDirectory(installDir);\r
2088   LoadAllSounds();\r
2089   SetCurrentDirectory(currDir);\r
2090 \r
2091   p = icsTextMenuString;\r
2092   if (p[0] == '@') {\r
2093     FILE* f = fopen(p + 1, "r");\r
2094     if (f == NULL) {\r
2095       DisplayFatalError(p + 1, errno, 2);\r
2096       return;\r
2097     }\r
2098     i = fread(buf, 1, sizeof(buf)-1, f);\r
2099     fclose(f);\r
2100     buf[i] = NULLCHAR;\r
2101     p = buf;\r
2102   }\r
2103   ParseIcsTextMenu(strdup(p));\r
2104 }\r
2105 \r
2106 \r
2107 VOID\r
2108 InitMenuChecks()\r
2109 {\r
2110   HMENU hmenu = GetMenu(hwndMain);\r
2111 \r
2112   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2113                         MF_BYCOMMAND|((appData.icsActive &&\r
2114                                        *appData.icsCommPort != NULLCHAR) ?\r
2115                                       MF_ENABLED : MF_GRAYED));\r
2116   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2117                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2118                                      MF_CHECKED : MF_UNCHECKED));\r
2119 }\r
2120 \r
2121 \r
2122 VOID\r
2123 SaveSettings(char* name)\r
2124 {\r
2125   FILE *f;\r
2126   ArgDescriptor *ad;\r
2127   WINDOWPLACEMENT wp;\r
2128   char dir[MSG_SIZ];\r
2129 \r
2130   if (!hwndMain) return;\r
2131 \r
2132   GetCurrentDirectory(MSG_SIZ, dir);\r
2133   SetCurrentDirectory(installDir);\r
2134   f = fopen(name, "w");\r
2135   SetCurrentDirectory(dir);\r
2136   if (f == NULL) {\r
2137     DisplayError(name, errno);\r
2138     return;\r
2139   }\r
2140   fprintf(f, ";\n");\r
2141   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2142   fprintf(f, ";\n");\r
2143   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2144   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2145   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2146   fprintf(f, ";\n");\r
2147 \r
2148   wp.length = sizeof(WINDOWPLACEMENT);\r
2149   GetWindowPlacement(hwndMain, &wp);\r
2150   boardX = wp.rcNormalPosition.left;\r
2151   boardY = wp.rcNormalPosition.top;\r
2152 \r
2153   if (hwndConsole) {\r
2154     GetWindowPlacement(hwndConsole, &wp);\r
2155     consoleX = wp.rcNormalPosition.left;\r
2156     consoleY = wp.rcNormalPosition.top;\r
2157     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2158     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2159   }\r
2160 \r
2161   if (analysisDialog) {\r
2162     GetWindowPlacement(analysisDialog, &wp);\r
2163     analysisX = wp.rcNormalPosition.left;\r
2164     analysisY = wp.rcNormalPosition.top;\r
2165     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2166     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2167   }\r
2168 \r
2169   if (commentDialog) {\r
2170     GetWindowPlacement(commentDialog, &wp);\r
2171     commentX = wp.rcNormalPosition.left;\r
2172     commentY = wp.rcNormalPosition.top;\r
2173     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2174     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2175   }\r
2176 \r
2177   if (editTagsDialog) {\r
2178     GetWindowPlacement(editTagsDialog, &wp);\r
2179     editTagsX = wp.rcNormalPosition.left;\r
2180     editTagsY = wp.rcNormalPosition.top;\r
2181     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2182     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2183   }\r
2184 \r
2185   if (gameListDialog) {\r
2186     GetWindowPlacement(gameListDialog, &wp);\r
2187     gameListX = wp.rcNormalPosition.left;\r
2188     gameListY = wp.rcNormalPosition.top;\r
2189     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2190     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2191   }\r
2192 \r
2193   /* [AS] Move history */\r
2194   wpMoveHistory.visible = MoveHistoryIsUp();\r
2195   \r
2196   if( moveHistoryDialog ) {\r
2197     GetWindowPlacement(moveHistoryDialog, &wp);\r
2198     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2199     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2200     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2201     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2202   }\r
2203 \r
2204   /* [AS] Eval graph */\r
2205   wpEvalGraph.visible = EvalGraphIsUp();\r
2206 \r
2207   if( evalGraphDialog ) {\r
2208     GetWindowPlacement(evalGraphDialog, &wp);\r
2209     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2210     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2211     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2212     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2213   }\r
2214 \r
2215   /* [AS] Engine output */\r
2216   wpEngineOutput.visible = EngineOutputIsUp();\r
2217 \r
2218   if( engineOutputDialog ) {\r
2219     GetWindowPlacement(engineOutputDialog, &wp);\r
2220     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2221     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2222     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2223     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2224   }\r
2225 \r
2226   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2227     if (!ad->save) continue;\r
2228     switch (ad->argType) {\r
2229     case ArgString:\r
2230       {\r
2231         char *p = *(char **)ad->argLoc;\r
2232         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2233           /* Quote multiline values or \-containing values\r
2234              with { } if possible */\r
2235           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2236         } else {\r
2237           /* Else quote with " " */\r
2238           fprintf(f, "/%s=\"", ad->argName);\r
2239           while (*p) {\r
2240             if (*p == '\n') fprintf(f, "\n");\r
2241             else if (*p == '\r') fprintf(f, "\\r");\r
2242             else if (*p == '\t') fprintf(f, "\\t");\r
2243             else if (*p == '\b') fprintf(f, "\\b");\r
2244             else if (*p == '\f') fprintf(f, "\\f");\r
2245             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2246             else if (*p == '\"') fprintf(f, "\\\"");\r
2247             else if (*p == '\\') fprintf(f, "\\\\");\r
2248             else putc(*p, f);\r
2249             p++;\r
2250           }\r
2251           fprintf(f, "\"\n");\r
2252         }\r
2253       }\r
2254       break;\r
2255     case ArgInt:\r
2256       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2257       break;\r
2258     case ArgFloat:\r
2259       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2260       break;\r
2261     case ArgBoolean:\r
2262       fprintf(f, "/%s=%s\n", ad->argName, \r
2263         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2264       break;\r
2265     case ArgTrue:\r
2266       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2267       break;\r
2268     case ArgFalse:\r
2269       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2270       break;\r
2271     case ArgColor:\r
2272       {\r
2273         COLORREF color = *(COLORREF *)ad->argLoc;\r
2274         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2275           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2276       }\r
2277       break;\r
2278     case ArgAttribs:\r
2279       {\r
2280         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2281         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2282           (ta->effects & CFE_BOLD) ? "b" : "",\r
2283           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2284           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2285           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2286           (ta->effects) ? " " : "",\r
2287           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2288       }\r
2289       break;\r
2290     case ArgFilename:\r
2291       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2292         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2293       } else {\r
2294         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2295       }\r
2296       break;\r
2297     case ArgBoardSize:\r
2298       fprintf(f, "/%s=%s\n", ad->argName,\r
2299               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2300       break;\r
2301     case ArgFont:\r
2302       {\r
2303         int bs;\r
2304         for (bs=0; bs<NUM_SIZES; bs++) {\r
2305           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2306           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2307           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2308             ad->argName, mfp->faceName, mfp->pointSize,\r
2309             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2310             mfp->bold ? "b" : "",\r
2311             mfp->italic ? "i" : "",\r
2312             mfp->underline ? "u" : "",\r
2313             mfp->strikeout ? "s" : "");\r
2314         }\r
2315       }\r
2316       break;\r
2317     case ArgCommSettings:\r
2318       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2319     }\r
2320   }\r
2321   fclose(f);\r
2322 }\r
2323 \r
2324 \r
2325 \r
2326 /*---------------------------------------------------------------------------*\\r
2327  *\r
2328  * GDI board drawing routines\r
2329  *\r
2330 \*---------------------------------------------------------------------------*/\r
2331 \r
2332 /* [AS] Draw square using background texture */\r
2333 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2334 {\r
2335     XFORM   x;\r
2336 \r
2337     if( mode == 0 ) {\r
2338         return; /* Should never happen! */\r
2339     }\r
2340 \r
2341     SetGraphicsMode( dst, GM_ADVANCED );\r
2342 \r
2343     switch( mode ) {\r
2344     case 1:\r
2345         /* Identity */\r
2346         break;\r
2347     case 2:\r
2348         /* X reflection */\r
2349         x.eM11 = -1.0;\r
2350         x.eM12 = 0;\r
2351         x.eM21 = 0;\r
2352         x.eM22 = 1.0;\r
2353         x.eDx = (FLOAT) dw + dx - 1;\r
2354         x.eDy = 0;\r
2355         dx = 0;\r
2356         SetWorldTransform( dst, &x );\r
2357         break;\r
2358     case 3:\r
2359         /* Y 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 = 0;\r
2365         x.eDy = (FLOAT) dh + dy - 1;\r
2366         dy = 0;\r
2367         SetWorldTransform( dst, &x );\r
2368         break;\r
2369     case 4:\r
2370         /* X/Y flip */\r
2371         x.eM11 = 0;\r
2372         x.eM12 = 1.0;\r
2373         x.eM21 = 1.0;\r
2374         x.eM22 = 0;\r
2375         x.eDx = (FLOAT) dx;\r
2376         x.eDy = (FLOAT) dy;\r
2377         dx = 0;\r
2378         dy = 0;\r
2379         SetWorldTransform( dst, &x );\r
2380         break;\r
2381     }\r
2382 \r
2383     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2384 \r
2385     x.eM11 = 1.0;\r
2386     x.eM12 = 0;\r
2387     x.eM21 = 0;\r
2388     x.eM22 = 1.0;\r
2389     x.eDx = 0;\r
2390     x.eDy = 0;\r
2391     SetWorldTransform( dst, &x );\r
2392 \r
2393     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2394 }\r
2395 \r
2396 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2397 enum {\r
2398     PM_WP = (int) WhitePawn, \r
2399     PM_WN = (int) WhiteKnight, \r
2400     PM_WB = (int) WhiteBishop, \r
2401     PM_WR = (int) WhiteRook, \r
2402     PM_WQ = (int) WhiteQueen, \r
2403     PM_WF = (int) WhiteFerz, \r
2404     PM_WW = (int) WhiteWazir, \r
2405     PM_WE = (int) WhiteAlfil, \r
2406     PM_WM = (int) WhiteMan, \r
2407     PM_WO = (int) WhiteCannon, \r
2408     PM_WU = (int) WhiteUnicorn, \r
2409     PM_WH = (int) WhiteNightrider, \r
2410     PM_WA = (int) WhiteAngel, \r
2411     PM_WC = (int) WhiteMarshall, \r
2412     PM_WAB = (int) WhiteCardinal, \r
2413     PM_WD = (int) WhiteDragon, \r
2414     PM_WL = (int) WhiteLance, \r
2415     PM_WS = (int) WhiteCobra, \r
2416     PM_WV = (int) WhiteFalcon, \r
2417     PM_WSG = (int) WhiteSilver, \r
2418     PM_WG = (int) WhiteGrasshopper, \r
2419     PM_WK = (int) WhiteKing,\r
2420     PM_BP = (int) BlackPawn, \r
2421     PM_BN = (int) BlackKnight, \r
2422     PM_BB = (int) BlackBishop, \r
2423     PM_BR = (int) BlackRook, \r
2424     PM_BQ = (int) BlackQueen, \r
2425     PM_BF = (int) BlackFerz, \r
2426     PM_BW = (int) BlackWazir, \r
2427     PM_BE = (int) BlackAlfil, \r
2428     PM_BM = (int) BlackMan,\r
2429     PM_BO = (int) BlackCannon, \r
2430     PM_BU = (int) BlackUnicorn, \r
2431     PM_BH = (int) BlackNightrider, \r
2432     PM_BA = (int) BlackAngel, \r
2433     PM_BC = (int) BlackMarshall, \r
2434     PM_BG = (int) BlackGrasshopper, \r
2435     PM_BAB = (int) BlackCardinal,\r
2436     PM_BD = (int) BlackDragon,\r
2437     PM_BL = (int) BlackLance,\r
2438     PM_BS = (int) BlackCobra,\r
2439     PM_BV = (int) BlackFalcon,\r
2440     PM_BSG = (int) BlackSilver,\r
2441     PM_BK = (int) BlackKing\r
2442 };\r
2443 \r
2444 static HFONT hPieceFont = NULL;\r
2445 static HBITMAP hPieceMask[(int) EmptySquare];\r
2446 static HBITMAP hPieceFace[(int) EmptySquare];\r
2447 static int fontBitmapSquareSize = 0;\r
2448 static char pieceToFontChar[(int) EmptySquare] =\r
2449                               { 'p', 'n', 'b', 'r', 'q', \r
2450                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2451                       'k', 'o', 'm', 'v', 't', 'w', \r
2452                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2453                                                               'l' };\r
2454 \r
2455 extern BOOL SetCharTable( char *table, const char * map );\r
2456 /* [HGM] moved to backend.c */\r
2457 \r
2458 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2459 {\r
2460     HBRUSH hbrush;\r
2461     BYTE r1 = GetRValue( color );\r
2462     BYTE g1 = GetGValue( color );\r
2463     BYTE b1 = GetBValue( color );\r
2464     BYTE r2 = r1 / 2;\r
2465     BYTE g2 = g1 / 2;\r
2466     BYTE b2 = b1 / 2;\r
2467     RECT rc;\r
2468 \r
2469     /* Create a uniform background first */\r
2470     hbrush = CreateSolidBrush( color );\r
2471     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2472     FillRect( hdc, &rc, hbrush );\r
2473     DeleteObject( hbrush );\r
2474     \r
2475     if( mode == 1 ) {\r
2476         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2477         int steps = squareSize / 2;\r
2478         int i;\r
2479 \r
2480         for( i=0; i<steps; i++ ) {\r
2481             BYTE r = r1 - (r1-r2) * i / steps;\r
2482             BYTE g = g1 - (g1-g2) * i / steps;\r
2483             BYTE b = b1 - (b1-b2) * i / steps;\r
2484 \r
2485             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2486             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2487             FillRect( hdc, &rc, hbrush );\r
2488             DeleteObject(hbrush);\r
2489         }\r
2490     }\r
2491     else if( mode == 2 ) {\r
2492         /* Diagonal gradient, good more or less for every piece */\r
2493         POINT triangle[3];\r
2494         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2495         HBRUSH hbrush_old;\r
2496         int steps = squareSize;\r
2497         int i;\r
2498 \r
2499         triangle[0].x = squareSize - steps;\r
2500         triangle[0].y = squareSize;\r
2501         triangle[1].x = squareSize;\r
2502         triangle[1].y = squareSize;\r
2503         triangle[2].x = squareSize;\r
2504         triangle[2].y = squareSize - steps;\r
2505 \r
2506         for( i=0; i<steps; i++ ) {\r
2507             BYTE r = r1 - (r1-r2) * i / steps;\r
2508             BYTE g = g1 - (g1-g2) * i / steps;\r
2509             BYTE b = b1 - (b1-b2) * i / steps;\r
2510 \r
2511             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2512             hbrush_old = SelectObject( hdc, hbrush );\r
2513             Polygon( hdc, triangle, 3 );\r
2514             SelectObject( hdc, hbrush_old );\r
2515             DeleteObject(hbrush);\r
2516             triangle[0].x++;\r
2517             triangle[2].y++;\r
2518         }\r
2519 \r
2520         SelectObject( hdc, hpen );\r
2521     }\r
2522 }\r
2523 \r
2524 /*\r
2525     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2526     seems to work ok. The main problem here is to find the "inside" of a chess\r
2527     piece: follow the steps as explained below.\r
2528 */\r
2529 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2530 {\r
2531     HBITMAP hbm;\r
2532     HBITMAP hbm_old;\r
2533     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2534     RECT rc;\r
2535     SIZE sz;\r
2536     POINT pt;\r
2537     int backColor = whitePieceColor; \r
2538     int foreColor = blackPieceColor;\r
2539     \r
2540     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2541         backColor = appData.fontBackColorWhite;\r
2542         foreColor = appData.fontForeColorWhite;\r
2543     }\r
2544     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2545         backColor = appData.fontBackColorBlack;\r
2546         foreColor = appData.fontForeColorBlack;\r
2547     }\r
2548 \r
2549     /* Mask */\r
2550     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2551 \r
2552     hbm_old = SelectObject( hdc, hbm );\r
2553 \r
2554     rc.left = 0;\r
2555     rc.top = 0;\r
2556     rc.right = squareSize;\r
2557     rc.bottom = squareSize;\r
2558 \r
2559     /* Step 1: background is now black */\r
2560     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2561 \r
2562     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2563 \r
2564     pt.x = (squareSize - sz.cx) / 2;\r
2565     pt.y = (squareSize - sz.cy) / 2;\r
2566 \r
2567     SetBkMode( hdc, TRANSPARENT );\r
2568     SetTextColor( hdc, chroma );\r
2569     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2570     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2571 \r
2572     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2573     /* Step 3: the area outside the piece is filled with white */\r
2574 //    FloodFill( hdc, 0, 0, chroma );\r
2575     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2576     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2577     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2578     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2579     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2580     /* \r
2581         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2582         but if the start point is not inside the piece we're lost!\r
2583         There should be a better way to do this... if we could create a region or path\r
2584         from the fill operation we would be fine for example.\r
2585     */\r
2586 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2587     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2588 \r
2589     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2590         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2591         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2592 \r
2593         SelectObject( dc2, bm2 );\r
2594         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2595         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2596         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2597         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2598         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2599 \r
2600         DeleteDC( dc2 );\r
2601         DeleteObject( bm2 );\r
2602     }\r
2603 \r
2604     SetTextColor( hdc, 0 );\r
2605     /* \r
2606         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2607         draw the piece again in black for safety.\r
2608     */\r
2609     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2610 \r
2611     SelectObject( hdc, hbm_old );\r
2612 \r
2613     if( hPieceMask[index] != NULL ) {\r
2614         DeleteObject( hPieceMask[index] );\r
2615     }\r
2616 \r
2617     hPieceMask[index] = hbm;\r
2618 \r
2619     /* Face */\r
2620     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2621 \r
2622     SelectObject( hdc, hbm );\r
2623 \r
2624     {\r
2625         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2626         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2627         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2628 \r
2629         SelectObject( dc1, hPieceMask[index] );\r
2630         SelectObject( dc2, bm2 );\r
2631         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2632         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2633         \r
2634         /* \r
2635             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2636             the piece background and deletes (makes transparent) the rest.\r
2637             Thanks to that mask, we are free to paint the background with the greates\r
2638             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2639             We use this, to make gradients and give the pieces a "roundish" look.\r
2640         */\r
2641         SetPieceBackground( hdc, backColor, 2 );\r
2642         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2643 \r
2644         DeleteDC( dc2 );\r
2645         DeleteDC( dc1 );\r
2646         DeleteObject( bm2 );\r
2647     }\r
2648 \r
2649     SetTextColor( hdc, foreColor );\r
2650     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2651 \r
2652     SelectObject( hdc, hbm_old );\r
2653 \r
2654     if( hPieceFace[index] != NULL ) {\r
2655         DeleteObject( hPieceFace[index] );\r
2656     }\r
2657 \r
2658     hPieceFace[index] = hbm;\r
2659 }\r
2660 \r
2661 static int TranslatePieceToFontPiece( int piece )\r
2662 {\r
2663     switch( piece ) {\r
2664     case BlackPawn:\r
2665         return PM_BP;\r
2666     case BlackKnight:\r
2667         return PM_BN;\r
2668     case BlackBishop:\r
2669         return PM_BB;\r
2670     case BlackRook:\r
2671         return PM_BR;\r
2672     case BlackQueen:\r
2673         return PM_BQ;\r
2674     case BlackKing:\r
2675         return PM_BK;\r
2676     case WhitePawn:\r
2677         return PM_WP;\r
2678     case WhiteKnight:\r
2679         return PM_WN;\r
2680     case WhiteBishop:\r
2681         return PM_WB;\r
2682     case WhiteRook:\r
2683         return PM_WR;\r
2684     case WhiteQueen:\r
2685         return PM_WQ;\r
2686     case WhiteKing:\r
2687         return PM_WK;\r
2688 \r
2689     case BlackAngel:\r
2690         return PM_BA;\r
2691     case BlackMarshall:\r
2692         return PM_BC;\r
2693     case BlackFerz:\r
2694         return PM_BF;\r
2695     case BlackNightrider:\r
2696         return PM_BH;\r
2697     case BlackAlfil:\r
2698         return PM_BE;\r
2699     case BlackWazir:\r
2700         return PM_BW;\r
2701     case BlackUnicorn:\r
2702         return PM_BU;\r
2703     case BlackCannon:\r
2704         return PM_BO;\r
2705     case BlackGrasshopper:\r
2706         return PM_BG;\r
2707     case BlackMan:\r
2708         return PM_BM;\r
2709     case BlackSilver:\r
2710         return PM_BSG;\r
2711     case BlackLance:\r
2712         return PM_BL;\r
2713     case BlackFalcon:\r
2714         return PM_BV;\r
2715     case BlackCobra:\r
2716         return PM_BS;\r
2717     case BlackCardinal:\r
2718         return PM_BAB;\r
2719     case BlackDragon:\r
2720         return PM_BD;\r
2721 \r
2722     case WhiteAngel:\r
2723         return PM_WA;\r
2724     case WhiteMarshall:\r
2725         return PM_WC;\r
2726     case WhiteFerz:\r
2727         return PM_WF;\r
2728     case WhiteNightrider:\r
2729         return PM_WH;\r
2730     case WhiteAlfil:\r
2731         return PM_WE;\r
2732     case WhiteWazir:\r
2733         return PM_WW;\r
2734     case WhiteUnicorn:\r
2735         return PM_WU;\r
2736     case WhiteCannon:\r
2737         return PM_WO;\r
2738     case WhiteGrasshopper:\r
2739         return PM_WG;\r
2740     case WhiteMan:\r
2741         return PM_WM;\r
2742     case WhiteSilver:\r
2743         return PM_WSG;\r
2744     case WhiteLance:\r
2745         return PM_WL;\r
2746     case WhiteFalcon:\r
2747         return PM_WV;\r
2748     case WhiteCobra:\r
2749         return PM_WS;\r
2750     case WhiteCardinal:\r
2751         return PM_WAB;\r
2752     case WhiteDragon:\r
2753         return PM_WD;\r
2754     }\r
2755 \r
2756     return 0;\r
2757 }\r
2758 \r
2759 void CreatePiecesFromFont()\r
2760 {\r
2761     LOGFONT lf;\r
2762     HDC hdc_window = NULL;\r
2763     HDC hdc = NULL;\r
2764     HFONT hfont_old;\r
2765     int fontHeight;\r
2766     int i;\r
2767 \r
2768     if( fontBitmapSquareSize < 0 ) {\r
2769         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2770         return;\r
2771     }\r
2772 \r
2773     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2774         fontBitmapSquareSize = -1;\r
2775         return;\r
2776     }\r
2777 \r
2778     if( fontBitmapSquareSize != squareSize ) {\r
2779         hdc_window = GetDC( hwndMain );\r
2780         hdc = CreateCompatibleDC( hdc_window );\r
2781 \r
2782         if( hPieceFont != NULL ) {\r
2783             DeleteObject( hPieceFont );\r
2784         }\r
2785         else {\r
2786             for( i=0; i<=(int)BlackKing; i++ ) {\r
2787                 hPieceMask[i] = NULL;\r
2788                 hPieceFace[i] = NULL;\r
2789             }\r
2790         }\r
2791 \r
2792         fontHeight = 75;\r
2793 \r
2794         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2795             fontHeight = appData.fontPieceSize;\r
2796         }\r
2797 \r
2798         fontHeight = (fontHeight * squareSize) / 100;\r
2799 \r
2800         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2801         lf.lfWidth = 0;\r
2802         lf.lfEscapement = 0;\r
2803         lf.lfOrientation = 0;\r
2804         lf.lfWeight = FW_NORMAL;\r
2805         lf.lfItalic = 0;\r
2806         lf.lfUnderline = 0;\r
2807         lf.lfStrikeOut = 0;\r
2808         lf.lfCharSet = DEFAULT_CHARSET;\r
2809         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2810         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2811         lf.lfQuality = PROOF_QUALITY;\r
2812         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2813         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2814         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2815 \r
2816         hPieceFont = CreateFontIndirect( &lf );\r
2817 \r
2818         if( hPieceFont == NULL ) {\r
2819             fontBitmapSquareSize = -2;\r
2820         }\r
2821         else {\r
2822             /* Setup font-to-piece character table */\r
2823             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2824                 /* No (or wrong) global settings, try to detect the font */\r
2825                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2826                     /* Alpha */\r
2827                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2828                 }\r
2829                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2830                     /* DiagramTT* family */\r
2831                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2832                 }\r
2833                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2834                     /* Fairy symbols */\r
2835                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2836                 }\r
2837                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2838                     /* Good Companion (Some characters get warped as literal :-( */\r
2839                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2840                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2841                     SetCharTable(pieceToFontChar, s);\r
2842                 }\r
2843                 else {\r
2844                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2845                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2846                 }\r
2847             }\r
2848 \r
2849             /* Create bitmaps */\r
2850             hfont_old = SelectObject( hdc, hPieceFont );\r
2851 #if 0\r
2852             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2853             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2854             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2855             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2856             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2857             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2858             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2859             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2860             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2861             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2862             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2863             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2864 \r
2865             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2866             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2867             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2868             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2869             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2870             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2871             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2872             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2873             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2874             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2875             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2876             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2877             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2878             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2879             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2880             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2881             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2882             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2883             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2884             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2885             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2886             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2887             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2888             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2889             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2890             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2891             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2892             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2893             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2894             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2895             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2896             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2897 #else\r
2898             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2899                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2900                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2901 #endif\r
2902             SelectObject( hdc, hfont_old );\r
2903 \r
2904             fontBitmapSquareSize = squareSize;\r
2905         }\r
2906     }\r
2907 \r
2908     if( hdc != NULL ) {\r
2909         DeleteDC( hdc );\r
2910     }\r
2911 \r
2912     if( hdc_window != NULL ) {\r
2913         ReleaseDC( hwndMain, hdc_window );\r
2914     }\r
2915 }\r
2916 \r
2917 HBITMAP\r
2918 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2919 {\r
2920   char name[128];\r
2921 \r
2922   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2923   if (gameInfo.event &&\r
2924       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2925       strcmp(name, "k80s") == 0) {\r
2926     strcpy(name, "tim");\r
2927   }\r
2928   return LoadBitmap(hinst, name);\r
2929 }\r
2930 \r
2931 \r
2932 /* Insert a color into the program's logical palette\r
2933    structure.  This code assumes the given color is\r
2934    the result of the RGB or PALETTERGB macro, and it\r
2935    knows how those macros work (which is documented).\r
2936 */\r
2937 VOID\r
2938 InsertInPalette(COLORREF color)\r
2939 {\r
2940   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2941 \r
2942   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2943     DisplayFatalError("Too many colors", 0, 1);\r
2944     pLogPal->palNumEntries--;\r
2945     return;\r
2946   }\r
2947 \r
2948   pe->peFlags = (char) 0;\r
2949   pe->peRed = (char) (0xFF & color);\r
2950   pe->peGreen = (char) (0xFF & (color >> 8));\r
2951   pe->peBlue = (char) (0xFF & (color >> 16));\r
2952   return;\r
2953 }\r
2954 \r
2955 \r
2956 VOID\r
2957 InitDrawingColors()\r
2958 {\r
2959   if (pLogPal == NULL) {\r
2960     /* Allocate enough memory for a logical palette with\r
2961      * PALETTESIZE entries and set the size and version fields\r
2962      * of the logical palette structure.\r
2963      */\r
2964     pLogPal = (NPLOGPALETTE)\r
2965       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2966                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2967     pLogPal->palVersion    = 0x300;\r
2968   }\r
2969   pLogPal->palNumEntries = 0;\r
2970 \r
2971   InsertInPalette(lightSquareColor);\r
2972   InsertInPalette(darkSquareColor);\r
2973   InsertInPalette(whitePieceColor);\r
2974   InsertInPalette(blackPieceColor);\r
2975   InsertInPalette(highlightSquareColor);\r
2976   InsertInPalette(premoveHighlightColor);\r
2977 \r
2978   /*  create a logical color palette according the information\r
2979    *  in the LOGPALETTE structure.\r
2980    */\r
2981   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2982 \r
2983   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2984   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2985   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2986   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2987   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2988   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2989 \r
2990   /* [AS] Force rendering of the font-based pieces */\r
2991   if( fontBitmapSquareSize > 0 ) {\r
2992     fontBitmapSquareSize = 0;\r
2993   }\r
2994 }\r
2995 \r
2996 \r
2997 int\r
2998 BoardWidth(int boardSize, int n)\r
2999 { /* [HGM] argument n added to allow different width and height */\r
3000   int lineGap = sizeInfo[boardSize].lineGap;\r
3001 \r
3002   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3003       lineGap = appData.overrideLineGap;\r
3004   }\r
3005 \r
3006   return (n + 1) * lineGap +\r
3007           n * sizeInfo[boardSize].squareSize;\r
3008 }\r
3009 \r
3010 /* Respond to board resize by dragging edge */\r
3011 VOID\r
3012 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3013 {\r
3014   BoardSize newSize = NUM_SIZES - 1;\r
3015   static int recurse = 0;\r
3016   if (IsIconic(hwndMain)) return;\r
3017   if (recurse > 0) return;\r
3018   recurse++;\r
3019   while (newSize > 0) {\r
3020         InitDrawingSizes(newSize, 0);\r
3021         if(newSizeX >= sizeInfo[newSize].cliWidth ||\r
3022            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3023     newSize--;\r
3024   } \r
3025   boardSize = newSize;\r
3026   InitDrawingSizes(boardSize, flags);\r
3027   recurse--;\r
3028 }\r
3029 \r
3030 \r
3031 \r
3032 VOID\r
3033 InitDrawingSizes(BoardSize boardSize, int flags)\r
3034 {\r
3035   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3036   ChessSquare piece;\r
3037   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3038   HDC hdc;\r
3039   SIZE clockSize, messageSize;\r
3040   HFONT oldFont;\r
3041   char buf[MSG_SIZ];\r
3042   char *str;\r
3043   HMENU hmenu = GetMenu(hwndMain);\r
3044   RECT crect, wrect;\r
3045   int offby;\r
3046   LOGBRUSH logbrush;\r
3047 \r
3048   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
3049   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3050 \r
3051   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3052   smallLayout = sizeInfo[boardSize].smallLayout;\r
3053   squareSize = sizeInfo[boardSize].squareSize;\r
3054   lineGap = sizeInfo[boardSize].lineGap;\r
3055   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3056 \r
3057   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3058       lineGap = appData.overrideLineGap;\r
3059   }\r
3060 \r
3061   if (tinyLayout != oldTinyLayout) {\r
3062     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3063     if (tinyLayout) {\r
3064       style &= ~WS_SYSMENU;\r
3065       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3066                  "&Minimize\tCtrl+F4");\r
3067     } else {\r
3068       style |= WS_SYSMENU;\r
3069       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3070     }\r
3071     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3072 \r
3073     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3074       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3075         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3076     }\r
3077     DrawMenuBar(hwndMain);\r
3078   }\r
3079 \r
3080   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3081   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3082 \r
3083   /* Get text area sizes */\r
3084   hdc = GetDC(hwndMain);\r
3085   if (appData.clockMode) {\r
3086     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3087   } else {\r
3088     sprintf(buf, "White");\r
3089   }\r
3090   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3091   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3092   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3093   str = "We only care about the height here";\r
3094   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3095   SelectObject(hdc, oldFont);\r
3096   ReleaseDC(hwndMain, hdc);\r
3097 \r
3098   /* Compute where everything goes */\r
3099   if(first.programLogo || second.programLogo) {\r
3100         /* [HGM] logo: if either logo is on, reserve space for it */\r
3101         logoHeight =  2*clockSize.cy;\r
3102         leftLogoRect.left   = OUTER_MARGIN;\r
3103         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3104         leftLogoRect.top    = OUTER_MARGIN;\r
3105         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3106 \r
3107         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3108         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3109         rightLogoRect.top    = OUTER_MARGIN;\r
3110         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3111 \r
3112 \r
3113     blackRect.left = leftLogoRect.right;\r
3114     blackRect.right = rightLogoRect.left;\r
3115     blackRect.top = OUTER_MARGIN;\r
3116     blackRect.bottom = blackRect.top + clockSize.cy;\r
3117 \r
3118     whiteRect.left = blackRect.left ;\r
3119     whiteRect.right = blackRect.right;\r
3120     whiteRect.top = blackRect.bottom;\r
3121     whiteRect.bottom = leftLogoRect.bottom;\r
3122   } else {\r
3123     whiteRect.left = OUTER_MARGIN;\r
3124     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3125     whiteRect.top = OUTER_MARGIN + logoHeight;\r
3126     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3127 \r
3128     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3129     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3130     blackRect.top = whiteRect.top;\r
3131     blackRect.bottom = whiteRect.bottom;\r
3132   }\r
3133 \r
3134   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3135   if (appData.showButtonBar) {\r
3136     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3137       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3138   } else {\r
3139     messageRect.right = OUTER_MARGIN + boardWidth;\r
3140   }\r
3141   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3142   messageRect.bottom = messageRect.top + messageSize.cy;\r
3143 \r
3144   boardRect.left = OUTER_MARGIN;\r
3145   boardRect.right = boardRect.left + boardWidth;\r
3146   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3147   boardRect.bottom = boardRect.top + boardHeight;\r
3148 \r
3149   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3150   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3151   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3152   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3153     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3154   GetWindowRect(hwndMain, &wrect);\r
3155   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3156                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3157   /* compensate if menu bar wrapped */\r
3158   GetClientRect(hwndMain, &crect);\r
3159   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3160   winHeight += offby;\r
3161   switch (flags) {\r
3162   case WMSZ_TOPLEFT:\r
3163     SetWindowPos(hwndMain, NULL, \r
3164                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3165                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3166     break;\r
3167 \r
3168   case WMSZ_TOPRIGHT:\r
3169   case WMSZ_TOP:\r
3170     SetWindowPos(hwndMain, NULL, \r
3171                  wrect.left, wrect.bottom - winHeight, \r
3172                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3173     break;\r
3174 \r
3175   case WMSZ_BOTTOMLEFT:\r
3176   case WMSZ_LEFT:\r
3177     SetWindowPos(hwndMain, NULL, \r
3178                  wrect.right - winWidth, wrect.top, \r
3179                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3180     break;\r
3181 \r
3182   case WMSZ_BOTTOMRIGHT:\r
3183   case WMSZ_BOTTOM:\r
3184   case WMSZ_RIGHT:\r
3185   default:\r
3186     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3187                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3188     break;\r
3189   }\r
3190 \r
3191   hwndPause = NULL;\r
3192   for (i = 0; i < N_BUTTONS; i++) {\r
3193     if (buttonDesc[i].hwnd != NULL) {\r
3194       DestroyWindow(buttonDesc[i].hwnd);\r
3195       buttonDesc[i].hwnd = NULL;\r
3196     }\r
3197     if (appData.showButtonBar) {\r
3198       buttonDesc[i].hwnd =\r
3199         CreateWindow("BUTTON", buttonDesc[i].label,\r
3200                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3201                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3202                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3203                      (HMENU) buttonDesc[i].id,\r
3204                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3205       if (tinyLayout) {\r
3206         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3207                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3208                     MAKELPARAM(FALSE, 0));\r
3209       }\r
3210       if (buttonDesc[i].id == IDM_Pause)\r
3211         hwndPause = buttonDesc[i].hwnd;\r
3212       buttonDesc[i].wndproc = (WNDPROC)\r
3213         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3214     }\r
3215   }\r
3216   if (gridPen != NULL) DeleteObject(gridPen);\r
3217   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3218   if (premovePen != NULL) DeleteObject(premovePen);\r
3219   if (lineGap != 0) {\r
3220     logbrush.lbStyle = BS_SOLID;\r
3221     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3222     gridPen =\r
3223       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3224                    lineGap, &logbrush, 0, NULL);\r
3225     logbrush.lbColor = highlightSquareColor;\r
3226     highlightPen =\r
3227       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3228                    lineGap, &logbrush, 0, NULL);\r
3229 \r
3230     logbrush.lbColor = premoveHighlightColor; \r
3231     premovePen =\r
3232       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3233                    lineGap, &logbrush, 0, NULL);\r
3234 \r
3235     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3236     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3237       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3238       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3239         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3240       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3241         BOARD_WIDTH * (squareSize + lineGap);\r
3242         lineGap / 2 + (i * (squareSize + lineGap));\r
3243       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3244     }\r
3245     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3246       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3247       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3248         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3249         lineGap / 2 + (i * (squareSize + lineGap));\r
3250       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3251         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3252       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3253     }\r
3254   }\r
3255 \r
3256   /* [HGM] Licensing requirement */\r
3257 #ifdef GOTHIC\r
3258   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3259 #endif\r
3260 #ifdef FALCON\r
3261   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3262 #endif\r
3263   GothicPopUp( "", VariantNormal);\r
3264 \r
3265 \r
3266 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3267   oldBoardSize = boardSize;\r
3268   oldTinyLayout = tinyLayout;\r
3269 \r
3270   /* Load piece bitmaps for this board size */\r
3271   for (i=0; i<=2; i++) {\r
3272     for (piece = WhitePawn;\r
3273          (int) piece < (int) BlackPawn;\r
3274          piece = (ChessSquare) ((int) piece + 1)) {\r
3275       if (pieceBitmap[i][piece] != NULL)\r
3276         DeleteObject(pieceBitmap[i][piece]);\r
3277     }\r
3278   }\r
3279 \r
3280   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3281   // Orthodox Chess pieces\r
3282   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3283   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3284   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3285   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3286   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3287   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3288   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3289   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3290   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3291   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3292   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3293   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3294   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3295   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3296   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3297   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3298     // in Shogi, Hijack the unused Queen for Lance\r
3299     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3300     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3301     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3302   } else {\r
3303     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3304     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3305     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3306   }\r
3307 \r
3308   if(squareSize <= 72 && squareSize >= 33) { \r
3309     /* A & C are available in most sizes now */\r
3310     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3311       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3312       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3313       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3314       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3315       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3316       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3317       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3318       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3319       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3320       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3321       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3322       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3323     } else { // Smirf-like\r
3324       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3325       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3326       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3327     }\r
3328     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3329       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3330       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3331       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3332     } else { // WinBoard standard\r
3333       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3334       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3335       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3336     }\r
3337   }\r
3338 \r
3339 \r
3340   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3341     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3342     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3343     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3344     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3345     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3346     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3347     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3348     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3349     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3350     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3351     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3352     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3353     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3354     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3355     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3356     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3357     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3358     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3359     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3360     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3361     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3362     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3363     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3364     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3365     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3366     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3367     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3368     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3369     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3370     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3371 \r
3372     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3373       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3374       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3375       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3376       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3377       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3378       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3379       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3380       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3381       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3382       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3383       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3384       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3385     } else {\r
3386       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3387       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3388       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3389       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3390       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3391       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3392       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3393       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3394       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3395       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3396       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3397       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3398     }\r
3399 \r
3400   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3401     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3402     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3403     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3404     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3405     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3406     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3407     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3408     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3409     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3410     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3411     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3412     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3413     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3414     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3415   }\r
3416 \r
3417 \r
3418   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3419   /* special Shogi support in this size */\r
3420   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3421       for (piece = WhitePawn;\r
3422            (int) piece < (int) BlackPawn;\r
3423            piece = (ChessSquare) ((int) piece + 1)) {\r
3424         if (pieceBitmap[i][piece] != NULL)\r
3425           DeleteObject(pieceBitmap[i][piece]);\r
3426       }\r
3427     }\r
3428   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3429   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3430   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3431   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3432   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3433   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3434   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3435   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3436   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3437   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3438   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3439   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3440   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3441   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3442   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3443   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3444   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3445   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3446   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3447   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3448   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3449   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3450   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3451   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3452   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3453   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3454   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3455   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3456   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3457   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3458   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3459   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3460   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3461   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3462   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3463   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3464   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3465   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3466   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3467   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3468   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3469   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3470   minorSize = 0;\r
3471   }\r
3472 }\r
3473 \r
3474 HBITMAP\r
3475 PieceBitmap(ChessSquare p, int kind)\r
3476 {\r
3477   if ((int) p >= (int) BlackPawn)\r
3478     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3479 \r
3480   return pieceBitmap[kind][(int) p];\r
3481 }\r
3482 \r
3483 /***************************************************************/\r
3484 \r
3485 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3486 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3487 /*\r
3488 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3489 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3490 */\r
3491 \r
3492 VOID\r
3493 SquareToPos(int row, int column, int * x, int * y)\r
3494 {\r
3495   if (flipView) {\r
3496     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3497     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3498   } else {\r
3499     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3500     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3501   }\r
3502 }\r
3503 \r
3504 VOID\r
3505 DrawCoordsOnDC(HDC hdc)\r
3506 {\r
3507   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
3508   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
3509   char str[2] = { NULLCHAR, NULLCHAR };\r
3510   int oldMode, oldAlign, x, y, start, i;\r
3511   HFONT oldFont;\r
3512   HBRUSH oldBrush;\r
3513 \r
3514   if (!appData.showCoords)\r
3515     return;\r
3516 \r
3517   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3518 \r
3519   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3520   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3521   oldAlign = GetTextAlign(hdc);\r
3522   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3523 \r
3524   y = boardRect.top + lineGap;\r
3525   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3526 \r
3527   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3528   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3529     str[0] = files[start + i];\r
3530     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3531     y += squareSize + lineGap;\r
3532   }\r
3533 \r
3534   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3535 \r
3536   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3537   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3538     str[0] = ranks[start + i];\r
3539     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3540     x += squareSize + lineGap;\r
3541   }    \r
3542 \r
3543   SelectObject(hdc, oldBrush);\r
3544   SetBkMode(hdc, oldMode);\r
3545   SetTextAlign(hdc, oldAlign);\r
3546   SelectObject(hdc, oldFont);\r
3547 }\r
3548 \r
3549 VOID\r
3550 DrawGridOnDC(HDC hdc)\r
3551 {\r
3552   HPEN oldPen;\r
3553  \r
3554   if (lineGap != 0) {\r
3555     oldPen = SelectObject(hdc, gridPen);\r
3556     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3557     SelectObject(hdc, oldPen);\r
3558   }\r
3559 }\r
3560 \r
3561 #define HIGHLIGHT_PEN 0\r
3562 #define PREMOVE_PEN   1\r
3563 \r
3564 VOID\r
3565 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3566 {\r
3567   int x1, y1;\r
3568   HPEN oldPen, hPen;\r
3569   if (lineGap == 0) return;\r
3570   if (flipView) {\r
3571     x1 = boardRect.left +\r
3572       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3573     y1 = boardRect.top +\r
3574       lineGap/2 + y * (squareSize + lineGap);\r
3575   } else {\r
3576     x1 = boardRect.left +\r
3577       lineGap/2 + x * (squareSize + lineGap);\r
3578     y1 = boardRect.top +\r
3579       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3580   }\r
3581   hPen = pen ? premovePen : highlightPen;\r
3582   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3583   MoveToEx(hdc, x1, y1, NULL);\r
3584   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3585   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3586   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3587   LineTo(hdc, x1, y1);\r
3588   SelectObject(hdc, oldPen);\r
3589 }\r
3590 \r
3591 VOID\r
3592 DrawHighlightsOnDC(HDC hdc)\r
3593 {\r
3594   int i;\r
3595   for (i=0; i<2; i++) {\r
3596     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3597       DrawHighlightOnDC(hdc, TRUE,\r
3598                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3599                         HIGHLIGHT_PEN);\r
3600   }\r
3601   for (i=0; i<2; i++) {\r
3602     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3603         premoveHighlightInfo.sq[i].y >= 0) {\r
3604         DrawHighlightOnDC(hdc, TRUE,\r
3605                           premoveHighlightInfo.sq[i].x, \r
3606                           premoveHighlightInfo.sq[i].y,\r
3607                           PREMOVE_PEN);\r
3608     }\r
3609   }\r
3610 }\r
3611 \r
3612 /* Note: sqcolor is used only in monoMode */\r
3613 /* Note that this code is largely duplicated in woptions.c,\r
3614    function DrawSampleSquare, so that needs to be updated too */\r
3615 VOID\r
3616 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3617 {\r
3618   HBITMAP oldBitmap;\r
3619   HBRUSH oldBrush;\r
3620   int tmpSize;\r
3621 \r
3622   if (appData.blindfold) return;\r
3623 \r
3624   /* [AS] Use font-based pieces if needed */\r
3625   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3626     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3627     CreatePiecesFromFont();\r
3628 \r
3629     if( fontBitmapSquareSize == squareSize ) {\r
3630         int index = TranslatePieceToFontPiece(piece);\r
3631 \r
3632         SelectObject( tmphdc, hPieceMask[ index ] );\r
3633 \r
3634         BitBlt( hdc,\r
3635             x, y,\r
3636             squareSize, squareSize,\r
3637             tmphdc,\r
3638             0, 0,\r
3639             SRCAND );\r
3640 \r
3641         SelectObject( tmphdc, hPieceFace[ index ] );\r
3642 \r
3643         BitBlt( hdc,\r
3644             x, y,\r
3645             squareSize, squareSize,\r
3646             tmphdc,\r
3647             0, 0,\r
3648             SRCPAINT );\r
3649 \r
3650         return;\r
3651     }\r
3652   }\r
3653 \r
3654   if (appData.monoMode) {\r
3655     SelectObject(tmphdc, PieceBitmap(piece, \r
3656       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3657     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3658            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3659   } else {\r
3660     tmpSize = squareSize;\r
3661     if(minorSize &&\r
3662         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3663          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3664       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3665       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3666       x += (squareSize - minorSize)>>1;\r
3667       y += squareSize - minorSize - 2;\r
3668       tmpSize = minorSize;\r
3669     }\r
3670     if (color || appData.allWhite ) {\r
3671       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3672       if( color )\r
3673               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3674       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3675       if(appData.upsideDown && color==flipView)\r
3676         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3677       else\r
3678         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3679 #if 0\r
3680       /* Use black piece color for outline of white pieces */\r
3681       /* Not sure this looks really good (though xboard does it).\r
3682          Maybe better to have another selectable color, default black */\r
3683       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3684       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3685       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3686 #else\r
3687       /* Use black for outline of white pieces */\r
3688       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3689       if(appData.upsideDown && color==flipView)\r
3690         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3691       else\r
3692         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3693 #endif\r
3694     } else {\r
3695 #if 0\r
3696       /* Use white piece color for details of black pieces */\r
3697       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3698          WHITE_PIECE ones aren't always the right shape. */\r
3699       /* Not sure this looks really good (though xboard does it).\r
3700          Maybe better to have another selectable color, default medium gray? */\r
3701       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3702       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3703       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3704       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3705       SelectObject(hdc, blackPieceBrush);\r
3706       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3707 #else\r
3708       /* Use square color for details of black pieces */\r
3709       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3710       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3711       if(appData.upsideDown && !flipView)\r
3712         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3713       else\r
3714         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3715 #endif\r
3716     }\r
3717     SelectObject(hdc, oldBrush);\r
3718     SelectObject(tmphdc, oldBitmap);\r
3719   }\r
3720 }\r
3721 \r
3722 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3723 int GetBackTextureMode( int algo )\r
3724 {\r
3725     int result = BACK_TEXTURE_MODE_DISABLED;\r
3726 \r
3727     switch( algo ) \r
3728     {\r
3729         case BACK_TEXTURE_MODE_PLAIN:\r
3730             result = 1; /* Always use identity map */\r
3731             break;\r
3732         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3733             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3734             break;\r
3735     }\r
3736 \r
3737     return result;\r
3738 }\r
3739 \r
3740 /* \r
3741     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3742     to handle redraws cleanly (as random numbers would always be different).\r
3743 */\r
3744 VOID RebuildTextureSquareInfo()\r
3745 {\r
3746     BITMAP bi;\r
3747     int lite_w = 0;\r
3748     int lite_h = 0;\r
3749     int dark_w = 0;\r
3750     int dark_h = 0;\r
3751     int row;\r
3752     int col;\r
3753 \r
3754     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3755 \r
3756     if( liteBackTexture != NULL ) {\r
3757         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3758             lite_w = bi.bmWidth;\r
3759             lite_h = bi.bmHeight;\r
3760         }\r
3761     }\r
3762 \r
3763     if( darkBackTexture != NULL ) {\r
3764         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3765             dark_w = bi.bmWidth;\r
3766             dark_h = bi.bmHeight;\r
3767         }\r
3768     }\r
3769 \r
3770     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3771         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3772             if( (col + row) & 1 ) {\r
3773                 /* Lite square */\r
3774                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3775                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3776                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3777                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3778                 }\r
3779             }\r
3780             else {\r
3781                 /* Dark square */\r
3782                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3783                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3784                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3785                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3786                 }\r
3787             }\r
3788         }\r
3789     }\r
3790 }\r
3791 \r
3792 /* [AS] Arrow highlighting support */\r
3793 \r
3794 static int A_WIDTH = 5; /* Width of arrow body */\r
3795 \r
3796 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3797 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3798 \r
3799 static double Sqr( double x )\r
3800 {\r
3801     return x*x;\r
3802 }\r
3803 \r
3804 static int Round( double x )\r
3805 {\r
3806     return (int) (x + 0.5);\r
3807 }\r
3808 \r
3809 /* Draw an arrow between two points using current settings */\r
3810 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3811 {\r
3812     POINT arrow[7];\r
3813     double dx, dy, j, k, x, y;\r
3814 \r
3815     if( d_x == s_x ) {\r
3816         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3817 \r
3818         arrow[0].x = s_x + A_WIDTH;\r
3819         arrow[0].y = s_y;\r
3820 \r
3821         arrow[1].x = s_x + A_WIDTH;\r
3822         arrow[1].y = d_y - h;\r
3823 \r
3824         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3825         arrow[2].y = d_y - h;\r
3826 \r
3827         arrow[3].x = d_x;\r
3828         arrow[3].y = d_y;\r
3829 \r
3830         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3831         arrow[4].y = d_y - h;\r
3832 \r
3833         arrow[5].x = s_x - A_WIDTH;\r
3834         arrow[5].y = d_y - h;\r
3835 \r
3836         arrow[6].x = s_x - A_WIDTH;\r
3837         arrow[6].y = s_y;\r
3838     }\r
3839     else if( d_y == s_y ) {\r
3840         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3841 \r
3842         arrow[0].x = s_x;\r
3843         arrow[0].y = s_y + A_WIDTH;\r
3844 \r
3845         arrow[1].x = d_x - w;\r
3846         arrow[1].y = s_y + A_WIDTH;\r
3847 \r
3848         arrow[2].x = d_x - w;\r
3849         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3850 \r
3851         arrow[3].x = d_x;\r
3852         arrow[3].y = d_y;\r
3853 \r
3854         arrow[4].x = d_x - w;\r
3855         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3856 \r
3857         arrow[5].x = d_x - w;\r
3858         arrow[5].y = s_y - A_WIDTH;\r
3859 \r
3860         arrow[6].x = s_x;\r
3861         arrow[6].y = s_y - A_WIDTH;\r
3862     }\r
3863     else {\r
3864         /* [AS] Needed a lot of paper for this! :-) */\r
3865         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3866         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3867   \r
3868         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3869 \r
3870         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3871 \r
3872         x = s_x;\r
3873         y = s_y;\r
3874 \r
3875         arrow[0].x = Round(x - j);\r
3876         arrow[0].y = Round(y + j*dx);\r
3877 \r
3878         arrow[1].x = Round(x + j);\r
3879         arrow[1].y = Round(y - j*dx);\r
3880 \r
3881         if( d_x > s_x ) {\r
3882             x = (double) d_x - k;\r
3883             y = (double) d_y - k*dy;\r
3884         }\r
3885         else {\r
3886             x = (double) d_x + k;\r
3887             y = (double) d_y + k*dy;\r
3888         }\r
3889 \r
3890         arrow[2].x = Round(x + j);\r
3891         arrow[2].y = Round(y - j*dx);\r
3892 \r
3893         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3894         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3895 \r
3896         arrow[4].x = d_x;\r
3897         arrow[4].y = d_y;\r
3898 \r
3899         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3900         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3901 \r
3902         arrow[6].x = Round(x - j);\r
3903         arrow[6].y = Round(y + j*dx);\r
3904     }\r
3905 \r
3906     Polygon( hdc, arrow, 7 );\r
3907 }\r
3908 \r
3909 /* [AS] Draw an arrow between two squares */\r
3910 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3911 {\r
3912     int s_x, s_y, d_x, d_y;\r
3913     HPEN hpen;\r
3914     HPEN holdpen;\r
3915     HBRUSH hbrush;\r
3916     HBRUSH holdbrush;\r
3917     LOGBRUSH stLB;\r
3918 \r
3919     if( s_col == d_col && s_row == d_row ) {\r
3920         return;\r
3921     }\r
3922 \r
3923     /* Get source and destination points */\r
3924     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3925     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3926 \r
3927     if( d_y > s_y ) {\r
3928         d_y += squareSize / 4;\r
3929     }\r
3930     else if( d_y < s_y ) {\r
3931         d_y += 3 * squareSize / 4;\r
3932     }\r
3933     else {\r
3934         d_y += squareSize / 2;\r
3935     }\r
3936 \r
3937     if( d_x > s_x ) {\r
3938         d_x += squareSize / 4;\r
3939     }\r
3940     else if( d_x < s_x ) {\r
3941         d_x += 3 * squareSize / 4;\r
3942     }\r
3943     else {\r
3944         d_x += squareSize / 2;\r
3945     }\r
3946 \r
3947     s_x += squareSize / 2;\r
3948     s_y += squareSize / 2;\r
3949 \r
3950     /* Adjust width */\r
3951     A_WIDTH = squareSize / 14;\r
3952 \r
3953     /* Draw */\r
3954     stLB.lbStyle = BS_SOLID;\r
3955     stLB.lbColor = appData.highlightArrowColor;\r
3956     stLB.lbHatch = 0;\r
3957 \r
3958     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3959     holdpen = SelectObject( hdc, hpen );\r
3960     hbrush = CreateBrushIndirect( &stLB );\r
3961     holdbrush = SelectObject( hdc, hbrush );\r
3962 \r
3963     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3964 \r
3965     SelectObject( hdc, holdpen );\r
3966     SelectObject( hdc, holdbrush );\r
3967     DeleteObject( hpen );\r
3968     DeleteObject( hbrush );\r
3969 }\r
3970 \r
3971 BOOL HasHighlightInfo()\r
3972 {\r
3973     BOOL result = FALSE;\r
3974 \r
3975     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3976         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3977     {\r
3978         result = TRUE;\r
3979     }\r
3980 \r
3981     return result;\r
3982 }\r
3983 \r
3984 BOOL IsDrawArrowEnabled()\r
3985 {\r
3986     BOOL result = FALSE;\r
3987 \r
3988     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3989         result = TRUE;\r
3990     }\r
3991 \r
3992     return result;\r
3993 }\r
3994 \r
3995 VOID DrawArrowHighlight( HDC hdc )\r
3996 {\r
3997     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3998         DrawArrowBetweenSquares( hdc,\r
3999             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4000             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4001     }\r
4002 }\r
4003 \r
4004 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4005 {\r
4006     HRGN result = NULL;\r
4007 \r
4008     if( HasHighlightInfo() ) {\r
4009         int x1, y1, x2, y2;\r
4010         int sx, sy, dx, dy;\r
4011 \r
4012         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4013         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4014 \r
4015         sx = MIN( x1, x2 );\r
4016         sy = MIN( y1, y2 );\r
4017         dx = MAX( x1, x2 ) + squareSize;\r
4018         dy = MAX( y1, y2 ) + squareSize;\r
4019 \r
4020         result = CreateRectRgn( sx, sy, dx, dy );\r
4021     }\r
4022 \r
4023     return result;\r
4024 }\r
4025 \r
4026 /*\r
4027     Warning: this function modifies the behavior of several other functions. \r
4028     \r
4029     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4030     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4031     repaint is scattered all over the place, which is not good for features such as\r
4032     "arrow highlighting" that require a full repaint of the board.\r
4033 \r
4034     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4035     user interaction, when speed is not so important) but especially to avoid errors\r
4036     in the displayed graphics.\r
4037 \r
4038     In such patched places, I always try refer to this function so there is a single\r
4039     place to maintain knowledge.\r
4040     \r
4041     To restore the original behavior, just return FALSE unconditionally.\r
4042 */\r
4043 BOOL IsFullRepaintPreferrable()\r
4044 {\r
4045     BOOL result = FALSE;\r
4046 \r
4047     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4048         /* Arrow may appear on the board */\r
4049         result = TRUE;\r
4050     }\r
4051 \r
4052     return result;\r
4053 }\r
4054 \r
4055 /* \r
4056     This function is called by DrawPosition to know whether a full repaint must\r
4057     be forced or not.\r
4058 \r
4059     Only DrawPosition may directly call this function, which makes use of \r
4060     some state information. Other function should call DrawPosition specifying \r
4061     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4062 */\r
4063 BOOL DrawPositionNeedsFullRepaint()\r
4064 {\r
4065     BOOL result = FALSE;\r
4066 \r
4067     /* \r
4068         Probably a slightly better policy would be to trigger a full repaint\r
4069         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4070         but animation is fast enough that it's difficult to notice.\r
4071     */\r
4072     if( animInfo.piece == EmptySquare ) {\r
4073         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4074             result = TRUE;\r
4075         }\r
4076     }\r
4077 \r
4078     return result;\r
4079 }\r
4080 \r
4081 VOID\r
4082 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4083 {\r
4084   int row, column, x, y, square_color, piece_color;\r
4085   ChessSquare piece;\r
4086   HBRUSH oldBrush;\r
4087   HDC texture_hdc = NULL;\r
4088 \r
4089   /* [AS] Initialize background textures if needed */\r
4090   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4091       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4092       if( backTextureSquareSize != squareSize \r
4093        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4094           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4095           backTextureSquareSize = squareSize;\r
4096           RebuildTextureSquareInfo();\r
4097       }\r
4098 \r
4099       texture_hdc = CreateCompatibleDC( hdc );\r
4100   }\r
4101 \r
4102   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4103     for (column = 0; column < BOARD_WIDTH; column++) {\r
4104   \r
4105       SquareToPos(row, column, &x, &y);\r
4106 \r
4107       piece = board[row][column];\r
4108 \r
4109       square_color = ((column + row) % 2) == 1;\r
4110       if( gameInfo.variant == VariantXiangqi ) {\r
4111           square_color = !InPalace(row, column);\r
4112           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4113           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4114       }\r
4115       piece_color = (int) piece < (int) BlackPawn;\r
4116 \r
4117 \r
4118       /* [HGM] holdings file: light square or black */\r
4119       if(column == BOARD_LEFT-2) {\r
4120             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4121                 square_color = 1;\r
4122             else {\r
4123                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4124                 continue;\r
4125             }\r
4126       } else\r
4127       if(column == BOARD_RGHT + 1 ) {\r
4128             if( row < gameInfo.holdingsSize )\r
4129                 square_color = 1;\r
4130             else {\r
4131                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4132                 continue;\r
4133             }\r
4134       }\r
4135       if(column == BOARD_LEFT-1 ) /* left align */\r
4136             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4137       else if( column == BOARD_RGHT) /* right align */\r
4138             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4139       else\r
4140       if (appData.monoMode) {\r
4141         if (piece == EmptySquare) {\r
4142           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4143                  square_color ? WHITENESS : BLACKNESS);\r
4144         } else {\r
4145           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4146         }\r
4147       } \r
4148       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4149           /* [AS] Draw the square using a texture bitmap */\r
4150           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4151           int r = row, c = column; // [HGM] do not flip board in flipView\r
4152           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4153 \r
4154           DrawTile( x, y, \r
4155               squareSize, squareSize, \r
4156               hdc, \r
4157               texture_hdc,\r
4158               backTextureSquareInfo[r][c].mode,\r
4159               backTextureSquareInfo[r][c].x,\r
4160               backTextureSquareInfo[r][c].y );\r
4161 \r
4162           SelectObject( texture_hdc, hbm );\r
4163 \r
4164           if (piece != EmptySquare) {\r
4165               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4166           }\r
4167       }\r
4168       else {\r
4169         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4170 \r
4171         oldBrush = SelectObject(hdc, brush );\r
4172         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4173         SelectObject(hdc, oldBrush);\r
4174         if (piece != EmptySquare)\r
4175           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4176       }\r
4177     }\r
4178   }\r
4179 \r
4180   if( texture_hdc != NULL ) {\r
4181     DeleteDC( texture_hdc );\r
4182   }\r
4183 }\r
4184 \r
4185 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4186 void fputDW(FILE *f, int x)\r
4187 {\r
4188         fputc(x     & 255, f);\r
4189         fputc(x>>8  & 255, f);\r
4190         fputc(x>>16 & 255, f);\r
4191         fputc(x>>24 & 255, f);\r
4192 }\r
4193 \r
4194 #define MAX_CLIPS 200   /* more than enough */\r
4195 \r
4196 VOID\r
4197 DrawLogoOnDC(HDC hdc, RECT logoRect, ChessProgramState *cps)\r
4198 {\r
4199   HBITMAP bufferBitmap;\r
4200   BITMAP bi;\r
4201   RECT Rect;\r
4202   HDC tmphdc;\r
4203   HBITMAP hbm;\r
4204   int w = 100, h = 50;\r
4205 \r
4206   if(cps->programLogo == NULL) return;\r
4207 //  GetClientRect(hwndMain, &Rect);\r
4208 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4209 //                                      Rect.bottom-Rect.top+1);\r
4210   tmphdc = CreateCompatibleDC(hdc);\r
4211   hbm = SelectObject(tmphdc, (HBITMAP) cps->programLogo);\r
4212   if( GetObject( cps->programLogo, sizeof(bi), &bi ) > 0 ) {\r
4213             w = bi.bmWidth;\r
4214             h = bi.bmHeight;\r
4215   }\r
4216   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4217                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4218   SelectObject(tmphdc, hbm);\r
4219   DeleteDC(tmphdc);\r
4220 }\r
4221 \r
4222 VOID\r
4223 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4224 {\r
4225   static Board lastReq, lastDrawn;\r
4226   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4227   static int lastDrawnFlipView = 0;\r
4228   static int lastReqValid = 0, lastDrawnValid = 0;\r
4229   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4230   HDC tmphdc;\r
4231   HDC hdcmem;\r
4232   HBITMAP bufferBitmap;\r
4233   HBITMAP oldBitmap;\r
4234   RECT Rect;\r
4235   HRGN clips[MAX_CLIPS];\r
4236   ChessSquare dragged_piece = EmptySquare;\r
4237 \r
4238   /* I'm undecided on this - this function figures out whether a full\r
4239    * repaint is necessary on its own, so there's no real reason to have the\r
4240    * caller tell it that.  I think this can safely be set to FALSE - but\r
4241    * if we trust the callers not to request full repaints unnessesarily, then\r
4242    * we could skip some clipping work.  In other words, only request a full\r
4243    * redraw when the majority of pieces have changed positions (ie. flip, \r
4244    * gamestart and similar)  --Hawk\r
4245    */\r
4246   Boolean fullrepaint = repaint;\r
4247 \r
4248   if( DrawPositionNeedsFullRepaint() ) {\r
4249       fullrepaint = TRUE;\r
4250   }\r
4251 \r
4252 #if 0\r
4253   if( fullrepaint ) {\r
4254       static int repaint_count = 0;\r
4255       char buf[128];\r
4256 \r
4257       repaint_count++;\r
4258       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4259       OutputDebugString( buf );\r
4260   }\r
4261 #endif\r
4262 \r
4263   if (board == NULL) {\r
4264     if (!lastReqValid) {\r
4265       return;\r
4266     }\r
4267     board = lastReq;\r
4268   } else {\r
4269     CopyBoard(lastReq, board);\r
4270     lastReqValid = 1;\r
4271   }\r
4272 \r
4273   if (doingSizing) {\r
4274     return;\r
4275   }\r
4276 \r
4277   if (IsIconic(hwndMain)) {\r
4278     return;\r
4279   }\r
4280 \r
4281   if (hdc == NULL) {\r
4282     hdc = GetDC(hwndMain);\r
4283     if (!appData.monoMode) {\r
4284       SelectPalette(hdc, hPal, FALSE);\r
4285       RealizePalette(hdc);\r
4286     }\r
4287     releaseDC = TRUE;\r
4288   } else {\r
4289     releaseDC = FALSE;\r
4290   }\r
4291 \r
4292 #if 0\r
4293   fprintf(debugFP, "*******************************\n"\r
4294                    "repaint = %s\n"\r
4295                    "dragInfo.from (%d,%d)\n"\r
4296                    "dragInfo.start (%d,%d)\n"\r
4297                    "dragInfo.pos (%d,%d)\n"\r
4298                    "dragInfo.lastpos (%d,%d)\n", \r
4299                     repaint ? "TRUE" : "FALSE",\r
4300                     dragInfo.from.x, dragInfo.from.y, \r
4301                     dragInfo.start.x, dragInfo.start.y,\r
4302                     dragInfo.pos.x, dragInfo.pos.y,\r
4303                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4304   fprintf(debugFP, "prev:  ");\r
4305   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4306     for (column = 0; column < BOARD_WIDTH; column++) {\r
4307       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4308     }\r
4309   }\r
4310   fprintf(debugFP, "\n");\r
4311   fprintf(debugFP, "board: ");\r
4312   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4313     for (column = 0; column < BOARD_WIDTH; column++) {\r
4314       fprintf(debugFP, "%d ", board[row][column]);\r
4315     }\r
4316   }\r
4317   fprintf(debugFP, "\n");\r
4318   fflush(debugFP);\r
4319 #endif\r
4320 \r
4321   /* Create some work-DCs */\r
4322   hdcmem = CreateCompatibleDC(hdc);\r
4323   tmphdc = CreateCompatibleDC(hdc);\r
4324 \r
4325   /* If dragging is in progress, we temporarely remove the piece */\r
4326   /* [HGM] or temporarily decrease count if stacked              */\r
4327   /*       !! Moved to before board compare !!                   */\r
4328   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4329     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4330     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4331             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4332         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4333     } else \r
4334     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4335             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4336         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4337     } else \r
4338         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4339   }\r
4340 \r
4341   /* Figure out which squares need updating by comparing the \r
4342    * newest board with the last drawn board and checking if\r
4343    * flipping has changed.\r
4344    */\r
4345   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4346     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4347       for (column = 0; column < BOARD_WIDTH; column++) {\r
4348         if (lastDrawn[row][column] != board[row][column]) {\r
4349           SquareToPos(row, column, &x, &y);\r
4350           clips[num_clips++] =\r
4351             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4352         }\r
4353       }\r
4354     }\r
4355     for (i=0; i<2; i++) {\r
4356       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4357           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4358         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4359             lastDrawnHighlight.sq[i].y >= 0) {\r
4360           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4361                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4362           clips[num_clips++] =\r
4363             CreateRectRgn(x - lineGap, y - lineGap, \r
4364                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4365         }\r
4366         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4367           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4368           clips[num_clips++] =\r
4369             CreateRectRgn(x - lineGap, y - lineGap, \r
4370                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4371         }\r
4372       }\r
4373     }\r
4374     for (i=0; i<2; i++) {\r
4375       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4376           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4377         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4378             lastDrawnPremove.sq[i].y >= 0) {\r
4379           SquareToPos(lastDrawnPremove.sq[i].y,\r
4380                       lastDrawnPremove.sq[i].x, &x, &y);\r
4381           clips[num_clips++] =\r
4382             CreateRectRgn(x - lineGap, y - lineGap, \r
4383                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4384         }\r
4385         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4386             premoveHighlightInfo.sq[i].y >= 0) {\r
4387           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4388                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4389           clips[num_clips++] =\r
4390             CreateRectRgn(x - lineGap, y - lineGap, \r
4391                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4392         }\r
4393       }\r
4394     }\r
4395   } else {\r
4396     fullrepaint = TRUE;\r
4397   }\r
4398 \r
4399   /* Create a buffer bitmap - this is the actual bitmap\r
4400    * being written to.  When all the work is done, we can\r
4401    * copy it to the real DC (the screen).  This avoids\r
4402    * the problems with flickering.\r
4403    */\r
4404   GetClientRect(hwndMain, &Rect);\r
4405   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4406                                         Rect.bottom-Rect.top+1);\r
4407   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4408   if (!appData.monoMode) {\r
4409     SelectPalette(hdcmem, hPal, FALSE);\r
4410   }\r
4411 \r
4412   /* Create clips for dragging */\r
4413   if (!fullrepaint) {\r
4414     if (dragInfo.from.x >= 0) {\r
4415       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4416       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4417     }\r
4418     if (dragInfo.start.x >= 0) {\r
4419       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4420       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4421     }\r
4422     if (dragInfo.pos.x >= 0) {\r
4423       x = dragInfo.pos.x - squareSize / 2;\r
4424       y = dragInfo.pos.y - squareSize / 2;\r
4425       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4426     }\r
4427     if (dragInfo.lastpos.x >= 0) {\r
4428       x = dragInfo.lastpos.x - squareSize / 2;\r
4429       y = dragInfo.lastpos.y - squareSize / 2;\r
4430       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4431     }\r
4432   }\r
4433 \r
4434   /* Are we animating a move?  \r
4435    * If so, \r
4436    *   - remove the piece from the board (temporarely)\r
4437    *   - calculate the clipping region\r
4438    */\r
4439   if (!fullrepaint) {\r
4440     if (animInfo.piece != EmptySquare) {\r
4441       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4442       x = boardRect.left + animInfo.lastpos.x;\r
4443       y = boardRect.top + animInfo.lastpos.y;\r
4444       x2 = boardRect.left + animInfo.pos.x;\r
4445       y2 = boardRect.top + animInfo.pos.y;\r
4446       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4447       /* Slight kludge.  The real problem is that after AnimateMove is\r
4448          done, the position on the screen does not match lastDrawn.\r
4449          This currently causes trouble only on e.p. captures in\r
4450          atomic, where the piece moves to an empty square and then\r
4451          explodes.  The old and new positions both had an empty square\r
4452          at the destination, but animation has drawn a piece there and\r
4453          we have to remember to erase it. */\r
4454       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4455     }\r
4456   }\r
4457 \r
4458   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4459   if (num_clips == 0)\r
4460     fullrepaint = TRUE;\r
4461 \r
4462   /* Set clipping on the memory DC */\r
4463   if (!fullrepaint) {\r
4464     SelectClipRgn(hdcmem, clips[0]);\r
4465     for (x = 1; x < num_clips; x++) {\r
4466       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4467         abort();  // this should never ever happen!\r
4468     }\r
4469   }\r
4470 \r
4471   /* Do all the drawing to the memory DC */\r
4472   DrawGridOnDC(hdcmem);\r
4473   DrawHighlightsOnDC(hdcmem);\r
4474   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4475 \r
4476   if(logoHeight) {\r
4477         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? &second : &first);\r
4478         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? &first : &second);\r
4479   }\r
4480 \r
4481   if( appData.highlightMoveWithArrow ) {\r
4482     DrawArrowHighlight(hdcmem);\r
4483   }\r
4484 \r
4485   DrawCoordsOnDC(hdcmem);\r
4486 \r
4487   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4488                  /* to make sure lastDrawn contains what is actually drawn */\r
4489 \r
4490   /* Put the dragged piece back into place and draw it (out of place!) */\r
4491     if (dragged_piece != EmptySquare) {\r
4492     /* [HGM] or restack */\r
4493     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4494                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4495     else\r
4496     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4497                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4498     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4499     x = dragInfo.pos.x - squareSize / 2;\r
4500     y = dragInfo.pos.y - squareSize / 2;\r
4501     DrawPieceOnDC(hdcmem, dragged_piece,\r
4502                   ((int) dragged_piece < (int) BlackPawn), \r
4503                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4504   }   \r
4505   \r
4506   /* Put the animated piece back into place and draw it */\r
4507   if (animInfo.piece != EmptySquare) {\r
4508     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4509     x = boardRect.left + animInfo.pos.x;\r
4510     y = boardRect.top + animInfo.pos.y;\r
4511     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4512                   ((int) animInfo.piece < (int) BlackPawn),\r
4513                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4514   }\r
4515 \r
4516   /* Release the bufferBitmap by selecting in the old bitmap \r
4517    * and delete the memory DC\r
4518    */\r
4519   SelectObject(hdcmem, oldBitmap);\r
4520   DeleteDC(hdcmem);\r
4521 \r
4522   /* Set clipping on the target DC */\r
4523   if (!fullrepaint) {\r
4524     SelectClipRgn(hdc, clips[0]);\r
4525     for (x = 1; x < num_clips; x++) {\r
4526       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4527         abort();   // this should never ever happen!\r
4528     } \r
4529   }\r
4530 \r
4531   /* Copy the new bitmap onto the screen in one go.\r
4532    * This way we avoid any flickering\r
4533    */\r
4534   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4535   BitBlt(hdc, boardRect.left, boardRect.top,\r
4536          boardRect.right - boardRect.left,\r
4537          boardRect.bottom - boardRect.top,\r
4538          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4539   if(saveDiagFlag) { \r
4540     BITMAP b; int i, j, m, w, wb, fac=0; char pData[1000000]; \r
4541     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4542 \r
4543     GetObject(bufferBitmap, sizeof(b), &b);\r
4544     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4545         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4546         bih.biWidth = b.bmWidth;\r
4547         bih.biHeight = b.bmHeight;\r
4548         bih.biPlanes = 1;\r
4549         bih.biBitCount = b.bmBitsPixel;\r
4550         bih.biCompression = 0;\r
4551         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4552         bih.biXPelsPerMeter = 0;\r
4553         bih.biYPelsPerMeter = 0;\r
4554         bih.biClrUsed = 0;\r
4555         bih.biClrImportant = 0;\r
4556 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4557 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4558         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4559 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4560 \r
4561 #if 1\r
4562         wb = b.bmWidthBytes;\r
4563         // count colors\r
4564         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4565                 int k = ((int*) pData)[i];\r
4566                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4567                 if(j >= 16) break;\r
4568                 color[j] = k;\r
4569                 if(j >= nrColors) nrColors = j+1;\r
4570         }\r
4571         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4572                 INT p = 0;\r
4573                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4574                     for(w=0; w<(wb>>2); w+=2) {\r
4575                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4576                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4577                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4578                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4579                         pData[p++] = m | j<<4;\r
4580                     }\r
4581                     while(p&3) pData[p++] = 0;\r
4582                 }\r
4583                 fac = 3;\r
4584                 wb = (wb+31>>5)<<2;\r
4585         }\r
4586         // write BITMAPFILEHEADER\r
4587         fprintf(diagFile, "BM");\r
4588         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4589         fputDW(diagFile, 0);\r
4590         fputDW(diagFile, 0x36 + (fac?64:0));\r
4591         // write BITMAPINFOHEADER\r
4592         fputDW(diagFile, 40);\r
4593         fputDW(diagFile, b.bmWidth);\r
4594         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4595         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4596         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4597         fputDW(diagFile, 0);\r
4598         fputDW(diagFile, 0);\r
4599         fputDW(diagFile, 0);\r
4600         fputDW(diagFile, 0);\r
4601         fputDW(diagFile, 0);\r
4602         fputDW(diagFile, 0);\r
4603         // write color table\r
4604         if(fac)\r
4605         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4606         // write bitmap data\r
4607         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4608                 fputc(pData[i], diagFile);\r
4609 #endif\r
4610      }\r
4611   }\r
4612 \r
4613   SelectObject(tmphdc, oldBitmap);\r
4614 \r
4615   /* Massive cleanup */\r
4616   for (x = 0; x < num_clips; x++)\r
4617     DeleteObject(clips[x]);\r
4618 \r
4619   DeleteDC(tmphdc);\r
4620   DeleteObject(bufferBitmap);\r
4621 \r
4622   if (releaseDC) \r
4623     ReleaseDC(hwndMain, hdc);\r
4624   \r
4625   if (lastDrawnFlipView != flipView) {\r
4626     if (flipView)\r
4627       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4628     else\r
4629       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4630   }\r
4631 \r
4632 /*  CopyBoard(lastDrawn, board);*/\r
4633   lastDrawnHighlight = highlightInfo;\r
4634   lastDrawnPremove   = premoveHighlightInfo;\r
4635   lastDrawnFlipView = flipView;\r
4636   lastDrawnValid = 1;\r
4637 }\r
4638 \r
4639 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4640 int\r
4641 SaveDiagram(f)\r
4642      FILE *f;\r
4643 {\r
4644     time_t tm;\r
4645     char *fen;\r
4646 \r
4647     saveDiagFlag = 1; diagFile = f;\r
4648     HDCDrawPosition(NULL, TRUE, NULL);\r
4649 \r
4650     saveDiagFlag = 0;\r
4651 \r
4652 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4653     \r
4654     fclose(f);\r
4655     return TRUE;\r
4656 }\r
4657 \r
4658 \r
4659 /*---------------------------------------------------------------------------*\\r
4660 | CLIENT PAINT PROCEDURE\r
4661 |   This is the main event-handler for the WM_PAINT message.\r
4662 |\r
4663 \*---------------------------------------------------------------------------*/\r
4664 VOID\r
4665 PaintProc(HWND hwnd)\r
4666 {\r
4667   HDC         hdc;\r
4668   PAINTSTRUCT ps;\r
4669   HFONT       oldFont;\r
4670 \r
4671   if(hdc = BeginPaint(hwnd, &ps)) {\r
4672     if (IsIconic(hwnd)) {\r
4673       DrawIcon(hdc, 2, 2, iconCurrent);\r
4674     } else {\r
4675       if (!appData.monoMode) {\r
4676         SelectPalette(hdc, hPal, FALSE);\r
4677         RealizePalette(hdc);\r
4678       }\r
4679       HDCDrawPosition(hdc, 1, NULL);\r
4680       oldFont =\r
4681         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4682       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4683                  ETO_CLIPPED|ETO_OPAQUE,\r
4684                  &messageRect, messageText, strlen(messageText), NULL);\r
4685       SelectObject(hdc, oldFont);\r
4686       DisplayBothClocks();\r
4687     }\r
4688     EndPaint(hwnd,&ps);\r
4689   }\r
4690 \r
4691   return;\r
4692 }\r
4693 \r
4694 \r
4695 /*\r
4696  * If the user selects on a border boundary, return -1; if off the board,\r
4697  *   return -2.  Otherwise map the event coordinate to the square.\r
4698  * The offset boardRect.left or boardRect.top must already have been\r
4699  *   subtracted from x.\r
4700  */\r
4701 int\r
4702 EventToSquare(int x)\r
4703 {\r
4704   if (x <= 0)\r
4705     return -2;\r
4706   if (x < lineGap)\r
4707     return -1;\r
4708   x -= lineGap;\r
4709   if ((x % (squareSize + lineGap)) >= squareSize)\r
4710     return -1;\r
4711   x /= (squareSize + lineGap);\r
4712   if (x >= BOARD_SIZE)\r
4713     return -2;\r
4714   return x;\r
4715 }\r
4716 \r
4717 typedef struct {\r
4718   char piece;\r
4719   int command;\r
4720   char* name;\r
4721 } DropEnable;\r
4722 \r
4723 DropEnable dropEnables[] = {\r
4724   { 'P', DP_Pawn, "Pawn" },\r
4725   { 'N', DP_Knight, "Knight" },\r
4726   { 'B', DP_Bishop, "Bishop" },\r
4727   { 'R', DP_Rook, "Rook" },\r
4728   { 'Q', DP_Queen, "Queen" },\r
4729 };\r
4730 \r
4731 VOID\r
4732 SetupDropMenu(HMENU hmenu)\r
4733 {\r
4734   int i, count, enable;\r
4735   char *p;\r
4736   extern char white_holding[], black_holding[];\r
4737   char item[MSG_SIZ];\r
4738 \r
4739   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4740     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4741                dropEnables[i].piece);\r
4742     count = 0;\r
4743     while (p && *p++ == dropEnables[i].piece) count++;\r
4744     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4745     enable = count > 0 || !appData.testLegality\r
4746       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4747                       && !appData.icsActive);\r
4748     ModifyMenu(hmenu, dropEnables[i].command,\r
4749                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4750                dropEnables[i].command, item);\r
4751   }\r
4752 }\r
4753 \r
4754 static int fromX = -1, fromY = -1, toX, toY;\r
4755 \r
4756 /* Event handler for mouse messages */\r
4757 VOID\r
4758 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4759 {\r
4760   int x, y;\r
4761   POINT pt;\r
4762   static int recursive = 0;\r
4763   HMENU hmenu;\r
4764   BOOLEAN needsRedraw = FALSE;\r
4765   BOOLEAN saveAnimate;\r
4766   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4767   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4768   ChessMove moveType;\r
4769 \r
4770   if (recursive) {\r
4771     if (message == WM_MBUTTONUP) {\r
4772       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4773          to the middle button: we simulate pressing the left button too!\r
4774          */\r
4775       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4776       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4777     }\r
4778     return;\r
4779   }\r
4780   recursive++;\r
4781   \r
4782   pt.x = LOWORD(lParam);\r
4783   pt.y = HIWORD(lParam);\r
4784   x = EventToSquare(pt.x - boardRect.left);\r
4785   y = EventToSquare(pt.y - boardRect.top);\r
4786   if (!flipView && y >= 0) {\r
4787     y = BOARD_HEIGHT - 1 - y;\r
4788   }\r
4789   if (flipView && x >= 0) {\r
4790     x = BOARD_WIDTH - 1 - x;\r
4791   }\r
4792 \r
4793   switch (message) {\r
4794   case WM_LBUTTONDOWN:\r
4795     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4796         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4797         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4798         if(gameInfo.holdingsWidth && \r
4799                 (WhiteOnMove(currentMove) \r
4800                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4801                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4802             // click in right holdings, for determining promotion piece\r
4803             ChessSquare p = boards[currentMove][y][x];\r
4804             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4805             if(p != EmptySquare) {\r
4806                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4807                 fromX = fromY = -1;\r
4808                 break;\r
4809             }\r
4810         }\r
4811         DrawPosition(FALSE, boards[currentMove]);\r
4812         break;\r
4813     }\r
4814     ErrorPopDown();\r
4815     sameAgain = FALSE;\r
4816     if (y == -2) {\r
4817       /* Downclick vertically off board; check if on clock */\r
4818       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4819         if (gameMode == EditPosition) {\r
4820           SetWhiteToPlayEvent();\r
4821         } else if (gameMode == IcsPlayingBlack ||\r
4822                    gameMode == MachinePlaysWhite) {\r
4823           CallFlagEvent();\r
4824         } else if (gameMode == EditGame) {\r
4825           AdjustClock((logoHeight > 0 ? flipView: flipClock), -1);\r
4826         }\r
4827       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4828         if (gameMode == EditPosition) {\r
4829           SetBlackToPlayEvent();\r
4830         } else if (gameMode == IcsPlayingWhite ||\r
4831                    gameMode == MachinePlaysBlack) {\r
4832           CallFlagEvent();\r
4833         } else if (gameMode == EditGame) {\r
4834           AdjustClock(!(logoHeight > 0 ? flipView: flipClock), -1);\r
4835         }\r
4836       }\r
4837       if (!appData.highlightLastMove) {\r
4838         ClearHighlights();\r
4839         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4840       }\r
4841       fromX = fromY = -1;\r
4842       dragInfo.start.x = dragInfo.start.y = -1;\r
4843       dragInfo.from = dragInfo.start;\r
4844       break;\r
4845     } else if (x < 0 || y < 0\r
4846       /* [HGM] block clicks between board and holdings */\r
4847               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4848               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4849               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4850         /* EditPosition, empty square, or different color piece;\r
4851            click-click move is possible */\r
4852                                ) {\r
4853       break;\r
4854     } else if (fromX == x && fromY == y) {\r
4855       /* Downclick on same square again */\r
4856       ClearHighlights();\r
4857       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4858       sameAgain = TRUE;  \r
4859     } else if (fromX != -1 &&\r
4860                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4861                                                                         ) {\r
4862       /* Downclick on different square. */\r
4863       /* [HGM] if on holdings file, should count as new first click ! */\r
4864       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4865         toX = x;\r
4866         toY = y;\r
4867         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4868            to make sure move is legal before showing promotion popup */\r
4869         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4870         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4871                 fromX = fromY = -1; \r
4872                 ClearHighlights();\r
4873                 DrawPosition(FALSE, boards[currentMove]);\r
4874                 break; \r
4875         } else \r
4876         if(moveType != ImpossibleMove) {\r
4877           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4878           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4879              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4880               appData.alwaysPromoteToQueen) {\r
4881                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4882                   if (!appData.highlightLastMove) {\r
4883                       ClearHighlights();\r
4884                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4885                   }\r
4886           } else\r
4887           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4888                   SetHighlights(fromX, fromY, toX, toY);\r
4889                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4890                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4891                      If promotion to Q is legal, all are legal! */\r
4892                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4893                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4894                     // kludge to temporarily execute move on display, wthout promotng yet\r
4895                     promotionChoice = TRUE;\r
4896                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4897                     boards[currentMove][toY][toX] = p;\r
4898                     DrawPosition(FALSE, boards[currentMove]);\r
4899                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4900                     boards[currentMove][toY][toX] = q;\r
4901                   } else\r
4902                   PromotionPopup(hwnd);\r
4903           } else {       /* not a promotion */\r
4904              if (appData.animate || appData.highlightLastMove) {\r
4905                  SetHighlights(fromX, fromY, toX, toY);\r
4906              } else {\r
4907                  ClearHighlights();\r
4908              }\r
4909              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4910              fromX = fromY = -1;\r
4911              if (appData.animate && !appData.highlightLastMove) {\r
4912                   ClearHighlights();\r
4913                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4914              }\r
4915           }\r
4916           break;\r
4917         }\r
4918         if (gotPremove) {\r
4919             /* [HGM] it seemed that braces were missing here */\r
4920             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4921             fromX = fromY = -1;\r
4922             break;\r
4923         }\r
4924       }\r
4925       ClearHighlights();\r
4926       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4927     }\r
4928     /* First downclick, or restart on a square with same color piece */\r
4929     if (!frozen && OKToStartUserMove(x, y)) {\r
4930       fromX = x;\r
4931       fromY = y;\r
4932       dragInfo.lastpos = pt;\r
4933       dragInfo.from.x = fromX;\r
4934       dragInfo.from.y = fromY;\r
4935       dragInfo.start = dragInfo.from;\r
4936       SetCapture(hwndMain);\r
4937     } else {\r
4938       fromX = fromY = -1;\r
4939       dragInfo.start.x = dragInfo.start.y = -1;\r
4940       dragInfo.from = dragInfo.start;\r
4941       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4942     }\r
4943     break;\r
4944 \r
4945   case WM_LBUTTONUP:\r
4946     ReleaseCapture();\r
4947     if (fromX == -1) break;\r
4948     if (x == fromX && y == fromY) {\r
4949       dragInfo.from.x = dragInfo.from.y = -1;\r
4950       /* Upclick on same square */\r
4951       if (sameAgain) {\r
4952         /* Clicked same square twice: abort click-click move */\r
4953         fromX = fromY = -1;\r
4954         gotPremove = 0;\r
4955         ClearPremoveHighlights();\r
4956       } else {\r
4957         /* First square clicked: start click-click move */\r
4958         SetHighlights(fromX, fromY, -1, -1);\r
4959       }\r
4960       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4961     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4962       /* Errant click; ignore */\r
4963       break;\r
4964     } else {\r
4965       /* Finish drag move. */\r
4966     if (appData.debugMode) {\r
4967         fprintf(debugFP, "release\n");\r
4968     }\r
4969       dragInfo.from.x = dragInfo.from.y = -1;\r
4970       toX = x;\r
4971       toY = y;\r
4972       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4973       appData.animate = appData.animate && !appData.animateDragging;\r
4974       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4975       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4976                 fromX = fromY = -1; \r
4977                 ClearHighlights();\r
4978                 DrawPosition(FALSE, boards[currentMove]);\r
4979                 break; \r
4980       } else \r
4981       if(moveType != ImpossibleMove) {\r
4982           /* [HGM] use move type to determine if move is promotion.\r
4983              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4984           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4985              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4986               appData.alwaysPromoteToQueen) \r
4987                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4988           else \r
4989           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4990                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4991                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
4992                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4993                     // kludge to temporarily execute move on display, wthout promotng yet\r
4994                     promotionChoice = TRUE;\r
4995                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4996                     boards[currentMove][toY][toX] = p;\r
4997                     DrawPosition(FALSE, boards[currentMove]);\r
4998                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4999                     boards[currentMove][toY][toX] = q;\r
5000                     break;\r
5001                   } else\r
5002                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5003         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5004       }\r
5005       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5006       appData.animate = saveAnimate;\r
5007       fromX = fromY = -1;\r
5008       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5009         ClearHighlights();\r
5010       }\r
5011       if (appData.animate || appData.animateDragging ||\r
5012           appData.highlightDragging || gotPremove) {\r
5013         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5014       }\r
5015     }\r
5016     dragInfo.start.x = dragInfo.start.y = -1; \r
5017     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5018     break;\r
5019 \r
5020   case WM_MOUSEMOVE:\r
5021     if ((appData.animateDragging || appData.highlightDragging)\r
5022         && (wParam & MK_LBUTTON)\r
5023         && dragInfo.from.x >= 0) \r
5024     {\r
5025       BOOL full_repaint = FALSE;\r
5026 \r
5027       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5028       if (appData.animateDragging) {\r
5029         dragInfo.pos = pt;\r
5030       }\r
5031       if (appData.highlightDragging) {\r
5032         SetHighlights(fromX, fromY, x, y);\r
5033         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5034             full_repaint = TRUE;\r
5035         }\r
5036       }\r
5037       \r
5038       DrawPosition( full_repaint, NULL);\r
5039       \r
5040       dragInfo.lastpos = dragInfo.pos;\r
5041     }\r
5042     break;\r
5043 \r
5044   case WM_MOUSEWHEEL: // [DM]\r
5045        /* Mouse Wheel is being rolled forward\r
5046         * Play moves forward\r
5047         */\r
5048        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) ForwardEvent();\r
5049        /* Mouse Wheel is being rolled backward\r
5050         * Play moves backward\r
5051         */\r
5052        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) BackwardEvent();\r
5053        break;\r
5054 \r
5055   case WM_MBUTTONDOWN:\r
5056   case WM_RBUTTONDOWN:\r
5057     ErrorPopDown();\r
5058     ReleaseCapture();\r
5059     fromX = fromY = -1;\r
5060     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5061     dragInfo.start.x = dragInfo.start.y = -1;\r
5062     dragInfo.from = dragInfo.start;\r
5063     dragInfo.lastpos = dragInfo.pos;\r
5064     if (appData.highlightDragging) {\r
5065       ClearHighlights();\r
5066     }\r
5067     if(y == -2) {\r
5068       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5069       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5070           if (gameMode == EditGame) AdjustClock((logoHeight > 0 ? flipView: flipClock), 1);\r
5071       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5072           if (gameMode == EditGame) AdjustClock(!(logoHeight > 0 ? flipView: flipClock), 1);\r
5073       }\r
5074     }\r
5075     DrawPosition(TRUE, NULL);\r
5076 \r
5077     switch (gameMode) {\r
5078     case EditPosition:\r
5079     case IcsExamining:\r
5080       if (x < 0 || y < 0) break;\r
5081       fromX = x;\r
5082       fromY = y;\r
5083       if (message == WM_MBUTTONDOWN) {\r
5084         buttonCount = 3;  /* even if system didn't think so */\r
5085         if (wParam & MK_SHIFT) \r
5086           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5087         else\r
5088           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5089       } else { /* message == WM_RBUTTONDOWN */\r
5090 #if 0\r
5091         if (buttonCount == 3) {\r
5092           if (wParam & MK_SHIFT) \r
5093             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5094           else\r
5095             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5096         } else {\r
5097           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5098         }\r
5099 #else\r
5100         /* Just have one menu, on the right button.  Windows users don't\r
5101            think to try the middle one, and sometimes other software steals\r
5102            it, or it doesn't really exist. */\r
5103         if(gameInfo.variant != VariantShogi)\r
5104             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5105         else\r
5106             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5107 #endif\r
5108       }\r
5109       break;\r
5110     case IcsPlayingWhite:\r
5111     case IcsPlayingBlack:\r
5112     case EditGame:\r
5113     case MachinePlaysWhite:\r
5114     case MachinePlaysBlack:\r
5115       if (appData.testLegality &&\r
5116           gameInfo.variant != VariantBughouse &&\r
5117           gameInfo.variant != VariantCrazyhouse) break;\r
5118       if (x < 0 || y < 0) break;\r
5119       fromX = x;\r
5120       fromY = y;\r
5121       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5122       SetupDropMenu(hmenu);\r
5123       MenuPopup(hwnd, pt, hmenu, -1);\r
5124       break;\r
5125     default:\r
5126       break;\r
5127     }\r
5128     break;\r
5129   }\r
5130 \r
5131   recursive--;\r
5132 }\r
5133 \r
5134 /* Preprocess messages for buttons in main window */\r
5135 LRESULT CALLBACK\r
5136 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5137 {\r
5138   int id = GetWindowLong(hwnd, GWL_ID);\r
5139   int i, dir;\r
5140 \r
5141   for (i=0; i<N_BUTTONS; i++) {\r
5142     if (buttonDesc[i].id == id) break;\r
5143   }\r
5144   if (i == N_BUTTONS) return 0;\r
5145   switch (message) {\r
5146   case WM_KEYDOWN:\r
5147     switch (wParam) {\r
5148     case VK_LEFT:\r
5149     case VK_RIGHT:\r
5150       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5151       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5152       return TRUE;\r
5153     }\r
5154     break;\r
5155   case WM_CHAR:\r
5156     switch (wParam) {\r
5157     case '\r':\r
5158       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5159       return TRUE;\r
5160     case '\t':\r
5161       if (appData.icsActive) {\r
5162         if (GetKeyState(VK_SHIFT) < 0) {\r
5163           /* shifted */\r
5164           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5165           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5166           SetFocus(h);\r
5167         } else {\r
5168           /* unshifted */\r
5169           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5170           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5171           SetFocus(h);\r
5172         }\r
5173         return TRUE;\r
5174       }\r
5175       break;\r
5176     default:\r
5177       if (appData.icsActive) {\r
5178         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5179         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5180         SetFocus(h);\r
5181         SendMessage(h, WM_CHAR, wParam, lParam);\r
5182         return TRUE;\r
5183       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5184         PopUpMoveDialog((char)wParam);\r
5185       }\r
5186       break;\r
5187     }\r
5188     break;\r
5189   }\r
5190   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5191 }\r
5192 \r
5193 /* Process messages for Promotion dialog box */\r
5194 LRESULT CALLBACK\r
5195 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5196 {\r
5197   char promoChar;\r
5198 \r
5199   switch (message) {\r
5200   case WM_INITDIALOG: /* message: initialize dialog box */\r
5201     /* Center the dialog over the application window */\r
5202     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5203     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5204       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5205        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5206                SW_SHOW : SW_HIDE);\r
5207     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5208     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5209        (PieceToChar(WhiteAngel) >= 'A' &&\r
5210         PieceToChar(WhiteAngel) != '~' ||\r
5211         PieceToChar(BlackAngel) >= 'A' &&\r
5212         PieceToChar(BlackAngel) != '~'   ) ?\r
5213                SW_SHOW : SW_HIDE);\r
5214     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5215        (PieceToChar(WhiteMarshall) >= 'A' &&\r
5216         PieceToChar(WhiteMarshall) != '~' ||\r
5217         PieceToChar(BlackMarshall) >= 'A' &&\r
5218         PieceToChar(BlackMarshall) != '~'   ) ?\r
5219                SW_SHOW : SW_HIDE);\r
5220     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5221     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5222        gameInfo.variant != VariantShogi ?\r
5223                SW_SHOW : SW_HIDE);\r
5224     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5225        gameInfo.variant != VariantShogi ?\r
5226                SW_SHOW : SW_HIDE);\r
5227     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5228        gameInfo.variant == VariantShogi ?\r
5229                SW_SHOW : SW_HIDE);\r
5230     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5231        gameInfo.variant == VariantShogi ?\r
5232                SW_SHOW : SW_HIDE);\r
5233     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5234        gameInfo.variant == VariantSuper ?\r
5235                SW_SHOW : SW_HIDE);\r
5236     return TRUE;\r
5237 \r
5238   case WM_COMMAND: /* message: received a command */\r
5239     switch (LOWORD(wParam)) {\r
5240     case IDCANCEL:\r
5241       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5242       ClearHighlights();\r
5243       DrawPosition(FALSE, NULL);\r
5244       return TRUE;\r
5245     case PB_King:\r
5246       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5247       break;\r
5248     case PB_Queen:\r
5249       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5250       break;\r
5251     case PB_Rook:\r
5252       promoChar = PieceToChar(BlackRook);\r
5253       break;\r
5254     case PB_Bishop:\r
5255       promoChar = PieceToChar(BlackBishop);\r
5256       break;\r
5257     case PB_Chancellor:\r
5258       promoChar = PieceToChar(BlackMarshall);\r
5259       break;\r
5260     case PB_Archbishop:\r
5261       promoChar = PieceToChar(BlackAngel);\r
5262       break;\r
5263     case PB_Knight:\r
5264       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5265       break;\r
5266     default:\r
5267       return FALSE;\r
5268     }\r
5269     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5270     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5271        only show the popup when we are already sure the move is valid or\r
5272        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5273        will figure out it is a promotion from the promoChar. */\r
5274     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5275     if (!appData.highlightLastMove) {\r
5276       ClearHighlights();\r
5277       DrawPosition(FALSE, NULL);\r
5278     }\r
5279     return TRUE;\r
5280   }\r
5281   return FALSE;\r
5282 }\r
5283 \r
5284 /* Pop up promotion dialog */\r
5285 VOID\r
5286 PromotionPopup(HWND hwnd)\r
5287 {\r
5288   FARPROC lpProc;\r
5289 \r
5290   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5291   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5292     hwnd, (DLGPROC)lpProc);\r
5293   FreeProcInstance(lpProc);\r
5294 }\r
5295 \r
5296 /* Toggle ShowThinking */\r
5297 VOID\r
5298 ToggleShowThinking()\r
5299 {\r
5300   appData.showThinking = !appData.showThinking;\r
5301   ShowThinkingEvent();\r
5302 }\r
5303 \r
5304 VOID\r
5305 LoadGameDialog(HWND hwnd, char* title)\r
5306 {\r
5307   UINT number = 0;\r
5308   FILE *f;\r
5309   char fileTitle[MSG_SIZ];\r
5310   f = OpenFileDialog(hwnd, "rb", "",\r
5311                      appData.oldSaveStyle ? "gam" : "pgn",\r
5312                      GAME_FILT,\r
5313                      title, &number, fileTitle, NULL);\r
5314   if (f != NULL) {\r
5315     cmailMsgLoaded = FALSE;\r
5316     if (number == 0) {\r
5317       int error = GameListBuild(f);\r
5318       if (error) {\r
5319         DisplayError("Cannot build game list", error);\r
5320       } else if (!ListEmpty(&gameList) &&\r
5321                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5322         GameListPopUp(f, fileTitle);\r
5323         return;\r
5324       }\r
5325       GameListDestroy();\r
5326       number = 1;\r
5327     }\r
5328     LoadGame(f, number, fileTitle, FALSE);\r
5329   }\r
5330 }\r
5331 \r
5332 VOID\r
5333 ChangedConsoleFont()\r
5334 {\r
5335   CHARFORMAT cfmt;\r
5336   CHARRANGE tmpsel, sel;\r
5337   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5338   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5339   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5340   PARAFORMAT paraf;\r
5341 \r
5342   cfmt.cbSize = sizeof(CHARFORMAT);\r
5343   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5344   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5345   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5346    * size.  This was undocumented in the version of MSVC++ that I had\r
5347    * when I wrote the code, but is apparently documented now.\r
5348    */\r
5349   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5350   cfmt.bCharSet = f->lf.lfCharSet;\r
5351   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5352   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5353   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5354   /* Why are the following seemingly needed too? */\r
5355   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5356   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5357   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5358   tmpsel.cpMin = 0;\r
5359   tmpsel.cpMax = -1; /*999999?*/\r
5360   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5361   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5362   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5363    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5364    */\r
5365   paraf.cbSize = sizeof(paraf);\r
5366   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5367   paraf.dxStartIndent = 0;\r
5368   paraf.dxOffset = WRAP_INDENT;\r
5369   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5370   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5371 }\r
5372 \r
5373 /*---------------------------------------------------------------------------*\\r
5374  *\r
5375  * Window Proc for main window\r
5376  *\r
5377 \*---------------------------------------------------------------------------*/\r
5378 \r
5379 /* Process messages for main window, etc. */\r
5380 LRESULT CALLBACK\r
5381 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5382 {\r
5383   FARPROC lpProc;\r
5384   int wmId, wmEvent;\r
5385   char *defName;\r
5386   FILE *f;\r
5387   UINT number;\r
5388   char fileTitle[MSG_SIZ];\r
5389   char buf[MSG_SIZ];\r
5390   static SnapData sd;\r
5391 \r
5392   switch (message) {\r
5393 \r
5394   case WM_PAINT: /* message: repaint portion of window */\r
5395     PaintProc(hwnd);\r
5396     break;\r
5397 \r
5398   case WM_ERASEBKGND:\r
5399     if (IsIconic(hwnd)) {\r
5400       /* Cheat; change the message */\r
5401       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5402     } else {\r
5403       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5404     }\r
5405     break;\r
5406 \r
5407   case WM_LBUTTONDOWN:\r
5408   case WM_MBUTTONDOWN:\r
5409   case WM_RBUTTONDOWN:\r
5410   case WM_LBUTTONUP:\r
5411   case WM_MBUTTONUP:\r
5412   case WM_RBUTTONUP:\r
5413   case WM_MOUSEMOVE:\r
5414   case WM_MOUSEWHEEL:\r
5415     MouseEvent(hwnd, message, wParam, lParam);\r
5416     break;\r
5417 \r
5418   case WM_CHAR:\r
5419     \r
5420     if (appData.icsActive) {\r
5421       if (wParam == '\t') {\r
5422         if (GetKeyState(VK_SHIFT) < 0) {\r
5423           /* shifted */\r
5424           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5425           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5426           SetFocus(h);\r
5427         } else {\r
5428           /* unshifted */\r
5429           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5430           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5431           SetFocus(h);\r
5432         }\r
5433       } else {\r
5434         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5435         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5436         SetFocus(h);\r
5437         SendMessage(h, message, wParam, lParam);\r
5438       }\r
5439     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5440       PopUpMoveDialog((char)wParam);\r
5441     }\r
5442     break;\r
5443 \r
5444   case WM_PALETTECHANGED:\r
5445     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5446       int nnew;\r
5447       HDC hdc = GetDC(hwndMain);\r
5448       SelectPalette(hdc, hPal, TRUE);\r
5449       nnew = RealizePalette(hdc);\r
5450       if (nnew > 0) {\r
5451         paletteChanged = TRUE;\r
5452 #if 0\r
5453         UpdateColors(hdc);\r
5454 #else\r
5455         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5456 #endif\r
5457       }\r
5458       ReleaseDC(hwnd, hdc);\r
5459     }\r
5460     break;\r
5461 \r
5462   case WM_QUERYNEWPALETTE:\r
5463     if (!appData.monoMode /*&& paletteChanged*/) {\r
5464       int nnew;\r
5465       HDC hdc = GetDC(hwndMain);\r
5466       paletteChanged = FALSE;\r
5467       SelectPalette(hdc, hPal, FALSE);\r
5468       nnew = RealizePalette(hdc);\r
5469       if (nnew > 0) {\r
5470         InvalidateRect(hwnd, &boardRect, FALSE);\r
5471       }\r
5472       ReleaseDC(hwnd, hdc);\r
5473       return TRUE;\r
5474     }\r
5475     return FALSE;\r
5476 \r
5477   case WM_COMMAND: /* message: command from application menu */\r
5478     wmId    = LOWORD(wParam);\r
5479     wmEvent = HIWORD(wParam);\r
5480 \r
5481     switch (wmId) {\r
5482     case IDM_NewGame:\r
5483       ResetGameEvent();\r
5484       AnalysisPopDown();\r
5485       break;\r
5486 \r
5487     case IDM_NewGameFRC:\r
5488       if( NewGameFRC() == 0 ) {\r
5489         ResetGameEvent();\r
5490         AnalysisPopDown();\r
5491       }\r
5492       break;\r
5493 \r
5494     case IDM_NewVariant:\r
5495       NewVariantPopup(hwnd);\r
5496       break;\r
5497 \r
5498     case IDM_LoadGame:\r
5499       LoadGameDialog(hwnd, "Load Game from File");\r
5500       break;\r
5501 \r
5502     case IDM_LoadNextGame:\r
5503       ReloadGame(1);\r
5504       break;\r
5505 \r
5506     case IDM_LoadPrevGame:\r
5507       ReloadGame(-1);\r
5508       break;\r
5509 \r
5510     case IDM_ReloadGame:\r
5511       ReloadGame(0);\r
5512       break;\r
5513 \r
5514     case IDM_LoadPosition:\r
5515       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5516         Reset(FALSE, TRUE);\r
5517       }\r
5518       number = 1;\r
5519       f = OpenFileDialog(hwnd, "rb", "",\r
5520                          appData.oldSaveStyle ? "pos" : "fen",\r
5521                          POSITION_FILT,\r
5522                          "Load Position from File", &number, fileTitle, NULL);\r
5523       if (f != NULL) {\r
5524         LoadPosition(f, number, fileTitle);\r
5525       }\r
5526       break;\r
5527 \r
5528     case IDM_LoadNextPosition:\r
5529       ReloadPosition(1);\r
5530       break;\r
5531 \r
5532     case IDM_LoadPrevPosition:\r
5533       ReloadPosition(-1);\r
5534       break;\r
5535 \r
5536     case IDM_ReloadPosition:\r
5537       ReloadPosition(0);\r
5538       break;\r
5539 \r
5540     case IDM_SaveGame:\r
5541       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5542       f = OpenFileDialog(hwnd, "a", defName,\r
5543                          appData.oldSaveStyle ? "gam" : "pgn",\r
5544                          GAME_FILT,\r
5545                          "Save Game to File", NULL, fileTitle, NULL);\r
5546       if (f != NULL) {\r
5547         SaveGame(f, 0, "");\r
5548       }\r
5549       break;\r
5550 \r
5551     case IDM_SavePosition:\r
5552       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5553       f = OpenFileDialog(hwnd, "a", defName,\r
5554                          appData.oldSaveStyle ? "pos" : "fen",\r
5555                          POSITION_FILT,\r
5556                          "Save Position to File", NULL, fileTitle, NULL);\r
5557       if (f != NULL) {\r
5558         SavePosition(f, 0, "");\r
5559       }\r
5560       break;\r
5561 \r
5562     case IDM_SaveDiagram:\r
5563       defName = "diagram";\r
5564       f = OpenFileDialog(hwnd, "wb", defName,\r
5565                          "bmp",\r
5566                          DIAGRAM_FILT,\r
5567                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5568       if (f != NULL) {\r
5569         SaveDiagram(f);\r
5570       }\r
5571       break;\r
5572 \r
5573     case IDM_CopyGame:\r
5574       CopyGameToClipboard();\r
5575       break;\r
5576 \r
5577     case IDM_PasteGame:\r
5578       PasteGameFromClipboard();\r
5579       break;\r
5580 \r
5581     case IDM_CopyGameListToClipboard:\r
5582       CopyGameListToClipboard();\r
5583       break;\r
5584 \r
5585     /* [AS] Autodetect FEN or PGN data */\r
5586     case IDM_PasteAny:\r
5587       PasteGameOrFENFromClipboard();\r
5588       break;\r
5589 \r
5590     /* [AS] Move history */\r
5591     case IDM_ShowMoveHistory:\r
5592         if( MoveHistoryIsUp() ) {\r
5593             MoveHistoryPopDown();\r
5594         }\r
5595         else {\r
5596             MoveHistoryPopUp();\r
5597         }\r
5598         break;\r
5599 \r
5600     /* [AS] Eval graph */\r
5601     case IDM_ShowEvalGraph:\r
5602         if( EvalGraphIsUp() ) {\r
5603             EvalGraphPopDown();\r
5604         }\r
5605         else {\r
5606             EvalGraphPopUp();\r
5607         }\r
5608         break;\r
5609 \r
5610     /* [AS] Engine output */\r
5611     case IDM_ShowEngineOutput:\r
5612         if( EngineOutputIsUp() ) {\r
5613             EngineOutputPopDown();\r
5614         }\r
5615         else {\r
5616             EngineOutputPopUp();\r
5617         }\r
5618         break;\r
5619 \r
5620     /* [AS] User adjudication */\r
5621     case IDM_UserAdjudication_White:\r
5622         UserAdjudicationEvent( +1 );\r
5623         break;\r
5624 \r
5625     case IDM_UserAdjudication_Black:\r
5626         UserAdjudicationEvent( -1 );\r
5627         break;\r
5628 \r
5629     case IDM_UserAdjudication_Draw:\r
5630         UserAdjudicationEvent( 0 );\r
5631         break;\r
5632 \r
5633     /* [AS] Game list options dialog */\r
5634     case IDM_GameListOptions:\r
5635       GameListOptions();\r
5636       break;\r
5637 \r
5638     case IDM_CopyPosition:\r
5639       CopyFENToClipboard();\r
5640       break;\r
5641 \r
5642     case IDM_PastePosition:\r
5643       PasteFENFromClipboard();\r
5644       break;\r
5645 \r
5646     case IDM_MailMove:\r
5647       MailMoveEvent();\r
5648       break;\r
5649 \r
5650     case IDM_ReloadCMailMsg:\r
5651       Reset(TRUE, TRUE);\r
5652       ReloadCmailMsgEvent(FALSE);\r
5653       break;\r
5654 \r
5655     case IDM_Minimize:\r
5656       ShowWindow(hwnd, SW_MINIMIZE);\r
5657       break;\r
5658 \r
5659     case IDM_Exit:\r
5660       ExitEvent(0);\r
5661       break;\r
5662 \r
5663     case IDM_MachineWhite:\r
5664       MachineWhiteEvent();\r
5665       /*\r
5666        * refresh the tags dialog only if it's visible\r
5667        */\r
5668       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5669           char *tags;\r
5670           tags = PGNTags(&gameInfo);\r
5671           TagsPopUp(tags, CmailMsg());\r
5672           free(tags);\r
5673       }\r
5674       break;\r
5675 \r
5676     case IDM_MachineBlack:\r
5677       MachineBlackEvent();\r
5678       /*\r
5679        * refresh the tags dialog only if it's visible\r
5680        */\r
5681       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5682           char *tags;\r
5683           tags = PGNTags(&gameInfo);\r
5684           TagsPopUp(tags, CmailMsg());\r
5685           free(tags);\r
5686       }\r
5687       break;\r
5688 \r
5689     case IDM_TwoMachines:\r
5690       TwoMachinesEvent();\r
5691       /*\r
5692        * refresh the tags dialog only if it's visible\r
5693        */\r
5694       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5695           char *tags;\r
5696           tags = PGNTags(&gameInfo);\r
5697           TagsPopUp(tags, CmailMsg());\r
5698           free(tags);\r
5699       }\r
5700       break;\r
5701 \r
5702     case IDM_AnalysisMode:\r
5703       if (!first.analysisSupport) {\r
5704         sprintf(buf, "%s does not support analysis", first.tidy);\r
5705         DisplayError(buf, 0);\r
5706       } else {\r
5707         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5708         if (appData.icsActive) {\r
5709                if (gameMode != IcsObserving) {\r
5710                        sprintf(buf, "You are not observing a game");\r
5711                        DisplayError(buf, 0);\r
5712                        /* secure check */\r
5713                        if (appData.icsEngineAnalyze) {\r
5714                                if (appData.debugMode) \r
5715                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5716                                ExitAnalyzeMode();\r
5717                                ModeHighlight();\r
5718                                break;\r
5719                        }\r
5720                        break;\r
5721                } else {\r
5722                        /* if enable, user want disable icsEngineAnalyze */\r
5723                        if (appData.icsEngineAnalyze) {\r
5724                                ExitAnalyzeMode();\r
5725                                ModeHighlight();\r
5726                                break;\r
5727                        }\r
5728                        appData.icsEngineAnalyze = TRUE;\r
5729                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5730                }\r
5731         } \r
5732         if (!appData.showThinking) ToggleShowThinking();\r
5733         AnalyzeModeEvent();\r
5734       }\r
5735       break;\r
5736 \r
5737     case IDM_AnalyzeFile:\r
5738       if (!first.analysisSupport) {\r
5739         char buf[MSG_SIZ];\r
5740         sprintf(buf, "%s does not support analysis", first.tidy);\r
5741         DisplayError(buf, 0);\r
5742       } else {\r
5743         if (!appData.showThinking) ToggleShowThinking();\r
5744         AnalyzeFileEvent();\r
5745         LoadGameDialog(hwnd, "Analyze Game from File");\r
5746         AnalysisPeriodicEvent(1);\r
5747       }\r
5748       break;\r
5749 \r
5750     case IDM_IcsClient:\r
5751       IcsClientEvent();\r
5752       break;\r
5753 \r
5754     case IDM_EditGame:\r
5755       EditGameEvent();\r
5756       break;\r
5757 \r
5758     case IDM_EditPosition:\r
5759       EditPositionEvent();\r
5760       break;\r
5761 \r
5762     case IDM_Training:\r
5763       TrainingEvent();\r
5764       break;\r
5765 \r
5766     case IDM_ShowGameList:\r
5767       ShowGameListProc();\r
5768       break;\r
5769 \r
5770     case IDM_EditTags:\r
5771       EditTagsProc();\r
5772       break;\r
5773 \r
5774     case IDM_EditComment:\r
5775       if (commentDialogUp && editComment) {\r
5776         CommentPopDown();\r
5777       } else {\r
5778         EditCommentEvent();\r
5779       }\r
5780       break;\r
5781 \r
5782     case IDM_Pause:\r
5783       PauseEvent();\r
5784       break;\r
5785 \r
5786     case IDM_Accept:\r
5787       AcceptEvent();\r
5788       break;\r
5789 \r
5790     case IDM_Decline:\r
5791       DeclineEvent();\r
5792       break;\r
5793 \r
5794     case IDM_Rematch:\r
5795       RematchEvent();\r
5796       break;\r
5797 \r
5798     case IDM_CallFlag:\r
5799       CallFlagEvent();\r
5800       break;\r
5801 \r
5802     case IDM_Draw:\r
5803       DrawEvent();\r
5804       break;\r
5805 \r
5806     case IDM_Adjourn:\r
5807       AdjournEvent();\r
5808       break;\r
5809 \r
5810     case IDM_Abort:\r
5811       AbortEvent();\r
5812       break;\r
5813 \r
5814     case IDM_Resign:\r
5815       ResignEvent();\r
5816       break;\r
5817 \r
5818     case IDM_StopObserving:\r
5819       StopObservingEvent();\r
5820       break;\r
5821 \r
5822     case IDM_StopExamining:\r
5823       StopExaminingEvent();\r
5824       break;\r
5825 \r
5826     case IDM_TypeInMove:\r
5827       PopUpMoveDialog('\000');\r
5828       break;\r
5829 \r
5830     case IDM_TypeInName:\r
5831       PopUpNameDialog('\000');\r
5832       break;\r
5833 \r
5834     case IDM_Backward:\r
5835       BackwardEvent();\r
5836       SetFocus(hwndMain);\r
5837       break;\r
5838 \r
5839     case IDM_Forward:\r
5840       ForwardEvent();\r
5841       SetFocus(hwndMain);\r
5842       break;\r
5843 \r
5844     case IDM_ToStart:\r
5845       ToStartEvent();\r
5846       SetFocus(hwndMain);\r
5847       break;\r
5848 \r
5849     case IDM_ToEnd:\r
5850       ToEndEvent();\r
5851       SetFocus(hwndMain);\r
5852       break;\r
5853 \r
5854     case IDM_Revert:\r
5855       RevertEvent();\r
5856       break;\r
5857 \r
5858     case IDM_TruncateGame:\r
5859       TruncateGameEvent();\r
5860       break;\r
5861 \r
5862     case IDM_MoveNow:\r
5863       MoveNowEvent();\r
5864       break;\r
5865 \r
5866     case IDM_RetractMove:\r
5867       RetractMoveEvent();\r
5868       break;\r
5869 \r
5870     case IDM_FlipView:\r
5871       flipView = !flipView;\r
5872       DrawPosition(FALSE, NULL);\r
5873       break;\r
5874 \r
5875     case IDM_FlipClock:\r
5876       flipClock = !flipClock;\r
5877       DisplayBothClocks();\r
5878       break;\r
5879 \r
5880     case IDM_GeneralOptions:\r
5881       GeneralOptionsPopup(hwnd);\r
5882       DrawPosition(TRUE, NULL);\r
5883       break;\r
5884 \r
5885     case IDM_BoardOptions:\r
5886       BoardOptionsPopup(hwnd);\r
5887       break;\r
5888 \r
5889     case IDM_EnginePlayOptions:\r
5890       EnginePlayOptionsPopup(hwnd);\r
5891       break;\r
5892 \r
5893     case IDM_OptionsUCI:\r
5894       UciOptionsPopup(hwnd);\r
5895       break;\r
5896 \r
5897     case IDM_IcsOptions:\r
5898       IcsOptionsPopup(hwnd);\r
5899       break;\r
5900 \r
5901     case IDM_Fonts:\r
5902       FontsOptionsPopup(hwnd);\r
5903       break;\r
5904 \r
5905     case IDM_Sounds:\r
5906       SoundOptionsPopup(hwnd);\r
5907       break;\r
5908 \r
5909     case IDM_CommPort:\r
5910       CommPortOptionsPopup(hwnd);\r
5911       break;\r
5912 \r
5913     case IDM_LoadOptions:\r
5914       LoadOptionsPopup(hwnd);\r
5915       break;\r
5916 \r
5917     case IDM_SaveOptions:\r
5918       SaveOptionsPopup(hwnd);\r
5919       break;\r
5920 \r
5921     case IDM_TimeControl:\r
5922       TimeControlOptionsPopup(hwnd);\r
5923       break;\r
5924 \r
5925     case IDM_SaveSettings:\r
5926       SaveSettings(settingsFileName);\r
5927       break;\r
5928 \r
5929     case IDM_SaveSettingsOnExit:\r
5930       saveSettingsOnExit = !saveSettingsOnExit;\r
5931       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5932                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5933                                          MF_CHECKED : MF_UNCHECKED));\r
5934       break;\r
5935 \r
5936     case IDM_Hint:\r
5937       HintEvent();\r
5938       break;\r
5939 \r
5940     case IDM_Book:\r
5941       BookEvent();\r
5942       break;\r
5943 \r
5944     case IDM_AboutGame:\r
5945       AboutGameEvent();\r
5946       break;\r
5947 \r
5948     case IDM_Debug:\r
5949       appData.debugMode = !appData.debugMode;\r
5950       if (appData.debugMode) {\r
5951         char dir[MSG_SIZ];\r
5952         GetCurrentDirectory(MSG_SIZ, dir);\r
5953         SetCurrentDirectory(installDir);\r
5954         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5955         SetCurrentDirectory(dir);\r
5956         setbuf(debugFP, NULL);\r
5957       } else {\r
5958         fclose(debugFP);\r
5959         debugFP = NULL;\r
5960       }\r
5961       break;\r
5962 \r
5963     case IDM_HELPCONTENTS:\r
5964       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5965         MessageBox (GetFocus(),\r
5966                     "Unable to activate help",\r
5967                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5968       }\r
5969       break;\r
5970 \r
5971     case IDM_HELPSEARCH:\r
5972       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5973         MessageBox (GetFocus(),\r
5974                     "Unable to activate help",\r
5975                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5976       }\r
5977       break;\r
5978 \r
5979     case IDM_HELPHELP:\r
5980       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5981         MessageBox (GetFocus(),\r
5982                     "Unable to activate help",\r
5983                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5984       }\r
5985       break;\r
5986 \r
5987     case IDM_ABOUT:\r
5988       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5989       DialogBox(hInst, \r
5990         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5991         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5992       FreeProcInstance(lpProc);\r
5993       break;\r
5994 \r
5995     case IDM_DirectCommand1:\r
5996       AskQuestionEvent("Direct Command",\r
5997                        "Send to chess program:", "", "1");\r
5998       break;\r
5999     case IDM_DirectCommand2:\r
6000       AskQuestionEvent("Direct Command",\r
6001                        "Send to second chess program:", "", "2");\r
6002       break;\r
6003 \r
6004     case EP_WhitePawn:\r
6005       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6006       fromX = fromY = -1;\r
6007       break;\r
6008 \r
6009     case EP_WhiteKnight:\r
6010       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6011       fromX = fromY = -1;\r
6012       break;\r
6013 \r
6014     case EP_WhiteBishop:\r
6015       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6016       fromX = fromY = -1;\r
6017       break;\r
6018 \r
6019     case EP_WhiteRook:\r
6020       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6021       fromX = fromY = -1;\r
6022       break;\r
6023 \r
6024     case EP_WhiteQueen:\r
6025       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6026       fromX = fromY = -1;\r
6027       break;\r
6028 \r
6029     case EP_WhiteFerz:\r
6030       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6031       fromX = fromY = -1;\r
6032       break;\r
6033 \r
6034     case EP_WhiteWazir:\r
6035       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6036       fromX = fromY = -1;\r
6037       break;\r
6038 \r
6039     case EP_WhiteAlfil:\r
6040       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6041       fromX = fromY = -1;\r
6042       break;\r
6043 \r
6044     case EP_WhiteCannon:\r
6045       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6046       fromX = fromY = -1;\r
6047       break;\r
6048 \r
6049     case EP_WhiteCardinal:\r
6050       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6051       fromX = fromY = -1;\r
6052       break;\r
6053 \r
6054     case EP_WhiteMarshall:\r
6055       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6056       fromX = fromY = -1;\r
6057       break;\r
6058 \r
6059     case EP_WhiteKing:\r
6060       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6061       fromX = fromY = -1;\r
6062       break;\r
6063 \r
6064     case EP_BlackPawn:\r
6065       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6066       fromX = fromY = -1;\r
6067       break;\r
6068 \r
6069     case EP_BlackKnight:\r
6070       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6071       fromX = fromY = -1;\r
6072       break;\r
6073 \r
6074     case EP_BlackBishop:\r
6075       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6076       fromX = fromY = -1;\r
6077       break;\r
6078 \r
6079     case EP_BlackRook:\r
6080       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6081       fromX = fromY = -1;\r
6082       break;\r
6083 \r
6084     case EP_BlackQueen:\r
6085       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6086       fromX = fromY = -1;\r
6087       break;\r
6088 \r
6089     case EP_BlackFerz:\r
6090       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6091       fromX = fromY = -1;\r
6092       break;\r
6093 \r
6094     case EP_BlackWazir:\r
6095       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6096       fromX = fromY = -1;\r
6097       break;\r
6098 \r
6099     case EP_BlackAlfil:\r
6100       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6101       fromX = fromY = -1;\r
6102       break;\r
6103 \r
6104     case EP_BlackCannon:\r
6105       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6106       fromX = fromY = -1;\r
6107       break;\r
6108 \r
6109     case EP_BlackCardinal:\r
6110       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6111       fromX = fromY = -1;\r
6112       break;\r
6113 \r
6114     case EP_BlackMarshall:\r
6115       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6116       fromX = fromY = -1;\r
6117       break;\r
6118 \r
6119     case EP_BlackKing:\r
6120       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6121       fromX = fromY = -1;\r
6122       break;\r
6123 \r
6124     case EP_EmptySquare:\r
6125       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6126       fromX = fromY = -1;\r
6127       break;\r
6128 \r
6129     case EP_ClearBoard:\r
6130       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6131       fromX = fromY = -1;\r
6132       break;\r
6133 \r
6134     case EP_White:\r
6135       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6136       fromX = fromY = -1;\r
6137       break;\r
6138 \r
6139     case EP_Black:\r
6140       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6141       fromX = fromY = -1;\r
6142       break;\r
6143 \r
6144     case EP_Promote:\r
6145       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6146       fromX = fromY = -1;\r
6147       break;\r
6148 \r
6149     case EP_Demote:\r
6150       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6151       fromX = fromY = -1;\r
6152       break;\r
6153 \r
6154     case DP_Pawn:\r
6155       DropMenuEvent(WhitePawn, fromX, fromY);\r
6156       fromX = fromY = -1;\r
6157       break;\r
6158 \r
6159     case DP_Knight:\r
6160       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6161       fromX = fromY = -1;\r
6162       break;\r
6163 \r
6164     case DP_Bishop:\r
6165       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6166       fromX = fromY = -1;\r
6167       break;\r
6168 \r
6169     case DP_Rook:\r
6170       DropMenuEvent(WhiteRook, fromX, fromY);\r
6171       fromX = fromY = -1;\r
6172       break;\r
6173 \r
6174     case DP_Queen:\r
6175       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6176       fromX = fromY = -1;\r
6177       break;\r
6178 \r
6179     default:\r
6180       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6181     }\r
6182     break;\r
6183 \r
6184   case WM_TIMER:\r
6185     switch (wParam) {\r
6186     case CLOCK_TIMER_ID:\r
6187       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6188       clockTimerEvent = 0;\r
6189       DecrementClocks(); /* call into back end */\r
6190       break;\r
6191     case LOAD_GAME_TIMER_ID:\r
6192       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6193       loadGameTimerEvent = 0;\r
6194       AutoPlayGameLoop(); /* call into back end */\r
6195       break;\r
6196     case ANALYSIS_TIMER_ID:\r
6197       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6198                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6199         AnalysisPeriodicEvent(0);\r
6200       } else {\r
6201         KillTimer(hwnd, analysisTimerEvent);\r
6202         analysisTimerEvent = 0;\r
6203       }\r
6204       break;\r
6205     case DELAYED_TIMER_ID:\r
6206       KillTimer(hwnd, delayedTimerEvent);\r
6207       delayedTimerEvent = 0;\r
6208       delayedTimerCallback();\r
6209       break;\r
6210     }\r
6211     break;\r
6212 \r
6213   case WM_USER_Input:\r
6214     InputEvent(hwnd, message, wParam, lParam);\r
6215     break;\r
6216 \r
6217   /* [AS] Also move "attached" child windows */\r
6218   case WM_WINDOWPOSCHANGING:\r
6219     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6220         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6221 \r
6222         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6223             /* Window is moving */\r
6224             RECT rcMain;\r
6225 \r
6226             GetWindowRect( hwnd, &rcMain );\r
6227             \r
6228             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6229             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6230             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6231         }\r
6232     }\r
6233     break;\r
6234 \r
6235   /* [AS] Snapping */\r
6236   case WM_ENTERSIZEMOVE:\r
6237     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6238     if (hwnd == hwndMain) {\r
6239       doingSizing = TRUE;\r
6240       lastSizing = 0;\r
6241     }\r
6242     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6243     break;\r
6244 \r
6245   case WM_SIZING:\r
6246     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6247     if (hwnd == hwndMain) {\r
6248       lastSizing = wParam;\r
6249     }\r
6250     break;\r
6251 \r
6252   case WM_MOVING:\r
6253     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6254       return OnMoving( &sd, hwnd, wParam, lParam );\r
6255 \r
6256   case WM_EXITSIZEMOVE:\r
6257     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6258     if (hwnd == hwndMain) {\r
6259       RECT client;\r
6260       doingSizing = FALSE;\r
6261       InvalidateRect(hwnd, &boardRect, FALSE);\r
6262       GetClientRect(hwnd, &client);\r
6263       ResizeBoard(client.right, client.bottom, lastSizing);\r
6264       lastSizing = 0;\r
6265       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6266     }\r
6267     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6268     break;\r
6269 \r
6270   case WM_DESTROY: /* message: window being destroyed */\r
6271     PostQuitMessage(0);\r
6272     break;\r
6273 \r
6274   case WM_CLOSE:\r
6275     if (hwnd == hwndMain) {\r
6276       ExitEvent(0);\r
6277     }\r
6278     break;\r
6279 \r
6280   default:      /* Passes it on if unprocessed */\r
6281     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6282   }\r
6283   return 0;\r
6284 }\r
6285 \r
6286 /*---------------------------------------------------------------------------*\\r
6287  *\r
6288  * Misc utility routines\r
6289  *\r
6290 \*---------------------------------------------------------------------------*/\r
6291 \r
6292 /*\r
6293  * Decent random number generator, at least not as bad as Windows\r
6294  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6295  */\r
6296 unsigned int randstate;\r
6297 \r
6298 int\r
6299 myrandom(void)\r
6300 {\r
6301   randstate = randstate * 1664525 + 1013904223;\r
6302   return (int) randstate & 0x7fffffff;\r
6303 }\r
6304 \r
6305 void\r
6306 mysrandom(unsigned int seed)\r
6307 {\r
6308   randstate = seed;\r
6309 }\r
6310 \r
6311 \r
6312 /* \r
6313  * returns TRUE if user selects a different color, FALSE otherwise \r
6314  */\r
6315 \r
6316 BOOL\r
6317 ChangeColor(HWND hwnd, COLORREF *which)\r
6318 {\r
6319   static BOOL firstTime = TRUE;\r
6320   static DWORD customColors[16];\r
6321   CHOOSECOLOR cc;\r
6322   COLORREF newcolor;\r
6323   int i;\r
6324   ColorClass ccl;\r
6325 \r
6326   if (firstTime) {\r
6327     /* Make initial colors in use available as custom colors */\r
6328     /* Should we put the compiled-in defaults here instead? */\r
6329     i = 0;\r
6330     customColors[i++] = lightSquareColor & 0xffffff;\r
6331     customColors[i++] = darkSquareColor & 0xffffff;\r
6332     customColors[i++] = whitePieceColor & 0xffffff;\r
6333     customColors[i++] = blackPieceColor & 0xffffff;\r
6334     customColors[i++] = highlightSquareColor & 0xffffff;\r
6335     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6336 \r
6337     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6338       customColors[i++] = textAttribs[ccl].color;\r
6339     }\r
6340     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6341     firstTime = FALSE;\r
6342   }\r
6343 \r
6344   cc.lStructSize = sizeof(cc);\r
6345   cc.hwndOwner = hwnd;\r
6346   cc.hInstance = NULL;\r
6347   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6348   cc.lpCustColors = (LPDWORD) customColors;\r
6349   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6350 \r
6351   if (!ChooseColor(&cc)) return FALSE;\r
6352 \r
6353   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6354   if (newcolor == *which) return FALSE;\r
6355   *which = newcolor;\r
6356   return TRUE;\r
6357 \r
6358   /*\r
6359   InitDrawingColors();\r
6360   InvalidateRect(hwnd, &boardRect, FALSE);\r
6361   */\r
6362 }\r
6363 \r
6364 BOOLEAN\r
6365 MyLoadSound(MySound *ms)\r
6366 {\r
6367   BOOL ok = FALSE;\r
6368   struct stat st;\r
6369   FILE *f;\r
6370 \r
6371   if (ms->data) free(ms->data);\r
6372   ms->data = NULL;\r
6373 \r
6374   switch (ms->name[0]) {\r
6375   case NULLCHAR:\r
6376     /* Silence */\r
6377     ok = TRUE;\r
6378     break;\r
6379   case '$':\r
6380     /* System sound from Control Panel.  Don't preload here. */\r
6381     ok = TRUE;\r
6382     break;\r
6383   case '!':\r
6384     if (ms->name[1] == NULLCHAR) {\r
6385       /* "!" alone = silence */\r
6386       ok = TRUE;\r
6387     } else {\r
6388       /* Builtin wave resource.  Error if not found. */\r
6389       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6390       if (h == NULL) break;\r
6391       ms->data = (void *)LoadResource(hInst, h);\r
6392       if (h == NULL) break;\r
6393       ok = TRUE;\r
6394     }\r
6395     break;\r
6396   default:\r
6397     /* .wav file.  Error if not found. */\r
6398     f = fopen(ms->name, "rb");\r
6399     if (f == NULL) break;\r
6400     if (fstat(fileno(f), &st) < 0) break;\r
6401     ms->data = malloc(st.st_size);\r
6402     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6403     fclose(f);\r
6404     ok = TRUE;\r
6405     break;\r
6406   }\r
6407   if (!ok) {\r
6408     char buf[MSG_SIZ];\r
6409     sprintf(buf, "Error loading sound %s", ms->name);\r
6410     DisplayError(buf, GetLastError());\r
6411   }\r
6412   return ok;\r
6413 }\r
6414 \r
6415 BOOLEAN\r
6416 MyPlaySound(MySound *ms)\r
6417 {\r
6418   BOOLEAN ok = FALSE;\r
6419   switch (ms->name[0]) {\r
6420   case NULLCHAR:\r
6421     /* Silence */\r
6422     ok = TRUE;\r
6423     break;\r
6424   case '$':\r
6425     /* System sound from Control Panel (deprecated feature).\r
6426        "$" alone or an unset sound name gets default beep (still in use). */\r
6427     if (ms->name[1]) {\r
6428       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6429     }\r
6430     if (!ok) ok = MessageBeep(MB_OK);\r
6431     break; \r
6432   case '!':\r
6433     /* Builtin wave resource, or "!" alone for silence */\r
6434     if (ms->name[1]) {\r
6435       if (ms->data == NULL) return FALSE;\r
6436       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6437     } else {\r
6438       ok = TRUE;\r
6439     }\r
6440     break;\r
6441   default:\r
6442     /* .wav file.  Error if not found. */\r
6443     if (ms->data == NULL) return FALSE;\r
6444     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6445     break;\r
6446   }\r
6447   /* Don't print an error: this can happen innocently if the sound driver\r
6448      is busy; for instance, if another instance of WinBoard is playing\r
6449      a sound at about the same time. */\r
6450 #if 0\r
6451   if (!ok) {\r
6452     char buf[MSG_SIZ];\r
6453     sprintf(buf, "Error playing sound %s", ms->name);\r
6454     DisplayError(buf, GetLastError());\r
6455   }\r
6456 #endif\r
6457   return ok;\r
6458 }\r
6459 \r
6460 \r
6461 LRESULT CALLBACK\r
6462 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6463 {\r
6464   BOOL ok;\r
6465   OPENFILENAME *ofn;\r
6466   static UINT *number; /* gross that this is static */\r
6467 \r
6468   switch (message) {\r
6469   case WM_INITDIALOG: /* message: initialize dialog box */\r
6470     /* Center the dialog over the application window */\r
6471     ofn = (OPENFILENAME *) lParam;\r
6472     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6473       number = (UINT *) ofn->lCustData;\r
6474       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6475     } else {\r
6476       number = NULL;\r
6477     }\r
6478     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6479     return FALSE;  /* Allow for further processing */\r
6480 \r
6481   case WM_COMMAND:\r
6482     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6483       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6484     }\r
6485     return FALSE;  /* Allow for further processing */\r
6486   }\r
6487   return FALSE;\r
6488 }\r
6489 \r
6490 UINT APIENTRY\r
6491 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6492 {\r
6493   static UINT *number;\r
6494   OPENFILENAME *ofname;\r
6495   OFNOTIFY *ofnot;\r
6496   switch (uiMsg) {\r
6497   case WM_INITDIALOG:\r
6498     ofname = (OPENFILENAME *)lParam;\r
6499     number = (UINT *)(ofname->lCustData);\r
6500     break;\r
6501   case WM_NOTIFY:\r
6502     ofnot = (OFNOTIFY *)lParam;\r
6503     if (ofnot->hdr.code == CDN_FILEOK) {\r
6504       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6505     }\r
6506     break;\r
6507   }\r
6508   return 0;\r
6509 }\r
6510 \r
6511 \r
6512 FILE *\r
6513 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6514                char *nameFilt, char *dlgTitle, UINT *number,\r
6515                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6516 {\r
6517   OPENFILENAME openFileName;\r
6518   char buf1[MSG_SIZ];\r
6519   FILE *f;\r
6520 \r
6521   if (fileName == NULL) fileName = buf1;\r
6522   if (defName == NULL) {\r
6523     strcpy(fileName, "*.");\r
6524     strcat(fileName, defExt);\r
6525   } else {\r
6526     strcpy(fileName, defName);\r
6527   }\r
6528   if (fileTitle) strcpy(fileTitle, "");\r
6529   if (number) *number = 0;\r
6530 \r
6531   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6532   openFileName.hwndOwner         = hwnd;\r
6533   openFileName.hInstance         = (HANDLE) hInst;\r
6534   openFileName.lpstrFilter       = nameFilt;\r
6535   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6536   openFileName.nMaxCustFilter    = 0L;\r
6537   openFileName.nFilterIndex      = 1L;\r
6538   openFileName.lpstrFile         = fileName;\r
6539   openFileName.nMaxFile          = MSG_SIZ;\r
6540   openFileName.lpstrFileTitle    = fileTitle;\r
6541   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6542   openFileName.lpstrInitialDir   = NULL;\r
6543   openFileName.lpstrTitle        = dlgTitle;\r
6544   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6545     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6546     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6547     | (oldDialog ? 0 : OFN_EXPLORER);\r
6548   openFileName.nFileOffset       = 0;\r
6549   openFileName.nFileExtension    = 0;\r
6550   openFileName.lpstrDefExt       = defExt;\r
6551   openFileName.lCustData         = (LONG) number;\r
6552   openFileName.lpfnHook          = oldDialog ?\r
6553     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6554   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6555 \r
6556   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6557                         GetOpenFileName(&openFileName)) {\r
6558     /* open the file */\r
6559     f = fopen(openFileName.lpstrFile, write);\r
6560     if (f == NULL) {\r
6561       MessageBox(hwnd, "File open failed", NULL,\r
6562                  MB_OK|MB_ICONEXCLAMATION);\r
6563       return NULL;\r
6564     }\r
6565   } else {\r
6566     int err = CommDlgExtendedError();\r
6567     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6568     return FALSE;\r
6569   }\r
6570   return f;\r
6571 }\r
6572 \r
6573 \r
6574 \r
6575 VOID APIENTRY\r
6576 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6577 {\r
6578   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6579 \r
6580   /*\r
6581    * Get the first pop-up menu in the menu template. This is the\r
6582    * menu that TrackPopupMenu displays.\r
6583    */\r
6584   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6585 \r
6586   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6587 \r
6588   /*\r
6589    * TrackPopup uses screen coordinates, so convert the\r
6590    * coordinates of the mouse click to screen coordinates.\r
6591    */\r
6592   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6593 \r
6594   /* Draw and track the floating pop-up menu. */\r
6595   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6596                  pt.x, pt.y, 0, hwnd, NULL);\r
6597 \r
6598   /* Destroy the menu.*/\r
6599   DestroyMenu(hmenu);\r
6600 }\r
6601    \r
6602 typedef struct {\r
6603   HWND hDlg, hText;\r
6604   int sizeX, sizeY, newSizeX, newSizeY;\r
6605   HDWP hdwp;\r
6606 } ResizeEditPlusButtonsClosure;\r
6607 \r
6608 BOOL CALLBACK\r
6609 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6610 {\r
6611   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6612   RECT rect;\r
6613   POINT pt;\r
6614 \r
6615   if (hChild == cl->hText) return TRUE;\r
6616   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6617   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6618   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6619   ScreenToClient(cl->hDlg, &pt);\r
6620   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6621     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6622   return TRUE;\r
6623 }\r
6624 \r
6625 /* Resize a dialog that has a (rich) edit field filling most of\r
6626    the top, with a row of buttons below */\r
6627 VOID\r
6628 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6629 {\r
6630   RECT rectText;\r
6631   int newTextHeight, newTextWidth;\r
6632   ResizeEditPlusButtonsClosure cl;\r
6633   \r
6634   /*if (IsIconic(hDlg)) return;*/\r
6635   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6636   \r
6637   cl.hdwp = BeginDeferWindowPos(8);\r
6638 \r
6639   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6640   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6641   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6642   if (newTextHeight < 0) {\r
6643     newSizeY += -newTextHeight;\r
6644     newTextHeight = 0;\r
6645   }\r
6646   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6647     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6648 \r
6649   cl.hDlg = hDlg;\r
6650   cl.hText = hText;\r
6651   cl.sizeX = sizeX;\r
6652   cl.sizeY = sizeY;\r
6653   cl.newSizeX = newSizeX;\r
6654   cl.newSizeY = newSizeY;\r
6655   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6656 \r
6657   EndDeferWindowPos(cl.hdwp);\r
6658 }\r
6659 \r
6660 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6661 {\r
6662     RECT    rChild, rParent;\r
6663     int     wChild, hChild, wParent, hParent;\r
6664     int     wScreen, hScreen, xNew, yNew;\r
6665     HDC     hdc;\r
6666 \r
6667     /* Get the Height and Width of the child window */\r
6668     GetWindowRect (hwndChild, &rChild);\r
6669     wChild = rChild.right - rChild.left;\r
6670     hChild = rChild.bottom - rChild.top;\r
6671 \r
6672     /* Get the Height and Width of the parent window */\r
6673     GetWindowRect (hwndParent, &rParent);\r
6674     wParent = rParent.right - rParent.left;\r
6675     hParent = rParent.bottom - rParent.top;\r
6676 \r
6677     /* Get the display limits */\r
6678     hdc = GetDC (hwndChild);\r
6679     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6680     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6681     ReleaseDC(hwndChild, hdc);\r
6682 \r
6683     /* Calculate new X position, then adjust for screen */\r
6684     xNew = rParent.left + ((wParent - wChild) /2);\r
6685     if (xNew < 0) {\r
6686         xNew = 0;\r
6687     } else if ((xNew+wChild) > wScreen) {\r
6688         xNew = wScreen - wChild;\r
6689     }\r
6690 \r
6691     /* Calculate new Y position, then adjust for screen */\r
6692     if( mode == 0 ) {\r
6693         yNew = rParent.top  + ((hParent - hChild) /2);\r
6694     }\r
6695     else {\r
6696         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6697     }\r
6698 \r
6699     if (yNew < 0) {\r
6700         yNew = 0;\r
6701     } else if ((yNew+hChild) > hScreen) {\r
6702         yNew = hScreen - hChild;\r
6703     }\r
6704 \r
6705     /* Set it, and return */\r
6706     return SetWindowPos (hwndChild, NULL,\r
6707                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6708 }\r
6709 \r
6710 /* Center one window over another */\r
6711 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6712 {\r
6713     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6714 }\r
6715 \r
6716 /*---------------------------------------------------------------------------*\\r
6717  *\r
6718  * Startup Dialog functions\r
6719  *\r
6720 \*---------------------------------------------------------------------------*/\r
6721 void\r
6722 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6723 {\r
6724   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6725 \r
6726   while (*cd != NULL) {\r
6727     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6728     cd++;\r
6729   }\r
6730 }\r
6731 \r
6732 void\r
6733 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6734 {\r
6735   char buf1[ARG_MAX];\r
6736   int len;\r
6737 \r
6738   if (str[0] == '@') {\r
6739     FILE* f = fopen(str + 1, "r");\r
6740     if (f == NULL) {\r
6741       DisplayFatalError(str + 1, errno, 2);\r
6742       return;\r
6743     }\r
6744     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6745     fclose(f);\r
6746     buf1[len] = NULLCHAR;\r
6747     str = buf1;\r
6748   }\r
6749 \r
6750   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6751 \r
6752   for (;;) {\r
6753     char buf[MSG_SIZ];\r
6754     char *end = strchr(str, '\n');\r
6755     if (end == NULL) return;\r
6756     memcpy(buf, str, end - str);\r
6757     buf[end - str] = NULLCHAR;\r
6758     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6759     str = end + 1;\r
6760   }\r
6761 }\r
6762 \r
6763 void\r
6764 SetStartupDialogEnables(HWND hDlg)\r
6765 {\r
6766   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6767     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6768     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6769   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6770     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6771   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6772     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6773   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6774     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6775   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6776     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6777     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6778     IsDlgButtonChecked(hDlg, OPT_View));\r
6779 }\r
6780 \r
6781 char *\r
6782 QuoteForFilename(char *filename)\r
6783 {\r
6784   int dquote, space;\r
6785   dquote = strchr(filename, '"') != NULL;\r
6786   space = strchr(filename, ' ') != NULL;\r
6787   if (dquote || space) {\r
6788     if (dquote) {\r
6789       return "'";\r
6790     } else {\r
6791       return "\"";\r
6792     }\r
6793   } else {\r
6794     return "";\r
6795   }\r
6796 }\r
6797 \r
6798 VOID\r
6799 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6800 {\r
6801   char buf[MSG_SIZ];\r
6802   char *q;\r
6803 \r
6804   InitComboStringsFromOption(hwndCombo, nthnames);\r
6805   q = QuoteForFilename(nthcp);\r
6806   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6807   if (*nthdir != NULLCHAR) {\r
6808     q = QuoteForFilename(nthdir);\r
6809     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6810   }\r
6811   if (*nthcp == NULLCHAR) {\r
6812     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6813   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6814     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6815     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6816   }\r
6817 }\r
6818 \r
6819 LRESULT CALLBACK\r
6820 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6821 {\r
6822   char buf[MSG_SIZ];\r
6823   HANDLE hwndCombo;\r
6824   char *p;\r
6825 \r
6826   switch (message) {\r
6827   case WM_INITDIALOG:\r
6828     /* Center the dialog */\r
6829     CenterWindow (hDlg, GetDesktopWindow());\r
6830     /* Initialize the dialog items */\r
6831     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6832                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6833                   firstChessProgramNames);\r
6834     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6835                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6836                   secondChessProgramNames);\r
6837     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6838     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6839     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6840     if (*appData.icsHelper != NULLCHAR) {\r
6841       char *q = QuoteForFilename(appData.icsHelper);\r
6842       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6843     }\r
6844     if (*appData.icsHost == NULLCHAR) {\r
6845       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6846       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6847     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6848       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6849       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6850     }\r
6851 \r
6852     if (appData.icsActive) {\r
6853       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6854     }\r
6855     else if (appData.noChessProgram) {\r
6856       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6857     }\r
6858     else {\r
6859       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6860     }\r
6861 \r
6862     SetStartupDialogEnables(hDlg);\r
6863     return TRUE;\r
6864 \r
6865   case WM_COMMAND:\r
6866     switch (LOWORD(wParam)) {\r
6867     case IDOK:\r
6868       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6869         strcpy(buf, "/fcp=");\r
6870         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6871         p = buf;\r
6872         ParseArgs(StringGet, &p);\r
6873         strcpy(buf, "/scp=");\r
6874         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6875         p = buf;\r
6876         ParseArgs(StringGet, &p);\r
6877         appData.noChessProgram = FALSE;\r
6878         appData.icsActive = FALSE;\r
6879       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6880         strcpy(buf, "/ics /icshost=");\r
6881         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6882         p = buf;\r
6883         ParseArgs(StringGet, &p);\r
6884         if (appData.zippyPlay) {\r
6885           strcpy(buf, "/fcp=");\r
6886           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6887           p = buf;\r
6888           ParseArgs(StringGet, &p);\r
6889         }\r
6890       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6891         appData.noChessProgram = TRUE;\r
6892         appData.icsActive = FALSE;\r
6893       } else {\r
6894         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6895                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6896         return TRUE;\r
6897       }\r
6898       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6899         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6900         p = buf;\r
6901         ParseArgs(StringGet, &p);\r
6902       }\r
6903       EndDialog(hDlg, TRUE);\r
6904       return TRUE;\r
6905 \r
6906     case IDCANCEL:\r
6907       ExitEvent(0);\r
6908       return TRUE;\r
6909 \r
6910     case IDM_HELPCONTENTS:\r
6911       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6912         MessageBox (GetFocus(),\r
6913                     "Unable to activate help",\r
6914                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6915       }\r
6916       break;\r
6917 \r
6918     default:\r
6919       SetStartupDialogEnables(hDlg);\r
6920       break;\r
6921     }\r
6922     break;\r
6923   }\r
6924   return FALSE;\r
6925 }\r
6926 \r
6927 /*---------------------------------------------------------------------------*\\r
6928  *\r
6929  * About box dialog functions\r
6930  *\r
6931 \*---------------------------------------------------------------------------*/\r
6932 \r
6933 /* Process messages for "About" dialog box */\r
6934 LRESULT CALLBACK\r
6935 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6936 {\r
6937   switch (message) {\r
6938   case WM_INITDIALOG: /* message: initialize dialog box */\r
6939     /* Center the dialog over the application window */\r
6940     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6941     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6942     return (TRUE);\r
6943 \r
6944   case WM_COMMAND: /* message: received a command */\r
6945     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6946         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6947       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6948       return (TRUE);\r
6949     }\r
6950     break;\r
6951   }\r
6952   return (FALSE);\r
6953 }\r
6954 \r
6955 /*---------------------------------------------------------------------------*\\r
6956  *\r
6957  * Comment Dialog functions\r
6958  *\r
6959 \*---------------------------------------------------------------------------*/\r
6960 \r
6961 LRESULT CALLBACK\r
6962 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6963 {\r
6964   static HANDLE hwndText = NULL;\r
6965   int len, newSizeX, newSizeY, flags;\r
6966   static int sizeX, sizeY;\r
6967   char *str;\r
6968   RECT rect;\r
6969   MINMAXINFO *mmi;\r
6970 \r
6971   switch (message) {\r
6972   case WM_INITDIALOG: /* message: initialize dialog box */\r
6973     /* Initialize the dialog items */\r
6974     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6975     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6976     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6977     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6978     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6979     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6980     SetWindowText(hDlg, commentTitle);\r
6981     if (editComment) {\r
6982       SetFocus(hwndText);\r
6983     } else {\r
6984       SetFocus(GetDlgItem(hDlg, IDOK));\r
6985     }\r
6986     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6987                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6988                 MAKELPARAM(FALSE, 0));\r
6989     /* Size and position the dialog */\r
6990     if (!commentDialog) {\r
6991       commentDialog = hDlg;\r
6992       flags = SWP_NOZORDER;\r
6993       GetClientRect(hDlg, &rect);\r
6994       sizeX = rect.right;\r
6995       sizeY = rect.bottom;\r
6996       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6997           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6998         WINDOWPLACEMENT wp;\r
6999         EnsureOnScreen(&commentX, &commentY);\r
7000         wp.length = sizeof(WINDOWPLACEMENT);\r
7001         wp.flags = 0;\r
7002         wp.showCmd = SW_SHOW;\r
7003         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7004         wp.rcNormalPosition.left = commentX;\r
7005         wp.rcNormalPosition.right = commentX + commentW;\r
7006         wp.rcNormalPosition.top = commentY;\r
7007         wp.rcNormalPosition.bottom = commentY + commentH;\r
7008         SetWindowPlacement(hDlg, &wp);\r
7009 \r
7010         GetClientRect(hDlg, &rect);\r
7011         newSizeX = rect.right;\r
7012         newSizeY = rect.bottom;\r
7013         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7014                               newSizeX, newSizeY);\r
7015         sizeX = newSizeX;\r
7016         sizeY = newSizeY;\r
7017       }\r
7018     }\r
7019     return FALSE;\r
7020 \r
7021   case WM_COMMAND: /* message: received a command */\r
7022     switch (LOWORD(wParam)) {\r
7023     case IDOK:\r
7024       if (editComment) {\r
7025         char *p, *q;\r
7026         /* Read changed options from the dialog box */\r
7027         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7028         len = GetWindowTextLength(hwndText);\r
7029         str = (char *) malloc(len + 1);\r
7030         GetWindowText(hwndText, str, len + 1);\r
7031         p = q = str;\r
7032         while (*q) {\r
7033           if (*q == '\r')\r
7034             q++;\r
7035           else\r
7036             *p++ = *q++;\r
7037         }\r
7038         *p = NULLCHAR;\r
7039         ReplaceComment(commentIndex, str);\r
7040         free(str);\r
7041       }\r
7042       CommentPopDown();\r
7043       return TRUE;\r
7044 \r
7045     case IDCANCEL:\r
7046     case OPT_CancelComment:\r
7047       CommentPopDown();\r
7048       return TRUE;\r
7049 \r
7050     case OPT_ClearComment:\r
7051       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7052       break;\r
7053 \r
7054     case OPT_EditComment:\r
7055       EditCommentEvent();\r
7056       return TRUE;\r
7057 \r
7058     default:\r
7059       break;\r
7060     }\r
7061     break;\r
7062 \r
7063   case WM_SIZE:\r
7064     newSizeX = LOWORD(lParam);\r
7065     newSizeY = HIWORD(lParam);\r
7066     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7067     sizeX = newSizeX;\r
7068     sizeY = newSizeY;\r
7069     break;\r
7070 \r
7071   case WM_GETMINMAXINFO:\r
7072     /* Prevent resizing window too small */\r
7073     mmi = (MINMAXINFO *) lParam;\r
7074     mmi->ptMinTrackSize.x = 100;\r
7075     mmi->ptMinTrackSize.y = 100;\r
7076     break;\r
7077   }\r
7078   return FALSE;\r
7079 }\r
7080 \r
7081 VOID\r
7082 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7083 {\r
7084   FARPROC lpProc;\r
7085   char *p, *q;\r
7086 \r
7087   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7088 \r
7089   if (str == NULL) str = "";\r
7090   p = (char *) malloc(2 * strlen(str) + 2);\r
7091   q = p;\r
7092   while (*str) {\r
7093     if (*str == '\n') *q++ = '\r';\r
7094     *q++ = *str++;\r
7095   }\r
7096   *q = NULLCHAR;\r
7097   if (commentText != NULL) free(commentText);\r
7098 \r
7099   commentIndex = index;\r
7100   commentTitle = title;\r
7101   commentText = p;\r
7102   editComment = edit;\r
7103 \r
7104   if (commentDialog) {\r
7105     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7106     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7107   } else {\r
7108     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7109     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7110                  hwndMain, (DLGPROC)lpProc);\r
7111     FreeProcInstance(lpProc);\r
7112   }\r
7113   commentDialogUp = TRUE;\r
7114 }\r
7115 \r
7116 \r
7117 /*---------------------------------------------------------------------------*\\r
7118  *\r
7119  * Type-in move dialog functions\r
7120  * \r
7121 \*---------------------------------------------------------------------------*/\r
7122 \r
7123 LRESULT CALLBACK\r
7124 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7125 {\r
7126   char move[MSG_SIZ];\r
7127   HWND hInput;\r
7128   ChessMove moveType;\r
7129   int fromX, fromY, toX, toY;\r
7130   char promoChar;\r
7131 \r
7132   switch (message) {\r
7133   case WM_INITDIALOG:\r
7134     move[0] = (char) lParam;\r
7135     move[1] = NULLCHAR;\r
7136     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7137     hInput = GetDlgItem(hDlg, OPT_Move);\r
7138     SetWindowText(hInput, move);\r
7139     SetFocus(hInput);\r
7140     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7141     return FALSE;\r
7142 \r
7143   case WM_COMMAND:\r
7144     switch (LOWORD(wParam)) {\r
7145     case IDOK:\r
7146       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7147         gameMode != Training) {\r
7148         DisplayMoveError("Displayed move is not current");\r
7149       } else {\r
7150         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7151         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7152           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7153           if (gameMode != Training)\r
7154               forwardMostMove = currentMove;\r
7155           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7156         } else {\r
7157           DisplayMoveError("Could not parse move");\r
7158         }\r
7159       }\r
7160       EndDialog(hDlg, TRUE);\r
7161       return TRUE;\r
7162     case IDCANCEL:\r
7163       EndDialog(hDlg, FALSE);\r
7164       return TRUE;\r
7165     default:\r
7166       break;\r
7167     }\r
7168     break;\r
7169   }\r
7170   return FALSE;\r
7171 }\r
7172 \r
7173 VOID\r
7174 PopUpMoveDialog(char firstchar)\r
7175 {\r
7176     FARPROC lpProc;\r
7177     \r
7178     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7179         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7180         gameMode == AnalyzeMode || gameMode == EditGame || \r
7181         gameMode == EditPosition || gameMode == IcsExamining ||\r
7182         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7183         gameMode == Training) {\r
7184       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7185       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7186         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7187       FreeProcInstance(lpProc);\r
7188     }\r
7189 }\r
7190 \r
7191 /*---------------------------------------------------------------------------*\\r
7192  *\r
7193  * Type-in name dialog functions\r
7194  * \r
7195 \*---------------------------------------------------------------------------*/\r
7196 \r
7197 LRESULT CALLBACK\r
7198 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7199 {\r
7200   char move[MSG_SIZ];\r
7201   HWND hInput;\r
7202 \r
7203   switch (message) {\r
7204   case WM_INITDIALOG:\r
7205     move[0] = (char) lParam;\r
7206     move[1] = NULLCHAR;\r
7207     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7208     hInput = GetDlgItem(hDlg, OPT_Name);\r
7209     SetWindowText(hInput, move);\r
7210     SetFocus(hInput);\r
7211     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7212     return FALSE;\r
7213 \r
7214   case WM_COMMAND:\r
7215     switch (LOWORD(wParam)) {\r
7216     case IDOK:\r
7217       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7218       appData.userName = strdup(move);\r
7219 \r
7220       EndDialog(hDlg, TRUE);\r
7221       return TRUE;\r
7222     case IDCANCEL:\r
7223       EndDialog(hDlg, FALSE);\r
7224       return TRUE;\r
7225     default:\r
7226       break;\r
7227     }\r
7228     break;\r
7229   }\r
7230   return FALSE;\r
7231 }\r
7232 \r
7233 VOID\r
7234 PopUpNameDialog(char firstchar)\r
7235 {\r
7236     FARPROC lpProc;\r
7237     \r
7238       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7239       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7240         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7241       FreeProcInstance(lpProc);\r
7242 }\r
7243 \r
7244 /*---------------------------------------------------------------------------*\\r
7245  *\r
7246  *  Error dialogs\r
7247  * \r
7248 \*---------------------------------------------------------------------------*/\r
7249 \r
7250 /* Nonmodal error box */\r
7251 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7252                              WPARAM wParam, LPARAM lParam);\r
7253 \r
7254 VOID\r
7255 ErrorPopUp(char *title, char *content)\r
7256 {\r
7257   FARPROC lpProc;\r
7258   char *p, *q;\r
7259   BOOLEAN modal = hwndMain == NULL;\r
7260 \r
7261   p = content;\r
7262   q = errorMessage;\r
7263   while (*p) {\r
7264     if (*p == '\n') {\r
7265       if (modal) {\r
7266         *q++ = ' ';\r
7267         p++;\r
7268       } else {\r
7269         *q++ = '\r';\r
7270         *q++ = *p++;\r
7271       }\r
7272     } else {\r
7273       *q++ = *p++;\r
7274     }\r
7275   }\r
7276   *q = NULLCHAR;\r
7277   strncpy(errorTitle, title, sizeof(errorTitle));\r
7278   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7279   \r
7280   if (modal) {\r
7281     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7282   } else {\r
7283     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7284     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7285                  hwndMain, (DLGPROC)lpProc);\r
7286     FreeProcInstance(lpProc);\r
7287   }\r
7288 }\r
7289 \r
7290 VOID\r
7291 ErrorPopDown()\r
7292 {\r
7293   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7294   if (errorDialog == NULL) return;\r
7295   DestroyWindow(errorDialog);\r
7296   errorDialog = NULL;\r
7297 }\r
7298 \r
7299 LRESULT CALLBACK\r
7300 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7301 {\r
7302   HANDLE hwndText;\r
7303   RECT rChild;\r
7304 \r
7305   switch (message) {\r
7306   case WM_INITDIALOG:\r
7307     GetWindowRect(hDlg, &rChild);\r
7308 \r
7309     /*\r
7310     SetWindowPos(hDlg, NULL, rChild.left,\r
7311       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7312       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7313     */\r
7314 \r
7315     /* \r
7316         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7317         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7318         and it doesn't work when you resize the dialog.\r
7319         For now, just give it a default position.\r
7320     */\r
7321     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7322 \r
7323     errorDialog = hDlg;\r
7324     SetWindowText(hDlg, errorTitle);\r
7325     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7326     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7327     return FALSE;\r
7328 \r
7329   case WM_COMMAND:\r
7330     switch (LOWORD(wParam)) {\r
7331     case IDOK:\r
7332     case IDCANCEL:\r
7333       if (errorDialog == hDlg) errorDialog = NULL;\r
7334       DestroyWindow(hDlg);\r
7335       return TRUE;\r
7336 \r
7337     default:\r
7338       break;\r
7339     }\r
7340     break;\r
7341   }\r
7342   return FALSE;\r
7343 }\r
7344 \r
7345 #ifdef GOTHIC\r
7346 HWND gothicDialog = NULL;\r
7347 \r
7348 LRESULT CALLBACK\r
7349 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7350 {\r
7351   HANDLE hwndText;\r
7352   RECT rChild;\r
7353   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7354 \r
7355   switch (message) {\r
7356   case WM_INITDIALOG:\r
7357     GetWindowRect(hDlg, &rChild);\r
7358 \r
7359     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7360                                                              SWP_NOZORDER);\r
7361 \r
7362     /* \r
7363         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7364         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7365         and it doesn't work when you resize the dialog.\r
7366         For now, just give it a default position.\r
7367     */\r
7368     gothicDialog = hDlg;\r
7369     SetWindowText(hDlg, errorTitle);\r
7370     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7371     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7372     return FALSE;\r
7373 \r
7374   case WM_COMMAND:\r
7375     switch (LOWORD(wParam)) {\r
7376     case IDOK:\r
7377     case IDCANCEL:\r
7378       if (errorDialog == hDlg) errorDialog = NULL;\r
7379       DestroyWindow(hDlg);\r
7380       return TRUE;\r
7381 \r
7382     default:\r
7383       break;\r
7384     }\r
7385     break;\r
7386   }\r
7387   return FALSE;\r
7388 }\r
7389 \r
7390 VOID\r
7391 GothicPopUp(char *title, VariantClass variant)\r
7392 {\r
7393   FARPROC lpProc;\r
7394   char *p, *q;\r
7395   BOOLEAN modal = hwndMain == NULL;\r
7396   static char *lastTitle;\r
7397 \r
7398   strncpy(errorTitle, title, sizeof(errorTitle));\r
7399   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7400 \r
7401   if(lastTitle != title && gothicDialog != NULL) {\r
7402     DestroyWindow(gothicDialog);\r
7403     gothicDialog = NULL;\r
7404   }\r
7405   if(variant != VariantNormal && gothicDialog == NULL) {\r
7406     title = lastTitle;\r
7407     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7408     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7409                  hwndMain, (DLGPROC)lpProc);\r
7410     FreeProcInstance(lpProc);\r
7411   }\r
7412 }\r
7413 #endif\r
7414 \r
7415 /*---------------------------------------------------------------------------*\\r
7416  *\r
7417  *  Ics Interaction console functions\r
7418  *\r
7419 \*---------------------------------------------------------------------------*/\r
7420 \r
7421 #define HISTORY_SIZE 64\r
7422 static char *history[HISTORY_SIZE];\r
7423 int histIn = 0, histP = 0;\r
7424 \r
7425 VOID\r
7426 SaveInHistory(char *cmd)\r
7427 {\r
7428   if (history[histIn] != NULL) {\r
7429     free(history[histIn]);\r
7430     history[histIn] = NULL;\r
7431   }\r
7432   if (*cmd == NULLCHAR) return;\r
7433   history[histIn] = StrSave(cmd);\r
7434   histIn = (histIn + 1) % HISTORY_SIZE;\r
7435   if (history[histIn] != NULL) {\r
7436     free(history[histIn]);\r
7437     history[histIn] = NULL;\r
7438   }\r
7439   histP = histIn;\r
7440 }\r
7441 \r
7442 char *\r
7443 PrevInHistory(char *cmd)\r
7444 {\r
7445   int newhp;\r
7446   if (histP == histIn) {\r
7447     if (history[histIn] != NULL) free(history[histIn]);\r
7448     history[histIn] = StrSave(cmd);\r
7449   }\r
7450   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7451   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7452   histP = newhp;\r
7453   return history[histP];\r
7454 }\r
7455 \r
7456 char *\r
7457 NextInHistory()\r
7458 {\r
7459   if (histP == histIn) return NULL;\r
7460   histP = (histP + 1) % HISTORY_SIZE;\r
7461   return history[histP];\r
7462 }\r
7463 \r
7464 typedef struct {\r
7465   char *item;\r
7466   char *command;\r
7467   BOOLEAN getname;\r
7468   BOOLEAN immediate;\r
7469 } IcsTextMenuEntry;\r
7470 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7471 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7472 \r
7473 void\r
7474 ParseIcsTextMenu(char *icsTextMenuString)\r
7475 {\r
7476   int flags = 0;\r
7477   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7478   char *p = icsTextMenuString;\r
7479   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7480     free(e->item);\r
7481     e->item = NULL;\r
7482     if (e->command != NULL) {\r
7483       free(e->command);\r
7484       e->command = NULL;\r
7485     }\r
7486     e++;\r
7487   }\r
7488   e = icsTextMenuEntry;\r
7489   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7490     if (*p == ';' || *p == '\n') {\r
7491       e->item = strdup("-");\r
7492       e->command = NULL;\r
7493       p++;\r
7494     } else if (*p == '-') {\r
7495       e->item = strdup("-");\r
7496       e->command = NULL;\r
7497       p++;\r
7498       if (*p) p++;\r
7499     } else {\r
7500       char *q, *r, *s, *t;\r
7501       char c;\r
7502       q = strchr(p, ',');\r
7503       if (q == NULL) break;\r
7504       *q = NULLCHAR;\r
7505       r = strchr(q + 1, ',');\r
7506       if (r == NULL) break;\r
7507       *r = NULLCHAR;\r
7508       s = strchr(r + 1, ',');\r
7509       if (s == NULL) break;\r
7510       *s = NULLCHAR;\r
7511       c = ';';\r
7512       t = strchr(s + 1, c);\r
7513       if (t == NULL) {\r
7514         c = '\n';\r
7515         t = strchr(s + 1, c);\r
7516       }\r
7517       if (t != NULL) *t = NULLCHAR;\r
7518       e->item = strdup(p);\r
7519       e->command = strdup(q + 1);\r
7520       e->getname = *(r + 1) != '0';\r
7521       e->immediate = *(s + 1) != '0';\r
7522       *q = ',';\r
7523       *r = ',';\r
7524       *s = ',';\r
7525       if (t == NULL) break;\r
7526       *t = c;\r
7527       p = t + 1;\r
7528     }\r
7529     e++;\r
7530   } \r
7531 }\r
7532 \r
7533 HMENU\r
7534 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7535 {\r
7536   HMENU hmenu, h;\r
7537   int i = 0;\r
7538   hmenu = LoadMenu(hInst, "TextMenu");\r
7539   h = GetSubMenu(hmenu, 0);\r
7540   while (e->item) {\r
7541     if (strcmp(e->item, "-") == 0) {\r
7542       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7543     } else {\r
7544       if (e->item[0] == '|') {\r
7545         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7546                    IDM_CommandX + i, &e->item[1]);\r
7547       } else {\r
7548         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7549       }\r
7550     }\r
7551     e++;\r
7552     i++;\r
7553   } \r
7554   return hmenu;\r
7555 }\r
7556 \r
7557 WNDPROC consoleTextWindowProc;\r
7558 \r
7559 void\r
7560 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7561 {\r
7562   char buf[MSG_SIZ], name[MSG_SIZ];\r
7563   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7564   CHARRANGE sel;\r
7565 \r
7566   if (!getname) {\r
7567     SetWindowText(hInput, command);\r
7568     if (immediate) {\r
7569       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7570     } else {\r
7571       sel.cpMin = 999999;\r
7572       sel.cpMax = 999999;\r
7573       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7574       SetFocus(hInput);\r
7575     }\r
7576     return;\r
7577   }    \r
7578   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7579   if (sel.cpMin == sel.cpMax) {\r
7580     /* Expand to surrounding word */\r
7581     TEXTRANGE tr;\r
7582     do {\r
7583       tr.chrg.cpMax = sel.cpMin;\r
7584       tr.chrg.cpMin = --sel.cpMin;\r
7585       if (sel.cpMin < 0) break;\r
7586       tr.lpstrText = name;\r
7587       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7588     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7589     sel.cpMin++;\r
7590 \r
7591     do {\r
7592       tr.chrg.cpMin = sel.cpMax;\r
7593       tr.chrg.cpMax = ++sel.cpMax;\r
7594       tr.lpstrText = name;\r
7595       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7596     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7597     sel.cpMax--;\r
7598 \r
7599     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7600       MessageBeep(MB_ICONEXCLAMATION);\r
7601       return;\r
7602     }\r
7603     tr.chrg = sel;\r
7604     tr.lpstrText = name;\r
7605     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7606   } else {\r
7607     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7608       MessageBeep(MB_ICONEXCLAMATION);\r
7609       return;\r
7610     }\r
7611     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7612   }\r
7613   if (immediate) {\r
7614     sprintf(buf, "%s %s", command, name);\r
7615     SetWindowText(hInput, buf);\r
7616     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7617   } else {\r
7618     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7619     SetWindowText(hInput, buf);\r
7620     sel.cpMin = 999999;\r
7621     sel.cpMax = 999999;\r
7622     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7623     SetFocus(hInput);\r
7624   }\r
7625 }\r
7626 \r
7627 LRESULT CALLBACK \r
7628 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7629 {\r
7630   HWND hInput;\r
7631   CHARRANGE sel;\r
7632 \r
7633   switch (message) {\r
7634   case WM_KEYDOWN:\r
7635     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7636     switch (wParam) {\r
7637     case VK_PRIOR:\r
7638       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7639       return 0;\r
7640     case VK_NEXT:\r
7641       sel.cpMin = 999999;\r
7642       sel.cpMax = 999999;\r
7643       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7644       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7645       return 0;\r
7646     }\r
7647     break;\r
7648   case WM_CHAR:\r
7649     if (wParam == '\t') {\r
7650       if (GetKeyState(VK_SHIFT) < 0) {\r
7651         /* shifted */\r
7652         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7653         if (buttonDesc[0].hwnd) {\r
7654           SetFocus(buttonDesc[0].hwnd);\r
7655         } else {\r
7656           SetFocus(hwndMain);\r
7657         }\r
7658       } else {\r
7659         /* unshifted */\r
7660         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7661       }\r
7662     } else {\r
7663       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7664       SetFocus(hInput);\r
7665       SendMessage(hInput, message, wParam, lParam);\r
7666     }\r
7667     return 0;\r
7668   case WM_PASTE:\r
7669     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7670     SetFocus(hInput);\r
7671     return SendMessage(hInput, message, wParam, lParam);\r
7672   case WM_MBUTTONDOWN:\r
7673     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7674   case WM_RBUTTONDOWN:\r
7675     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7676       /* Move selection here if it was empty */\r
7677       POINT pt;\r
7678       pt.x = LOWORD(lParam);\r
7679       pt.y = HIWORD(lParam);\r
7680       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7681       if (sel.cpMin == sel.cpMax) {\r
7682         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7683         sel.cpMax = sel.cpMin;\r
7684         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7685       }\r
7686       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7687     }\r
7688     return 0;\r
7689   case WM_RBUTTONUP:\r
7690     if (GetKeyState(VK_SHIFT) & ~1) {\r
7691       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7692         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7693     } else {\r
7694       POINT pt;\r
7695       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7696       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7697       if (sel.cpMin == sel.cpMax) {\r
7698         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7699         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7700       }\r
7701       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7702         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7703       }\r
7704       pt.x = LOWORD(lParam);\r
7705       pt.y = HIWORD(lParam);\r
7706       MenuPopup(hwnd, pt, hmenu, -1);\r
7707     }\r
7708     return 0;\r
7709   case WM_COMMAND:\r
7710     switch (LOWORD(wParam)) {\r
7711     case IDM_QuickPaste:\r
7712       {\r
7713         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7714         if (sel.cpMin == sel.cpMax) {\r
7715           MessageBeep(MB_ICONEXCLAMATION);\r
7716           return 0;\r
7717         }\r
7718         SendMessage(hwnd, WM_COPY, 0, 0);\r
7719         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7720         SendMessage(hInput, WM_PASTE, 0, 0);\r
7721         SetFocus(hInput);\r
7722         return 0;\r
7723       }\r
7724     case IDM_Cut:\r
7725       SendMessage(hwnd, WM_CUT, 0, 0);\r
7726       return 0;\r
7727     case IDM_Paste:\r
7728       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7729       return 0;\r
7730     case IDM_Copy:\r
7731       SendMessage(hwnd, WM_COPY, 0, 0);\r
7732       return 0;\r
7733     default:\r
7734       {\r
7735         int i = LOWORD(wParam) - IDM_CommandX;\r
7736         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7737             icsTextMenuEntry[i].command != NULL) {\r
7738           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7739                    icsTextMenuEntry[i].getname,\r
7740                    icsTextMenuEntry[i].immediate);\r
7741           return 0;\r
7742         }\r
7743       }\r
7744       break;\r
7745     }\r
7746     break;\r
7747   }\r
7748   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7749 }\r
7750 \r
7751 WNDPROC consoleInputWindowProc;\r
7752 \r
7753 LRESULT CALLBACK\r
7754 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7755 {\r
7756   char buf[MSG_SIZ];\r
7757   char *p;\r
7758   static BOOL sendNextChar = FALSE;\r
7759   static BOOL quoteNextChar = FALSE;\r
7760   InputSource *is = consoleInputSource;\r
7761   CHARFORMAT cf;\r
7762   CHARRANGE sel;\r
7763 \r
7764   switch (message) {\r
7765   case WM_CHAR:\r
7766     if (!appData.localLineEditing || sendNextChar) {\r
7767       is->buf[0] = (CHAR) wParam;\r
7768       is->count = 1;\r
7769       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7770       sendNextChar = FALSE;\r
7771       return 0;\r
7772     }\r
7773     if (quoteNextChar) {\r
7774       buf[0] = (char) wParam;\r
7775       buf[1] = NULLCHAR;\r
7776       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7777       quoteNextChar = FALSE;\r
7778       return 0;\r
7779     }\r
7780     switch (wParam) {\r
7781     case '\r':   /* Enter key */\r
7782       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7783       if (consoleEcho) SaveInHistory(is->buf);\r
7784       is->buf[is->count++] = '\n';\r
7785       is->buf[is->count] = NULLCHAR;\r
7786       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7787       if (consoleEcho) {\r
7788         ConsoleOutput(is->buf, is->count, TRUE);\r
7789       } else if (appData.localLineEditing) {\r
7790         ConsoleOutput("\n", 1, TRUE);\r
7791       }\r
7792       /* fall thru */\r
7793     case '\033': /* Escape key */\r
7794       SetWindowText(hwnd, "");\r
7795       cf.cbSize = sizeof(CHARFORMAT);\r
7796       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7797       if (consoleEcho) {\r
7798         cf.crTextColor = textAttribs[ColorNormal].color;\r
7799       } else {\r
7800         cf.crTextColor = COLOR_ECHOOFF;\r
7801       }\r
7802       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7803       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7804       return 0;\r
7805     case '\t':   /* Tab key */\r
7806       if (GetKeyState(VK_SHIFT) < 0) {\r
7807         /* shifted */\r
7808         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7809       } else {\r
7810         /* unshifted */\r
7811         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7812         if (buttonDesc[0].hwnd) {\r
7813           SetFocus(buttonDesc[0].hwnd);\r
7814         } else {\r
7815           SetFocus(hwndMain);\r
7816         }\r
7817       }\r
7818       return 0;\r
7819     case '\023': /* Ctrl+S */\r
7820       sendNextChar = TRUE;\r
7821       return 0;\r
7822     case '\021': /* Ctrl+Q */\r
7823       quoteNextChar = TRUE;\r
7824       return 0;\r
7825     default:\r
7826       break;\r
7827     }\r
7828     break;\r
7829   case WM_KEYDOWN:\r
7830     switch (wParam) {\r
7831     case VK_UP:\r
7832       GetWindowText(hwnd, buf, MSG_SIZ);\r
7833       p = PrevInHistory(buf);\r
7834       if (p != NULL) {\r
7835         SetWindowText(hwnd, p);\r
7836         sel.cpMin = 999999;\r
7837         sel.cpMax = 999999;\r
7838         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7839         return 0;\r
7840       }\r
7841       break;\r
7842     case VK_DOWN:\r
7843       p = NextInHistory();\r
7844       if (p != NULL) {\r
7845         SetWindowText(hwnd, p);\r
7846         sel.cpMin = 999999;\r
7847         sel.cpMax = 999999;\r
7848         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7849         return 0;\r
7850       }\r
7851       break;\r
7852     case VK_HOME:\r
7853     case VK_END:\r
7854       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7855       /* fall thru */\r
7856     case VK_PRIOR:\r
7857     case VK_NEXT:\r
7858       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7859       return 0;\r
7860     }\r
7861     break;\r
7862   case WM_MBUTTONDOWN:\r
7863     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7864       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7865     break;\r
7866   case WM_RBUTTONUP:\r
7867     if (GetKeyState(VK_SHIFT) & ~1) {\r
7868       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7869         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7870     } else {\r
7871       POINT pt;\r
7872       HMENU hmenu;\r
7873       hmenu = LoadMenu(hInst, "InputMenu");\r
7874       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7875       if (sel.cpMin == sel.cpMax) {\r
7876         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7877         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7878       }\r
7879       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7880         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7881       }\r
7882       pt.x = LOWORD(lParam);\r
7883       pt.y = HIWORD(lParam);\r
7884       MenuPopup(hwnd, pt, hmenu, -1);\r
7885     }\r
7886     return 0;\r
7887   case WM_COMMAND:\r
7888     switch (LOWORD(wParam)) { \r
7889     case IDM_Undo:\r
7890       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7891       return 0;\r
7892     case IDM_SelectAll:\r
7893       sel.cpMin = 0;\r
7894       sel.cpMax = -1; /*999999?*/\r
7895       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7896       return 0;\r
7897     case IDM_Cut:\r
7898       SendMessage(hwnd, WM_CUT, 0, 0);\r
7899       return 0;\r
7900     case IDM_Paste:\r
7901       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7902       return 0;\r
7903     case IDM_Copy:\r
7904       SendMessage(hwnd, WM_COPY, 0, 0);\r
7905       return 0;\r
7906     }\r
7907     break;\r
7908   }\r
7909   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7910 }\r
7911 \r
7912 #define CO_MAX  100000\r
7913 #define CO_TRIM   1000\r
7914 \r
7915 LRESULT CALLBACK\r
7916 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7917 {\r
7918   static SnapData sd;\r
7919   static HWND hText, hInput, hFocus;\r
7920   InputSource *is = consoleInputSource;\r
7921   RECT rect;\r
7922   static int sizeX, sizeY;\r
7923   int newSizeX, newSizeY;\r
7924   MINMAXINFO *mmi;\r
7925 \r
7926   switch (message) {\r
7927   case WM_INITDIALOG: /* message: initialize dialog box */\r
7928     hwndConsole = hDlg;\r
7929     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7930     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7931     SetFocus(hInput);\r
7932     consoleTextWindowProc = (WNDPROC)\r
7933       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7934     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7935     consoleInputWindowProc = (WNDPROC)\r
7936       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7937     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7938     Colorize(ColorNormal, TRUE);\r
7939     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7940     ChangedConsoleFont();\r
7941     GetClientRect(hDlg, &rect);\r
7942     sizeX = rect.right;\r
7943     sizeY = rect.bottom;\r
7944     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7945         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7946       WINDOWPLACEMENT wp;\r
7947       EnsureOnScreen(&consoleX, &consoleY);\r
7948       wp.length = sizeof(WINDOWPLACEMENT);\r
7949       wp.flags = 0;\r
7950       wp.showCmd = SW_SHOW;\r
7951       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7952       wp.rcNormalPosition.left = consoleX;\r
7953       wp.rcNormalPosition.right = consoleX + consoleW;\r
7954       wp.rcNormalPosition.top = consoleY;\r
7955       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7956       SetWindowPlacement(hDlg, &wp);\r
7957     }\r
7958 #if 0 \r
7959    // [HGM] Chessknight's change 2004-07-13\r
7960    else { /* Determine Defaults */\r
7961        WINDOWPLACEMENT wp;\r
7962        consoleX = winWidth + 1;\r
7963        consoleY = boardY;\r
7964        consoleW = screenWidth -  winWidth;\r
7965        consoleH = winHeight;\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 #endif\r
7978     return FALSE;\r
7979 \r
7980   case WM_SETFOCUS:\r
7981     SetFocus(hInput);\r
7982     return 0;\r
7983 \r
7984   case WM_CLOSE:\r
7985     ExitEvent(0);\r
7986     /* not reached */\r
7987     break;\r
7988 \r
7989   case WM_SIZE:\r
7990     if (IsIconic(hDlg)) break;\r
7991     newSizeX = LOWORD(lParam);\r
7992     newSizeY = HIWORD(lParam);\r
7993     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7994       RECT rectText, rectInput;\r
7995       POINT pt;\r
7996       int newTextHeight, newTextWidth;\r
7997       GetWindowRect(hText, &rectText);\r
7998       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7999       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8000       if (newTextHeight < 0) {\r
8001         newSizeY += -newTextHeight;\r
8002         newTextHeight = 0;\r
8003       }\r
8004       SetWindowPos(hText, NULL, 0, 0,\r
8005         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8006       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8007       pt.x = rectInput.left;\r
8008       pt.y = rectInput.top + newSizeY - sizeY;\r
8009       ScreenToClient(hDlg, &pt);\r
8010       SetWindowPos(hInput, NULL, \r
8011         pt.x, pt.y, /* needs client coords */   \r
8012         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8013         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8014     }\r
8015     sizeX = newSizeX;\r
8016     sizeY = newSizeY;\r
8017     break;\r
8018 \r
8019   case WM_GETMINMAXINFO:\r
8020     /* Prevent resizing window too small */\r
8021     mmi = (MINMAXINFO *) lParam;\r
8022     mmi->ptMinTrackSize.x = 100;\r
8023     mmi->ptMinTrackSize.y = 100;\r
8024     break;\r
8025 \r
8026   /* [AS] Snapping */\r
8027   case WM_ENTERSIZEMOVE:\r
8028     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8029 \r
8030   case WM_SIZING:\r
8031     return OnSizing( &sd, hDlg, wParam, lParam );\r
8032 \r
8033   case WM_MOVING:\r
8034     return OnMoving( &sd, hDlg, wParam, lParam );\r
8035 \r
8036   case WM_EXITSIZEMOVE:\r
8037     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8038   }\r
8039 \r
8040   return DefWindowProc(hDlg, message, wParam, lParam);\r
8041 }\r
8042 \r
8043 \r
8044 VOID\r
8045 ConsoleCreate()\r
8046 {\r
8047   HWND hCons;\r
8048   if (hwndConsole) return;\r
8049   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8050   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8051 }\r
8052 \r
8053 \r
8054 VOID\r
8055 ConsoleOutput(char* data, int length, int forceVisible)\r
8056 {\r
8057   HWND hText;\r
8058   int trim, exlen;\r
8059   char *p, *q;\r
8060   char buf[CO_MAX+1];\r
8061   POINT pEnd;\r
8062   RECT rect;\r
8063   static int delayLF = 0;\r
8064   CHARRANGE savesel, sel;\r
8065 \r
8066   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8067   p = data;\r
8068   q = buf;\r
8069   if (delayLF) {\r
8070     *q++ = '\r';\r
8071     *q++ = '\n';\r
8072     delayLF = 0;\r
8073   }\r
8074   while (length--) {\r
8075     if (*p == '\n') {\r
8076       if (*++p) {\r
8077         *q++ = '\r';\r
8078         *q++ = '\n';\r
8079       } else {\r
8080         delayLF = 1;\r
8081       }\r
8082     } else if (*p == '\007') {\r
8083        MyPlaySound(&sounds[(int)SoundBell]);\r
8084        p++;\r
8085     } else {\r
8086       *q++ = *p++;\r
8087     }\r
8088   }\r
8089   *q = NULLCHAR;\r
8090   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8091   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8092   /* Save current selection */\r
8093   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8094   exlen = GetWindowTextLength(hText);\r
8095   /* Find out whether current end of text is visible */\r
8096   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8097   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8098   /* Trim existing text if it's too long */\r
8099   if (exlen + (q - buf) > CO_MAX) {\r
8100     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8101     sel.cpMin = 0;\r
8102     sel.cpMax = trim;\r
8103     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8104     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8105     exlen -= trim;\r
8106     savesel.cpMin -= trim;\r
8107     savesel.cpMax -= trim;\r
8108     if (exlen < 0) exlen = 0;\r
8109     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8110     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8111   }\r
8112   /* Append the new text */\r
8113   sel.cpMin = exlen;\r
8114   sel.cpMax = exlen;\r
8115   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8116   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8117   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8118   if (forceVisible || exlen == 0 ||\r
8119       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8120        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8121     /* Scroll to make new end of text visible if old end of text\r
8122        was visible or new text is an echo of user typein */\r
8123     sel.cpMin = 9999999;\r
8124     sel.cpMax = 9999999;\r
8125     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8126     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8127     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8128     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8129   }\r
8130   if (savesel.cpMax == exlen || forceVisible) {\r
8131     /* Move insert point to new end of text if it was at the old\r
8132        end of text or if the new text is an echo of user typein */\r
8133     sel.cpMin = 9999999;\r
8134     sel.cpMax = 9999999;\r
8135     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8136   } else {\r
8137     /* Restore previous selection */\r
8138     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8139   }\r
8140   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8141 }\r
8142 \r
8143 /*---------*/\r
8144 \r
8145 \r
8146 void\r
8147 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8148 {\r
8149   char buf[100];\r
8150   char *str;\r
8151   COLORREF oldFg, oldBg;\r
8152   HFONT oldFont;\r
8153   RECT rect;\r
8154 \r
8155   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8156 \r
8157   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8158   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8159   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8160 \r
8161   rect.left = x;\r
8162   rect.right = x + squareSize;\r
8163   rect.top  = y;\r
8164   rect.bottom = y + squareSize;\r
8165   str = buf;\r
8166 \r
8167   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8168                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8169              y, ETO_CLIPPED|ETO_OPAQUE,\r
8170              &rect, str, strlen(str), NULL);\r
8171 \r
8172   (void) SetTextColor(hdc, oldFg);\r
8173   (void) SetBkColor(hdc, oldBg);\r
8174   (void) SelectObject(hdc, oldFont);\r
8175 }\r
8176 \r
8177 void\r
8178 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8179               RECT *rect, char *color, char *flagFell)\r
8180 {\r
8181   char buf[100];\r
8182   char *str;\r
8183   COLORREF oldFg, oldBg;\r
8184   HFONT oldFont;\r
8185 \r
8186   if (appData.clockMode) {\r
8187     if (tinyLayout)\r
8188       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8189     else\r
8190       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8191     str = buf;\r
8192   } else {\r
8193     str = color;\r
8194   }\r
8195 \r
8196   if (highlight) {\r
8197     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8198     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8199   } else {\r
8200     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8201     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8202   }\r
8203   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8204 \r
8205   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8206              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8207              rect, str, strlen(str), NULL);\r
8208 \r
8209   (void) SetTextColor(hdc, oldFg);\r
8210   (void) SetBkColor(hdc, oldBg);\r
8211   (void) SelectObject(hdc, oldFont);\r
8212 }\r
8213 \r
8214 \r
8215 int\r
8216 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8217            OVERLAPPED *ovl)\r
8218 {\r
8219   int ok, err;\r
8220 \r
8221   /* [AS]  */\r
8222   if( count <= 0 ) {\r
8223     if (appData.debugMode) {\r
8224       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8225     }\r
8226 \r
8227     return ERROR_INVALID_USER_BUFFER;\r
8228   }\r
8229 \r
8230   ResetEvent(ovl->hEvent);\r
8231   ovl->Offset = ovl->OffsetHigh = 0;\r
8232   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8233   if (ok) {\r
8234     err = NO_ERROR;\r
8235   } else {\r
8236     err = GetLastError();\r
8237     if (err == ERROR_IO_PENDING) {\r
8238       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8239       if (ok)\r
8240         err = NO_ERROR;\r
8241       else\r
8242         err = GetLastError();\r
8243     }\r
8244   }\r
8245   return err;\r
8246 }\r
8247 \r
8248 int\r
8249 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8250             OVERLAPPED *ovl)\r
8251 {\r
8252   int ok, err;\r
8253 \r
8254   ResetEvent(ovl->hEvent);\r
8255   ovl->Offset = ovl->OffsetHigh = 0;\r
8256   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8257   if (ok) {\r
8258     err = NO_ERROR;\r
8259   } else {\r
8260     err = GetLastError();\r
8261     if (err == ERROR_IO_PENDING) {\r
8262       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8263       if (ok)\r
8264         err = NO_ERROR;\r
8265       else\r
8266         err = GetLastError();\r
8267     }\r
8268   }\r
8269   return err;\r
8270 }\r
8271 \r
8272 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8273 void CheckForInputBufferFull( InputSource * is )\r
8274 {\r
8275     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8276         /* Look for end of line */\r
8277         char * p = is->buf;\r
8278         \r
8279         while( p < is->next && *p != '\n' ) {\r
8280             p++;\r
8281         }\r
8282 \r
8283         if( p >= is->next ) {\r
8284             if (appData.debugMode) {\r
8285                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
8286             }\r
8287 \r
8288             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8289             is->count = (DWORD) -1;\r
8290             is->next = is->buf;\r
8291         }\r
8292     }\r
8293 }\r
8294 \r
8295 DWORD\r
8296 InputThread(LPVOID arg)\r
8297 {\r
8298   InputSource *is;\r
8299   OVERLAPPED ovl;\r
8300 \r
8301   is = (InputSource *) arg;\r
8302   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8303   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8304   while (is->hThread != NULL) {\r
8305     is->error = DoReadFile(is->hFile, is->next,\r
8306                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8307                            &is->count, &ovl);\r
8308     if (is->error == NO_ERROR) {\r
8309       is->next += is->count;\r
8310     } else {\r
8311       if (is->error == ERROR_BROKEN_PIPE) {\r
8312         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8313         is->count = 0;\r
8314       } else {\r
8315         is->count = (DWORD) -1;\r
8316         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8317         break; \r
8318       }\r
8319     }\r
8320 \r
8321     CheckForInputBufferFull( is );\r
8322 \r
8323     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8324 \r
8325     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8326 \r
8327     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8328   }\r
8329 \r
8330   CloseHandle(ovl.hEvent);\r
8331   CloseHandle(is->hFile);\r
8332 \r
8333   if (appData.debugMode) {\r
8334     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
8335   }\r
8336 \r
8337   return 0;\r
8338 }\r
8339 \r
8340 \r
8341 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8342 DWORD\r
8343 NonOvlInputThread(LPVOID arg)\r
8344 {\r
8345   InputSource *is;\r
8346   char *p, *q;\r
8347   int i;\r
8348   char prev;\r
8349 \r
8350   is = (InputSource *) arg;\r
8351   while (is->hThread != NULL) {\r
8352     is->error = ReadFile(is->hFile, is->next,\r
8353                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8354                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8355     if (is->error == NO_ERROR) {\r
8356       /* Change CRLF to LF */\r
8357       if (is->next > is->buf) {\r
8358         p = is->next - 1;\r
8359         i = is->count + 1;\r
8360       } else {\r
8361         p = is->next;\r
8362         i = is->count;\r
8363       }\r
8364       q = p;\r
8365       prev = NULLCHAR;\r
8366       while (i > 0) {\r
8367         if (prev == '\r' && *p == '\n') {\r
8368           *(q-1) = '\n';\r
8369           is->count--;\r
8370         } else { \r
8371           *q++ = *p;\r
8372         }\r
8373         prev = *p++;\r
8374         i--;\r
8375       }\r
8376       *q = NULLCHAR;\r
8377       is->next = q;\r
8378     } else {\r
8379       if (is->error == ERROR_BROKEN_PIPE) {\r
8380         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8381         is->count = 0; \r
8382       } else {\r
8383         is->count = (DWORD) -1;\r
8384       }\r
8385     }\r
8386 \r
8387     CheckForInputBufferFull( is );\r
8388 \r
8389     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8390 \r
8391     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8392 \r
8393     if (is->count < 0) break;  /* Quit on error */\r
8394   }\r
8395   CloseHandle(is->hFile);\r
8396   return 0;\r
8397 }\r
8398 \r
8399 DWORD\r
8400 SocketInputThread(LPVOID arg)\r
8401 {\r
8402   InputSource *is;\r
8403 \r
8404   is = (InputSource *) arg;\r
8405   while (is->hThread != NULL) {\r
8406     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8407     if ((int)is->count == SOCKET_ERROR) {\r
8408       is->count = (DWORD) -1;\r
8409       is->error = WSAGetLastError();\r
8410     } else {\r
8411       is->error = NO_ERROR;\r
8412       is->next += is->count;\r
8413       if (is->count == 0 && is->second == is) {\r
8414         /* End of file on stderr; quit with no message */\r
8415         break;\r
8416       }\r
8417     }\r
8418     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8419 \r
8420     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8421 \r
8422     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8423   }\r
8424   return 0;\r
8425 }\r
8426 \r
8427 VOID\r
8428 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8429 {\r
8430   InputSource *is;\r
8431 \r
8432   is = (InputSource *) lParam;\r
8433   if (is->lineByLine) {\r
8434     /* Feed in lines one by one */\r
8435     char *p = is->buf;\r
8436     char *q = p;\r
8437     while (q < is->next) {\r
8438       if (*q++ == '\n') {\r
8439         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8440         p = q;\r
8441       }\r
8442     }\r
8443     \r
8444     /* Move any partial line to the start of the buffer */\r
8445     q = is->buf;\r
8446     while (p < is->next) {\r
8447       *q++ = *p++;\r
8448     }\r
8449     is->next = q;\r
8450 \r
8451     if (is->error != NO_ERROR || is->count == 0) {\r
8452       /* Notify backend of the error.  Note: If there was a partial\r
8453          line at the end, it is not flushed through. */\r
8454       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8455     }\r
8456   } else {\r
8457     /* Feed in the whole chunk of input at once */\r
8458     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8459     is->next = is->buf;\r
8460   }\r
8461 }\r
8462 \r
8463 /*---------------------------------------------------------------------------*\\r
8464  *\r
8465  *  Menu enables. Used when setting various modes.\r
8466  *\r
8467 \*---------------------------------------------------------------------------*/\r
8468 \r
8469 typedef struct {\r
8470   int item;\r
8471   int flags;\r
8472 } Enables;\r
8473 \r
8474 VOID\r
8475 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8476 {\r
8477   while (enab->item > 0) {\r
8478     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8479     enab++;\r
8480   }\r
8481 }\r
8482 \r
8483 Enables gnuEnables[] = {\r
8484   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8485   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8486   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8487   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8488   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8489   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8490   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8491   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8492   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8493   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8494   { -1, -1 }\r
8495 };\r
8496 \r
8497 Enables icsEnables[] = {\r
8498   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8499   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8500   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8501   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8502   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8503   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8504   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8505   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8506   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8507   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8508   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8509   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8510   { -1, -1 }\r
8511 };\r
8512 \r
8513 #ifdef ZIPPY\r
8514 Enables zippyEnables[] = {\r
8515   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8516   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8517   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8518   { -1, -1 }\r
8519 };\r
8520 #endif\r
8521 \r
8522 Enables ncpEnables[] = {\r
8523   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8524   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8525   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8526   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8527   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8528   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8529   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8530   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8531   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8532   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8533   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8534   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8535   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8536   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8537   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8538   { -1, -1 }\r
8539 };\r
8540 \r
8541 Enables trainingOnEnables[] = {\r
8542   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8543   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8544   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8545   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8546   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8547   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8548   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8549   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8550   { -1, -1 }\r
8551 };\r
8552 \r
8553 Enables trainingOffEnables[] = {\r
8554   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8555   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8556   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8557   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8558   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8559   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8560   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8561   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8562   { -1, -1 }\r
8563 };\r
8564 \r
8565 /* These modify either ncpEnables or gnuEnables */\r
8566 Enables cmailEnables[] = {\r
8567   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8568   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8569   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8570   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8571   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8572   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8573   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8574   { -1, -1 }\r
8575 };\r
8576 \r
8577 Enables machineThinkingEnables[] = {\r
8578   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8580   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8581   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8583   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8584   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8586   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8587   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8588   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8589   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8593   { -1, -1 }\r
8594 };\r
8595 \r
8596 Enables userThinkingEnables[] = {\r
8597   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8598   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8599   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8600   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8601   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8602   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8603   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8604   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8605   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8606   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8607   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8608   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8609   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8610   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8611   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8612   { -1, -1 }\r
8613 };\r
8614 \r
8615 /*---------------------------------------------------------------------------*\\r
8616  *\r
8617  *  Front-end interface functions exported by XBoard.\r
8618  *  Functions appear in same order as prototypes in frontend.h.\r
8619  * \r
8620 \*---------------------------------------------------------------------------*/\r
8621 VOID\r
8622 ModeHighlight()\r
8623 {\r
8624   static UINT prevChecked = 0;\r
8625   static int prevPausing = 0;\r
8626   UINT nowChecked;\r
8627 \r
8628   if (pausing != prevPausing) {\r
8629     prevPausing = pausing;\r
8630     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8631                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8632     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8633   }\r
8634 \r
8635   switch (gameMode) {\r
8636   case BeginningOfGame:\r
8637     if (appData.icsActive)\r
8638       nowChecked = IDM_IcsClient;\r
8639     else if (appData.noChessProgram)\r
8640       nowChecked = IDM_EditGame;\r
8641     else\r
8642       nowChecked = IDM_MachineBlack;\r
8643     break;\r
8644   case MachinePlaysBlack:\r
8645     nowChecked = IDM_MachineBlack;\r
8646     break;\r
8647   case MachinePlaysWhite:\r
8648     nowChecked = IDM_MachineWhite;\r
8649     break;\r
8650   case TwoMachinesPlay:\r
8651     nowChecked = IDM_TwoMachines;\r
8652     break;\r
8653   case AnalyzeMode:\r
8654     nowChecked = IDM_AnalysisMode;\r
8655     break;\r
8656   case AnalyzeFile:\r
8657     nowChecked = IDM_AnalyzeFile;\r
8658     break;\r
8659   case EditGame:\r
8660     nowChecked = IDM_EditGame;\r
8661     break;\r
8662   case PlayFromGameFile:\r
8663     nowChecked = IDM_LoadGame;\r
8664     break;\r
8665   case EditPosition:\r
8666     nowChecked = IDM_EditPosition;\r
8667     break;\r
8668   case Training:\r
8669     nowChecked = IDM_Training;\r
8670     break;\r
8671   case IcsPlayingWhite:\r
8672   case IcsPlayingBlack:\r
8673   case IcsObserving:\r
8674   case IcsIdle:\r
8675     nowChecked = IDM_IcsClient;\r
8676     break;\r
8677   default:\r
8678   case EndOfGame:\r
8679     nowChecked = 0;\r
8680     break;\r
8681   }\r
8682   if (prevChecked != 0)\r
8683     (void) CheckMenuItem(GetMenu(hwndMain),\r
8684                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8685   if (nowChecked != 0)\r
8686     (void) CheckMenuItem(GetMenu(hwndMain),\r
8687                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8688 \r
8689   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8690     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8691                           MF_BYCOMMAND|MF_ENABLED);\r
8692   } else {\r
8693     (void) EnableMenuItem(GetMenu(hwndMain), \r
8694                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8695   }\r
8696 \r
8697   prevChecked = nowChecked;\r
8698 \r
8699   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8700   if (appData.icsActive) {\r
8701        if (appData.icsEngineAnalyze) {\r
8702                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8703                        MF_BYCOMMAND|MF_CHECKED);\r
8704        } else {\r
8705                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8706                        MF_BYCOMMAND|MF_UNCHECKED);\r
8707        }\r
8708   }\r
8709 }\r
8710 \r
8711 VOID\r
8712 SetICSMode()\r
8713 {\r
8714   HMENU hmenu = GetMenu(hwndMain);\r
8715   SetMenuEnables(hmenu, icsEnables);\r
8716   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8717     MF_BYPOSITION|MF_ENABLED);\r
8718 #ifdef ZIPPY\r
8719   if (appData.zippyPlay) {\r
8720     SetMenuEnables(hmenu, zippyEnables);\r
8721     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8722          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8723           MF_BYCOMMAND|MF_ENABLED);\r
8724   }\r
8725 #endif\r
8726 }\r
8727 \r
8728 VOID\r
8729 SetGNUMode()\r
8730 {\r
8731   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8732 }\r
8733 \r
8734 VOID\r
8735 SetNCPMode()\r
8736 {\r
8737   HMENU hmenu = GetMenu(hwndMain);\r
8738   SetMenuEnables(hmenu, ncpEnables);\r
8739   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8740     MF_BYPOSITION|MF_GRAYED);\r
8741     DrawMenuBar(hwndMain);\r
8742 }\r
8743 \r
8744 VOID\r
8745 SetCmailMode()\r
8746 {\r
8747   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8748 }\r
8749 \r
8750 VOID \r
8751 SetTrainingModeOn()\r
8752 {\r
8753   int i;\r
8754   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8755   for (i = 0; i < N_BUTTONS; i++) {\r
8756     if (buttonDesc[i].hwnd != NULL)\r
8757       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8758   }\r
8759   CommentPopDown();\r
8760 }\r
8761 \r
8762 VOID SetTrainingModeOff()\r
8763 {\r
8764   int i;\r
8765   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8766   for (i = 0; i < N_BUTTONS; i++) {\r
8767     if (buttonDesc[i].hwnd != NULL)\r
8768       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8769   }\r
8770 }\r
8771 \r
8772 \r
8773 VOID\r
8774 SetUserThinkingEnables()\r
8775 {\r
8776   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8777 }\r
8778 \r
8779 VOID\r
8780 SetMachineThinkingEnables()\r
8781 {\r
8782   HMENU hMenu = GetMenu(hwndMain);\r
8783   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8784 \r
8785   SetMenuEnables(hMenu, machineThinkingEnables);\r
8786 \r
8787   if (gameMode == MachinePlaysBlack) {\r
8788     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8789   } else if (gameMode == MachinePlaysWhite) {\r
8790     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8791   } else if (gameMode == TwoMachinesPlay) {\r
8792     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8793   }\r
8794 }\r
8795 \r
8796 \r
8797 VOID\r
8798 DisplayTitle(char *str)\r
8799 {\r
8800   char title[MSG_SIZ], *host;\r
8801   if (str[0] != NULLCHAR) {\r
8802     strcpy(title, str);\r
8803   } else if (appData.icsActive) {\r
8804     if (appData.icsCommPort[0] != NULLCHAR)\r
8805       host = "ICS";\r
8806     else \r
8807       host = appData.icsHost;\r
8808     sprintf(title, "%s: %s", szTitle, host);\r
8809   } else if (appData.noChessProgram) {\r
8810     strcpy(title, szTitle);\r
8811   } else {\r
8812     strcpy(title, szTitle);\r
8813     strcat(title, ": ");\r
8814     strcat(title, first.tidy);\r
8815   }\r
8816   SetWindowText(hwndMain, title);\r
8817 }\r
8818 \r
8819 \r
8820 VOID\r
8821 DisplayMessage(char *str1, char *str2)\r
8822 {\r
8823   HDC hdc;\r
8824   HFONT oldFont;\r
8825   int remain = MESSAGE_TEXT_MAX - 1;\r
8826   int len;\r
8827 \r
8828   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8829   messageText[0] = NULLCHAR;\r
8830   if (*str1) {\r
8831     len = strlen(str1);\r
8832     if (len > remain) len = remain;\r
8833     strncpy(messageText, str1, len);\r
8834     messageText[len] = NULLCHAR;\r
8835     remain -= len;\r
8836   }\r
8837   if (*str2 && remain >= 2) {\r
8838     if (*str1) {\r
8839       strcat(messageText, "  ");\r
8840       remain -= 2;\r
8841     }\r
8842     len = strlen(str2);\r
8843     if (len > remain) len = remain;\r
8844     strncat(messageText, str2, len);\r
8845   }\r
8846   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8847 \r
8848   if (IsIconic(hwndMain)) return;\r
8849   hdc = GetDC(hwndMain);\r
8850   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8851   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8852              &messageRect, messageText, strlen(messageText), NULL);\r
8853   (void) SelectObject(hdc, oldFont);\r
8854   (void) ReleaseDC(hwndMain, hdc);\r
8855 }\r
8856 \r
8857 VOID\r
8858 DisplayError(char *str, int error)\r
8859 {\r
8860   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8861   int len;\r
8862 \r
8863   if (error == 0) {\r
8864     strcpy(buf, str);\r
8865   } else {\r
8866     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8867                         NULL, error, LANG_NEUTRAL,\r
8868                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8869     if (len > 0) {\r
8870       sprintf(buf, "%s:\n%s", str, buf2);\r
8871     } else {\r
8872       ErrorMap *em = errmap;\r
8873       while (em->err != 0 && em->err != error) em++;\r
8874       if (em->err != 0) {\r
8875         sprintf(buf, "%s:\n%s", str, em->msg);\r
8876       } else {\r
8877         sprintf(buf, "%s:\nError code %d", str, error);\r
8878       }\r
8879     }\r
8880   }\r
8881   \r
8882   ErrorPopUp("Error", buf);\r
8883 }\r
8884 \r
8885 \r
8886 VOID\r
8887 DisplayMoveError(char *str)\r
8888 {\r
8889   fromX = fromY = -1;\r
8890   ClearHighlights();\r
8891   DrawPosition(FALSE, NULL);\r
8892   if (appData.popupMoveErrors) {\r
8893     ErrorPopUp("Error", str);\r
8894   } else {\r
8895     DisplayMessage(str, "");\r
8896     moveErrorMessageUp = TRUE;\r
8897   }\r
8898 }\r
8899 \r
8900 VOID\r
8901 DisplayFatalError(char *str, int error, int exitStatus)\r
8902 {\r
8903   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8904   int len;\r
8905   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8906 \r
8907   if (error != 0) {\r
8908     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8909                         NULL, error, LANG_NEUTRAL,\r
8910                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8911     if (len > 0) {\r
8912       sprintf(buf, "%s:\n%s", str, buf2);\r
8913     } else {\r
8914       ErrorMap *em = errmap;\r
8915       while (em->err != 0 && em->err != error) em++;\r
8916       if (em->err != 0) {\r
8917         sprintf(buf, "%s:\n%s", str, em->msg);\r
8918       } else {\r
8919         sprintf(buf, "%s:\nError code %d", str, error);\r
8920       }\r
8921     }\r
8922     str = buf;\r
8923   }\r
8924   if (appData.debugMode) {\r
8925     fprintf(debugFP, "%s: %s\n", label, str);\r
8926   }\r
8927   if (appData.popupExitMessage) {\r
8928     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8929                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8930   }\r
8931   ExitEvent(exitStatus);\r
8932 }\r
8933 \r
8934 \r
8935 VOID\r
8936 DisplayInformation(char *str)\r
8937 {\r
8938   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8939 }\r
8940 \r
8941 \r
8942 VOID\r
8943 DisplayNote(char *str)\r
8944 {\r
8945   ErrorPopUp("Note", str);\r
8946 }\r
8947 \r
8948 \r
8949 typedef struct {\r
8950   char *title, *question, *replyPrefix;\r
8951   ProcRef pr;\r
8952 } QuestionParams;\r
8953 \r
8954 LRESULT CALLBACK\r
8955 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8956 {\r
8957   static QuestionParams *qp;\r
8958   char reply[MSG_SIZ];\r
8959   int len, err;\r
8960 \r
8961   switch (message) {\r
8962   case WM_INITDIALOG:\r
8963     qp = (QuestionParams *) lParam;\r
8964     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8965     SetWindowText(hDlg, qp->title);\r
8966     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8967     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8968     return FALSE;\r
8969 \r
8970   case WM_COMMAND:\r
8971     switch (LOWORD(wParam)) {\r
8972     case IDOK:\r
8973       strcpy(reply, qp->replyPrefix);\r
8974       if (*reply) strcat(reply, " ");\r
8975       len = strlen(reply);\r
8976       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8977       strcat(reply, "\n");\r
8978       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8979       EndDialog(hDlg, TRUE);\r
8980       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8981       return TRUE;\r
8982     case IDCANCEL:\r
8983       EndDialog(hDlg, FALSE);\r
8984       return TRUE;\r
8985     default:\r
8986       break;\r
8987     }\r
8988     break;\r
8989   }\r
8990   return FALSE;\r
8991 }\r
8992 \r
8993 VOID\r
8994 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8995 {\r
8996     QuestionParams qp;\r
8997     FARPROC lpProc;\r
8998     \r
8999     qp.title = title;\r
9000     qp.question = question;\r
9001     qp.replyPrefix = replyPrefix;\r
9002     qp.pr = pr;\r
9003     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9004     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9005       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9006     FreeProcInstance(lpProc);\r
9007 }\r
9008 \r
9009 /* [AS] Pick FRC position */\r
9010 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9011 {\r
9012     static int * lpIndexFRC;\r
9013     BOOL index_is_ok;\r
9014     char buf[16];\r
9015 \r
9016     switch( message )\r
9017     {\r
9018     case WM_INITDIALOG:\r
9019         lpIndexFRC = (int *) lParam;\r
9020 \r
9021         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9022 \r
9023         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9024         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9025         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9026         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9027 \r
9028         break;\r
9029 \r
9030     case WM_COMMAND:\r
9031         switch( LOWORD(wParam) ) {\r
9032         case IDOK:\r
9033             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9034             EndDialog( hDlg, 0 );\r
9035             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9036             return TRUE;\r
9037         case IDCANCEL:\r
9038             EndDialog( hDlg, 1 );   \r
9039             return TRUE;\r
9040         case IDC_NFG_Edit:\r
9041             if( HIWORD(wParam) == EN_CHANGE ) {\r
9042                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9043 \r
9044                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9045             }\r
9046             return TRUE;\r
9047         case IDC_NFG_Random:\r
9048             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9049             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9050             return TRUE;\r
9051         }\r
9052 \r
9053         break;\r
9054     }\r
9055 \r
9056     return FALSE;\r
9057 }\r
9058 \r
9059 int NewGameFRC()\r
9060 {\r
9061     int result;\r
9062     int index = appData.defaultFrcPosition;\r
9063     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9064 \r
9065     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9066 \r
9067     if( result == 0 ) {\r
9068         appData.defaultFrcPosition = index;\r
9069     }\r
9070 \r
9071     return result;\r
9072 }\r
9073 \r
9074 /* [AS] Game list options */\r
9075 typedef struct {\r
9076     char id;\r
9077     char * name;\r
9078 } GLT_Item;\r
9079 \r
9080 static GLT_Item GLT_ItemInfo[] = {\r
9081     { GLT_EVENT,      "Event" },\r
9082     { GLT_SITE,       "Site" },\r
9083     { GLT_DATE,       "Date" },\r
9084     { GLT_ROUND,      "Round" },\r
9085     { GLT_PLAYERS,    "Players" },\r
9086     { GLT_RESULT,     "Result" },\r
9087     { GLT_WHITE_ELO,  "White Rating" },\r
9088     { GLT_BLACK_ELO,  "Black Rating" },\r
9089     { GLT_TIME_CONTROL,"Time Control" },\r
9090     { GLT_VARIANT,    "Variant" },\r
9091     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9092     { 0, 0 }\r
9093 };\r
9094 \r
9095 const char * GLT_FindItem( char id )\r
9096 {\r
9097     const char * result = 0;\r
9098 \r
9099     GLT_Item * list = GLT_ItemInfo;\r
9100 \r
9101     while( list->id != 0 ) {\r
9102         if( list->id == id ) {\r
9103             result = list->name;\r
9104             break;\r
9105         }\r
9106 \r
9107         list++;\r
9108     }\r
9109 \r
9110     return result;\r
9111 }\r
9112 \r
9113 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9114 {\r
9115     const char * name = GLT_FindItem( id );\r
9116 \r
9117     if( name != 0 ) {\r
9118         if( index >= 0 ) {\r
9119             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9120         }\r
9121         else {\r
9122             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9123         }\r
9124     }\r
9125 }\r
9126 \r
9127 void GLT_TagsToList( HWND hDlg, char * tags )\r
9128 {\r
9129     char * pc = tags;\r
9130 \r
9131     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9132 \r
9133     while( *pc ) {\r
9134         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9135         pc++;\r
9136     }\r
9137 \r
9138     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9139 \r
9140     pc = GLT_ALL_TAGS;\r
9141 \r
9142     while( *pc ) {\r
9143         if( strchr( tags, *pc ) == 0 ) {\r
9144             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9145         }\r
9146         pc++;\r
9147     }\r
9148 \r
9149     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9150 }\r
9151 \r
9152 char GLT_ListItemToTag( HWND hDlg, int index )\r
9153 {\r
9154     char result = '\0';\r
9155     char name[128];\r
9156 \r
9157     GLT_Item * list = GLT_ItemInfo;\r
9158 \r
9159     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9160         while( list->id != 0 ) {\r
9161             if( strcmp( list->name, name ) == 0 ) {\r
9162                 result = list->id;\r
9163                 break;\r
9164             }\r
9165 \r
9166             list++;\r
9167         }\r
9168     }\r
9169 \r
9170     return result;\r
9171 }\r
9172 \r
9173 void GLT_MoveSelection( HWND hDlg, int delta )\r
9174 {\r
9175     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9176     int idx2 = idx1 + delta;\r
9177     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9178 \r
9179     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9180         char buf[128];\r
9181 \r
9182         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9183         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9184         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9185         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9186     }\r
9187 }\r
9188 \r
9189 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9190 {\r
9191     static char glt[64];\r
9192     static char * lpUserGLT;\r
9193 \r
9194     switch( message )\r
9195     {\r
9196     case WM_INITDIALOG:\r
9197         lpUserGLT = (char *) lParam;\r
9198         \r
9199         strcpy( glt, lpUserGLT );\r
9200 \r
9201         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9202 \r
9203         /* Initialize list */\r
9204         GLT_TagsToList( hDlg, glt );\r
9205 \r
9206         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9207 \r
9208         break;\r
9209 \r
9210     case WM_COMMAND:\r
9211         switch( LOWORD(wParam) ) {\r
9212         case IDOK:\r
9213             {\r
9214                 char * pc = lpUserGLT;\r
9215                 int idx = 0;\r
9216                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9217                 char id;\r
9218 \r
9219                 do {\r
9220                     id = GLT_ListItemToTag( hDlg, idx );\r
9221 \r
9222                     *pc++ = id;\r
9223                     idx++;\r
9224                 } while( id != '\0' );\r
9225             }\r
9226             EndDialog( hDlg, 0 );\r
9227             return TRUE;\r
9228         case IDCANCEL:\r
9229             EndDialog( hDlg, 1 );\r
9230             return TRUE;\r
9231 \r
9232         case IDC_GLT_Default:\r
9233             strcpy( glt, GLT_DEFAULT_TAGS );\r
9234             GLT_TagsToList( hDlg, glt );\r
9235             return TRUE;\r
9236 \r
9237         case IDC_GLT_Restore:\r
9238             strcpy( glt, lpUserGLT );\r
9239             GLT_TagsToList( hDlg, glt );\r
9240             return TRUE;\r
9241 \r
9242         case IDC_GLT_Up:\r
9243             GLT_MoveSelection( hDlg, -1 );\r
9244             return TRUE;\r
9245 \r
9246         case IDC_GLT_Down:\r
9247             GLT_MoveSelection( hDlg, +1 );\r
9248             return TRUE;\r
9249         }\r
9250 \r
9251         break;\r
9252     }\r
9253 \r
9254     return FALSE;\r
9255 }\r
9256 \r
9257 int GameListOptions()\r
9258 {\r
9259     char glt[64];\r
9260     int result;\r
9261     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9262 \r
9263     strcpy( glt, appData.gameListTags );\r
9264 \r
9265     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9266 \r
9267     if( result == 0 ) {\r
9268         /* [AS] Memory leak here! */\r
9269         appData.gameListTags = strdup( glt ); \r
9270     }\r
9271 \r
9272     return result;\r
9273 }\r
9274 \r
9275 \r
9276 VOID\r
9277 DisplayIcsInteractionTitle(char *str)\r
9278 {\r
9279   char consoleTitle[MSG_SIZ];\r
9280 \r
9281   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9282   SetWindowText(hwndConsole, consoleTitle);\r
9283 }\r
9284 \r
9285 void\r
9286 DrawPosition(int fullRedraw, Board board)\r
9287 {\r
9288   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9289 }\r
9290 \r
9291 \r
9292 VOID\r
9293 ResetFrontEnd()\r
9294 {\r
9295   fromX = fromY = -1;\r
9296   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9297     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9298     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9299     dragInfo.lastpos = dragInfo.pos;\r
9300     dragInfo.start.x = dragInfo.start.y = -1;\r
9301     dragInfo.from = dragInfo.start;\r
9302     ReleaseCapture();\r
9303     DrawPosition(TRUE, NULL);\r
9304   }\r
9305 }\r
9306 \r
9307 \r
9308 VOID\r
9309 CommentPopUp(char *title, char *str)\r
9310 {\r
9311   HWND hwnd = GetActiveWindow();\r
9312   EitherCommentPopUp(0, title, str, FALSE);\r
9313   SetActiveWindow(hwnd);\r
9314 }\r
9315 \r
9316 VOID\r
9317 CommentPopDown(void)\r
9318 {\r
9319   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9320   if (commentDialog) {\r
9321     ShowWindow(commentDialog, SW_HIDE);\r
9322   }\r
9323   commentDialogUp = FALSE;\r
9324 }\r
9325 \r
9326 VOID\r
9327 EditCommentPopUp(int index, char *title, char *str)\r
9328 {\r
9329   EitherCommentPopUp(index, title, str, TRUE);\r
9330 }\r
9331 \r
9332 \r
9333 VOID\r
9334 RingBell()\r
9335 {\r
9336   MyPlaySound(&sounds[(int)SoundMove]);\r
9337 }\r
9338 \r
9339 VOID PlayIcsWinSound()\r
9340 {\r
9341   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9342 }\r
9343 \r
9344 VOID PlayIcsLossSound()\r
9345 {\r
9346   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9347 }\r
9348 \r
9349 VOID PlayIcsDrawSound()\r
9350 {\r
9351   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9352 }\r
9353 \r
9354 VOID PlayIcsUnfinishedSound()\r
9355 {\r
9356   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9357 }\r
9358 \r
9359 VOID\r
9360 PlayAlarmSound()\r
9361 {\r
9362   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9363 }\r
9364 \r
9365 \r
9366 VOID\r
9367 EchoOn()\r
9368 {\r
9369   HWND hInput;\r
9370   consoleEcho = TRUE;\r
9371   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9372   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9373   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9374 }\r
9375 \r
9376 \r
9377 VOID\r
9378 EchoOff()\r
9379 {\r
9380   CHARFORMAT cf;\r
9381   HWND hInput;\r
9382   consoleEcho = FALSE;\r
9383   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9384   /* This works OK: set text and background both to the same color */\r
9385   cf = consoleCF;\r
9386   cf.crTextColor = COLOR_ECHOOFF;\r
9387   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9388   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9389 }\r
9390 \r
9391 /* No Raw()...? */\r
9392 \r
9393 void Colorize(ColorClass cc, int continuation)\r
9394 {\r
9395   currentColorClass = cc;\r
9396   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9397   consoleCF.crTextColor = textAttribs[cc].color;\r
9398   consoleCF.dwEffects = textAttribs[cc].effects;\r
9399   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9400 }\r
9401 \r
9402 char *\r
9403 UserName()\r
9404 {\r
9405   static char buf[MSG_SIZ];\r
9406   DWORD bufsiz = MSG_SIZ;\r
9407 \r
9408   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9409         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9410   }\r
9411   if (!GetUserName(buf, &bufsiz)) {\r
9412     /*DisplayError("Error getting user name", GetLastError());*/\r
9413     strcpy(buf, "User");\r
9414   }\r
9415   return buf;\r
9416 }\r
9417 \r
9418 char *\r
9419 HostName()\r
9420 {\r
9421   static char buf[MSG_SIZ];\r
9422   DWORD bufsiz = MSG_SIZ;\r
9423 \r
9424   if (!GetComputerName(buf, &bufsiz)) {\r
9425     /*DisplayError("Error getting host name", GetLastError());*/\r
9426     strcpy(buf, "Unknown");\r
9427   }\r
9428   return buf;\r
9429 }\r
9430 \r
9431 \r
9432 int\r
9433 ClockTimerRunning()\r
9434 {\r
9435   return clockTimerEvent != 0;\r
9436 }\r
9437 \r
9438 int\r
9439 StopClockTimer()\r
9440 {\r
9441   if (clockTimerEvent == 0) return FALSE;\r
9442   KillTimer(hwndMain, clockTimerEvent);\r
9443   clockTimerEvent = 0;\r
9444   return TRUE;\r
9445 }\r
9446 \r
9447 void\r
9448 StartClockTimer(long millisec)\r
9449 {\r
9450   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9451                              (UINT) millisec, NULL);\r
9452 }\r
9453 \r
9454 void\r
9455 DisplayWhiteClock(long timeRemaining, int highlight)\r
9456 {\r
9457   HDC hdc;\r
9458   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9459 \r
9460   if(appData.noGUI) return;\r
9461   hdc = GetDC(hwndMain);\r
9462   if (!IsIconic(hwndMain)) {\r
9463     DisplayAClock(hdc, timeRemaining, highlight, \r
9464                         (logoHeight > 0 ? flipView: flipClock) ? &blackRect : &whiteRect, "White", flag);\r
9465   }\r
9466   if (highlight && iconCurrent == iconBlack) {\r
9467     iconCurrent = iconWhite;\r
9468     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9469     if (IsIconic(hwndMain)) {\r
9470       DrawIcon(hdc, 2, 2, iconCurrent);\r
9471     }\r
9472   }\r
9473   (void) ReleaseDC(hwndMain, hdc);\r
9474   if (hwndConsole)\r
9475     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9476 }\r
9477 \r
9478 void\r
9479 DisplayBlackClock(long timeRemaining, int highlight)\r
9480 {\r
9481   HDC hdc;\r
9482   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9483 \r
9484   if(appData.noGUI) return;\r
9485   hdc = GetDC(hwndMain);\r
9486   if (!IsIconic(hwndMain)) {\r
9487     DisplayAClock(hdc, timeRemaining, highlight, \r
9488                         (logoHeight > 0 ? flipView: flipClock) ? &whiteRect : &blackRect, "Black", flag);\r
9489   }\r
9490   if (highlight && iconCurrent == iconWhite) {\r
9491     iconCurrent = iconBlack;\r
9492     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9493     if (IsIconic(hwndMain)) {\r
9494       DrawIcon(hdc, 2, 2, iconCurrent);\r
9495     }\r
9496   }\r
9497   (void) ReleaseDC(hwndMain, hdc);\r
9498   if (hwndConsole)\r
9499     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9500 }\r
9501 \r
9502 \r
9503 int\r
9504 LoadGameTimerRunning()\r
9505 {\r
9506   return loadGameTimerEvent != 0;\r
9507 }\r
9508 \r
9509 int\r
9510 StopLoadGameTimer()\r
9511 {\r
9512   if (loadGameTimerEvent == 0) return FALSE;\r
9513   KillTimer(hwndMain, loadGameTimerEvent);\r
9514   loadGameTimerEvent = 0;\r
9515   return TRUE;\r
9516 }\r
9517 \r
9518 void\r
9519 StartLoadGameTimer(long millisec)\r
9520 {\r
9521   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9522                                 (UINT) millisec, NULL);\r
9523 }\r
9524 \r
9525 void\r
9526 AutoSaveGame()\r
9527 {\r
9528   char *defName;\r
9529   FILE *f;\r
9530   char fileTitle[MSG_SIZ];\r
9531 \r
9532   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9533   f = OpenFileDialog(hwndMain, "a", defName,\r
9534                      appData.oldSaveStyle ? "gam" : "pgn",\r
9535                      GAME_FILT, \r
9536                      "Save Game to File", NULL, fileTitle, NULL);\r
9537   if (f != NULL) {\r
9538     SaveGame(f, 0, "");\r
9539     fclose(f);\r
9540   }\r
9541 }\r
9542 \r
9543 \r
9544 void\r
9545 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9546 {\r
9547   if (delayedTimerEvent != 0) {\r
9548     if (appData.debugMode) {\r
9549       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9550     }\r
9551     KillTimer(hwndMain, delayedTimerEvent);\r
9552     delayedTimerEvent = 0;\r
9553     delayedTimerCallback();\r
9554   }\r
9555   delayedTimerCallback = cb;\r
9556   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9557                                 (UINT) millisec, NULL);\r
9558 }\r
9559 \r
9560 DelayedEventCallback\r
9561 GetDelayedEvent()\r
9562 {\r
9563   if (delayedTimerEvent) {\r
9564     return delayedTimerCallback;\r
9565   } else {\r
9566     return NULL;\r
9567   }\r
9568 }\r
9569 \r
9570 void\r
9571 CancelDelayedEvent()\r
9572 {\r
9573   if (delayedTimerEvent) {\r
9574     KillTimer(hwndMain, delayedTimerEvent);\r
9575     delayedTimerEvent = 0;\r
9576   }\r
9577 }\r
9578 \r
9579 DWORD GetWin32Priority(int nice)\r
9580 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9581 /*\r
9582 REALTIME_PRIORITY_CLASS     0x00000100\r
9583 HIGH_PRIORITY_CLASS         0x00000080\r
9584 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9585 NORMAL_PRIORITY_CLASS       0x00000020\r
9586 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9587 IDLE_PRIORITY_CLASS         0x00000040\r
9588 */\r
9589         if (nice < -15) return 0x00000080;\r
9590         if (nice < 0)   return 0x00008000;\r
9591         if (nice == 0)  return 0x00000020;\r
9592         if (nice < 15)  return 0x00004000;\r
9593         return 0x00000040;\r
9594 }\r
9595 \r
9596 /* Start a child process running the given program.\r
9597    The process's standard output can be read from "from", and its\r
9598    standard input can be written to "to".\r
9599    Exit with fatal error if anything goes wrong.\r
9600    Returns an opaque pointer that can be used to destroy the process\r
9601    later.\r
9602 */\r
9603 int\r
9604 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9605 {\r
9606 #define BUFSIZE 4096\r
9607 \r
9608   HANDLE hChildStdinRd, hChildStdinWr,\r
9609     hChildStdoutRd, hChildStdoutWr;\r
9610   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9611   SECURITY_ATTRIBUTES saAttr;\r
9612   BOOL fSuccess;\r
9613   PROCESS_INFORMATION piProcInfo;\r
9614   STARTUPINFO siStartInfo;\r
9615   ChildProc *cp;\r
9616   char buf[MSG_SIZ];\r
9617   DWORD err;\r
9618 \r
9619   if (appData.debugMode) {\r
9620     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9621   }\r
9622 \r
9623   *pr = NoProc;\r
9624 \r
9625   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9626   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9627   saAttr.bInheritHandle = TRUE;\r
9628   saAttr.lpSecurityDescriptor = NULL;\r
9629 \r
9630   /*\r
9631    * The steps for redirecting child's STDOUT:\r
9632    *     1. Create anonymous pipe to be STDOUT for child.\r
9633    *     2. Create a noninheritable duplicate of read handle,\r
9634    *         and close the inheritable read handle.\r
9635    */\r
9636 \r
9637   /* Create a pipe for the child's STDOUT. */\r
9638   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9639     return GetLastError();\r
9640   }\r
9641 \r
9642   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9643   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9644                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9645                              FALSE,     /* not inherited */\r
9646                              DUPLICATE_SAME_ACCESS);\r
9647   if (! fSuccess) {\r
9648     return GetLastError();\r
9649   }\r
9650   CloseHandle(hChildStdoutRd);\r
9651 \r
9652   /*\r
9653    * The steps for redirecting child's STDIN:\r
9654    *     1. Create anonymous pipe to be STDIN for child.\r
9655    *     2. Create a noninheritable duplicate of write handle,\r
9656    *         and close the inheritable write handle.\r
9657    */\r
9658 \r
9659   /* Create a pipe for the child's STDIN. */\r
9660   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9661     return GetLastError();\r
9662   }\r
9663 \r
9664   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9665   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9666                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9667                              FALSE,     /* not inherited */\r
9668                              DUPLICATE_SAME_ACCESS);\r
9669   if (! fSuccess) {\r
9670     return GetLastError();\r
9671   }\r
9672   CloseHandle(hChildStdinWr);\r
9673 \r
9674   /* Arrange to (1) look in dir for the child .exe file, and\r
9675    * (2) have dir be the child's working directory.  Interpret\r
9676    * dir relative to the directory WinBoard loaded from. */\r
9677   GetCurrentDirectory(MSG_SIZ, buf);\r
9678   SetCurrentDirectory(installDir);\r
9679   SetCurrentDirectory(dir);\r
9680 \r
9681   /* Now create the child process. */\r
9682 \r
9683   siStartInfo.cb = sizeof(STARTUPINFO);\r
9684   siStartInfo.lpReserved = NULL;\r
9685   siStartInfo.lpDesktop = NULL;\r
9686   siStartInfo.lpTitle = NULL;\r
9687   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9688   siStartInfo.cbReserved2 = 0;\r
9689   siStartInfo.lpReserved2 = NULL;\r
9690   siStartInfo.hStdInput = hChildStdinRd;\r
9691   siStartInfo.hStdOutput = hChildStdoutWr;\r
9692   siStartInfo.hStdError = hChildStdoutWr;\r
9693 \r
9694   fSuccess = CreateProcess(NULL,\r
9695                            cmdLine,        /* command line */\r
9696                            NULL,           /* process security attributes */\r
9697                            NULL,           /* primary thread security attrs */\r
9698                            TRUE,           /* handles are inherited */\r
9699                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9700                            NULL,           /* use parent's environment */\r
9701                            NULL,\r
9702                            &siStartInfo, /* STARTUPINFO pointer */\r
9703                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9704 \r
9705   err = GetLastError();\r
9706   SetCurrentDirectory(buf); /* return to prev directory */\r
9707   if (! fSuccess) {\r
9708     return err;\r
9709   }\r
9710 \r
9711   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9712     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9713     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9714   }\r
9715 \r
9716   /* Close the handles we don't need in the parent */\r
9717   CloseHandle(piProcInfo.hThread);\r
9718   CloseHandle(hChildStdinRd);\r
9719   CloseHandle(hChildStdoutWr);\r
9720 \r
9721   /* Prepare return value */\r
9722   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9723   cp->kind = CPReal;\r
9724   cp->hProcess = piProcInfo.hProcess;\r
9725   cp->pid = piProcInfo.dwProcessId;\r
9726   cp->hFrom = hChildStdoutRdDup;\r
9727   cp->hTo = hChildStdinWrDup;\r
9728 \r
9729   *pr = (void *) cp;\r
9730 \r
9731   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9732      2000 where engines sometimes don't see the initial command(s)\r
9733      from WinBoard and hang.  I don't understand how that can happen,\r
9734      but the Sleep is harmless, so I've put it in.  Others have also\r
9735      reported what may be the same problem, so hopefully this will fix\r
9736      it for them too.  */\r
9737   Sleep(500);\r
9738 \r
9739   return NO_ERROR;\r
9740 }\r
9741 \r
9742 \r
9743 void\r
9744 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9745 {\r
9746   ChildProc *cp; int result;\r
9747 \r
9748   cp = (ChildProc *) pr;\r
9749   if (cp == NULL) return;\r
9750 \r
9751   switch (cp->kind) {\r
9752   case CPReal:\r
9753     /* TerminateProcess is considered harmful, so... */\r
9754     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9755     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9756     /* The following doesn't work because the chess program\r
9757        doesn't "have the same console" as WinBoard.  Maybe\r
9758        we could arrange for this even though neither WinBoard\r
9759        nor the chess program uses a console for stdio? */\r
9760     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9761 \r
9762     /* [AS] Special termination modes for misbehaving programs... */\r
9763     if( signal == 9 ) { \r
9764         result = TerminateProcess( cp->hProcess, 0 );\r
9765 \r
9766         if ( appData.debugMode) {\r
9767             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9768         }\r
9769     }\r
9770     else if( signal == 10 ) {\r
9771         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9772 \r
9773         if( dw != WAIT_OBJECT_0 ) {\r
9774             result = TerminateProcess( cp->hProcess, 0 );\r
9775 \r
9776             if ( appData.debugMode) {\r
9777                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9778             }\r
9779 \r
9780         }\r
9781     }\r
9782 \r
9783     CloseHandle(cp->hProcess);\r
9784     break;\r
9785 \r
9786   case CPComm:\r
9787     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9788     break;\r
9789 \r
9790   case CPSock:\r
9791     closesocket(cp->sock);\r
9792     WSACleanup();\r
9793     break;\r
9794 \r
9795   case CPRcmd:\r
9796     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9797     closesocket(cp->sock);\r
9798     closesocket(cp->sock2);\r
9799     WSACleanup();\r
9800     break;\r
9801   }\r
9802   free(cp);\r
9803 }\r
9804 \r
9805 void\r
9806 InterruptChildProcess(ProcRef pr)\r
9807 {\r
9808   ChildProc *cp;\r
9809 \r
9810   cp = (ChildProc *) pr;\r
9811   if (cp == NULL) return;\r
9812   switch (cp->kind) {\r
9813   case CPReal:\r
9814     /* The following doesn't work because the chess program\r
9815        doesn't "have the same console" as WinBoard.  Maybe\r
9816        we could arrange for this even though neither WinBoard\r
9817        nor the chess program uses a console for stdio */\r
9818     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9819     break;\r
9820 \r
9821   case CPComm:\r
9822   case CPSock:\r
9823     /* Can't interrupt */\r
9824     break;\r
9825 \r
9826   case CPRcmd:\r
9827     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9828     break;\r
9829   }\r
9830 }\r
9831 \r
9832 \r
9833 int\r
9834 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9835 {\r
9836   char cmdLine[MSG_SIZ];\r
9837 \r
9838   if (port[0] == NULLCHAR) {\r
9839     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9840   } else {\r
9841     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9842   }\r
9843   return StartChildProcess(cmdLine, "", pr);\r
9844 }\r
9845 \r
9846 \r
9847 /* Code to open TCP sockets */\r
9848 \r
9849 int\r
9850 OpenTCP(char *host, char *port, ProcRef *pr)\r
9851 {\r
9852   ChildProc *cp;\r
9853   int err;\r
9854   SOCKET s;\r
9855   struct sockaddr_in sa, mysa;\r
9856   struct hostent FAR *hp;\r
9857   unsigned short uport;\r
9858   WORD wVersionRequested;\r
9859   WSADATA wsaData;\r
9860 \r
9861   /* Initialize socket DLL */\r
9862   wVersionRequested = MAKEWORD(1, 1);\r
9863   err = WSAStartup(wVersionRequested, &wsaData);\r
9864   if (err != 0) return err;\r
9865 \r
9866   /* Make socket */\r
9867   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9868     err = WSAGetLastError();\r
9869     WSACleanup();\r
9870     return err;\r
9871   }\r
9872 \r
9873   /* Bind local address using (mostly) don't-care values.\r
9874    */\r
9875   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9876   mysa.sin_family = AF_INET;\r
9877   mysa.sin_addr.s_addr = INADDR_ANY;\r
9878   uport = (unsigned short) 0;\r
9879   mysa.sin_port = htons(uport);\r
9880   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9881       == SOCKET_ERROR) {\r
9882     err = WSAGetLastError();\r
9883     WSACleanup();\r
9884     return err;\r
9885   }\r
9886 \r
9887   /* Resolve remote host name */\r
9888   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9889   if (!(hp = gethostbyname(host))) {\r
9890     unsigned int b0, b1, b2, b3;\r
9891 \r
9892     err = WSAGetLastError();\r
9893 \r
9894     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9895       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9896       hp->h_addrtype = AF_INET;\r
9897       hp->h_length = 4;\r
9898       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9899       hp->h_addr_list[0] = (char *) malloc(4);\r
9900       hp->h_addr_list[0][0] = (char) b0;\r
9901       hp->h_addr_list[0][1] = (char) b1;\r
9902       hp->h_addr_list[0][2] = (char) b2;\r
9903       hp->h_addr_list[0][3] = (char) b3;\r
9904     } else {\r
9905       WSACleanup();\r
9906       return err;\r
9907     }\r
9908   }\r
9909   sa.sin_family = hp->h_addrtype;\r
9910   uport = (unsigned short) atoi(port);\r
9911   sa.sin_port = htons(uport);\r
9912   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9913 \r
9914   /* Make connection */\r
9915   if (connect(s, (struct sockaddr *) &sa,\r
9916               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9917     err = WSAGetLastError();\r
9918     WSACleanup();\r
9919     return err;\r
9920   }\r
9921 \r
9922   /* Prepare return value */\r
9923   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9924   cp->kind = CPSock;\r
9925   cp->sock = s;\r
9926   *pr = (ProcRef *) cp;\r
9927 \r
9928   return NO_ERROR;\r
9929 }\r
9930 \r
9931 int\r
9932 OpenCommPort(char *name, ProcRef *pr)\r
9933 {\r
9934   HANDLE h;\r
9935   COMMTIMEOUTS ct;\r
9936   ChildProc *cp;\r
9937   char fullname[MSG_SIZ];\r
9938 \r
9939   if (*name != '\\')\r
9940     sprintf(fullname, "\\\\.\\%s", name);\r
9941   else\r
9942     strcpy(fullname, name);\r
9943 \r
9944   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9945                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9946   if (h == (HANDLE) -1) {\r
9947     return GetLastError();\r
9948   }\r
9949   hCommPort = h;\r
9950 \r
9951   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9952 \r
9953   /* Accumulate characters until a 100ms pause, then parse */\r
9954   ct.ReadIntervalTimeout = 100;\r
9955   ct.ReadTotalTimeoutMultiplier = 0;\r
9956   ct.ReadTotalTimeoutConstant = 0;\r
9957   ct.WriteTotalTimeoutMultiplier = 0;\r
9958   ct.WriteTotalTimeoutConstant = 0;\r
9959   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9960 \r
9961   /* Prepare return value */\r
9962   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9963   cp->kind = CPComm;\r
9964   cp->hFrom = h;\r
9965   cp->hTo = h;\r
9966   *pr = (ProcRef *) cp;\r
9967 \r
9968   return NO_ERROR;\r
9969 }\r
9970 \r
9971 int\r
9972 OpenLoopback(ProcRef *pr)\r
9973 {\r
9974   DisplayFatalError("Not implemented", 0, 1);\r
9975   return NO_ERROR;\r
9976 }\r
9977 \r
9978 \r
9979 int\r
9980 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9981 {\r
9982   ChildProc *cp;\r
9983   int err;\r
9984   SOCKET s, s2, s3;\r
9985   struct sockaddr_in sa, mysa;\r
9986   struct hostent FAR *hp;\r
9987   unsigned short uport;\r
9988   WORD wVersionRequested;\r
9989   WSADATA wsaData;\r
9990   int fromPort;\r
9991   char stderrPortStr[MSG_SIZ];\r
9992 \r
9993   /* Initialize socket DLL */\r
9994   wVersionRequested = MAKEWORD(1, 1);\r
9995   err = WSAStartup(wVersionRequested, &wsaData);\r
9996   if (err != 0) return err;\r
9997 \r
9998   /* Resolve remote host name */\r
9999   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10000   if (!(hp = gethostbyname(host))) {\r
10001     unsigned int b0, b1, b2, b3;\r
10002 \r
10003     err = WSAGetLastError();\r
10004 \r
10005     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10006       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10007       hp->h_addrtype = AF_INET;\r
10008       hp->h_length = 4;\r
10009       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10010       hp->h_addr_list[0] = (char *) malloc(4);\r
10011       hp->h_addr_list[0][0] = (char) b0;\r
10012       hp->h_addr_list[0][1] = (char) b1;\r
10013       hp->h_addr_list[0][2] = (char) b2;\r
10014       hp->h_addr_list[0][3] = (char) b3;\r
10015     } else {\r
10016       WSACleanup();\r
10017       return err;\r
10018     }\r
10019   }\r
10020   sa.sin_family = hp->h_addrtype;\r
10021   uport = (unsigned short) 514;\r
10022   sa.sin_port = htons(uport);\r
10023   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10024 \r
10025   /* Bind local socket to unused "privileged" port address\r
10026    */\r
10027   s = INVALID_SOCKET;\r
10028   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10029   mysa.sin_family = AF_INET;\r
10030   mysa.sin_addr.s_addr = INADDR_ANY;\r
10031   for (fromPort = 1023;; fromPort--) {\r
10032     if (fromPort < 0) {\r
10033       WSACleanup();\r
10034       return WSAEADDRINUSE;\r
10035     }\r
10036     if (s == INVALID_SOCKET) {\r
10037       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10038         err = WSAGetLastError();\r
10039         WSACleanup();\r
10040         return err;\r
10041       }\r
10042     }\r
10043     uport = (unsigned short) fromPort;\r
10044     mysa.sin_port = htons(uport);\r
10045     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10046         == SOCKET_ERROR) {\r
10047       err = WSAGetLastError();\r
10048       if (err == WSAEADDRINUSE) continue;\r
10049       WSACleanup();\r
10050       return err;\r
10051     }\r
10052     if (connect(s, (struct sockaddr *) &sa,\r
10053       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10054       err = WSAGetLastError();\r
10055       if (err == WSAEADDRINUSE) {\r
10056         closesocket(s);\r
10057         s = -1;\r
10058         continue;\r
10059       }\r
10060       WSACleanup();\r
10061       return err;\r
10062     }\r
10063     break;\r
10064   }\r
10065 \r
10066   /* Bind stderr local socket to unused "privileged" port address\r
10067    */\r
10068   s2 = INVALID_SOCKET;\r
10069   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10070   mysa.sin_family = AF_INET;\r
10071   mysa.sin_addr.s_addr = INADDR_ANY;\r
10072   for (fromPort = 1023;; fromPort--) {\r
10073     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10074     if (fromPort < 0) {\r
10075       (void) closesocket(s);\r
10076       WSACleanup();\r
10077       return WSAEADDRINUSE;\r
10078     }\r
10079     if (s2 == INVALID_SOCKET) {\r
10080       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10081         err = WSAGetLastError();\r
10082         closesocket(s);\r
10083         WSACleanup();\r
10084         return err;\r
10085       }\r
10086     }\r
10087     uport = (unsigned short) fromPort;\r
10088     mysa.sin_port = htons(uport);\r
10089     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10090         == SOCKET_ERROR) {\r
10091       err = WSAGetLastError();\r
10092       if (err == WSAEADDRINUSE) continue;\r
10093       (void) closesocket(s);\r
10094       WSACleanup();\r
10095       return err;\r
10096     }\r
10097     if (listen(s2, 1) == SOCKET_ERROR) {\r
10098       err = WSAGetLastError();\r
10099       if (err == WSAEADDRINUSE) {\r
10100         closesocket(s2);\r
10101         s2 = INVALID_SOCKET;\r
10102         continue;\r
10103       }\r
10104       (void) closesocket(s);\r
10105       (void) closesocket(s2);\r
10106       WSACleanup();\r
10107       return err;\r
10108     }\r
10109     break;\r
10110   }\r
10111   prevStderrPort = fromPort; // remember port used\r
10112   sprintf(stderrPortStr, "%d", fromPort);\r
10113 \r
10114   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10115     err = WSAGetLastError();\r
10116     (void) closesocket(s);\r
10117     (void) closesocket(s2);\r
10118     WSACleanup();\r
10119     return err;\r
10120   }\r
10121 \r
10122   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10123     err = WSAGetLastError();\r
10124     (void) closesocket(s);\r
10125     (void) closesocket(s2);\r
10126     WSACleanup();\r
10127     return err;\r
10128   }\r
10129   if (*user == NULLCHAR) user = UserName();\r
10130   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10131     err = WSAGetLastError();\r
10132     (void) closesocket(s);\r
10133     (void) closesocket(s2);\r
10134     WSACleanup();\r
10135     return err;\r
10136   }\r
10137   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10138     err = WSAGetLastError();\r
10139     (void) closesocket(s);\r
10140     (void) closesocket(s2);\r
10141     WSACleanup();\r
10142     return err;\r
10143   }\r
10144 \r
10145   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10146     err = WSAGetLastError();\r
10147     (void) closesocket(s);\r
10148     (void) closesocket(s2);\r
10149     WSACleanup();\r
10150     return err;\r
10151   }\r
10152   (void) closesocket(s2);  /* Stop listening */\r
10153 \r
10154   /* Prepare return value */\r
10155   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10156   cp->kind = CPRcmd;\r
10157   cp->sock = s;\r
10158   cp->sock2 = s3;\r
10159   *pr = (ProcRef *) cp;\r
10160 \r
10161   return NO_ERROR;\r
10162 }\r
10163 \r
10164 \r
10165 InputSourceRef\r
10166 AddInputSource(ProcRef pr, int lineByLine,\r
10167                InputCallback func, VOIDSTAR closure)\r
10168 {\r
10169   InputSource *is, *is2 = NULL;\r
10170   ChildProc *cp = (ChildProc *) pr;\r
10171 \r
10172   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10173   is->lineByLine = lineByLine;\r
10174   is->func = func;\r
10175   is->closure = closure;\r
10176   is->second = NULL;\r
10177   is->next = is->buf;\r
10178   if (pr == NoProc) {\r
10179     is->kind = CPReal;\r
10180     consoleInputSource = is;\r
10181   } else {\r
10182     is->kind = cp->kind;\r
10183     /* \r
10184         [AS] Try to avoid a race condition if the thread is given control too early:\r
10185         we create all threads suspended so that the is->hThread variable can be\r
10186         safely assigned, then let the threads start with ResumeThread.\r
10187     */\r
10188     switch (cp->kind) {\r
10189     case CPReal:\r
10190       is->hFile = cp->hFrom;\r
10191       cp->hFrom = NULL; /* now owned by InputThread */\r
10192       is->hThread =\r
10193         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10194                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10195       break;\r
10196 \r
10197     case CPComm:\r
10198       is->hFile = cp->hFrom;\r
10199       cp->hFrom = NULL; /* now owned by InputThread */\r
10200       is->hThread =\r
10201         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10202                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10203       break;\r
10204 \r
10205     case CPSock:\r
10206       is->sock = cp->sock;\r
10207       is->hThread =\r
10208         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10209                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10210       break;\r
10211 \r
10212     case CPRcmd:\r
10213       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10214       *is2 = *is;\r
10215       is->sock = cp->sock;\r
10216       is->second = is2;\r
10217       is2->sock = cp->sock2;\r
10218       is2->second = is2;\r
10219       is->hThread =\r
10220         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10221                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10222       is2->hThread =\r
10223         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10224                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10225       break;\r
10226     }\r
10227 \r
10228     if( is->hThread != NULL ) {\r
10229         ResumeThread( is->hThread );\r
10230     }\r
10231 \r
10232     if( is2 != NULL && is2->hThread != NULL ) {\r
10233         ResumeThread( is2->hThread );\r
10234     }\r
10235   }\r
10236 \r
10237   return (InputSourceRef) is;\r
10238 }\r
10239 \r
10240 void\r
10241 RemoveInputSource(InputSourceRef isr)\r
10242 {\r
10243   InputSource *is;\r
10244 \r
10245   is = (InputSource *) isr;\r
10246   is->hThread = NULL;  /* tell thread to stop */\r
10247   CloseHandle(is->hThread);\r
10248   if (is->second != NULL) {\r
10249     is->second->hThread = NULL;\r
10250     CloseHandle(is->second->hThread);\r
10251   }\r
10252 }\r
10253 \r
10254 \r
10255 int\r
10256 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10257 {\r
10258   DWORD dOutCount;\r
10259   int outCount = SOCKET_ERROR;\r
10260   ChildProc *cp = (ChildProc *) pr;\r
10261   static OVERLAPPED ovl;\r
10262 \r
10263   if (pr == NoProc) {\r
10264     ConsoleOutput(message, count, FALSE);\r
10265     return count;\r
10266   } \r
10267 \r
10268   if (ovl.hEvent == NULL) {\r
10269     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10270   }\r
10271   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10272 \r
10273   switch (cp->kind) {\r
10274   case CPSock:\r
10275   case CPRcmd:\r
10276     outCount = send(cp->sock, message, count, 0);\r
10277     if (outCount == SOCKET_ERROR) {\r
10278       *outError = WSAGetLastError();\r
10279     } else {\r
10280       *outError = NO_ERROR;\r
10281     }\r
10282     break;\r
10283 \r
10284   case CPReal:\r
10285     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10286                   &dOutCount, NULL)) {\r
10287       *outError = NO_ERROR;\r
10288       outCount = (int) dOutCount;\r
10289     } else {\r
10290       *outError = GetLastError();\r
10291     }\r
10292     break;\r
10293 \r
10294   case CPComm:\r
10295     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10296                             &dOutCount, &ovl);\r
10297     if (*outError == NO_ERROR) {\r
10298       outCount = (int) dOutCount;\r
10299     }\r
10300     break;\r
10301   }\r
10302   return outCount;\r
10303 }\r
10304 \r
10305 int\r
10306 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10307                        long msdelay)\r
10308 {\r
10309   /* Ignore delay, not implemented for WinBoard */\r
10310   return OutputToProcess(pr, message, count, outError);\r
10311 }\r
10312 \r
10313 \r
10314 void\r
10315 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10316                         char *buf, int count, int error)\r
10317 {\r
10318   DisplayFatalError("Not implemented", 0, 1);\r
10319 }\r
10320 \r
10321 /* see wgamelist.c for Game List functions */\r
10322 /* see wedittags.c for Edit Tags functions */\r
10323 \r
10324 \r
10325 VOID\r
10326 ICSInitScript()\r
10327 {\r
10328   FILE *f;\r
10329   char buf[MSG_SIZ];\r
10330   char *dummy;\r
10331 \r
10332   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10333     f = fopen(buf, "r");\r
10334     if (f != NULL) {\r
10335       ProcessICSInitScript(f);\r
10336       fclose(f);\r
10337     }\r
10338   }\r
10339 }\r
10340 \r
10341 \r
10342 VOID\r
10343 StartAnalysisClock()\r
10344 {\r
10345   if (analysisTimerEvent) return;\r
10346   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10347                                         (UINT) 2000, NULL);\r
10348 }\r
10349 \r
10350 LRESULT CALLBACK\r
10351 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10352 {\r
10353   static HANDLE hwndText;\r
10354   RECT rect;\r
10355   static int sizeX, sizeY;\r
10356   int newSizeX, newSizeY, flags;\r
10357   MINMAXINFO *mmi;\r
10358 \r
10359   switch (message) {\r
10360   case WM_INITDIALOG: /* message: initialize dialog box */\r
10361     /* Initialize the dialog items */\r
10362     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10363     SetWindowText(hDlg, analysisTitle);\r
10364     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10365     /* Size and position the dialog */\r
10366     if (!analysisDialog) {\r
10367       analysisDialog = hDlg;\r
10368       flags = SWP_NOZORDER;\r
10369       GetClientRect(hDlg, &rect);\r
10370       sizeX = rect.right;\r
10371       sizeY = rect.bottom;\r
10372       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10373           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10374         WINDOWPLACEMENT wp;\r
10375         EnsureOnScreen(&analysisX, &analysisY);\r
10376         wp.length = sizeof(WINDOWPLACEMENT);\r
10377         wp.flags = 0;\r
10378         wp.showCmd = SW_SHOW;\r
10379         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10380         wp.rcNormalPosition.left = analysisX;\r
10381         wp.rcNormalPosition.right = analysisX + analysisW;\r
10382         wp.rcNormalPosition.top = analysisY;\r
10383         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10384         SetWindowPlacement(hDlg, &wp);\r
10385 \r
10386         GetClientRect(hDlg, &rect);\r
10387         newSizeX = rect.right;\r
10388         newSizeY = rect.bottom;\r
10389         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10390                               newSizeX, newSizeY);\r
10391         sizeX = newSizeX;\r
10392         sizeY = newSizeY;\r
10393       }\r
10394     }\r
10395     return FALSE;\r
10396 \r
10397   case WM_COMMAND: /* message: received a command */\r
10398     switch (LOWORD(wParam)) {\r
10399     case IDCANCEL:\r
10400       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10401           ExitAnalyzeMode();\r
10402           ModeHighlight();\r
10403           return TRUE;\r
10404       }\r
10405       EditGameEvent();\r
10406       return TRUE;\r
10407     default:\r
10408       break;\r
10409     }\r
10410     break;\r
10411 \r
10412   case WM_SIZE:\r
10413     newSizeX = LOWORD(lParam);\r
10414     newSizeY = HIWORD(lParam);\r
10415     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10416     sizeX = newSizeX;\r
10417     sizeY = newSizeY;\r
10418     break;\r
10419 \r
10420   case WM_GETMINMAXINFO:\r
10421     /* Prevent resizing window too small */\r
10422     mmi = (MINMAXINFO *) lParam;\r
10423     mmi->ptMinTrackSize.x = 100;\r
10424     mmi->ptMinTrackSize.y = 100;\r
10425     break;\r
10426   }\r
10427   return FALSE;\r
10428 }\r
10429 \r
10430 VOID\r
10431 AnalysisPopUp(char* title, char* str)\r
10432 {\r
10433   FARPROC lpProc;\r
10434   char *p, *q;\r
10435 \r
10436   /* [AS] */\r
10437   EngineOutputPopUp();\r
10438   return;\r
10439 \r
10440   if (str == NULL) str = "";\r
10441   p = (char *) malloc(2 * strlen(str) + 2);\r
10442   q = p;\r
10443   while (*str) {\r
10444     if (*str == '\n') *q++ = '\r';\r
10445     *q++ = *str++;\r
10446   }\r
10447   *q = NULLCHAR;\r
10448   if (analysisText != NULL) free(analysisText);\r
10449   analysisText = p;\r
10450 \r
10451   if (analysisDialog) {\r
10452     SetWindowText(analysisDialog, title);\r
10453     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10454     ShowWindow(analysisDialog, SW_SHOW);\r
10455   } else {\r
10456     analysisTitle = title;\r
10457     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10458     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10459                  hwndMain, (DLGPROC)lpProc);\r
10460     FreeProcInstance(lpProc);\r
10461   }\r
10462   analysisDialogUp = TRUE;  \r
10463 }\r
10464 \r
10465 VOID\r
10466 AnalysisPopDown()\r
10467 {\r
10468   if (analysisDialog) {\r
10469     ShowWindow(analysisDialog, SW_HIDE);\r
10470   }\r
10471   analysisDialogUp = FALSE;  \r
10472 }\r
10473 \r
10474 \r
10475 VOID\r
10476 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10477 {\r
10478   highlightInfo.sq[0].x = fromX;\r
10479   highlightInfo.sq[0].y = fromY;\r
10480   highlightInfo.sq[1].x = toX;\r
10481   highlightInfo.sq[1].y = toY;\r
10482 }\r
10483 \r
10484 VOID\r
10485 ClearHighlights()\r
10486 {\r
10487   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10488     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10489 }\r
10490 \r
10491 VOID\r
10492 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10493 {\r
10494   premoveHighlightInfo.sq[0].x = fromX;\r
10495   premoveHighlightInfo.sq[0].y = fromY;\r
10496   premoveHighlightInfo.sq[1].x = toX;\r
10497   premoveHighlightInfo.sq[1].y = toY;\r
10498 }\r
10499 \r
10500 VOID\r
10501 ClearPremoveHighlights()\r
10502 {\r
10503   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10504     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10505 }\r
10506 \r
10507 VOID\r
10508 ShutDownFrontEnd()\r
10509 {\r
10510   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10511   DeleteClipboardTempFiles();\r
10512 }\r
10513 \r
10514 void\r
10515 BoardToTop()\r
10516 {\r
10517     if (IsIconic(hwndMain))\r
10518       ShowWindow(hwndMain, SW_RESTORE);\r
10519 \r
10520     SetActiveWindow(hwndMain);\r
10521 }\r
10522 \r
10523 /*\r
10524  * Prototypes for animation support routines\r
10525  */\r
10526 static void ScreenSquare(int column, int row, POINT * pt);\r
10527 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10528      POINT frames[], int * nFrames);\r
10529 \r
10530 \r
10531 #define kFactor 4\r
10532 \r
10533 void\r
10534 AnimateMove(board, fromX, fromY, toX, toY)\r
10535      Board board;\r
10536      int fromX;\r
10537      int fromY;\r
10538      int toX;\r
10539      int toY;\r
10540 {\r
10541   ChessSquare piece;\r
10542   POINT start, finish, mid;\r
10543   POINT frames[kFactor * 2 + 1];\r
10544   int nFrames, n;\r
10545 \r
10546   if (!appData.animate) return;\r
10547   if (doingSizing) return;\r
10548   if (fromY < 0 || fromX < 0) return;\r
10549   piece = board[fromY][fromX];\r
10550   if (piece >= EmptySquare) return;\r
10551 \r
10552   ScreenSquare(fromX, fromY, &start);\r
10553   ScreenSquare(toX, toY, &finish);\r
10554 \r
10555   /* All pieces except knights move in straight line */\r
10556   if (piece != WhiteKnight && piece != BlackKnight) {\r
10557     mid.x = start.x + (finish.x - start.x) / 2;\r
10558     mid.y = start.y + (finish.y - start.y) / 2;\r
10559   } else {\r
10560     /* Knight: make diagonal movement then straight */\r
10561     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10562        mid.x = start.x + (finish.x - start.x) / 2;\r
10563        mid.y = finish.y;\r
10564      } else {\r
10565        mid.x = finish.x;\r
10566        mid.y = start.y + (finish.y - start.y) / 2;\r
10567      }\r
10568   }\r
10569   \r
10570   /* Don't use as many frames for very short moves */\r
10571   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10572     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10573   else\r
10574     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10575 \r
10576   animInfo.from.x = fromX;\r
10577   animInfo.from.y = fromY;\r
10578   animInfo.to.x = toX;\r
10579   animInfo.to.y = toY;\r
10580   animInfo.lastpos = start;\r
10581   animInfo.piece = piece;\r
10582   for (n = 0; n < nFrames; n++) {\r
10583     animInfo.pos = frames[n];\r
10584     DrawPosition(FALSE, NULL);\r
10585     animInfo.lastpos = animInfo.pos;\r
10586     Sleep(appData.animSpeed);\r
10587   }\r
10588   animInfo.pos = finish;\r
10589   DrawPosition(FALSE, NULL);\r
10590   animInfo.piece = EmptySquare;\r
10591 }\r
10592 \r
10593 /*      Convert board position to corner of screen rect and color       */\r
10594 \r
10595 static void\r
10596 ScreenSquare(column, row, pt)\r
10597      int column; int row; POINT * pt;\r
10598 {\r
10599   if (flipView) {\r
10600     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10601     pt->y = lineGap + row * (squareSize + lineGap);\r
10602   } else {\r
10603     pt->x = lineGap + column * (squareSize + lineGap);\r
10604     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10605   }\r
10606 }\r
10607 \r
10608 /*      Generate a series of frame coords from start->mid->finish.\r
10609         The movement rate doubles until the half way point is\r
10610         reached, then halves back down to the final destination,\r
10611         which gives a nice slow in/out effect. The algorithmn\r
10612         may seem to generate too many intermediates for short\r
10613         moves, but remember that the purpose is to attract the\r
10614         viewers attention to the piece about to be moved and\r
10615         then to where it ends up. Too few frames would be less\r
10616         noticeable.                                             */\r
10617 \r
10618 static void\r
10619 Tween(start, mid, finish, factor, frames, nFrames)\r
10620      POINT * start; POINT * mid;\r
10621      POINT * finish; int factor;\r
10622      POINT frames[]; int * nFrames;\r
10623 {\r
10624   int n, fraction = 1, count = 0;\r
10625 \r
10626   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10627   for (n = 0; n < factor; n++)\r
10628     fraction *= 2;\r
10629   for (n = 0; n < factor; n++) {\r
10630     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10631     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10632     count ++;\r
10633     fraction = fraction / 2;\r
10634   }\r
10635   \r
10636   /* Midpoint */\r
10637   frames[count] = *mid;\r
10638   count ++;\r
10639   \r
10640   /* Slow out, stepping 1/2, then 1/4, ... */\r
10641   fraction = 2;\r
10642   for (n = 0; n < factor; n++) {\r
10643     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10644     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10645     count ++;\r
10646     fraction = fraction * 2;\r
10647   }\r
10648   *nFrames = count;\r
10649 }\r
10650 \r
10651 void\r
10652 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10653 {\r
10654 #if 0\r
10655     char buf[256];\r
10656 \r
10657     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10658         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10659 \r
10660     OutputDebugString( buf );\r
10661 #endif\r
10662 \r
10663     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10664 \r
10665     EvalGraphSet( first, last, current, pvInfoList );\r
10666 }\r
10667 \r
10668 void SetProgramStats( FrontEndProgramStats * stats )\r
10669 {\r
10670 #if 0\r
10671     char buf[1024];\r
10672 \r
10673     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10674         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10675 \r
10676     OutputDebugString( buf );\r
10677 #endif\r
10678 \r
10679     EngineOutputUpdate( stats );\r
10680 }\r