major overhaul of the -stickyWindows feature
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
107 \r
108 typedef struct {\r
109   ChessSquare piece;  \r
110   POINT pos;      /* window coordinates of current pos */\r
111   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
112   POINT from;     /* board coordinates of the piece's orig pos */\r
113   POINT to;       /* board coordinates of the piece's new pos */\r
114 } AnimInfo;\r
115 \r
116 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT start;    /* window coordinates of start pos */\r
120   POINT pos;      /* window coordinates of current pos */\r
121   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
122   POINT from;     /* board coordinates of the piece's orig pos */\r
123 } DragInfo;\r
124 \r
125 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
126 \r
127 typedef struct {\r
128   POINT sq[2];    /* board coordinates of from, to squares */\r
129 } HighlightInfo;\r
130 \r
131 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
132 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
133 \r
134 typedef struct { // [HGM] atomic\r
135   int fromX, fromY, toX, toY, radius;\r
136 } ExplodeInfo;\r
137 \r
138 static ExplodeInfo explodeInfo;\r
139 \r
140 /* Window class names */\r
141 char szAppName[] = "WinBoard";\r
142 char szConsoleName[] = "WBConsole";\r
143 \r
144 /* Title bar text */\r
145 char szTitle[] = "WinBoard";\r
146 char szConsoleTitle[] = "ICS Interaction";\r
147 \r
148 char *programName;\r
149 char *settingsFileName;\r
150 BOOLEAN saveSettingsOnExit;\r
151 char installDir[MSG_SIZ];\r
152 \r
153 BoardSize boardSize;\r
154 BOOLEAN chessProgram;\r
155 static int boardX, boardY;\r
156 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
157 static int squareSize, lineGap, minorSize;\r
158 static int winWidth, winHeight;\r
159 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
160 static int logoHeight = 0;\r
161 static char messageText[MESSAGE_TEXT_MAX];\r
162 static int clockTimerEvent = 0;\r
163 static int loadGameTimerEvent = 0;\r
164 static int analysisTimerEvent = 0;\r
165 static DelayedEventCallback delayedTimerCallback;\r
166 static int delayedTimerEvent = 0;\r
167 static int buttonCount = 2;\r
168 char *icsTextMenuString;\r
169 char *icsNames;\r
170 char *firstChessProgramNames;\r
171 char *secondChessProgramNames;\r
172 \r
173 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
174 \r
175 #define PALETTESIZE 256\r
176 \r
177 HINSTANCE hInst;          /* current instance */\r
178 HWND hwndMain = NULL;        /* root window*/\r
179 HWND hwndConsole = NULL;\r
180 BOOLEAN alwaysOnTop = FALSE;\r
181 RECT boardRect;\r
182 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
183   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
184 HPALETTE hPal;\r
185 ColorClass currentColorClass;\r
186 \r
187 HWND hCommPort = NULL;    /* currently open comm port */\r
188 static HWND hwndPause;    /* pause button */\r
189 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
190 static HBRUSH lightSquareBrush, darkSquareBrush,\r
191   blackSquareBrush, /* [HGM] for band between board and holdings */\r
192   explodeBrush,     /* [HGM] atomic */\r
193   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
194 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
195 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
196 static HPEN gridPen = NULL;\r
197 static HPEN highlightPen = NULL;\r
198 static HPEN premovePen = NULL;\r
199 static NPLOGPALETTE pLogPal;\r
200 static BOOL paletteChanged = FALSE;\r
201 static HICON iconWhite, iconBlack, iconCurrent;\r
202 static int doingSizing = FALSE;\r
203 static int lastSizing = 0;\r
204 static int prevStderrPort;\r
205 static HBITMAP userLogo;\r
206 \r
207 /* [AS] Support for background textures */\r
208 #define BACK_TEXTURE_MODE_DISABLED      0\r
209 #define BACK_TEXTURE_MODE_PLAIN         1\r
210 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
211 \r
212 static HBITMAP liteBackTexture = NULL;\r
213 static HBITMAP darkBackTexture = NULL;\r
214 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
215 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int backTextureSquareSize = 0;\r
217 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
218 \r
219 #if __GNUC__ && !defined(_winmajor)\r
220 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
221 #else\r
222 #define oldDialog (_winmajor < 4)\r
223 #endif\r
224 \r
225 char *defaultTextAttribs[] = \r
226 {\r
227   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
228   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
229   COLOR_NONE\r
230 };\r
231 \r
232 typedef struct {\r
233   char *name;\r
234   int squareSize;\r
235   int lineGap;\r
236   int smallLayout;\r
237   int tinyLayout;\r
238   int cliWidth, cliHeight;\r
239 } SizeInfo;\r
240 \r
241 SizeInfo sizeInfo[] = \r
242 {\r
243   { "tiny",     21, 0, 1, 1, 0, 0 },\r
244   { "teeny",    25, 1, 1, 1, 0, 0 },\r
245   { "dinky",    29, 1, 1, 1, 0, 0 },\r
246   { "petite",   33, 1, 1, 1, 0, 0 },\r
247   { "slim",     37, 2, 1, 0, 0, 0 },\r
248   { "small",    40, 2, 1, 0, 0, 0 },\r
249   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
250   { "middling", 49, 2, 0, 0, 0, 0 },\r
251   { "average",  54, 2, 0, 0, 0, 0 },\r
252   { "moderate", 58, 3, 0, 0, 0, 0 },\r
253   { "medium",   64, 3, 0, 0, 0, 0 },\r
254   { "bulky",    72, 3, 0, 0, 0, 0 },\r
255   { "large",    80, 3, 0, 0, 0, 0 },\r
256   { "big",      87, 3, 0, 0, 0, 0 },\r
257   { "huge",     95, 3, 0, 0, 0, 0 },\r
258   { "giant",    108, 3, 0, 0, 0, 0 },\r
259   { "colossal", 116, 4, 0, 0, 0, 0 },\r
260   { "titanic",  129, 4, 0, 0, 0, 0 },\r
261   { NULL, 0, 0, 0, 0, 0, 0 }\r
262 };\r
263 \r
264 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
265 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
266 {\r
267   { 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
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285 };\r
286 \r
287 MyFont *font[NUM_SIZES][NUM_FONTS];\r
288 \r
289 typedef struct {\r
290   char *label;\r
291   int id;\r
292   HWND hwnd;\r
293   WNDPROC wndproc;\r
294 } MyButtonDesc;\r
295 \r
296 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
297 #define N_BUTTONS 5\r
298 \r
299 MyButtonDesc buttonDesc[N_BUTTONS] =\r
300 {\r
301   {"<<", IDM_ToStart, NULL, NULL},\r
302   {"<", IDM_Backward, NULL, NULL},\r
303   {"P", IDM_Pause, NULL, NULL},\r
304   {">", IDM_Forward, NULL, NULL},\r
305   {">>", IDM_ToEnd, NULL, NULL},\r
306 };\r
307 \r
308 int tinyLayout = 0, smallLayout = 0;\r
309 #define MENU_BAR_ITEMS 6\r
310 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
311   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
312   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
313 };\r
314 \r
315 \r
316 MySound sounds[(int)NSoundClasses];\r
317 MyTextAttribs textAttribs[(int)NColorClasses];\r
318 \r
319 MyColorizeAttribs colorizeAttribs[] = {\r
320   { (COLORREF)0, 0, "Shout Text" },\r
321   { (COLORREF)0, 0, "SShout/CShout" },\r
322   { (COLORREF)0, 0, "Channel 1 Text" },\r
323   { (COLORREF)0, 0, "Channel Text" },\r
324   { (COLORREF)0, 0, "Kibitz Text" },\r
325   { (COLORREF)0, 0, "Tell Text" },\r
326   { (COLORREF)0, 0, "Challenge Text" },\r
327   { (COLORREF)0, 0, "Request Text" },\r
328   { (COLORREF)0, 0, "Seek Text" },\r
329   { (COLORREF)0, 0, "Normal Text" },\r
330   { (COLORREF)0, 0, "None" }\r
331 };\r
332 \r
333 \r
334 \r
335 static char *commentTitle;\r
336 static char *commentText;\r
337 static int commentIndex;\r
338 static Boolean editComment = FALSE;\r
339 HWND commentDialog = NULL;\r
340 BOOLEAN commentDialogUp = FALSE;\r
341 static int commentX, commentY, commentH, commentW;\r
342 \r
343 static char *analysisTitle;\r
344 static char *analysisText;\r
345 HWND analysisDialog = NULL;\r
346 BOOLEAN analysisDialogUp = FALSE;\r
347 static int analysisX, analysisY, analysisH, analysisW;\r
348 \r
349 char errorTitle[MSG_SIZ];\r
350 char errorMessage[2*MSG_SIZ];\r
351 HWND errorDialog = NULL;\r
352 BOOLEAN moveErrorMessageUp = FALSE;\r
353 BOOLEAN consoleEcho = TRUE;\r
354 CHARFORMAT consoleCF;\r
355 COLORREF consoleBackgroundColor;\r
356 \r
357 char *programVersion;\r
358 \r
359 #define CPReal 1\r
360 #define CPComm 2\r
361 #define CPSock 3\r
362 #define CPRcmd 4\r
363 typedef int CPKind;\r
364 \r
365 typedef struct {\r
366   CPKind kind;\r
367   HANDLE hProcess;\r
368   DWORD pid;\r
369   HANDLE hTo;\r
370   HANDLE hFrom;\r
371   SOCKET sock;\r
372   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
373 } ChildProc;\r
374 \r
375 #define INPUT_SOURCE_BUF_SIZE 4096\r
376 \r
377 typedef struct _InputSource {\r
378   CPKind kind;\r
379   HANDLE hFile;\r
380   SOCKET sock;\r
381   int lineByLine;\r
382   HANDLE hThread;\r
383   DWORD id;\r
384   char buf[INPUT_SOURCE_BUF_SIZE];\r
385   char *next;\r
386   DWORD count;\r
387   int error;\r
388   InputCallback func;\r
389   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
390   VOIDSTAR closure;\r
391 } InputSource;\r
392 \r
393 InputSource *consoleInputSource;\r
394 \r
395 DCB dcb;\r
396 \r
397 /* forward */\r
398 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
399 VOID ConsoleCreate();\r
400 LRESULT CALLBACK\r
401   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
402 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
403 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
404 VOID ParseCommSettings(char *arg, DCB *dcb);\r
405 LRESULT CALLBACK\r
406   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
407 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
408 void ParseIcsTextMenu(char *icsTextMenuString);\r
409 VOID PopUpMoveDialog(char firstchar);\r
410 VOID PopUpNameDialog(char firstchar);\r
411 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
412 \r
413 /* [AS] */\r
414 int NewGameFRC();\r
415 int GameListOptions();\r
416 \r
417 HWND moveHistoryDialog = NULL;\r
418 BOOLEAN moveHistoryDialogUp = FALSE;\r
419 \r
420 WindowPlacement wpMoveHistory;\r
421 \r
422 HWND evalGraphDialog = NULL;\r
423 BOOLEAN evalGraphDialogUp = FALSE;\r
424 \r
425 WindowPlacement wpEvalGraph;\r
426 \r
427 HWND engineOutputDialog = NULL;\r
428 BOOLEAN engineOutputDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpEngineOutput;\r
431 WindowPlacement wpGameList;\r
432 WindowPlacement wpConsole;\r
433 \r
434 VOID MoveHistoryPopUp();\r
435 VOID MoveHistoryPopDown();\r
436 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
437 BOOL MoveHistoryIsUp();\r
438 \r
439 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 VOID EvalGraphPopUp();\r
441 VOID EvalGraphPopDown();\r
442 BOOL EvalGraphIsUp();\r
443 \r
444 VOID EngineOutputPopUp();\r
445 VOID EngineOutputPopDown();\r
446 BOOL EngineOutputIsUp();\r
447 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
448 \r
449 VOID GothicPopUp(char *title, VariantClass variant);\r
450 /*\r
451  * Setting "frozen" should disable all user input other than deleting\r
452  * the window.  We do this while engines are initializing themselves.\r
453  */\r
454 static int frozen = 0;\r
455 static int oldMenuItemState[MENU_BAR_ITEMS];\r
456 void FreezeUI()\r
457 {\r
458   HMENU hmenu;\r
459   int i;\r
460 \r
461   if (frozen) return;\r
462   frozen = 1;\r
463   hmenu = GetMenu(hwndMain);\r
464   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
465     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
466   }\r
467   DrawMenuBar(hwndMain);\r
468 }\r
469 \r
470 /* Undo a FreezeUI */\r
471 void ThawUI()\r
472 {\r
473   HMENU hmenu;\r
474   int i;\r
475 \r
476   if (!frozen) return;\r
477   frozen = 0;\r
478   hmenu = GetMenu(hwndMain);\r
479   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
480     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
481   }\r
482   DrawMenuBar(hwndMain);\r
483 }\r
484 \r
485 /*---------------------------------------------------------------------------*\\r
486  *\r
487  * WinMain\r
488  *\r
489 \*---------------------------------------------------------------------------*/\r
490 \r
491 int APIENTRY\r
492 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
493         LPSTR lpCmdLine, int nCmdShow)\r
494 {\r
495   MSG msg;\r
496   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
497 //  INITCOMMONCONTROLSEX ex;\r
498 \r
499   debugFP = stderr;\r
500 \r
501   LoadLibrary("RICHED32.DLL");\r
502   consoleCF.cbSize = sizeof(CHARFORMAT);\r
503 \r
504   if (!InitApplication(hInstance)) {\r
505     return (FALSE);\r
506   }\r
507   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
508     return (FALSE);\r
509   }\r
510 \r
511 //  InitCommonControlsEx(&ex);\r
512   InitCommonControls();\r
513 \r
514   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
515   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
516   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
517 \r
518   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
519 \r
520   while (GetMessage(&msg, /* message structure */\r
521                     NULL, /* handle of window receiving the message */\r
522                     0,    /* lowest message to examine */\r
523                     0))   /* highest message to examine */\r
524     {\r
525       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
526           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
527           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
528           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
529           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
530           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
531           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
532           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
533           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
534           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
535         TranslateMessage(&msg); /* Translates virtual key codes */\r
536         DispatchMessage(&msg);  /* Dispatches message to window */\r
537       }\r
538     }\r
539 \r
540 \r
541   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
542 }\r
543 \r
544 /*---------------------------------------------------------------------------*\\r
545  *\r
546  * Initialization functions\r
547  *\r
548 \*---------------------------------------------------------------------------*/\r
549 \r
550 void\r
551 SetUserLogo()\r
552 {   // update user logo if necessary\r
553     static char oldUserName[MSG_SIZ], *curName;\r
554 \r
555     if(appData.autoLogo) {\r
556           curName = UserName();\r
557           if(strcmp(curName, oldUserName)) {\r
558                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
559                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
560                 strcpy(oldUserName, curName);\r
561           }\r
562     }\r
563 }\r
564 \r
565 BOOL\r
566 InitApplication(HINSTANCE hInstance)\r
567 {\r
568   WNDCLASS wc;\r
569 \r
570   /* Fill in window class structure with parameters that describe the */\r
571   /* main window. */\r
572 \r
573   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
574   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
575   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
576   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
577   wc.hInstance     = hInstance;         /* Owner of this class */\r
578   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
579   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
580   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
581   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
582   wc.lpszClassName = szAppName;                 /* Name to register as */\r
583 \r
584   /* Register the window class and return success/failure code. */\r
585   if (!RegisterClass(&wc)) return FALSE;\r
586 \r
587   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
588   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
589   wc.cbClsExtra    = 0;\r
590   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
591   wc.hInstance     = hInstance;\r
592   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
593   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
594   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
595   wc.lpszMenuName  = NULL;\r
596   wc.lpszClassName = szConsoleName;\r
597 \r
598   if (!RegisterClass(&wc)) return FALSE;\r
599   return TRUE;\r
600 }\r
601 \r
602 \r
603 /* Set by InitInstance, used by EnsureOnScreen */\r
604 int screenHeight, screenWidth;\r
605 \r
606 void\r
607 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
608 {\r
609 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
610   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
611   if (*x > screenWidth - 32) *x = 0;\r
612   if (*y > screenHeight - 32) *y = 0;\r
613   if (*x < minX) *x = minX;\r
614   if (*y < minY) *y = minY;\r
615 }\r
616 \r
617 BOOL\r
618 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
619 {\r
620   HWND hwnd; /* Main window handle. */\r
621   int ibs;\r
622   WINDOWPLACEMENT wp;\r
623   char *filepart;\r
624 \r
625   hInst = hInstance;    /* Store instance handle in our global variable */\r
626 \r
627   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
628     *filepart = NULLCHAR;\r
629   } else {\r
630     GetCurrentDirectory(MSG_SIZ, installDir);\r
631   }\r
632   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
633   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
634   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
635   if (appData.debugMode) {\r
636     debugFP = fopen(appData.nameOfDebugFile, "w");\r
637     setbuf(debugFP, NULL);\r
638   }\r
639 \r
640   InitBackEnd1();\r
641 \r
642 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
643 //  InitEngineUCI( installDir, &second );\r
644 \r
645   /* Create a main window for this application instance. */\r
646   hwnd = CreateWindow(szAppName, szTitle,\r
647                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
648                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
649                       NULL, NULL, hInstance, NULL);\r
650   hwndMain = hwnd;\r
651 \r
652   /* If window could not be created, return "failure" */\r
653   if (!hwnd) {\r
654     return (FALSE);\r
655   }\r
656 \r
657   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
658   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
659       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
660 \r
661       if (first.programLogo == NULL && appData.debugMode) {\r
662           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
663       }\r
664   } else if(appData.autoLogo) {\r
665       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
666         char buf[MSG_SIZ];\r
667         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
668         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
669       }\r
670   }\r
671 \r
672   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
673       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
674 \r
675       if (second.programLogo == NULL && appData.debugMode) {\r
676           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
677       }\r
678   } else if(appData.autoLogo) {\r
679       char buf[MSG_SIZ];\r
680       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
681         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
682         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
683       } else\r
684       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
685         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
686         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
687       }\r
688   }\r
689 \r
690   SetUserLogo();\r
691 \r
692   iconWhite = LoadIcon(hInstance, "icon_white");\r
693   iconBlack = LoadIcon(hInstance, "icon_black");\r
694   iconCurrent = iconWhite;\r
695   InitDrawingColors();\r
696   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
697   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
698   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
699     /* Compute window size for each board size, and use the largest\r
700        size that fits on this screen as the default. */\r
701     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
702     if (boardSize == (BoardSize)-1 &&\r
703         winHeight <= screenHeight\r
704            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
705         && winWidth <= screenWidth) {\r
706       boardSize = (BoardSize)ibs;\r
707     }\r
708   }\r
709 \r
710   InitDrawingSizes(boardSize, 0);\r
711   InitMenuChecks();\r
712   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
713 \r
714   /* [AS] Load textures if specified */\r
715   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
716   \r
717   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
718       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
719       liteBackTextureMode = appData.liteBackTextureMode;\r
720 \r
721       if (liteBackTexture == NULL && appData.debugMode) {\r
722           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
723       }\r
724   }\r
725   \r
726   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
727       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
728       darkBackTextureMode = appData.darkBackTextureMode;\r
729 \r
730       if (darkBackTexture == NULL && appData.debugMode) {\r
731           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
732       }\r
733   }\r
734 \r
735   mysrandom( (unsigned) time(NULL) );\r
736 \r
737   /* [AS] Restore layout */\r
738   if( wpMoveHistory.visible ) {\r
739       MoveHistoryPopUp();\r
740   }\r
741 \r
742   if( wpEvalGraph.visible ) {\r
743       EvalGraphPopUp();\r
744   }\r
745 \r
746   if( wpEngineOutput.visible ) {\r
747       EngineOutputPopUp();\r
748   }\r
749 \r
750   InitBackEnd2();\r
751 \r
752   /* Make the window visible; update its client area; and return "success" */\r
753   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
754   wp.length = sizeof(WINDOWPLACEMENT);\r
755   wp.flags = 0;\r
756   wp.showCmd = nCmdShow;\r
757   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
758   wp.rcNormalPosition.left = boardX;\r
759   wp.rcNormalPosition.right = boardX + winWidth;\r
760   wp.rcNormalPosition.top = boardY;\r
761   wp.rcNormalPosition.bottom = boardY + winHeight;\r
762   SetWindowPlacement(hwndMain, &wp);\r
763 \r
764   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
765                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
766 \r
767   if (hwndConsole) {\r
768 #if AOT_CONSOLE\r
769     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
770                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
771 #endif\r
772     ShowWindow(hwndConsole, nCmdShow);\r
773   }\r
774   UpdateWindow(hwnd);\r
775 \r
776   return TRUE;\r
777 \r
778 }\r
779 \r
780 \r
781 typedef enum {\r
782   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
783   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
784   ArgSettingsFilename,\r
785   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
786 } ArgType;\r
787 \r
788 typedef struct {\r
789   char *argName;\r
790   ArgType argType;\r
791   /***\r
792   union {\r
793     String *pString;       // ArgString\r
794     int *pInt;             // ArgInt\r
795     float *pFloat;         // ArgFloat\r
796     Boolean *pBoolean;     // ArgBoolean\r
797     COLORREF *pColor;      // ArgColor\r
798     ColorClass cc;         // ArgAttribs\r
799     String *pFilename;     // ArgFilename\r
800     BoardSize *pBoardSize; // ArgBoardSize\r
801     int whichFont;         // ArgFont\r
802     DCB *pDCB;             // ArgCommSettings\r
803     String *pFilename;     // ArgSettingsFilename\r
804   } argLoc;\r
805   ***/\r
806   LPVOID argLoc;\r
807   BOOL save;\r
808 } ArgDescriptor;\r
809 \r
810 int junk;\r
811 ArgDescriptor argDescriptors[] = {\r
812   /* positional arguments */\r
813   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
814   { "", ArgNone, NULL },\r
815   /* keyword arguments */\r
816   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
817   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
818   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
819   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
820   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
821   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
822   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
823   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
824   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
825   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
826   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
827   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
828   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
829   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
830   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
831   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
832   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
833   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
834     FALSE },\r
835   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
836     FALSE },\r
837   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
838     FALSE },\r
839   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
840   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
841     FALSE },\r
842   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
843   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
844   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
845   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
846   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
847   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
848   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
849   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
850   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
851   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
852   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
853   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
854   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
855   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
856   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
857   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
858   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
859   /*!!bitmapDirectory?*/\r
860   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
861   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
862   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
863   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
864   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
865   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
866   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
867   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
868   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
869   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
870   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
871   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
872   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
873   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
874   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
875   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
876   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
877   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
878   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
879   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
880   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
881   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
882   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
883   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
884   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
885   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
886   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
887   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
888   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
889   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
890   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
891   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
892   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
893   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
894   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
895   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
896   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
897   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
898   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
899   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
900   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
901   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
902   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
903   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
904   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
905   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
906   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
907   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
908   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
909   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
910   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
911   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
912   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
913   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
914   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
915   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
916   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
917   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
918   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
919   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
920   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
921   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
922   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
923   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
924   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
925   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
926   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
927   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
928   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
929   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
930   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
931   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
932   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
933   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
934   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
935   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
936   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
937   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
938   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
939   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
940   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
941   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
942   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
943   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
944   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
945   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
946   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
947   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
948   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
949   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
950   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
951   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
952   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
953   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
954     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
955   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
956   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
957   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
958   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
959   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
960   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
961   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
962   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
963     TRUE }, /* must come after all fonts */\r
964   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
965   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
966     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
967   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
968   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
969   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
970   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
971   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
972   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
973   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
974   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
975   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
976   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
977   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
978   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
979   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
980   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
981   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
982   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
983   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
984   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
985   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
986   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
987   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
988   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
989   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
990   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
991   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
992   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
993   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
994   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
995 #if 0\r
996   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
997   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
998 #endif\r
999   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1000   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1001   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1002   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1003   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1004   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1005   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1006   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1007   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1008   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1009   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1010   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1011   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1012   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1013   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1014   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1015   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1016   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1017   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1018   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1019   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1020   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1021   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1022   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1023   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1024   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1025   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1026   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1027   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1028   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1029   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1030   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1031   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1032   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1033   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1034   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1035   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1036   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1037   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1038   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1039   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1040   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1041   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1042   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1043   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1044   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1045   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1046   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1047   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1048   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1049   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1050   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1051   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1052   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1053   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1054   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1055   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1056   { "highlightLastMove", ArgBoolean,\r
1057     (LPVOID) &appData.highlightLastMove, TRUE },\r
1058   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1059   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1060   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1061   { "highlightDragging", ArgBoolean,\r
1062     (LPVOID) &appData.highlightDragging, TRUE },\r
1063   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1064   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1065   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1066   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1067   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1068   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1069   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1070   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1071   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1072   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1073   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1074   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1075   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1076   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1077   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1078   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1079   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1080   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1081   { "soundShout", ArgFilename,\r
1082     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1083   { "soundSShout", ArgFilename,\r
1084     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1085   { "soundChannel1", ArgFilename,\r
1086     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1087   { "soundChannel", ArgFilename,\r
1088     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1089   { "soundKibitz", ArgFilename,\r
1090     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1091   { "soundTell", ArgFilename,\r
1092     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1093   { "soundChallenge", ArgFilename,\r
1094     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1095   { "soundRequest", ArgFilename,\r
1096     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1097   { "soundSeek", ArgFilename,\r
1098     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1099   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1100   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1101   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1102   { "soundIcsLoss", ArgFilename, \r
1103     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1104   { "soundIcsDraw", ArgFilename, \r
1105     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1106   { "soundIcsUnfinished", ArgFilename, \r
1107     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1108   { "soundIcsAlarm", ArgFilename, \r
1109     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1110   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1111   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1112   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1113   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1114   { "reuseChessPrograms", ArgBoolean,\r
1115     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1116   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1117   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1118   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1119   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1120   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1121   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1122   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1123   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1124   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1125   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1126   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1127   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1128   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1129   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1130   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1131     TRUE },\r
1132   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1133     TRUE },\r
1134   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1135   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1136   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1137   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1138   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1139   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1140   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1141   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1142   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1143   /* [AS] New features */\r
1144   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1145   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1146   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1147   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1148   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1149   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1150   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1151   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1152   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1153   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1154   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1155   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1156   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1157   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1158   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1159   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1160   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1161   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1162   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1163   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1164   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1165   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1166   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1167   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1168   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1169   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1170   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1171   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1172   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1173   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1174   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1175   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1176   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1177   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1178   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1179   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1180   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1181   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1182   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1183   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1184   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1185   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1186   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1187   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1188   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1189   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1190   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1191   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1192   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1193   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1194 \r
1195   /* [HGM] board-size, adjudication and misc. options */\r
1196   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1197   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1198   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1199   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1200   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1201   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1202   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1203   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1204   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1205   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1206   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1207   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1208   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1209   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1210   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1211   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1212   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1213   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1214   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1215   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1216   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1217   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1218   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1219   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1220   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1221   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1222   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1223   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1224   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1225   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1226   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1227 \r
1228 #ifdef ZIPPY\r
1229   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1230   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1231   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1232   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1233   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1234   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1235   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1236   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1237   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1238   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1239   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1240   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1241   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1242     FALSE },\r
1243   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1244   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1245   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1246   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1247   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1248   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1249   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1250     FALSE },\r
1251   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1252   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1253   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1254   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1255   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1256   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1257   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1258   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1259   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1260   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1261   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1262   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1263   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1264   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1265   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1266   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1267   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1268   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1269   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1270 #endif\r
1271   /* [HGM] options for broadcasting and time odds */\r
1272   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1273   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1274   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1275   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1276   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1277   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1278   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1279   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1280   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1281   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1282   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1283 \r
1284   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1285   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1286   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1287   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1288   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1289   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1290   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1291   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1292   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1293   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1294   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1295   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1296   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1297   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1298   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1299   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1300   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1301   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1302   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1303   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1304   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1305   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1306   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1307   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1308   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1309   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1310   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1311   /* [AS] Layout stuff */\r
1312   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1313   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1314   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1315   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1316   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1317 \r
1318   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1319   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1320   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1321   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1322   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1323 \r
1324   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1325   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1326   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1327   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1328   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1329 \r
1330   { NULL, ArgNone, NULL, FALSE }\r
1331 };\r
1332 \r
1333 \r
1334 /* Kludge for indirection files on command line */\r
1335 char* lastIndirectionFilename;\r
1336 ArgDescriptor argDescriptorIndirection =\r
1337 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1338 \r
1339 \r
1340 VOID\r
1341 ExitArgError(char *msg, char *badArg)\r
1342 {\r
1343   char buf[MSG_SIZ];\r
1344 \r
1345   sprintf(buf, "%s %s", msg, badArg);\r
1346   DisplayFatalError(buf, 0, 2);\r
1347   exit(2);\r
1348 }\r
1349 \r
1350 /* Command line font name parser.  NULL name means do nothing.\r
1351    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1352    For backward compatibility, syntax without the colon is also\r
1353    accepted, but font names with digits in them won't work in that case.\r
1354 */\r
1355 VOID\r
1356 ParseFontName(char *name, MyFontParams *mfp)\r
1357 {\r
1358   char *p, *q;\r
1359   if (name == NULL) return;\r
1360   p = name;\r
1361   q = strchr(p, ':');\r
1362   if (q) {\r
1363     if (q - p >= sizeof(mfp->faceName))\r
1364       ExitArgError("Font name too long:", name);\r
1365     memcpy(mfp->faceName, p, q - p);\r
1366     mfp->faceName[q - p] = NULLCHAR;\r
1367     p = q + 1;\r
1368   } else {\r
1369     q = mfp->faceName;\r
1370     while (*p && !isdigit(*p)) {\r
1371       *q++ = *p++;\r
1372       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1373         ExitArgError("Font name too long:", name);\r
1374     }\r
1375     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1376     *q = NULLCHAR;\r
1377   }\r
1378   if (!*p) ExitArgError("Font point size missing:", name);\r
1379   mfp->pointSize = (float) atof(p);\r
1380   mfp->bold = (strchr(p, 'b') != NULL);\r
1381   mfp->italic = (strchr(p, 'i') != NULL);\r
1382   mfp->underline = (strchr(p, 'u') != NULL);\r
1383   mfp->strikeout = (strchr(p, 's') != NULL);\r
1384 }\r
1385 \r
1386 /* Color name parser.\r
1387    X version accepts X color names, but this one\r
1388    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1389 COLORREF\r
1390 ParseColorName(char *name)\r
1391 {\r
1392   int red, green, blue, count;\r
1393   char buf[MSG_SIZ];\r
1394 \r
1395   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1396   if (count != 3) {\r
1397     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1398       &red, &green, &blue);\r
1399   }\r
1400   if (count != 3) {\r
1401     sprintf(buf, "Can't parse color name %s", name);\r
1402     DisplayError(buf, 0);\r
1403     return RGB(0, 0, 0);\r
1404   }\r
1405   return PALETTERGB(red, green, blue);\r
1406 }\r
1407 \r
1408 \r
1409 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1410 {\r
1411   char *e = argValue;\r
1412   int eff = 0;\r
1413 \r
1414   while (*e) {\r
1415     if (*e == 'b')      eff |= CFE_BOLD;\r
1416     else if (*e == 'i') eff |= CFE_ITALIC;\r
1417     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1418     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1419     else if (*e == '#' || isdigit(*e)) break;\r
1420     e++;\r
1421   }\r
1422   *effects = eff;\r
1423   *color   = ParseColorName(e);\r
1424 }\r
1425 \r
1426 \r
1427 BoardSize\r
1428 ParseBoardSize(char *name)\r
1429 {\r
1430   BoardSize bs = SizeTiny;\r
1431   while (sizeInfo[bs].name != NULL) {\r
1432     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1433     bs++;\r
1434   }\r
1435   ExitArgError("Unrecognized board size value", name);\r
1436   return bs; /* not reached */\r
1437 }\r
1438 \r
1439 \r
1440 char\r
1441 StringGet(void *getClosure)\r
1442 {\r
1443   char **p = (char **) getClosure;\r
1444   return *((*p)++);\r
1445 }\r
1446 \r
1447 char\r
1448 FileGet(void *getClosure)\r
1449 {\r
1450   int c;\r
1451   FILE* f = (FILE*) getClosure;\r
1452 \r
1453   c = getc(f);\r
1454   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1455   if (c == EOF)\r
1456     return NULLCHAR;\r
1457   else\r
1458     return (char) c;\r
1459 }\r
1460 \r
1461 /* Parse settings file named "name". If file found, return the\r
1462    full name in fullname and return TRUE; else return FALSE */\r
1463 BOOLEAN\r
1464 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1465 {\r
1466   char *dummy;\r
1467   FILE *f;\r
1468   int ok; char buf[MSG_SIZ];\r
1469 \r
1470   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1471   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1472     sprintf(buf, "%s.ini", name);\r
1473     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1474   }\r
1475   if (ok) {\r
1476     f = fopen(fullname, "r");\r
1477     if (f != NULL) {\r
1478       ParseArgs(FileGet, f);\r
1479       fclose(f);\r
1480       return TRUE;\r
1481     }\r
1482   }\r
1483   return FALSE;\r
1484 }\r
1485 \r
1486 VOID\r
1487 ParseArgs(GetFunc get, void *cl)\r
1488 {\r
1489   char argName[ARG_MAX];\r
1490   char argValue[ARG_MAX];\r
1491   ArgDescriptor *ad;\r
1492   char start;\r
1493   char *q;\r
1494   int i, octval;\r
1495   char ch;\r
1496   int posarg = 0;\r
1497 \r
1498   ch = get(cl);\r
1499   for (;;) {\r
1500     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1501     if (ch == NULLCHAR) break;\r
1502     if (ch == ';') {\r
1503       /* Comment to end of line */\r
1504       ch = get(cl);\r
1505       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1506       continue;\r
1507     } else if (ch == '/' || ch == '-') {\r
1508       /* Switch */\r
1509       q = argName;\r
1510       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1511              ch != '\n' && ch != '\t') {\r
1512         *q++ = ch;\r
1513         ch = get(cl);\r
1514       }\r
1515       *q = NULLCHAR;\r
1516 \r
1517       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1518         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1519 \r
1520       if (ad->argName == NULL)\r
1521         ExitArgError("Unrecognized argument", argName);\r
1522 \r
1523     } else if (ch == '@') {\r
1524       /* Indirection file */\r
1525       ad = &argDescriptorIndirection;\r
1526       ch = get(cl);\r
1527     } else {\r
1528       /* Positional argument */\r
1529       ad = &argDescriptors[posarg++];\r
1530       strcpy(argName, ad->argName);\r
1531     }\r
1532 \r
1533     if (ad->argType == ArgTrue) {\r
1534       *(Boolean *) ad->argLoc = TRUE;\r
1535       continue;\r
1536     }\r
1537     if (ad->argType == ArgFalse) {\r
1538       *(Boolean *) ad->argLoc = FALSE;\r
1539       continue;\r
1540     }\r
1541 \r
1542     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1543     if (ch == NULLCHAR || ch == '\n') {\r
1544       ExitArgError("No value provided for argument", argName);\r
1545     }\r
1546     q = argValue;\r
1547     if (ch == '{') {\r
1548       // Quoting with { }.  No characters have to (or can) be escaped.\r
1549       // Thus the string cannot contain a '}' character.\r
1550       start = ch;\r
1551       ch = get(cl);\r
1552       while (start) {\r
1553         switch (ch) {\r
1554         case NULLCHAR:\r
1555           start = NULLCHAR;\r
1556           break;\r
1557           \r
1558         case '}':\r
1559           ch = get(cl);\r
1560           start = NULLCHAR;\r
1561           break;\r
1562 \r
1563         default:\r
1564           *q++ = ch;\r
1565           ch = get(cl);\r
1566           break;\r
1567         }\r
1568       }   \r
1569     } else if (ch == '\'' || ch == '"') {\r
1570       // Quoting with ' ' or " ", with \ as escape character.\r
1571       // Inconvenient for long strings that may contain Windows filenames.\r
1572       start = ch;\r
1573       ch = get(cl);\r
1574       while (start) {\r
1575         switch (ch) {\r
1576         case NULLCHAR:\r
1577           start = NULLCHAR;\r
1578           break;\r
1579 \r
1580         default:\r
1581         not_special:\r
1582           *q++ = ch;\r
1583           ch = get(cl);\r
1584           break;\r
1585 \r
1586         case '\'':\r
1587         case '\"':\r
1588           if (ch == start) {\r
1589             ch = get(cl);\r
1590             start = NULLCHAR;\r
1591             break;\r
1592           } else {\r
1593             goto not_special;\r
1594           }\r
1595 \r
1596         case '\\':\r
1597           if (ad->argType == ArgFilename\r
1598               || ad->argType == ArgSettingsFilename) {\r
1599               goto not_special;\r
1600           }\r
1601           ch = get(cl);\r
1602           switch (ch) {\r
1603           case NULLCHAR:\r
1604             ExitArgError("Incomplete \\ escape in value for", argName);\r
1605             break;\r
1606           case 'n':\r
1607             *q++ = '\n';\r
1608             ch = get(cl);\r
1609             break;\r
1610           case 'r':\r
1611             *q++ = '\r';\r
1612             ch = get(cl);\r
1613             break;\r
1614           case 't':\r
1615             *q++ = '\t';\r
1616             ch = get(cl);\r
1617             break;\r
1618           case 'b':\r
1619             *q++ = '\b';\r
1620             ch = get(cl);\r
1621             break;\r
1622           case 'f':\r
1623             *q++ = '\f';\r
1624             ch = get(cl);\r
1625             break;\r
1626           default:\r
1627             octval = 0;\r
1628             for (i = 0; i < 3; i++) {\r
1629               if (ch >= '0' && ch <= '7') {\r
1630                 octval = octval*8 + (ch - '0');\r
1631                 ch = get(cl);\r
1632               } else {\r
1633                 break;\r
1634               }\r
1635             }\r
1636             if (i > 0) {\r
1637               *q++ = (char) octval;\r
1638             } else {\r
1639               *q++ = ch;\r
1640               ch = get(cl);\r
1641             }\r
1642             break;\r
1643           }\r
1644           break;\r
1645         }\r
1646       }\r
1647     } else {\r
1648       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1649         *q++ = ch;\r
1650         ch = get(cl);\r
1651       }\r
1652     }\r
1653     *q = NULLCHAR;\r
1654 \r
1655     switch (ad->argType) {\r
1656     case ArgInt:\r
1657       *(int *) ad->argLoc = atoi(argValue);\r
1658       break;\r
1659 \r
1660     case ArgX:\r
1661       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1662       break;\r
1663 \r
1664     case ArgY:\r
1665       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1666       break;\r
1667 \r
1668     case ArgZ:\r
1669       *(int *) ad->argLoc = atoi(argValue);\r
1670       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1671       break;\r
1672 \r
1673     case ArgFloat:\r
1674       *(float *) ad->argLoc = (float) atof(argValue);\r
1675       break;\r
1676 \r
1677     case ArgString:\r
1678     case ArgFilename:\r
1679       *(char **) ad->argLoc = strdup(argValue);\r
1680       break;\r
1681 \r
1682     case ArgSettingsFilename:\r
1683       {\r
1684         char fullname[MSG_SIZ];\r
1685         if (ParseSettingsFile(argValue, fullname)) {\r
1686           if (ad->argLoc != NULL) {\r
1687             *(char **) ad->argLoc = strdup(fullname);\r
1688           }\r
1689         } else {\r
1690           if (ad->argLoc != NULL) {\r
1691           } else {\r
1692             ExitArgError("Failed to open indirection file", argValue);\r
1693           }\r
1694         }\r
1695       }\r
1696       break;\r
1697 \r
1698     case ArgBoolean:\r
1699       switch (argValue[0]) {\r
1700       case 't':\r
1701       case 'T':\r
1702         *(Boolean *) ad->argLoc = TRUE;\r
1703         break;\r
1704       case 'f':\r
1705       case 'F':\r
1706         *(Boolean *) ad->argLoc = FALSE;\r
1707         break;\r
1708       default:\r
1709         ExitArgError("Unrecognized boolean argument value", argValue);\r
1710         break;\r
1711       }\r
1712       break;\r
1713 \r
1714     case ArgColor:\r
1715       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1716       break;\r
1717 \r
1718     case ArgAttribs: {\r
1719       ColorClass cc = (ColorClass)ad->argLoc;\r
1720       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1721       }\r
1722       break;\r
1723       \r
1724     case ArgBoardSize:\r
1725       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1726       break;\r
1727 \r
1728     case ArgFont:\r
1729       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1730       break;\r
1731 \r
1732     case ArgCommSettings:\r
1733       ParseCommSettings(argValue, &dcb);\r
1734       break;\r
1735 \r
1736     case ArgNone:\r
1737       ExitArgError("Unrecognized argument", argValue);\r
1738       break;\r
1739     case ArgTrue:\r
1740     case ArgFalse: ;\r
1741     }\r
1742   }\r
1743 }\r
1744 \r
1745 VOID\r
1746 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1747 {\r
1748   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1749   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1750   DeleteDC(hdc);\r
1751   lf->lfWidth = 0;\r
1752   lf->lfEscapement = 0;\r
1753   lf->lfOrientation = 0;\r
1754   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1755   lf->lfItalic = mfp->italic;\r
1756   lf->lfUnderline = mfp->underline;\r
1757   lf->lfStrikeOut = mfp->strikeout;\r
1758   lf->lfCharSet = DEFAULT_CHARSET;\r
1759   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1760   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1761   lf->lfQuality = DEFAULT_QUALITY;\r
1762   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1763   strcpy(lf->lfFaceName, mfp->faceName);\r
1764 }\r
1765 \r
1766 VOID\r
1767 CreateFontInMF(MyFont *mf)\r
1768 {\r
1769   LFfromMFP(&mf->lf, &mf->mfp);\r
1770   if (mf->hf) DeleteObject(mf->hf);\r
1771   mf->hf = CreateFontIndirect(&mf->lf);\r
1772 }\r
1773 \r
1774 VOID\r
1775 SetDefaultTextAttribs()\r
1776 {\r
1777   ColorClass cc;\r
1778   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1779     ParseAttribs(&textAttribs[cc].color, \r
1780                  &textAttribs[cc].effects, \r
1781                  defaultTextAttribs[cc]);\r
1782   }\r
1783 }\r
1784 \r
1785 VOID\r
1786 SetDefaultSounds()\r
1787 {\r
1788   ColorClass cc;\r
1789   SoundClass sc;\r
1790   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1791     textAttribs[cc].sound.name = strdup("");\r
1792     textAttribs[cc].sound.data = NULL;\r
1793   }\r
1794   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1795     sounds[sc].name = strdup("");\r
1796     sounds[sc].data = NULL;\r
1797   }\r
1798   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1799 }\r
1800 \r
1801 VOID\r
1802 LoadAllSounds()\r
1803 {\r
1804   ColorClass cc;\r
1805   SoundClass sc;\r
1806   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1807     MyLoadSound(&textAttribs[cc].sound);\r
1808   }\r
1809   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1810     MyLoadSound(&sounds[sc]);\r
1811   }\r
1812 }\r
1813 \r
1814 VOID\r
1815 InitAppData(LPSTR lpCmdLine)\r
1816 {\r
1817   int i, j;\r
1818   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1819   char *dummy, *p;\r
1820 \r
1821   programName = szAppName;\r
1822 \r
1823   /* Initialize to defaults */\r
1824   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1825   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1826   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1827   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1828   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1829   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1830   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1831   SetDefaultTextAttribs();\r
1832   SetDefaultSounds();\r
1833   appData.movesPerSession = MOVES_PER_SESSION;\r
1834   appData.initString = INIT_STRING;\r
1835   appData.secondInitString = INIT_STRING;\r
1836   appData.firstComputerString = COMPUTER_STRING;\r
1837   appData.secondComputerString = COMPUTER_STRING;\r
1838   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1839   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1840   appData.firstPlaysBlack = FALSE;\r
1841   appData.noChessProgram = FALSE;\r
1842   chessProgram = FALSE;\r
1843   appData.firstHost = FIRST_HOST;\r
1844   appData.secondHost = SECOND_HOST;\r
1845   appData.firstDirectory = FIRST_DIRECTORY;\r
1846   appData.secondDirectory = SECOND_DIRECTORY;\r
1847   appData.bitmapDirectory = "";\r
1848   appData.remoteShell = REMOTE_SHELL;\r
1849   appData.remoteUser = "";\r
1850   appData.timeDelay = TIME_DELAY;\r
1851   appData.timeControl = TIME_CONTROL;\r
1852   appData.timeIncrement = TIME_INCREMENT;\r
1853   appData.icsActive = FALSE;\r
1854   appData.icsHost = "";\r
1855   appData.icsPort = ICS_PORT;\r
1856   appData.icsCommPort = ICS_COMM_PORT;\r
1857   appData.icsLogon = ICS_LOGON;\r
1858   appData.icsHelper = "";\r
1859   appData.useTelnet = FALSE;\r
1860   appData.telnetProgram = TELNET_PROGRAM;\r
1861   appData.gateway = "";\r
1862   appData.loadGameFile = "";\r
1863   appData.loadGameIndex = 0;\r
1864   appData.saveGameFile = "";\r
1865   appData.autoSaveGames = FALSE;\r
1866   appData.loadPositionFile = "";\r
1867   appData.loadPositionIndex = 1;\r
1868   appData.savePositionFile = "";\r
1869   appData.matchMode = FALSE;\r
1870   appData.matchGames = 0;\r
1871   appData.monoMode = FALSE;\r
1872   appData.debugMode = FALSE;\r
1873   appData.clockMode = TRUE;\r
1874   boardSize = (BoardSize) -1; /* determine by screen size */\r
1875   appData.Iconic = FALSE; /*unused*/\r
1876   appData.searchTime = "";\r
1877   appData.searchDepth = 0;\r
1878   appData.showCoords = FALSE;\r
1879   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1880   appData.autoCallFlag = FALSE;\r
1881   appData.flipView = FALSE;\r
1882   appData.autoFlipView = TRUE;\r
1883   appData.cmailGameName = "";\r
1884   appData.alwaysPromoteToQueen = FALSE;\r
1885   appData.oldSaveStyle = FALSE;\r
1886   appData.quietPlay = FALSE;\r
1887   appData.showThinking = FALSE;\r
1888   appData.ponderNextMove = TRUE;\r
1889   appData.periodicUpdates = TRUE;\r
1890   appData.popupExitMessage = TRUE;\r
1891   appData.popupMoveErrors = FALSE;\r
1892   appData.autoObserve = FALSE;\r
1893   appData.autoComment = FALSE;\r
1894   appData.animate = TRUE;\r
1895   appData.animSpeed = 10;\r
1896   appData.animateDragging = TRUE;\r
1897   appData.highlightLastMove = TRUE;\r
1898   appData.getMoveList = TRUE;\r
1899   appData.testLegality = TRUE;\r
1900   appData.premove = TRUE;\r
1901   appData.premoveWhite = FALSE;\r
1902   appData.premoveWhiteText = "";\r
1903   appData.premoveBlack = FALSE;\r
1904   appData.premoveBlackText = "";\r
1905   appData.icsAlarm = TRUE;\r
1906   appData.icsAlarmTime = 5000;\r
1907   appData.autoRaiseBoard = TRUE;\r
1908   appData.localLineEditing = TRUE;\r
1909   appData.colorize = TRUE;\r
1910   appData.reuseFirst = TRUE;\r
1911   appData.reuseSecond = TRUE;\r
1912   appData.blindfold = FALSE;\r
1913   appData.icsEngineAnalyze = FALSE;\r
1914   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
1915   dcb.DCBlength = sizeof(DCB);\r
1916   dcb.BaudRate = 9600;\r
1917   dcb.fBinary = TRUE;\r
1918   dcb.fParity = FALSE;\r
1919   dcb.fOutxCtsFlow = FALSE;\r
1920   dcb.fOutxDsrFlow = FALSE;\r
1921   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1922   dcb.fDsrSensitivity = FALSE;\r
1923   dcb.fTXContinueOnXoff = TRUE;\r
1924   dcb.fOutX = FALSE;\r
1925   dcb.fInX = FALSE;\r
1926   dcb.fNull = FALSE;\r
1927   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1928   dcb.fAbortOnError = FALSE;\r
1929   dcb.ByteSize = 7;\r
1930   dcb.Parity = SPACEPARITY;\r
1931   dcb.StopBits = ONESTOPBIT;\r
1932   settingsFileName = SETTINGS_FILE;\r
1933   saveSettingsOnExit = TRUE;\r
1934   boardX = CW_USEDEFAULT;\r
1935   boardY = CW_USEDEFAULT;\r
1936   analysisX = CW_USEDEFAULT; \r
1937   analysisY = CW_USEDEFAULT; \r
1938   analysisW = CW_USEDEFAULT;\r
1939   analysisH = CW_USEDEFAULT;\r
1940   commentX = CW_USEDEFAULT; \r
1941   commentY = CW_USEDEFAULT; \r
1942   commentW = CW_USEDEFAULT;\r
1943   commentH = CW_USEDEFAULT;\r
1944   editTagsX = CW_USEDEFAULT; \r
1945   editTagsY = CW_USEDEFAULT; \r
1946   editTagsW = CW_USEDEFAULT;\r
1947   editTagsH = CW_USEDEFAULT;\r
1948   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1949   icsNames = ICS_NAMES;\r
1950   firstChessProgramNames = FCP_NAMES;\r
1951   secondChessProgramNames = SCP_NAMES;\r
1952   appData.initialMode = "";\r
1953   appData.variant = "normal";\r
1954   appData.firstProtocolVersion = PROTOVER;\r
1955   appData.secondProtocolVersion = PROTOVER;\r
1956   appData.showButtonBar = TRUE;\r
1957 \r
1958    /* [AS] New properties (see comments in header file) */\r
1959   appData.firstScoreIsAbsolute = FALSE;\r
1960   appData.secondScoreIsAbsolute = FALSE;\r
1961   appData.saveExtendedInfoInPGN = FALSE;\r
1962   appData.hideThinkingFromHuman = FALSE;\r
1963   appData.liteBackTextureFile = "";\r
1964   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1965   appData.darkBackTextureFile = "";\r
1966   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1967   appData.renderPiecesWithFont = "";\r
1968   appData.fontToPieceTable = "";\r
1969   appData.fontBackColorWhite = 0;\r
1970   appData.fontForeColorWhite = 0;\r
1971   appData.fontBackColorBlack = 0;\r
1972   appData.fontForeColorBlack = 0;\r
1973   appData.fontPieceSize = 80;\r
1974   appData.overrideLineGap = 1;\r
1975   appData.adjudicateLossThreshold = 0;\r
1976   appData.delayBeforeQuit = 0;\r
1977   appData.delayAfterQuit = 0;\r
1978   appData.nameOfDebugFile = "winboard.debug";\r
1979   appData.pgnEventHeader = "Computer Chess Game";\r
1980   appData.defaultFrcPosition = -1;\r
1981   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1982   appData.saveOutOfBookInfo = TRUE;\r
1983   appData.showEvalInMoveHistory = TRUE;\r
1984   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1985   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1986   appData.highlightMoveWithArrow = FALSE;\r
1987   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1988   appData.useStickyWindows = TRUE;\r
1989   appData.adjudicateDrawMoves = 0;\r
1990   appData.autoDisplayComment = TRUE;\r
1991   appData.autoDisplayTags = TRUE;\r
1992   appData.firstIsUCI = FALSE;\r
1993   appData.secondIsUCI = FALSE;\r
1994   appData.firstHasOwnBookUCI = TRUE;\r
1995   appData.secondHasOwnBookUCI = TRUE;\r
1996   appData.polyglotDir = "";\r
1997   appData.usePolyglotBook = FALSE;\r
1998   appData.polyglotBook = "";\r
1999   appData.defaultHashSize = 64;\r
2000   appData.defaultCacheSizeEGTB = 4;\r
2001   appData.defaultPathEGTB = "c:\\egtb";\r
2002   appData.firstOptions = "";\r
2003   appData.secondOptions = "";\r
2004 \r
2005   InitWindowPlacement( &wpGameList );\r
2006   InitWindowPlacement( &wpMoveHistory );\r
2007   InitWindowPlacement( &wpEvalGraph );\r
2008   InitWindowPlacement( &wpEngineOutput );\r
2009   InitWindowPlacement( &wpConsole );\r
2010 \r
2011   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2012   appData.NrFiles      = -1;\r
2013   appData.NrRanks      = -1;\r
2014   appData.holdingsSize = -1;\r
2015   appData.testClaims   = FALSE;\r
2016   appData.checkMates   = FALSE;\r
2017   appData.materialDraws= FALSE;\r
2018   appData.trivialDraws = FALSE;\r
2019   appData.ruleMoves    = 51;\r
2020   appData.drawRepeats  = 6;\r
2021   appData.matchPause   = 10000;\r
2022   appData.alphaRank    = FALSE;\r
2023   appData.allWhite     = FALSE;\r
2024   appData.upsideDown   = FALSE;\r
2025   appData.serverPause  = 15;\r
2026   appData.serverMovesName   = NULL;\r
2027   appData.suppressLoadMoves = FALSE;\r
2028   appData.firstTimeOdds  = 1;\r
2029   appData.secondTimeOdds = 1;\r
2030   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2031   appData.secondAccumulateTC = 1;\r
2032   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2033   appData.secondNPS = -1;\r
2034   appData.engineComments = 1;\r
2035   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2036   appData.egtFormats = "";\r
2037 \r
2038 #ifdef ZIPPY\r
2039   appData.zippyTalk = ZIPPY_TALK;\r
2040   appData.zippyPlay = ZIPPY_PLAY;\r
2041   appData.zippyLines = ZIPPY_LINES;\r
2042   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2043   appData.zippyPassword = ZIPPY_PASSWORD;\r
2044   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2045   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2046   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2047   appData.zippyUseI = ZIPPY_USE_I;\r
2048   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2049   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2050   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2051   appData.zippyGameStart = ZIPPY_GAME_START;\r
2052   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2053   appData.zippyAbort = ZIPPY_ABORT;\r
2054   appData.zippyVariants = ZIPPY_VARIANTS;\r
2055   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2056   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2057 #endif\r
2058 \r
2059   /* Point font array elements to structures and\r
2060      parse default font names */\r
2061   for (i=0; i<NUM_FONTS; i++) {\r
2062     for (j=0; j<NUM_SIZES; j++) {\r
2063       font[j][i] = &fontRec[j][i];\r
2064       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2065     }\r
2066   }\r
2067   \r
2068   /* Parse default settings file if any */\r
2069   if (ParseSettingsFile(settingsFileName, buf)) {\r
2070     settingsFileName = strdup(buf);\r
2071   }\r
2072 \r
2073   /* Parse command line */\r
2074   ParseArgs(StringGet, &lpCmdLine);\r
2075 \r
2076   /* [HGM] make sure board size is acceptable */\r
2077   if(appData.NrFiles > BOARD_SIZE ||\r
2078      appData.NrRanks > BOARD_SIZE   )\r
2079       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2080 \r
2081   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2082    * with options from the command line, we now make an even higher priority\r
2083    * overrule by WB options attached to the engine command line. This so that\r
2084    * tournament managers can use WB options (such as /timeOdds) that follow\r
2085    * the engines.\r
2086    */\r
2087   if(appData.firstChessProgram != NULL) {\r
2088       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2089       static char *f = "first";\r
2090       char buf[MSG_SIZ], *q = buf;\r
2091       if(p != NULL) { // engine command line contains WinBoard options\r
2092           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2093           ParseArgs(StringGet, &q);\r
2094           p[-1] = 0; // cut them offengine command line\r
2095       }\r
2096   }\r
2097   // now do same for second chess program\r
2098   if(appData.secondChessProgram != NULL) {\r
2099       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2100       static char *s = "second";\r
2101       char buf[MSG_SIZ], *q = buf;\r
2102       if(p != NULL) { // engine command line contains WinBoard options\r
2103           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2104           ParseArgs(StringGet, &q);\r
2105           p[-1] = 0; // cut them offengine command line\r
2106       }\r
2107   }\r
2108 \r
2109 \r
2110   /* Propagate options that affect others */\r
2111   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2112   if (appData.icsActive || appData.noChessProgram) {\r
2113      chessProgram = FALSE;  /* not local chess program mode */\r
2114   }\r
2115 \r
2116   /* Open startup dialog if needed */\r
2117   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2118       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2119       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2120                         *appData.secondChessProgram == NULLCHAR))) {\r
2121     FARPROC lpProc;\r
2122     \r
2123     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2124     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2125     FreeProcInstance(lpProc);\r
2126   }\r
2127 \r
2128   /* Make sure save files land in the right (?) directory */\r
2129   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2130     appData.saveGameFile = strdup(buf);\r
2131   }\r
2132   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2133     appData.savePositionFile = strdup(buf);\r
2134   }\r
2135 \r
2136   /* Finish initialization for fonts and sounds */\r
2137   for (i=0; i<NUM_FONTS; i++) {\r
2138     for (j=0; j<NUM_SIZES; j++) {\r
2139       CreateFontInMF(font[j][i]);\r
2140     }\r
2141   }\r
2142   /* xboard, and older WinBoards, controlled the move sound with the\r
2143      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2144      always turn the option on (so that the backend will call us),\r
2145      then let the user turn the sound off by setting it to silence if\r
2146      desired.  To accommodate old winboard.ini files saved by old\r
2147      versions of WinBoard, we also turn off the sound if the option\r
2148      was initially set to false. */\r
2149   if (!appData.ringBellAfterMoves) {\r
2150     sounds[(int)SoundMove].name = strdup("");\r
2151     appData.ringBellAfterMoves = TRUE;\r
2152   }\r
2153   GetCurrentDirectory(MSG_SIZ, currDir);\r
2154   SetCurrentDirectory(installDir);\r
2155   LoadAllSounds();\r
2156   SetCurrentDirectory(currDir);\r
2157 \r
2158   p = icsTextMenuString;\r
2159   if (p[0] == '@') {\r
2160     FILE* f = fopen(p + 1, "r");\r
2161     if (f == NULL) {\r
2162       DisplayFatalError(p + 1, errno, 2);\r
2163       return;\r
2164     }\r
2165     i = fread(buf, 1, sizeof(buf)-1, f);\r
2166     fclose(f);\r
2167     buf[i] = NULLCHAR;\r
2168     p = buf;\r
2169   }\r
2170   ParseIcsTextMenu(strdup(p));\r
2171 }\r
2172 \r
2173 \r
2174 VOID\r
2175 InitMenuChecks()\r
2176 {\r
2177   HMENU hmenu = GetMenu(hwndMain);\r
2178 \r
2179   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2180                         MF_BYCOMMAND|((appData.icsActive &&\r
2181                                        *appData.icsCommPort != NULLCHAR) ?\r
2182                                       MF_ENABLED : MF_GRAYED));\r
2183   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2184                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2185                                      MF_CHECKED : MF_UNCHECKED));\r
2186 }\r
2187 \r
2188 \r
2189 VOID\r
2190 SaveSettings(char* name)\r
2191 {\r
2192   FILE *f;\r
2193   ArgDescriptor *ad;\r
2194   WINDOWPLACEMENT wp;\r
2195   char dir[MSG_SIZ];\r
2196 \r
2197   if (!hwndMain) return;\r
2198 \r
2199   GetCurrentDirectory(MSG_SIZ, dir);\r
2200   SetCurrentDirectory(installDir);\r
2201   f = fopen(name, "w");\r
2202   SetCurrentDirectory(dir);\r
2203   if (f == NULL) {\r
2204     DisplayError(name, errno);\r
2205     return;\r
2206   }\r
2207   fprintf(f, ";\n");\r
2208   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2209   fprintf(f, ";\n");\r
2210   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2211   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2212   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2213   fprintf(f, ";\n");\r
2214 \r
2215   wp.length = sizeof(WINDOWPLACEMENT);\r
2216   GetWindowPlacement(hwndMain, &wp);\r
2217   boardX = wp.rcNormalPosition.left;\r
2218   boardY = wp.rcNormalPosition.top;\r
2219 \r
2220   if (hwndConsole) {\r
2221     GetWindowPlacement(hwndConsole, &wp);\r
2222     wpConsole.x = wp.rcNormalPosition.left;\r
2223     wpConsole.y = wp.rcNormalPosition.top;\r
2224     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2225     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2226   }\r
2227 \r
2228   if (analysisDialog) {\r
2229     GetWindowPlacement(analysisDialog, &wp);\r
2230     analysisX = wp.rcNormalPosition.left;\r
2231     analysisY = wp.rcNormalPosition.top;\r
2232     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2233     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2234   }\r
2235 \r
2236   if (commentDialog) {\r
2237     GetWindowPlacement(commentDialog, &wp);\r
2238     commentX = wp.rcNormalPosition.left;\r
2239     commentY = wp.rcNormalPosition.top;\r
2240     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2241     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2242   }\r
2243 \r
2244   if (editTagsDialog) {\r
2245     GetWindowPlacement(editTagsDialog, &wp);\r
2246     editTagsX = wp.rcNormalPosition.left;\r
2247     editTagsY = wp.rcNormalPosition.top;\r
2248     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2249     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2250   }\r
2251 \r
2252   if (gameListDialog) {\r
2253     GetWindowPlacement(gameListDialog, &wp);\r
2254     wpGameList.x = wp.rcNormalPosition.left;\r
2255     wpGameList.y = wp.rcNormalPosition.top;\r
2256     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2257     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2258   }\r
2259 \r
2260   /* [AS] Move history */\r
2261   wpMoveHistory.visible = MoveHistoryIsUp();\r
2262   \r
2263   if( moveHistoryDialog ) {\r
2264     GetWindowPlacement(moveHistoryDialog, &wp);\r
2265     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2266     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2267     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2268     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2269   }\r
2270 \r
2271   /* [AS] Eval graph */\r
2272   wpEvalGraph.visible = EvalGraphIsUp();\r
2273 \r
2274   if( evalGraphDialog ) {\r
2275     GetWindowPlacement(evalGraphDialog, &wp);\r
2276     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2277     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2278     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2279     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2280   }\r
2281 \r
2282   /* [AS] Engine output */\r
2283   wpEngineOutput.visible = EngineOutputIsUp();\r
2284 \r
2285   if( engineOutputDialog ) {\r
2286     GetWindowPlacement(engineOutputDialog, &wp);\r
2287     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2288     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2289     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2290     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2291   }\r
2292 \r
2293   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2294     if (!ad->save) continue;\r
2295     switch (ad->argType) {\r
2296     case ArgString:\r
2297       {\r
2298         char *p = *(char **)ad->argLoc;\r
2299         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2300           /* Quote multiline values or \-containing values\r
2301              with { } if possible */\r
2302           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2303         } else {\r
2304           /* Else quote with " " */\r
2305           fprintf(f, "/%s=\"", ad->argName);\r
2306           while (*p) {\r
2307             if (*p == '\n') fprintf(f, "\n");\r
2308             else if (*p == '\r') fprintf(f, "\\r");\r
2309             else if (*p == '\t') fprintf(f, "\\t");\r
2310             else if (*p == '\b') fprintf(f, "\\b");\r
2311             else if (*p == '\f') fprintf(f, "\\f");\r
2312             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2313             else if (*p == '\"') fprintf(f, "\\\"");\r
2314             else if (*p == '\\') fprintf(f, "\\\\");\r
2315             else putc(*p, f);\r
2316             p++;\r
2317           }\r
2318           fprintf(f, "\"\n");\r
2319         }\r
2320       }\r
2321       break;\r
2322     case ArgInt:\r
2323     case ArgZ:\r
2324       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2325       break;\r
2326     case ArgX:\r
2327       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2328       break;\r
2329     case ArgY:\r
2330       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2331       break;\r
2332     case ArgFloat:\r
2333       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2334       break;\r
2335     case ArgBoolean:\r
2336       fprintf(f, "/%s=%s\n", ad->argName, \r
2337         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2338       break;\r
2339     case ArgTrue:\r
2340       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2341       break;\r
2342     case ArgFalse:\r
2343       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2344       break;\r
2345     case ArgColor:\r
2346       {\r
2347         COLORREF color = *(COLORREF *)ad->argLoc;\r
2348         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2349           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2350       }\r
2351       break;\r
2352     case ArgAttribs:\r
2353       {\r
2354         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2355         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2356           (ta->effects & CFE_BOLD) ? "b" : "",\r
2357           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2358           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2359           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2360           (ta->effects) ? " " : "",\r
2361           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2362       }\r
2363       break;\r
2364     case ArgFilename:\r
2365       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2366         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2367       } else {\r
2368         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2369       }\r
2370       break;\r
2371     case ArgBoardSize:\r
2372       fprintf(f, "/%s=%s\n", ad->argName,\r
2373               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2374       break;\r
2375     case ArgFont:\r
2376       {\r
2377         int bs;\r
2378         for (bs=0; bs<NUM_SIZES; bs++) {\r
2379           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2380           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2381           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2382             ad->argName, mfp->faceName, mfp->pointSize,\r
2383             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2384             mfp->bold ? "b" : "",\r
2385             mfp->italic ? "i" : "",\r
2386             mfp->underline ? "u" : "",\r
2387             mfp->strikeout ? "s" : "");\r
2388         }\r
2389       }\r
2390       break;\r
2391     case ArgCommSettings:\r
2392       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2393     case ArgNone:\r
2394     case ArgSettingsFilename: ;\r
2395     }\r
2396   }\r
2397   fclose(f);\r
2398 }\r
2399 \r
2400 \r
2401 \r
2402 /*---------------------------------------------------------------------------*\\r
2403  *\r
2404  * GDI board drawing routines\r
2405  *\r
2406 \*---------------------------------------------------------------------------*/\r
2407 \r
2408 /* [AS] Draw square using background texture */\r
2409 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2410 {\r
2411     XFORM   x;\r
2412 \r
2413     if( mode == 0 ) {\r
2414         return; /* Should never happen! */\r
2415     }\r
2416 \r
2417     SetGraphicsMode( dst, GM_ADVANCED );\r
2418 \r
2419     switch( mode ) {\r
2420     case 1:\r
2421         /* Identity */\r
2422         break;\r
2423     case 2:\r
2424         /* X reflection */\r
2425         x.eM11 = -1.0;\r
2426         x.eM12 = 0;\r
2427         x.eM21 = 0;\r
2428         x.eM22 = 1.0;\r
2429         x.eDx = (FLOAT) dw + dx - 1;\r
2430         x.eDy = 0;\r
2431         dx = 0;\r
2432         SetWorldTransform( dst, &x );\r
2433         break;\r
2434     case 3:\r
2435         /* Y reflection */\r
2436         x.eM11 = 1.0;\r
2437         x.eM12 = 0;\r
2438         x.eM21 = 0;\r
2439         x.eM22 = -1.0;\r
2440         x.eDx = 0;\r
2441         x.eDy = (FLOAT) dh + dy - 1;\r
2442         dy = 0;\r
2443         SetWorldTransform( dst, &x );\r
2444         break;\r
2445     case 4:\r
2446         /* X/Y flip */\r
2447         x.eM11 = 0;\r
2448         x.eM12 = 1.0;\r
2449         x.eM21 = 1.0;\r
2450         x.eM22 = 0;\r
2451         x.eDx = (FLOAT) dx;\r
2452         x.eDy = (FLOAT) dy;\r
2453         dx = 0;\r
2454         dy = 0;\r
2455         SetWorldTransform( dst, &x );\r
2456         break;\r
2457     }\r
2458 \r
2459     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2460 \r
2461     x.eM11 = 1.0;\r
2462     x.eM12 = 0;\r
2463     x.eM21 = 0;\r
2464     x.eM22 = 1.0;\r
2465     x.eDx = 0;\r
2466     x.eDy = 0;\r
2467     SetWorldTransform( dst, &x );\r
2468 \r
2469     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2470 }\r
2471 \r
2472 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2473 enum {\r
2474     PM_WP = (int) WhitePawn, \r
2475     PM_WN = (int) WhiteKnight, \r
2476     PM_WB = (int) WhiteBishop, \r
2477     PM_WR = (int) WhiteRook, \r
2478     PM_WQ = (int) WhiteQueen, \r
2479     PM_WF = (int) WhiteFerz, \r
2480     PM_WW = (int) WhiteWazir, \r
2481     PM_WE = (int) WhiteAlfil, \r
2482     PM_WM = (int) WhiteMan, \r
2483     PM_WO = (int) WhiteCannon, \r
2484     PM_WU = (int) WhiteUnicorn, \r
2485     PM_WH = (int) WhiteNightrider, \r
2486     PM_WA = (int) WhiteAngel, \r
2487     PM_WC = (int) WhiteMarshall, \r
2488     PM_WAB = (int) WhiteCardinal, \r
2489     PM_WD = (int) WhiteDragon, \r
2490     PM_WL = (int) WhiteLance, \r
2491     PM_WS = (int) WhiteCobra, \r
2492     PM_WV = (int) WhiteFalcon, \r
2493     PM_WSG = (int) WhiteSilver, \r
2494     PM_WG = (int) WhiteGrasshopper, \r
2495     PM_WK = (int) WhiteKing,\r
2496     PM_BP = (int) BlackPawn, \r
2497     PM_BN = (int) BlackKnight, \r
2498     PM_BB = (int) BlackBishop, \r
2499     PM_BR = (int) BlackRook, \r
2500     PM_BQ = (int) BlackQueen, \r
2501     PM_BF = (int) BlackFerz, \r
2502     PM_BW = (int) BlackWazir, \r
2503     PM_BE = (int) BlackAlfil, \r
2504     PM_BM = (int) BlackMan,\r
2505     PM_BO = (int) BlackCannon, \r
2506     PM_BU = (int) BlackUnicorn, \r
2507     PM_BH = (int) BlackNightrider, \r
2508     PM_BA = (int) BlackAngel, \r
2509     PM_BC = (int) BlackMarshall, \r
2510     PM_BG = (int) BlackGrasshopper, \r
2511     PM_BAB = (int) BlackCardinal,\r
2512     PM_BD = (int) BlackDragon,\r
2513     PM_BL = (int) BlackLance,\r
2514     PM_BS = (int) BlackCobra,\r
2515     PM_BV = (int) BlackFalcon,\r
2516     PM_BSG = (int) BlackSilver,\r
2517     PM_BK = (int) BlackKing\r
2518 };\r
2519 \r
2520 static HFONT hPieceFont = NULL;\r
2521 static HBITMAP hPieceMask[(int) EmptySquare];\r
2522 static HBITMAP hPieceFace[(int) EmptySquare];\r
2523 static int fontBitmapSquareSize = 0;\r
2524 static char pieceToFontChar[(int) EmptySquare] =\r
2525                               { 'p', 'n', 'b', 'r', 'q', \r
2526                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2527                       'k', 'o', 'm', 'v', 't', 'w', \r
2528                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2529                                                               'l' };\r
2530 \r
2531 extern BOOL SetCharTable( char *table, const char * map );\r
2532 /* [HGM] moved to backend.c */\r
2533 \r
2534 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2535 {\r
2536     HBRUSH hbrush;\r
2537     BYTE r1 = GetRValue( color );\r
2538     BYTE g1 = GetGValue( color );\r
2539     BYTE b1 = GetBValue( color );\r
2540     BYTE r2 = r1 / 2;\r
2541     BYTE g2 = g1 / 2;\r
2542     BYTE b2 = b1 / 2;\r
2543     RECT rc;\r
2544 \r
2545     /* Create a uniform background first */\r
2546     hbrush = CreateSolidBrush( color );\r
2547     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2548     FillRect( hdc, &rc, hbrush );\r
2549     DeleteObject( hbrush );\r
2550     \r
2551     if( mode == 1 ) {\r
2552         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2553         int steps = squareSize / 2;\r
2554         int i;\r
2555 \r
2556         for( i=0; i<steps; i++ ) {\r
2557             BYTE r = r1 - (r1-r2) * i / steps;\r
2558             BYTE g = g1 - (g1-g2) * i / steps;\r
2559             BYTE b = b1 - (b1-b2) * i / steps;\r
2560 \r
2561             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2562             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2563             FillRect( hdc, &rc, hbrush );\r
2564             DeleteObject(hbrush);\r
2565         }\r
2566     }\r
2567     else if( mode == 2 ) {\r
2568         /* Diagonal gradient, good more or less for every piece */\r
2569         POINT triangle[3];\r
2570         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2571         HBRUSH hbrush_old;\r
2572         int steps = squareSize;\r
2573         int i;\r
2574 \r
2575         triangle[0].x = squareSize - steps;\r
2576         triangle[0].y = squareSize;\r
2577         triangle[1].x = squareSize;\r
2578         triangle[1].y = squareSize;\r
2579         triangle[2].x = squareSize;\r
2580         triangle[2].y = squareSize - steps;\r
2581 \r
2582         for( i=0; i<steps; i++ ) {\r
2583             BYTE r = r1 - (r1-r2) * i / steps;\r
2584             BYTE g = g1 - (g1-g2) * i / steps;\r
2585             BYTE b = b1 - (b1-b2) * i / steps;\r
2586 \r
2587             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2588             hbrush_old = SelectObject( hdc, hbrush );\r
2589             Polygon( hdc, triangle, 3 );\r
2590             SelectObject( hdc, hbrush_old );\r
2591             DeleteObject(hbrush);\r
2592             triangle[0].x++;\r
2593             triangle[2].y++;\r
2594         }\r
2595 \r
2596         SelectObject( hdc, hpen );\r
2597     }\r
2598 }\r
2599 \r
2600 /*\r
2601     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2602     seems to work ok. The main problem here is to find the "inside" of a chess\r
2603     piece: follow the steps as explained below.\r
2604 */\r
2605 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2606 {\r
2607     HBITMAP hbm;\r
2608     HBITMAP hbm_old;\r
2609     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2610     RECT rc;\r
2611     SIZE sz;\r
2612     POINT pt;\r
2613     int backColor = whitePieceColor; \r
2614     int foreColor = blackPieceColor;\r
2615     \r
2616     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2617         backColor = appData.fontBackColorWhite;\r
2618         foreColor = appData.fontForeColorWhite;\r
2619     }\r
2620     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2621         backColor = appData.fontBackColorBlack;\r
2622         foreColor = appData.fontForeColorBlack;\r
2623     }\r
2624 \r
2625     /* Mask */\r
2626     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2627 \r
2628     hbm_old = SelectObject( hdc, hbm );\r
2629 \r
2630     rc.left = 0;\r
2631     rc.top = 0;\r
2632     rc.right = squareSize;\r
2633     rc.bottom = squareSize;\r
2634 \r
2635     /* Step 1: background is now black */\r
2636     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2637 \r
2638     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2639 \r
2640     pt.x = (squareSize - sz.cx) / 2;\r
2641     pt.y = (squareSize - sz.cy) / 2;\r
2642 \r
2643     SetBkMode( hdc, TRANSPARENT );\r
2644     SetTextColor( hdc, chroma );\r
2645     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2646     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2647 \r
2648     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2649     /* Step 3: the area outside the piece is filled with white */\r
2650 //    FloodFill( hdc, 0, 0, chroma );\r
2651     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2652     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2653     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2654     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2655     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2656     /* \r
2657         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2658         but if the start point is not inside the piece we're lost!\r
2659         There should be a better way to do this... if we could create a region or path\r
2660         from the fill operation we would be fine for example.\r
2661     */\r
2662 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2663     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2664 \r
2665     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2666         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2667         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2668 \r
2669         SelectObject( dc2, bm2 );\r
2670         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2671         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2672         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2673         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2674         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2675 \r
2676         DeleteDC( dc2 );\r
2677         DeleteObject( bm2 );\r
2678     }\r
2679 \r
2680     SetTextColor( hdc, 0 );\r
2681     /* \r
2682         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2683         draw the piece again in black for safety.\r
2684     */\r
2685     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2686 \r
2687     SelectObject( hdc, hbm_old );\r
2688 \r
2689     if( hPieceMask[index] != NULL ) {\r
2690         DeleteObject( hPieceMask[index] );\r
2691     }\r
2692 \r
2693     hPieceMask[index] = hbm;\r
2694 \r
2695     /* Face */\r
2696     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2697 \r
2698     SelectObject( hdc, hbm );\r
2699 \r
2700     {\r
2701         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2702         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2703         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2704 \r
2705         SelectObject( dc1, hPieceMask[index] );\r
2706         SelectObject( dc2, bm2 );\r
2707         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2708         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2709         \r
2710         /* \r
2711             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2712             the piece background and deletes (makes transparent) the rest.\r
2713             Thanks to that mask, we are free to paint the background with the greates\r
2714             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2715             We use this, to make gradients and give the pieces a "roundish" look.\r
2716         */\r
2717         SetPieceBackground( hdc, backColor, 2 );\r
2718         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2719 \r
2720         DeleteDC( dc2 );\r
2721         DeleteDC( dc1 );\r
2722         DeleteObject( bm2 );\r
2723     }\r
2724 \r
2725     SetTextColor( hdc, foreColor );\r
2726     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2727 \r
2728     SelectObject( hdc, hbm_old );\r
2729 \r
2730     if( hPieceFace[index] != NULL ) {\r
2731         DeleteObject( hPieceFace[index] );\r
2732     }\r
2733 \r
2734     hPieceFace[index] = hbm;\r
2735 }\r
2736 \r
2737 static int TranslatePieceToFontPiece( int piece )\r
2738 {\r
2739     switch( piece ) {\r
2740     case BlackPawn:\r
2741         return PM_BP;\r
2742     case BlackKnight:\r
2743         return PM_BN;\r
2744     case BlackBishop:\r
2745         return PM_BB;\r
2746     case BlackRook:\r
2747         return PM_BR;\r
2748     case BlackQueen:\r
2749         return PM_BQ;\r
2750     case BlackKing:\r
2751         return PM_BK;\r
2752     case WhitePawn:\r
2753         return PM_WP;\r
2754     case WhiteKnight:\r
2755         return PM_WN;\r
2756     case WhiteBishop:\r
2757         return PM_WB;\r
2758     case WhiteRook:\r
2759         return PM_WR;\r
2760     case WhiteQueen:\r
2761         return PM_WQ;\r
2762     case WhiteKing:\r
2763         return PM_WK;\r
2764 \r
2765     case BlackAngel:\r
2766         return PM_BA;\r
2767     case BlackMarshall:\r
2768         return PM_BC;\r
2769     case BlackFerz:\r
2770         return PM_BF;\r
2771     case BlackNightrider:\r
2772         return PM_BH;\r
2773     case BlackAlfil:\r
2774         return PM_BE;\r
2775     case BlackWazir:\r
2776         return PM_BW;\r
2777     case BlackUnicorn:\r
2778         return PM_BU;\r
2779     case BlackCannon:\r
2780         return PM_BO;\r
2781     case BlackGrasshopper:\r
2782         return PM_BG;\r
2783     case BlackMan:\r
2784         return PM_BM;\r
2785     case BlackSilver:\r
2786         return PM_BSG;\r
2787     case BlackLance:\r
2788         return PM_BL;\r
2789     case BlackFalcon:\r
2790         return PM_BV;\r
2791     case BlackCobra:\r
2792         return PM_BS;\r
2793     case BlackCardinal:\r
2794         return PM_BAB;\r
2795     case BlackDragon:\r
2796         return PM_BD;\r
2797 \r
2798     case WhiteAngel:\r
2799         return PM_WA;\r
2800     case WhiteMarshall:\r
2801         return PM_WC;\r
2802     case WhiteFerz:\r
2803         return PM_WF;\r
2804     case WhiteNightrider:\r
2805         return PM_WH;\r
2806     case WhiteAlfil:\r
2807         return PM_WE;\r
2808     case WhiteWazir:\r
2809         return PM_WW;\r
2810     case WhiteUnicorn:\r
2811         return PM_WU;\r
2812     case WhiteCannon:\r
2813         return PM_WO;\r
2814     case WhiteGrasshopper:\r
2815         return PM_WG;\r
2816     case WhiteMan:\r
2817         return PM_WM;\r
2818     case WhiteSilver:\r
2819         return PM_WSG;\r
2820     case WhiteLance:\r
2821         return PM_WL;\r
2822     case WhiteFalcon:\r
2823         return PM_WV;\r
2824     case WhiteCobra:\r
2825         return PM_WS;\r
2826     case WhiteCardinal:\r
2827         return PM_WAB;\r
2828     case WhiteDragon:\r
2829         return PM_WD;\r
2830     }\r
2831 \r
2832     return 0;\r
2833 }\r
2834 \r
2835 void CreatePiecesFromFont()\r
2836 {\r
2837     LOGFONT lf;\r
2838     HDC hdc_window = NULL;\r
2839     HDC hdc = NULL;\r
2840     HFONT hfont_old;\r
2841     int fontHeight;\r
2842     int i;\r
2843 \r
2844     if( fontBitmapSquareSize < 0 ) {\r
2845         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2846         return;\r
2847     }\r
2848 \r
2849     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2850         fontBitmapSquareSize = -1;\r
2851         return;\r
2852     }\r
2853 \r
2854     if( fontBitmapSquareSize != squareSize ) {\r
2855         hdc_window = GetDC( hwndMain );\r
2856         hdc = CreateCompatibleDC( hdc_window );\r
2857 \r
2858         if( hPieceFont != NULL ) {\r
2859             DeleteObject( hPieceFont );\r
2860         }\r
2861         else {\r
2862             for( i=0; i<=(int)BlackKing; i++ ) {\r
2863                 hPieceMask[i] = NULL;\r
2864                 hPieceFace[i] = NULL;\r
2865             }\r
2866         }\r
2867 \r
2868         fontHeight = 75;\r
2869 \r
2870         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2871             fontHeight = appData.fontPieceSize;\r
2872         }\r
2873 \r
2874         fontHeight = (fontHeight * squareSize) / 100;\r
2875 \r
2876         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2877         lf.lfWidth = 0;\r
2878         lf.lfEscapement = 0;\r
2879         lf.lfOrientation = 0;\r
2880         lf.lfWeight = FW_NORMAL;\r
2881         lf.lfItalic = 0;\r
2882         lf.lfUnderline = 0;\r
2883         lf.lfStrikeOut = 0;\r
2884         lf.lfCharSet = DEFAULT_CHARSET;\r
2885         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2886         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2887         lf.lfQuality = PROOF_QUALITY;\r
2888         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2889         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2890         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2891 \r
2892         hPieceFont = CreateFontIndirect( &lf );\r
2893 \r
2894         if( hPieceFont == NULL ) {\r
2895             fontBitmapSquareSize = -2;\r
2896         }\r
2897         else {\r
2898             /* Setup font-to-piece character table */\r
2899             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2900                 /* No (or wrong) global settings, try to detect the font */\r
2901                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2902                     /* Alpha */\r
2903                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2904                 }\r
2905                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2906                     /* DiagramTT* family */\r
2907                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2908                 }\r
2909                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2910                     /* Fairy symbols */\r
2911                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2912                 }\r
2913                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2914                     /* Good Companion (Some characters get warped as literal :-( */\r
2915                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2916                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2917                     SetCharTable(pieceToFontChar, s);\r
2918                 }\r
2919                 else {\r
2920                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2921                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2922                 }\r
2923             }\r
2924 \r
2925             /* Create bitmaps */\r
2926             hfont_old = SelectObject( hdc, hPieceFont );\r
2927 #if 0\r
2928             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2929             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2930             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2931             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2932             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2933             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2934             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2935             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2936             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2937             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2938             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2939             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2940 \r
2941             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2942             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2943             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2944             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2945             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2946             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2947             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2948             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2949             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2950             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2951             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2952             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2953             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2954             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2955             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2956             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2957             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2958             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2959             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2960             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2961             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2962             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2963             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2964             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2965             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2966             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2967             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2968             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2969             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2970             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2971             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2972             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2973 #else\r
2974             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2975                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2976                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2977 #endif\r
2978             SelectObject( hdc, hfont_old );\r
2979 \r
2980             fontBitmapSquareSize = squareSize;\r
2981         }\r
2982     }\r
2983 \r
2984     if( hdc != NULL ) {\r
2985         DeleteDC( hdc );\r
2986     }\r
2987 \r
2988     if( hdc_window != NULL ) {\r
2989         ReleaseDC( hwndMain, hdc_window );\r
2990     }\r
2991 }\r
2992 \r
2993 HBITMAP\r
2994 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2995 {\r
2996   char name[128];\r
2997 \r
2998   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2999   if (gameInfo.event &&\r
3000       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3001       strcmp(name, "k80s") == 0) {\r
3002     strcpy(name, "tim");\r
3003   }\r
3004   return LoadBitmap(hinst, name);\r
3005 }\r
3006 \r
3007 \r
3008 /* Insert a color into the program's logical palette\r
3009    structure.  This code assumes the given color is\r
3010    the result of the RGB or PALETTERGB macro, and it\r
3011    knows how those macros work (which is documented).\r
3012 */\r
3013 VOID\r
3014 InsertInPalette(COLORREF color)\r
3015 {\r
3016   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3017 \r
3018   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3019     DisplayFatalError("Too many colors", 0, 1);\r
3020     pLogPal->palNumEntries--;\r
3021     return;\r
3022   }\r
3023 \r
3024   pe->peFlags = (char) 0;\r
3025   pe->peRed = (char) (0xFF & color);\r
3026   pe->peGreen = (char) (0xFF & (color >> 8));\r
3027   pe->peBlue = (char) (0xFF & (color >> 16));\r
3028   return;\r
3029 }\r
3030 \r
3031 \r
3032 VOID\r
3033 InitDrawingColors()\r
3034 {\r
3035   if (pLogPal == NULL) {\r
3036     /* Allocate enough memory for a logical palette with\r
3037      * PALETTESIZE entries and set the size and version fields\r
3038      * of the logical palette structure.\r
3039      */\r
3040     pLogPal = (NPLOGPALETTE)\r
3041       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3042                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3043     pLogPal->palVersion    = 0x300;\r
3044   }\r
3045   pLogPal->palNumEntries = 0;\r
3046 \r
3047   InsertInPalette(lightSquareColor);\r
3048   InsertInPalette(darkSquareColor);\r
3049   InsertInPalette(whitePieceColor);\r
3050   InsertInPalette(blackPieceColor);\r
3051   InsertInPalette(highlightSquareColor);\r
3052   InsertInPalette(premoveHighlightColor);\r
3053 \r
3054   /*  create a logical color palette according the information\r
3055    *  in the LOGPALETTE structure.\r
3056    */\r
3057   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3058 \r
3059   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3060   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3061   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3062   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3063   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3064   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3065   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3066   /* [AS] Force rendering of the font-based pieces */\r
3067   if( fontBitmapSquareSize > 0 ) {\r
3068     fontBitmapSquareSize = 0;\r
3069   }\r
3070 }\r
3071 \r
3072 \r
3073 int\r
3074 BoardWidth(int boardSize, int n)\r
3075 { /* [HGM] argument n added to allow different width and height */\r
3076   int lineGap = sizeInfo[boardSize].lineGap;\r
3077 \r
3078   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3079       lineGap = appData.overrideLineGap;\r
3080   }\r
3081 \r
3082   return (n + 1) * lineGap +\r
3083           n * sizeInfo[boardSize].squareSize;\r
3084 }\r
3085 \r
3086 /* Respond to board resize by dragging edge */\r
3087 VOID\r
3088 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3089 {\r
3090   BoardSize newSize = NUM_SIZES - 1;\r
3091   static int recurse = 0;\r
3092   if (IsIconic(hwndMain)) return;\r
3093   if (recurse > 0) return;\r
3094   recurse++;\r
3095   while (newSize > 0) {\r
3096         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3097         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3098            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3099     newSize--;\r
3100   } \r
3101   boardSize = newSize;\r
3102   InitDrawingSizes(boardSize, flags);\r
3103   recurse--;\r
3104 }\r
3105 \r
3106 \r
3107 \r
3108 VOID\r
3109 InitDrawingSizes(BoardSize boardSize, int flags)\r
3110 {\r
3111   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3112   ChessSquare piece;\r
3113   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3114   HDC hdc;\r
3115   SIZE clockSize, messageSize;\r
3116   HFONT oldFont;\r
3117   char buf[MSG_SIZ];\r
3118   char *str;\r
3119   HMENU hmenu = GetMenu(hwndMain);\r
3120   RECT crect, wrect, oldRect;\r
3121   int offby;\r
3122   LOGBRUSH logbrush;\r
3123 \r
3124   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3125   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3126 \r
3127   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3128   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3129 \r
3130   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3131   oldRect.top = boardY;\r
3132   oldRect.right = boardX + winWidth;\r
3133   oldRect.bottom = boardY + winHeight;\r
3134 \r
3135   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3136   smallLayout = sizeInfo[boardSize].smallLayout;\r
3137   squareSize = sizeInfo[boardSize].squareSize;\r
3138   lineGap = sizeInfo[boardSize].lineGap;\r
3139   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3140 \r
3141   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3142       lineGap = appData.overrideLineGap;\r
3143   }\r
3144 \r
3145   if (tinyLayout != oldTinyLayout) {\r
3146     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3147     if (tinyLayout) {\r
3148       style &= ~WS_SYSMENU;\r
3149       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3150                  "&Minimize\tCtrl+F4");\r
3151     } else {\r
3152       style |= WS_SYSMENU;\r
3153       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3154     }\r
3155     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3156 \r
3157     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3158       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3159         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3160     }\r
3161     DrawMenuBar(hwndMain);\r
3162   }\r
3163 \r
3164   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3165   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3166 \r
3167   /* Get text area sizes */\r
3168   hdc = GetDC(hwndMain);\r
3169   if (appData.clockMode) {\r
3170     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3171   } else {\r
3172     sprintf(buf, "White");\r
3173   }\r
3174   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3175   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3176   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3177   str = "We only care about the height here";\r
3178   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3179   SelectObject(hdc, oldFont);\r
3180   ReleaseDC(hwndMain, hdc);\r
3181 \r
3182   /* Compute where everything goes */\r
3183   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3184         /* [HGM] logo: if either logo is on, reserve space for it */\r
3185         logoHeight =  2*clockSize.cy;\r
3186         leftLogoRect.left   = OUTER_MARGIN;\r
3187         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3188         leftLogoRect.top    = OUTER_MARGIN;\r
3189         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3190 \r
3191         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3192         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3193         rightLogoRect.top    = OUTER_MARGIN;\r
3194         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3195 \r
3196 \r
3197     whiteRect.left = leftLogoRect.right;\r
3198     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3199     whiteRect.top = OUTER_MARGIN;\r
3200     whiteRect.bottom = whiteRect.top + logoHeight;\r
3201 \r
3202     blackRect.right = rightLogoRect.left;\r
3203     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3204     blackRect.top = whiteRect.top;\r
3205     blackRect.bottom = whiteRect.bottom;\r
3206   } else {\r
3207     whiteRect.left = OUTER_MARGIN;\r
3208     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3209     whiteRect.top = OUTER_MARGIN;\r
3210     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3211 \r
3212     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3213     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3214     blackRect.top = whiteRect.top;\r
3215     blackRect.bottom = whiteRect.bottom;\r
3216   }\r
3217 \r
3218   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3219   if (appData.showButtonBar) {\r
3220     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3221       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3222   } else {\r
3223     messageRect.right = OUTER_MARGIN + boardWidth;\r
3224   }\r
3225   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3226   messageRect.bottom = messageRect.top + messageSize.cy;\r
3227 \r
3228   boardRect.left = OUTER_MARGIN;\r
3229   boardRect.right = boardRect.left + boardWidth;\r
3230   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3231   boardRect.bottom = boardRect.top + boardHeight;\r
3232 \r
3233   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3234   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3235   oldBoardSize = boardSize;\r
3236   oldTinyLayout = tinyLayout;\r
3237   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3238   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3239   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3240     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3241   GetWindowRect(hwndMain, &wrect);\r
3242   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3243                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3244 \r
3245   // [HGM] placement: let attached windows follow size change.\r
3246   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3247   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3248   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3249   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3250   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3251 \r
3252   /* compensate if menu bar wrapped */\r
3253   GetClientRect(hwndMain, &crect);\r
3254   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3255   winHeight += offby;\r
3256   switch (flags) {\r
3257   case WMSZ_TOPLEFT:\r
3258     SetWindowPos(hwndMain, NULL, \r
3259                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3260                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3261     break;\r
3262 \r
3263   case WMSZ_TOPRIGHT:\r
3264   case WMSZ_TOP:\r
3265     SetWindowPos(hwndMain, NULL, \r
3266                  wrect.left, wrect.bottom - winHeight, \r
3267                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3268     break;\r
3269 \r
3270   case WMSZ_BOTTOMLEFT:\r
3271   case WMSZ_LEFT:\r
3272     SetWindowPos(hwndMain, NULL, \r
3273                  wrect.right - winWidth, wrect.top, \r
3274                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3275     break;\r
3276 \r
3277   case WMSZ_BOTTOMRIGHT:\r
3278   case WMSZ_BOTTOM:\r
3279   case WMSZ_RIGHT:\r
3280   default:\r
3281     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3282                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3283     break;\r
3284   }\r
3285 \r
3286   hwndPause = NULL;\r
3287   for (i = 0; i < N_BUTTONS; i++) {\r
3288     if (buttonDesc[i].hwnd != NULL) {\r
3289       DestroyWindow(buttonDesc[i].hwnd);\r
3290       buttonDesc[i].hwnd = NULL;\r
3291     }\r
3292     if (appData.showButtonBar) {\r
3293       buttonDesc[i].hwnd =\r
3294         CreateWindow("BUTTON", buttonDesc[i].label,\r
3295                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3296                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3297                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3298                      (HMENU) buttonDesc[i].id,\r
3299                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3300       if (tinyLayout) {\r
3301         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3302                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3303                     MAKELPARAM(FALSE, 0));\r
3304       }\r
3305       if (buttonDesc[i].id == IDM_Pause)\r
3306         hwndPause = buttonDesc[i].hwnd;\r
3307       buttonDesc[i].wndproc = (WNDPROC)\r
3308         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3309     }\r
3310   }\r
3311   if (gridPen != NULL) DeleteObject(gridPen);\r
3312   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3313   if (premovePen != NULL) DeleteObject(premovePen);\r
3314   if (lineGap != 0) {\r
3315     logbrush.lbStyle = BS_SOLID;\r
3316     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3317     gridPen =\r
3318       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3319                    lineGap, &logbrush, 0, NULL);\r
3320     logbrush.lbColor = highlightSquareColor;\r
3321     highlightPen =\r
3322       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3323                    lineGap, &logbrush, 0, NULL);\r
3324 \r
3325     logbrush.lbColor = premoveHighlightColor; \r
3326     premovePen =\r
3327       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3328                    lineGap, &logbrush, 0, NULL);\r
3329 \r
3330     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3331     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3332       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3333       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3334         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3335       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3336         BOARD_WIDTH * (squareSize + lineGap);\r
3337       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3338     }\r
3339     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3340       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3341       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3342         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3343         lineGap / 2 + (i * (squareSize + lineGap));\r
3344       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3345         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3346       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3347     }\r
3348   }\r
3349 \r
3350   /* [HGM] Licensing requirement */\r
3351 #ifdef GOTHIC\r
3352   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3353 #endif\r
3354 #ifdef FALCON\r
3355   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3356 #endif\r
3357   GothicPopUp( "", VariantNormal);\r
3358 \r
3359 \r
3360 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3361 \r
3362   /* Load piece bitmaps for this board size */\r
3363   for (i=0; i<=2; i++) {\r
3364     for (piece = WhitePawn;\r
3365          (int) piece < (int) BlackPawn;\r
3366          piece = (ChessSquare) ((int) piece + 1)) {\r
3367       if (pieceBitmap[i][piece] != NULL)\r
3368         DeleteObject(pieceBitmap[i][piece]);\r
3369     }\r
3370   }\r
3371 \r
3372   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3373   // Orthodox Chess pieces\r
3374   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3375   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3376   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3377   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3378   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3379   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3380   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3381   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3382   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3383   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3384   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3385   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3386   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3387   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3388   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3389   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3390     // in Shogi, Hijack the unused Queen for Lance\r
3391     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3392     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3393     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3394   } else {\r
3395     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3396     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3397     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3398   }\r
3399 \r
3400   if(squareSize <= 72 && squareSize >= 33) { \r
3401     /* A & C are available in most sizes now */\r
3402     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3403       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3404       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3405       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3406       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3407       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3408       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3409       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3410       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3411       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3412       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3413       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3414       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3415     } else { // Smirf-like\r
3416       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3417       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3418       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3419     }\r
3420     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3421       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3422       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3423       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3424     } else { // WinBoard standard\r
3425       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3426       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3427       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3428     }\r
3429   }\r
3430 \r
3431 \r
3432   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3433     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3434     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3435     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3436     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3437     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3438     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3439     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3440     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3441     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3442     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3443     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3444     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3445     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3446     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3447     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3448     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3449     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3450     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3451     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3452     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3453     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3454     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3455     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3456     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3457     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3458     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3459     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3460     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3461     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3462     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3463 \r
3464     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3465       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3466       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3467       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3468       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3469       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3470       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3471       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3472       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3473       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3474       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3475       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3476       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3477     } else {\r
3478       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3479       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3480       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3481       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3482       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3483       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3484       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3485       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3486       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3487       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3488       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3489       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3490     }\r
3491 \r
3492   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3493     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3494     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3495     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3496     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3497     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3498     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3499     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3500     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3501     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3502     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3503     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3504     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3505     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3506     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3507   }\r
3508 \r
3509 \r
3510   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3511   /* special Shogi support in this size */\r
3512   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3513       for (piece = WhitePawn;\r
3514            (int) piece < (int) BlackPawn;\r
3515            piece = (ChessSquare) ((int) piece + 1)) {\r
3516         if (pieceBitmap[i][piece] != NULL)\r
3517           DeleteObject(pieceBitmap[i][piece]);\r
3518       }\r
3519     }\r
3520   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3521   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3522   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3523   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3524   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3525   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3526   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3527   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3528   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3529   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3530   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3531   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3532   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3533   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3534   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3535   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3536   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3537   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3538   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3539   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3540   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3541   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3542   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3543   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3544   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3545   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3546   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3547   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3548   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3549   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3550   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3551   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3552   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3553   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3554   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3555   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3556   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3557   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3558   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3559   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3560   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3561   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3562   minorSize = 0;\r
3563   }\r
3564 }\r
3565 \r
3566 HBITMAP\r
3567 PieceBitmap(ChessSquare p, int kind)\r
3568 {\r
3569   if ((int) p >= (int) BlackPawn)\r
3570     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3571 \r
3572   return pieceBitmap[kind][(int) p];\r
3573 }\r
3574 \r
3575 /***************************************************************/\r
3576 \r
3577 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3578 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3579 /*\r
3580 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3581 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3582 */\r
3583 \r
3584 VOID\r
3585 SquareToPos(int row, int column, int * x, int * y)\r
3586 {\r
3587   if (flipView) {\r
3588     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3589     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3590   } else {\r
3591     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3592     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3593   }\r
3594 }\r
3595 \r
3596 VOID\r
3597 DrawCoordsOnDC(HDC hdc)\r
3598 {\r
3599   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
3600   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
3601   char str[2] = { NULLCHAR, NULLCHAR };\r
3602   int oldMode, oldAlign, x, y, start, i;\r
3603   HFONT oldFont;\r
3604   HBRUSH oldBrush;\r
3605 \r
3606   if (!appData.showCoords)\r
3607     return;\r
3608 \r
3609   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3610 \r
3611   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3612   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3613   oldAlign = GetTextAlign(hdc);\r
3614   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3615 \r
3616   y = boardRect.top + lineGap;\r
3617   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3618 \r
3619   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3620   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3621     str[0] = files[start + i];\r
3622     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3623     y += squareSize + lineGap;\r
3624   }\r
3625 \r
3626   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3627 \r
3628   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3629   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3630     str[0] = ranks[start + i];\r
3631     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3632     x += squareSize + lineGap;\r
3633   }    \r
3634 \r
3635   SelectObject(hdc, oldBrush);\r
3636   SetBkMode(hdc, oldMode);\r
3637   SetTextAlign(hdc, oldAlign);\r
3638   SelectObject(hdc, oldFont);\r
3639 }\r
3640 \r
3641 VOID\r
3642 DrawGridOnDC(HDC hdc)\r
3643 {\r
3644   HPEN oldPen;\r
3645  \r
3646   if (lineGap != 0) {\r
3647     oldPen = SelectObject(hdc, gridPen);\r
3648     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3649     SelectObject(hdc, oldPen);\r
3650   }\r
3651 }\r
3652 \r
3653 #define HIGHLIGHT_PEN 0\r
3654 #define PREMOVE_PEN   1\r
3655 \r
3656 VOID\r
3657 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3658 {\r
3659   int x1, y1;\r
3660   HPEN oldPen, hPen;\r
3661   if (lineGap == 0) return;\r
3662   if (flipView) {\r
3663     x1 = boardRect.left +\r
3664       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3665     y1 = boardRect.top +\r
3666       lineGap/2 + y * (squareSize + lineGap);\r
3667   } else {\r
3668     x1 = boardRect.left +\r
3669       lineGap/2 + x * (squareSize + lineGap);\r
3670     y1 = boardRect.top +\r
3671       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3672   }\r
3673   hPen = pen ? premovePen : highlightPen;\r
3674   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3675   MoveToEx(hdc, x1, y1, NULL);\r
3676   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3677   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3678   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3679   LineTo(hdc, x1, y1);\r
3680   SelectObject(hdc, oldPen);\r
3681 }\r
3682 \r
3683 VOID\r
3684 DrawHighlightsOnDC(HDC hdc)\r
3685 {\r
3686   int i;\r
3687   for (i=0; i<2; i++) {\r
3688     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3689       DrawHighlightOnDC(hdc, TRUE,\r
3690                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3691                         HIGHLIGHT_PEN);\r
3692   }\r
3693   for (i=0; i<2; i++) {\r
3694     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3695         premoveHighlightInfo.sq[i].y >= 0) {\r
3696         DrawHighlightOnDC(hdc, TRUE,\r
3697                           premoveHighlightInfo.sq[i].x, \r
3698                           premoveHighlightInfo.sq[i].y,\r
3699                           PREMOVE_PEN);\r
3700     }\r
3701   }\r
3702 }\r
3703 \r
3704 /* Note: sqcolor is used only in monoMode */\r
3705 /* Note that this code is largely duplicated in woptions.c,\r
3706    function DrawSampleSquare, so that needs to be updated too */\r
3707 VOID\r
3708 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3709 {\r
3710   HBITMAP oldBitmap;\r
3711   HBRUSH oldBrush;\r
3712   int tmpSize;\r
3713 \r
3714   if (appData.blindfold) return;\r
3715 \r
3716   /* [AS] Use font-based pieces if needed */\r
3717   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3718     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3719     CreatePiecesFromFont();\r
3720 \r
3721     if( fontBitmapSquareSize == squareSize ) {\r
3722         int index = TranslatePieceToFontPiece(piece);\r
3723 \r
3724         SelectObject( tmphdc, hPieceMask[ index ] );\r
3725 \r
3726         BitBlt( hdc,\r
3727             x, y,\r
3728             squareSize, squareSize,\r
3729             tmphdc,\r
3730             0, 0,\r
3731             SRCAND );\r
3732 \r
3733         SelectObject( tmphdc, hPieceFace[ index ] );\r
3734 \r
3735         BitBlt( hdc,\r
3736             x, y,\r
3737             squareSize, squareSize,\r
3738             tmphdc,\r
3739             0, 0,\r
3740             SRCPAINT );\r
3741 \r
3742         return;\r
3743     }\r
3744   }\r
3745 \r
3746   if (appData.monoMode) {\r
3747     SelectObject(tmphdc, PieceBitmap(piece, \r
3748       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3749     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3750            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3751   } else {\r
3752     tmpSize = squareSize;\r
3753     if(minorSize &&\r
3754         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3755          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3756       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3757       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3758       x += (squareSize - minorSize)>>1;\r
3759       y += squareSize - minorSize - 2;\r
3760       tmpSize = minorSize;\r
3761     }\r
3762     if (color || appData.allWhite ) {\r
3763       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3764       if( color )\r
3765               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3766       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3767       if(appData.upsideDown && color==flipView)\r
3768         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3769       else\r
3770         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3771 #if 0\r
3772       /* Use black piece color for outline of white pieces */\r
3773       /* Not sure this looks really good (though xboard does it).\r
3774          Maybe better to have another selectable color, default black */\r
3775       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3776       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3777       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3778 #else\r
3779       /* Use black for outline of white pieces */\r
3780       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3781       if(appData.upsideDown && color==flipView)\r
3782         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3783       else\r
3784         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3785 #endif\r
3786     } else {\r
3787 #if 0\r
3788       /* Use white piece color for details of black pieces */\r
3789       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3790          WHITE_PIECE ones aren't always the right shape. */\r
3791       /* Not sure this looks really good (though xboard does it).\r
3792          Maybe better to have another selectable color, default medium gray? */\r
3793       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3794       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3795       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3796       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3797       SelectObject(hdc, blackPieceBrush);\r
3798       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3799 #else\r
3800       /* Use square color for details of black pieces */\r
3801       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3802       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3803       if(appData.upsideDown && !flipView)\r
3804         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3805       else\r
3806         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3807 #endif\r
3808     }\r
3809     SelectObject(hdc, oldBrush);\r
3810     SelectObject(tmphdc, oldBitmap);\r
3811   }\r
3812 }\r
3813 \r
3814 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3815 int GetBackTextureMode( int algo )\r
3816 {\r
3817     int result = BACK_TEXTURE_MODE_DISABLED;\r
3818 \r
3819     switch( algo ) \r
3820     {\r
3821         case BACK_TEXTURE_MODE_PLAIN:\r
3822             result = 1; /* Always use identity map */\r
3823             break;\r
3824         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3825             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3826             break;\r
3827     }\r
3828 \r
3829     return result;\r
3830 }\r
3831 \r
3832 /* \r
3833     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3834     to handle redraws cleanly (as random numbers would always be different).\r
3835 */\r
3836 VOID RebuildTextureSquareInfo()\r
3837 {\r
3838     BITMAP bi;\r
3839     int lite_w = 0;\r
3840     int lite_h = 0;\r
3841     int dark_w = 0;\r
3842     int dark_h = 0;\r
3843     int row;\r
3844     int col;\r
3845 \r
3846     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3847 \r
3848     if( liteBackTexture != NULL ) {\r
3849         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3850             lite_w = bi.bmWidth;\r
3851             lite_h = bi.bmHeight;\r
3852         }\r
3853     }\r
3854 \r
3855     if( darkBackTexture != NULL ) {\r
3856         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3857             dark_w = bi.bmWidth;\r
3858             dark_h = bi.bmHeight;\r
3859         }\r
3860     }\r
3861 \r
3862     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3863         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3864             if( (col + row) & 1 ) {\r
3865                 /* Lite square */\r
3866                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3867                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3868                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3869                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3870                 }\r
3871             }\r
3872             else {\r
3873                 /* Dark square */\r
3874                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3875                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3876                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3877                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3878                 }\r
3879             }\r
3880         }\r
3881     }\r
3882 }\r
3883 \r
3884 /* [AS] Arrow highlighting support */\r
3885 \r
3886 static int A_WIDTH = 5; /* Width of arrow body */\r
3887 \r
3888 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3889 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3890 \r
3891 static double Sqr( double x )\r
3892 {\r
3893     return x*x;\r
3894 }\r
3895 \r
3896 static int Round( double x )\r
3897 {\r
3898     return (int) (x + 0.5);\r
3899 }\r
3900 \r
3901 /* Draw an arrow between two points using current settings */\r
3902 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3903 {\r
3904     POINT arrow[7];\r
3905     double dx, dy, j, k, x, y;\r
3906 \r
3907     if( d_x == s_x ) {\r
3908         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3909 \r
3910         arrow[0].x = s_x + A_WIDTH;\r
3911         arrow[0].y = s_y;\r
3912 \r
3913         arrow[1].x = s_x + A_WIDTH;\r
3914         arrow[1].y = d_y - h;\r
3915 \r
3916         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3917         arrow[2].y = d_y - h;\r
3918 \r
3919         arrow[3].x = d_x;\r
3920         arrow[3].y = d_y;\r
3921 \r
3922         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3923         arrow[4].y = d_y - h;\r
3924 \r
3925         arrow[5].x = s_x - A_WIDTH;\r
3926         arrow[5].y = d_y - h;\r
3927 \r
3928         arrow[6].x = s_x - A_WIDTH;\r
3929         arrow[6].y = s_y;\r
3930     }\r
3931     else if( d_y == s_y ) {\r
3932         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3933 \r
3934         arrow[0].x = s_x;\r
3935         arrow[0].y = s_y + A_WIDTH;\r
3936 \r
3937         arrow[1].x = d_x - w;\r
3938         arrow[1].y = s_y + A_WIDTH;\r
3939 \r
3940         arrow[2].x = d_x - w;\r
3941         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3942 \r
3943         arrow[3].x = d_x;\r
3944         arrow[3].y = d_y;\r
3945 \r
3946         arrow[4].x = d_x - w;\r
3947         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3948 \r
3949         arrow[5].x = d_x - w;\r
3950         arrow[5].y = s_y - A_WIDTH;\r
3951 \r
3952         arrow[6].x = s_x;\r
3953         arrow[6].y = s_y - A_WIDTH;\r
3954     }\r
3955     else {\r
3956         /* [AS] Needed a lot of paper for this! :-) */\r
3957         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3958         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3959   \r
3960         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3961 \r
3962         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3963 \r
3964         x = s_x;\r
3965         y = s_y;\r
3966 \r
3967         arrow[0].x = Round(x - j);\r
3968         arrow[0].y = Round(y + j*dx);\r
3969 \r
3970         arrow[1].x = Round(x + j);\r
3971         arrow[1].y = Round(y - j*dx);\r
3972 \r
3973         if( d_x > s_x ) {\r
3974             x = (double) d_x - k;\r
3975             y = (double) d_y - k*dy;\r
3976         }\r
3977         else {\r
3978             x = (double) d_x + k;\r
3979             y = (double) d_y + k*dy;\r
3980         }\r
3981 \r
3982         arrow[2].x = Round(x + j);\r
3983         arrow[2].y = Round(y - j*dx);\r
3984 \r
3985         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3986         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3987 \r
3988         arrow[4].x = d_x;\r
3989         arrow[4].y = d_y;\r
3990 \r
3991         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3992         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3993 \r
3994         arrow[6].x = Round(x - j);\r
3995         arrow[6].y = Round(y + j*dx);\r
3996     }\r
3997 \r
3998     Polygon( hdc, arrow, 7 );\r
3999 }\r
4000 \r
4001 /* [AS] Draw an arrow between two squares */\r
4002 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4003 {\r
4004     int s_x, s_y, d_x, d_y;\r
4005     HPEN hpen;\r
4006     HPEN holdpen;\r
4007     HBRUSH hbrush;\r
4008     HBRUSH holdbrush;\r
4009     LOGBRUSH stLB;\r
4010 \r
4011     if( s_col == d_col && s_row == d_row ) {\r
4012         return;\r
4013     }\r
4014 \r
4015     /* Get source and destination points */\r
4016     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4017     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4018 \r
4019     if( d_y > s_y ) {\r
4020         d_y += squareSize / 4;\r
4021     }\r
4022     else if( d_y < s_y ) {\r
4023         d_y += 3 * squareSize / 4;\r
4024     }\r
4025     else {\r
4026         d_y += squareSize / 2;\r
4027     }\r
4028 \r
4029     if( d_x > s_x ) {\r
4030         d_x += squareSize / 4;\r
4031     }\r
4032     else if( d_x < s_x ) {\r
4033         d_x += 3 * squareSize / 4;\r
4034     }\r
4035     else {\r
4036         d_x += squareSize / 2;\r
4037     }\r
4038 \r
4039     s_x += squareSize / 2;\r
4040     s_y += squareSize / 2;\r
4041 \r
4042     /* Adjust width */\r
4043     A_WIDTH = squareSize / 14;\r
4044 \r
4045     /* Draw */\r
4046     stLB.lbStyle = BS_SOLID;\r
4047     stLB.lbColor = appData.highlightArrowColor;\r
4048     stLB.lbHatch = 0;\r
4049 \r
4050     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4051     holdpen = SelectObject( hdc, hpen );\r
4052     hbrush = CreateBrushIndirect( &stLB );\r
4053     holdbrush = SelectObject( hdc, hbrush );\r
4054 \r
4055     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4056 \r
4057     SelectObject( hdc, holdpen );\r
4058     SelectObject( hdc, holdbrush );\r
4059     DeleteObject( hpen );\r
4060     DeleteObject( hbrush );\r
4061 }\r
4062 \r
4063 BOOL HasHighlightInfo()\r
4064 {\r
4065     BOOL result = FALSE;\r
4066 \r
4067     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4068         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4069     {\r
4070         result = TRUE;\r
4071     }\r
4072 \r
4073     return result;\r
4074 }\r
4075 \r
4076 BOOL IsDrawArrowEnabled()\r
4077 {\r
4078     BOOL result = FALSE;\r
4079 \r
4080     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4081         result = TRUE;\r
4082     }\r
4083 \r
4084     return result;\r
4085 }\r
4086 \r
4087 VOID DrawArrowHighlight( HDC hdc )\r
4088 {\r
4089     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4090         DrawArrowBetweenSquares( hdc,\r
4091             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4092             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4093     }\r
4094 }\r
4095 \r
4096 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4097 {\r
4098     HRGN result = NULL;\r
4099 \r
4100     if( HasHighlightInfo() ) {\r
4101         int x1, y1, x2, y2;\r
4102         int sx, sy, dx, dy;\r
4103 \r
4104         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4105         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4106 \r
4107         sx = MIN( x1, x2 );\r
4108         sy = MIN( y1, y2 );\r
4109         dx = MAX( x1, x2 ) + squareSize;\r
4110         dy = MAX( y1, y2 ) + squareSize;\r
4111 \r
4112         result = CreateRectRgn( sx, sy, dx, dy );\r
4113     }\r
4114 \r
4115     return result;\r
4116 }\r
4117 \r
4118 /*\r
4119     Warning: this function modifies the behavior of several other functions. \r
4120     \r
4121     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4122     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4123     repaint is scattered all over the place, which is not good for features such as\r
4124     "arrow highlighting" that require a full repaint of the board.\r
4125 \r
4126     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4127     user interaction, when speed is not so important) but especially to avoid errors\r
4128     in the displayed graphics.\r
4129 \r
4130     In such patched places, I always try refer to this function so there is a single\r
4131     place to maintain knowledge.\r
4132     \r
4133     To restore the original behavior, just return FALSE unconditionally.\r
4134 */\r
4135 BOOL IsFullRepaintPreferrable()\r
4136 {\r
4137     BOOL result = FALSE;\r
4138 \r
4139     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4140         /* Arrow may appear on the board */\r
4141         result = TRUE;\r
4142     }\r
4143 \r
4144     return result;\r
4145 }\r
4146 \r
4147 /* \r
4148     This function is called by DrawPosition to know whether a full repaint must\r
4149     be forced or not.\r
4150 \r
4151     Only DrawPosition may directly call this function, which makes use of \r
4152     some state information. Other function should call DrawPosition specifying \r
4153     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4154 */\r
4155 BOOL DrawPositionNeedsFullRepaint()\r
4156 {\r
4157     BOOL result = FALSE;\r
4158 \r
4159     /* \r
4160         Probably a slightly better policy would be to trigger a full repaint\r
4161         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4162         but animation is fast enough that it's difficult to notice.\r
4163     */\r
4164     if( animInfo.piece == EmptySquare ) {\r
4165         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4166             result = TRUE;\r
4167         }\r
4168     }\r
4169 \r
4170     return result;\r
4171 }\r
4172 \r
4173 VOID\r
4174 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4175 {\r
4176   int row, column, x, y, square_color, piece_color;\r
4177   ChessSquare piece;\r
4178   HBRUSH oldBrush;\r
4179   HDC texture_hdc = NULL;\r
4180 \r
4181   /* [AS] Initialize background textures if needed */\r
4182   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4183       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4184       if( backTextureSquareSize != squareSize \r
4185        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4186           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4187           backTextureSquareSize = squareSize;\r
4188           RebuildTextureSquareInfo();\r
4189       }\r
4190 \r
4191       texture_hdc = CreateCompatibleDC( hdc );\r
4192   }\r
4193 \r
4194   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4195     for (column = 0; column < BOARD_WIDTH; column++) {\r
4196   \r
4197       SquareToPos(row, column, &x, &y);\r
4198 \r
4199       piece = board[row][column];\r
4200 \r
4201       square_color = ((column + row) % 2) == 1;\r
4202       if( gameInfo.variant == VariantXiangqi ) {\r
4203           square_color = !InPalace(row, column);\r
4204           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4205           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4206       }\r
4207       piece_color = (int) piece < (int) BlackPawn;\r
4208 \r
4209 \r
4210       /* [HGM] holdings file: light square or black */\r
4211       if(column == BOARD_LEFT-2) {\r
4212             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4213                 square_color = 1;\r
4214             else {\r
4215                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4216                 continue;\r
4217             }\r
4218       } else\r
4219       if(column == BOARD_RGHT + 1 ) {\r
4220             if( row < gameInfo.holdingsSize )\r
4221                 square_color = 1;\r
4222             else {\r
4223                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4224                 continue;\r
4225             }\r
4226       }\r
4227       if(column == BOARD_LEFT-1 ) /* left align */\r
4228             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4229       else if( column == BOARD_RGHT) /* right align */\r
4230             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4231       else\r
4232       if (appData.monoMode) {\r
4233         if (piece == EmptySquare) {\r
4234           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4235                  square_color ? WHITENESS : BLACKNESS);\r
4236         } else {\r
4237           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4238         }\r
4239       } \r
4240       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4241           /* [AS] Draw the square using a texture bitmap */\r
4242           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4243           int r = row, c = column; // [HGM] do not flip board in flipView\r
4244           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4245 \r
4246           DrawTile( x, y, \r
4247               squareSize, squareSize, \r
4248               hdc, \r
4249               texture_hdc,\r
4250               backTextureSquareInfo[r][c].mode,\r
4251               backTextureSquareInfo[r][c].x,\r
4252               backTextureSquareInfo[r][c].y );\r
4253 \r
4254           SelectObject( texture_hdc, hbm );\r
4255 \r
4256           if (piece != EmptySquare) {\r
4257               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4258           }\r
4259       }\r
4260       else {\r
4261         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4262 \r
4263         oldBrush = SelectObject(hdc, brush );\r
4264         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4265         SelectObject(hdc, oldBrush);\r
4266         if (piece != EmptySquare)\r
4267           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4268       }\r
4269     }\r
4270   }\r
4271 \r
4272   if( texture_hdc != NULL ) {\r
4273     DeleteDC( texture_hdc );\r
4274   }\r
4275 }\r
4276 \r
4277 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4278 void fputDW(FILE *f, int x)\r
4279 {\r
4280         fputc(x     & 255, f);\r
4281         fputc(x>>8  & 255, f);\r
4282         fputc(x>>16 & 255, f);\r
4283         fputc(x>>24 & 255, f);\r
4284 }\r
4285 \r
4286 #define MAX_CLIPS 200   /* more than enough */\r
4287 \r
4288 VOID\r
4289 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4290 {\r
4291 //  HBITMAP bufferBitmap;\r
4292   BITMAP bi;\r
4293 //  RECT Rect;\r
4294   HDC tmphdc;\r
4295   HBITMAP hbm;\r
4296   int w = 100, h = 50;\r
4297 \r
4298   if(logo == NULL) return;\r
4299 //  GetClientRect(hwndMain, &Rect);\r
4300 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4301 //                                      Rect.bottom-Rect.top+1);\r
4302   tmphdc = CreateCompatibleDC(hdc);\r
4303   hbm = SelectObject(tmphdc, logo);\r
4304   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4305             w = bi.bmWidth;\r
4306             h = bi.bmHeight;\r
4307   }\r
4308   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4309                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4310   SelectObject(tmphdc, hbm);\r
4311   DeleteDC(tmphdc);\r
4312 }\r
4313 \r
4314 VOID\r
4315 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4316 {\r
4317   static Board lastReq, lastDrawn;\r
4318   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4319   static int lastDrawnFlipView = 0;\r
4320   static int lastReqValid = 0, lastDrawnValid = 0;\r
4321   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4322   HDC tmphdc;\r
4323   HDC hdcmem;\r
4324   HBITMAP bufferBitmap;\r
4325   HBITMAP oldBitmap;\r
4326   RECT Rect;\r
4327   HRGN clips[MAX_CLIPS];\r
4328   ChessSquare dragged_piece = EmptySquare;\r
4329 \r
4330   /* I'm undecided on this - this function figures out whether a full\r
4331    * repaint is necessary on its own, so there's no real reason to have the\r
4332    * caller tell it that.  I think this can safely be set to FALSE - but\r
4333    * if we trust the callers not to request full repaints unnessesarily, then\r
4334    * we could skip some clipping work.  In other words, only request a full\r
4335    * redraw when the majority of pieces have changed positions (ie. flip, \r
4336    * gamestart and similar)  --Hawk\r
4337    */\r
4338   Boolean fullrepaint = repaint;\r
4339 \r
4340   if( DrawPositionNeedsFullRepaint() ) {\r
4341       fullrepaint = TRUE;\r
4342   }\r
4343 \r
4344 #if 0\r
4345   if( fullrepaint ) {\r
4346       static int repaint_count = 0;\r
4347       char buf[128];\r
4348 \r
4349       repaint_count++;\r
4350       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4351       OutputDebugString( buf );\r
4352   }\r
4353 #endif\r
4354 \r
4355   if (board == NULL) {\r
4356     if (!lastReqValid) {\r
4357       return;\r
4358     }\r
4359     board = lastReq;\r
4360   } else {\r
4361     CopyBoard(lastReq, board);\r
4362     lastReqValid = 1;\r
4363   }\r
4364 \r
4365   if (doingSizing) {\r
4366     return;\r
4367   }\r
4368 \r
4369   if (IsIconic(hwndMain)) {\r
4370     return;\r
4371   }\r
4372 \r
4373   if (hdc == NULL) {\r
4374     hdc = GetDC(hwndMain);\r
4375     if (!appData.monoMode) {\r
4376       SelectPalette(hdc, hPal, FALSE);\r
4377       RealizePalette(hdc);\r
4378     }\r
4379     releaseDC = TRUE;\r
4380   } else {\r
4381     releaseDC = FALSE;\r
4382   }\r
4383 \r
4384 #if 0\r
4385   fprintf(debugFP, "*******************************\n"\r
4386                    "repaint = %s\n"\r
4387                    "dragInfo.from (%d,%d)\n"\r
4388                    "dragInfo.start (%d,%d)\n"\r
4389                    "dragInfo.pos (%d,%d)\n"\r
4390                    "dragInfo.lastpos (%d,%d)\n", \r
4391                     repaint ? "TRUE" : "FALSE",\r
4392                     dragInfo.from.x, dragInfo.from.y, \r
4393                     dragInfo.start.x, dragInfo.start.y,\r
4394                     dragInfo.pos.x, dragInfo.pos.y,\r
4395                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4396   fprintf(debugFP, "prev:  ");\r
4397   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4398     for (column = 0; column < BOARD_WIDTH; column++) {\r
4399       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4400     }\r
4401   }\r
4402   fprintf(debugFP, "\n");\r
4403   fprintf(debugFP, "board: ");\r
4404   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4405     for (column = 0; column < BOARD_WIDTH; column++) {\r
4406       fprintf(debugFP, "%d ", board[row][column]);\r
4407     }\r
4408   }\r
4409   fprintf(debugFP, "\n");\r
4410   fflush(debugFP);\r
4411 #endif\r
4412 \r
4413   /* Create some work-DCs */\r
4414   hdcmem = CreateCompatibleDC(hdc);\r
4415   tmphdc = CreateCompatibleDC(hdc);\r
4416 \r
4417   /* If dragging is in progress, we temporarely remove the piece */\r
4418   /* [HGM] or temporarily decrease count if stacked              */\r
4419   /*       !! Moved to before board compare !!                   */\r
4420   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4421     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4422     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4423             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4424         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4425     } else \r
4426     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4427             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4428         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4429     } else \r
4430         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4431   }\r
4432 \r
4433   /* Figure out which squares need updating by comparing the \r
4434    * newest board with the last drawn board and checking if\r
4435    * flipping has changed.\r
4436    */\r
4437   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4438     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4439       for (column = 0; column < BOARD_WIDTH; column++) {\r
4440         if (lastDrawn[row][column] != board[row][column]) {\r
4441           SquareToPos(row, column, &x, &y);\r
4442           clips[num_clips++] =\r
4443             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4444         }\r
4445       }\r
4446     }\r
4447     for (i=0; i<2; i++) {\r
4448       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4449           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4450         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4451             lastDrawnHighlight.sq[i].y >= 0) {\r
4452           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4453                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4454           clips[num_clips++] =\r
4455             CreateRectRgn(x - lineGap, y - lineGap, \r
4456                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4457         }\r
4458         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4459           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4460           clips[num_clips++] =\r
4461             CreateRectRgn(x - lineGap, y - lineGap, \r
4462                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4463         }\r
4464       }\r
4465     }\r
4466     for (i=0; i<2; i++) {\r
4467       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4468           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4469         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4470             lastDrawnPremove.sq[i].y >= 0) {\r
4471           SquareToPos(lastDrawnPremove.sq[i].y,\r
4472                       lastDrawnPremove.sq[i].x, &x, &y);\r
4473           clips[num_clips++] =\r
4474             CreateRectRgn(x - lineGap, y - lineGap, \r
4475                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4476         }\r
4477         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4478             premoveHighlightInfo.sq[i].y >= 0) {\r
4479           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4480                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4481           clips[num_clips++] =\r
4482             CreateRectRgn(x - lineGap, y - lineGap, \r
4483                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4484         }\r
4485       }\r
4486     }\r
4487   } else {\r
4488     fullrepaint = TRUE;\r
4489   }\r
4490 \r
4491   /* Create a buffer bitmap - this is the actual bitmap\r
4492    * being written to.  When all the work is done, we can\r
4493    * copy it to the real DC (the screen).  This avoids\r
4494    * the problems with flickering.\r
4495    */\r
4496   GetClientRect(hwndMain, &Rect);\r
4497   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4498                                         Rect.bottom-Rect.top+1);\r
4499   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4500   if (!appData.monoMode) {\r
4501     SelectPalette(hdcmem, hPal, FALSE);\r
4502   }\r
4503 \r
4504   /* Create clips for dragging */\r
4505   if (!fullrepaint) {\r
4506     if (dragInfo.from.x >= 0) {\r
4507       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4508       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4509     }\r
4510     if (dragInfo.start.x >= 0) {\r
4511       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4512       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4513     }\r
4514     if (dragInfo.pos.x >= 0) {\r
4515       x = dragInfo.pos.x - squareSize / 2;\r
4516       y = dragInfo.pos.y - squareSize / 2;\r
4517       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4518     }\r
4519     if (dragInfo.lastpos.x >= 0) {\r
4520       x = dragInfo.lastpos.x - squareSize / 2;\r
4521       y = dragInfo.lastpos.y - squareSize / 2;\r
4522       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4523     }\r
4524   }\r
4525 \r
4526   /* Are we animating a move?  \r
4527    * If so, \r
4528    *   - remove the piece from the board (temporarely)\r
4529    *   - calculate the clipping region\r
4530    */\r
4531   if (!fullrepaint) {\r
4532     if (animInfo.piece != EmptySquare) {\r
4533       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4534       x = boardRect.left + animInfo.lastpos.x;\r
4535       y = boardRect.top + animInfo.lastpos.y;\r
4536       x2 = boardRect.left + animInfo.pos.x;\r
4537       y2 = boardRect.top + animInfo.pos.y;\r
4538       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4539       /* Slight kludge.  The real problem is that after AnimateMove is\r
4540          done, the position on the screen does not match lastDrawn.\r
4541          This currently causes trouble only on e.p. captures in\r
4542          atomic, where the piece moves to an empty square and then\r
4543          explodes.  The old and new positions both had an empty square\r
4544          at the destination, but animation has drawn a piece there and\r
4545          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4546       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4547     }\r
4548   }\r
4549 \r
4550   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4551   if (num_clips == 0)\r
4552     fullrepaint = TRUE;\r
4553 \r
4554   /* Set clipping on the memory DC */\r
4555   if (!fullrepaint) {\r
4556     SelectClipRgn(hdcmem, clips[0]);\r
4557     for (x = 1; x < num_clips; x++) {\r
4558       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4559         abort();  // this should never ever happen!\r
4560     }\r
4561   }\r
4562 \r
4563   /* Do all the drawing to the memory DC */\r
4564   if(explodeInfo.radius) { // [HGM] atomic\r
4565         HBRUSH oldBrush;\r
4566         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4567         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4568         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4569         x += squareSize/2;\r
4570         y += squareSize/2;\r
4571         if(!fullrepaint) {\r
4572           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4573           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4574         }\r
4575         DrawGridOnDC(hdcmem);\r
4576         DrawHighlightsOnDC(hdcmem);\r
4577         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4578         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4579         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4580         SelectObject(hdcmem, oldBrush);\r
4581   } else {\r
4582     DrawGridOnDC(hdcmem);\r
4583     DrawHighlightsOnDC(hdcmem);\r
4584     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4585   }\r
4586   if(logoHeight) {\r
4587         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4588         if(appData.autoLogo) {\r
4589           \r
4590           switch(gameMode) { // pick logos based on game mode\r
4591             case IcsObserving:\r
4592                 whiteLogo = second.programLogo; // ICS logo\r
4593                 blackLogo = second.programLogo;\r
4594             default:\r
4595                 break;\r
4596             case IcsPlayingWhite:\r
4597                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4598                 blackLogo = second.programLogo; // ICS logo\r
4599                 break;\r
4600             case IcsPlayingBlack:\r
4601                 whiteLogo = second.programLogo; // ICS logo\r
4602                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4603                 break;\r
4604             case TwoMachinesPlay:\r
4605                 if(first.twoMachinesColor[0] == 'b') {\r
4606                     whiteLogo = second.programLogo;\r
4607                     blackLogo = first.programLogo;\r
4608                 }\r
4609                 break;\r
4610             case MachinePlaysWhite:\r
4611                 blackLogo = userLogo;\r
4612                 break;\r
4613             case MachinePlaysBlack:\r
4614                 whiteLogo = userLogo;\r
4615                 blackLogo = first.programLogo;\r
4616           }\r
4617         }\r
4618         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4619         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4620   }\r
4621 \r
4622   if( appData.highlightMoveWithArrow ) {\r
4623     DrawArrowHighlight(hdcmem);\r
4624   }\r
4625 \r
4626   DrawCoordsOnDC(hdcmem);\r
4627 \r
4628   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4629                  /* to make sure lastDrawn contains what is actually drawn */\r
4630 \r
4631   /* Put the dragged piece back into place and draw it (out of place!) */\r
4632     if (dragged_piece != EmptySquare) {\r
4633     /* [HGM] or restack */\r
4634     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4635                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4636     else\r
4637     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4638                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4639     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4640     x = dragInfo.pos.x - squareSize / 2;\r
4641     y = dragInfo.pos.y - squareSize / 2;\r
4642     DrawPieceOnDC(hdcmem, dragged_piece,\r
4643                   ((int) dragged_piece < (int) BlackPawn), \r
4644                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4645   }   \r
4646   \r
4647   /* Put the animated piece back into place and draw it */\r
4648   if (animInfo.piece != EmptySquare) {\r
4649     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4650     x = boardRect.left + animInfo.pos.x;\r
4651     y = boardRect.top + animInfo.pos.y;\r
4652     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4653                   ((int) animInfo.piece < (int) BlackPawn),\r
4654                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4655   }\r
4656 \r
4657   /* Release the bufferBitmap by selecting in the old bitmap \r
4658    * and delete the memory DC\r
4659    */\r
4660   SelectObject(hdcmem, oldBitmap);\r
4661   DeleteDC(hdcmem);\r
4662 \r
4663   /* Set clipping on the target DC */\r
4664   if (!fullrepaint) {\r
4665     SelectClipRgn(hdc, clips[0]);\r
4666     for (x = 1; x < num_clips; x++) {\r
4667       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4668         abort();   // this should never ever happen!\r
4669     } \r
4670   }\r
4671 \r
4672   /* Copy the new bitmap onto the screen in one go.\r
4673    * This way we avoid any flickering\r
4674    */\r
4675   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4676   BitBlt(hdc, boardRect.left, boardRect.top,\r
4677          boardRect.right - boardRect.left,\r
4678          boardRect.bottom - boardRect.top,\r
4679          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4680   if(saveDiagFlag) { \r
4681     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4682     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4683 \r
4684     GetObject(bufferBitmap, sizeof(b), &b);\r
4685     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4686         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4687         bih.biWidth = b.bmWidth;\r
4688         bih.biHeight = b.bmHeight;\r
4689         bih.biPlanes = 1;\r
4690         bih.biBitCount = b.bmBitsPixel;\r
4691         bih.biCompression = 0;\r
4692         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4693         bih.biXPelsPerMeter = 0;\r
4694         bih.biYPelsPerMeter = 0;\r
4695         bih.biClrUsed = 0;\r
4696         bih.biClrImportant = 0;\r
4697 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4698 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4699         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4700 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4701 \r
4702 #if 1\r
4703         wb = b.bmWidthBytes;\r
4704         // count colors\r
4705         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4706                 int k = ((int*) pData)[i];\r
4707                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4708                 if(j >= 16) break;\r
4709                 color[j] = k;\r
4710                 if(j >= nrColors) nrColors = j+1;\r
4711         }\r
4712         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4713                 INT p = 0;\r
4714                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4715                     for(w=0; w<(wb>>2); w+=2) {\r
4716                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4717                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4718                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4719                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4720                         pData[p++] = m | j<<4;\r
4721                     }\r
4722                     while(p&3) pData[p++] = 0;\r
4723                 }\r
4724                 fac = 3;\r
4725                 wb = ((wb+31)>>5)<<2;\r
4726         }\r
4727         // write BITMAPFILEHEADER\r
4728         fprintf(diagFile, "BM");\r
4729         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4730         fputDW(diagFile, 0);\r
4731         fputDW(diagFile, 0x36 + (fac?64:0));\r
4732         // write BITMAPINFOHEADER\r
4733         fputDW(diagFile, 40);\r
4734         fputDW(diagFile, b.bmWidth);\r
4735         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4736         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4737         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4738         fputDW(diagFile, 0);\r
4739         fputDW(diagFile, 0);\r
4740         fputDW(diagFile, 0);\r
4741         fputDW(diagFile, 0);\r
4742         fputDW(diagFile, 0);\r
4743         fputDW(diagFile, 0);\r
4744         // write color table\r
4745         if(fac)\r
4746         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4747         // write bitmap data\r
4748         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4749                 fputc(pData[i], diagFile);\r
4750 #endif\r
4751      }\r
4752   }\r
4753 \r
4754   SelectObject(tmphdc, oldBitmap);\r
4755 \r
4756   /* Massive cleanup */\r
4757   for (x = 0; x < num_clips; x++)\r
4758     DeleteObject(clips[x]);\r
4759 \r
4760   DeleteDC(tmphdc);\r
4761   DeleteObject(bufferBitmap);\r
4762 \r
4763   if (releaseDC) \r
4764     ReleaseDC(hwndMain, hdc);\r
4765   \r
4766   if (lastDrawnFlipView != flipView) {\r
4767     if (flipView)\r
4768       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4769     else\r
4770       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4771   }\r
4772 \r
4773 /*  CopyBoard(lastDrawn, board);*/\r
4774   lastDrawnHighlight = highlightInfo;\r
4775   lastDrawnPremove   = premoveHighlightInfo;\r
4776   lastDrawnFlipView = flipView;\r
4777   lastDrawnValid = 1;\r
4778 }\r
4779 \r
4780 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4781 int\r
4782 SaveDiagram(f)\r
4783      FILE *f;\r
4784 {\r
4785     saveDiagFlag = 1; diagFile = f;\r
4786     HDCDrawPosition(NULL, TRUE, NULL);\r
4787 \r
4788     saveDiagFlag = 0;\r
4789 \r
4790 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4791     \r
4792     fclose(f);\r
4793     return TRUE;\r
4794 }\r
4795 \r
4796 \r
4797 /*---------------------------------------------------------------------------*\\r
4798 | CLIENT PAINT PROCEDURE\r
4799 |   This is the main event-handler for the WM_PAINT message.\r
4800 |\r
4801 \*---------------------------------------------------------------------------*/\r
4802 VOID\r
4803 PaintProc(HWND hwnd)\r
4804 {\r
4805   HDC         hdc;\r
4806   PAINTSTRUCT ps;\r
4807   HFONT       oldFont;\r
4808 \r
4809   if((hdc = BeginPaint(hwnd, &ps))) {\r
4810     if (IsIconic(hwnd)) {\r
4811       DrawIcon(hdc, 2, 2, iconCurrent);\r
4812     } else {\r
4813       if (!appData.monoMode) {\r
4814         SelectPalette(hdc, hPal, FALSE);\r
4815         RealizePalette(hdc);\r
4816       }\r
4817       HDCDrawPosition(hdc, 1, NULL);\r
4818       oldFont =\r
4819         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4820       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4821                  ETO_CLIPPED|ETO_OPAQUE,\r
4822                  &messageRect, messageText, strlen(messageText), NULL);\r
4823       SelectObject(hdc, oldFont);\r
4824       DisplayBothClocks();\r
4825     }\r
4826     EndPaint(hwnd,&ps);\r
4827   }\r
4828 \r
4829   return;\r
4830 }\r
4831 \r
4832 \r
4833 /*\r
4834  * If the user selects on a border boundary, return -1; if off the board,\r
4835  *   return -2.  Otherwise map the event coordinate to the square.\r
4836  * The offset boardRect.left or boardRect.top must already have been\r
4837  *   subtracted from x.\r
4838  */\r
4839 int\r
4840 EventToSquare(int x)\r
4841 {\r
4842   if (x <= 0)\r
4843     return -2;\r
4844   if (x < lineGap)\r
4845     return -1;\r
4846   x -= lineGap;\r
4847   if ((x % (squareSize + lineGap)) >= squareSize)\r
4848     return -1;\r
4849   x /= (squareSize + lineGap);\r
4850   if (x >= BOARD_SIZE)\r
4851     return -2;\r
4852   return x;\r
4853 }\r
4854 \r
4855 typedef struct {\r
4856   char piece;\r
4857   int command;\r
4858   char* name;\r
4859 } DropEnable;\r
4860 \r
4861 DropEnable dropEnables[] = {\r
4862   { 'P', DP_Pawn, "Pawn" },\r
4863   { 'N', DP_Knight, "Knight" },\r
4864   { 'B', DP_Bishop, "Bishop" },\r
4865   { 'R', DP_Rook, "Rook" },\r
4866   { 'Q', DP_Queen, "Queen" },\r
4867 };\r
4868 \r
4869 VOID\r
4870 SetupDropMenu(HMENU hmenu)\r
4871 {\r
4872   int i, count, enable;\r
4873   char *p;\r
4874   extern char white_holding[], black_holding[];\r
4875   char item[MSG_SIZ];\r
4876 \r
4877   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4878     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4879                dropEnables[i].piece);\r
4880     count = 0;\r
4881     while (p && *p++ == dropEnables[i].piece) count++;\r
4882     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4883     enable = count > 0 || !appData.testLegality\r
4884       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4885                       && !appData.icsActive);\r
4886     ModifyMenu(hmenu, dropEnables[i].command,\r
4887                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4888                dropEnables[i].command, item);\r
4889   }\r
4890 }\r
4891 \r
4892 static int fromX = -1, fromY = -1, toX, toY;\r
4893 \r
4894 /* Event handler for mouse messages */\r
4895 VOID\r
4896 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4897 {\r
4898   int x, y;\r
4899   POINT pt;\r
4900   static int recursive = 0;\r
4901   HMENU hmenu;\r
4902 //  BOOLEAN needsRedraw = FALSE;\r
4903   BOOLEAN saveAnimate;\r
4904   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4905   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4906   ChessMove moveType;\r
4907 \r
4908   if (recursive) {\r
4909     if (message == WM_MBUTTONUP) {\r
4910       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4911          to the middle button: we simulate pressing the left button too!\r
4912          */\r
4913       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4914       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4915     }\r
4916     return;\r
4917   }\r
4918   recursive++;\r
4919   \r
4920   pt.x = LOWORD(lParam);\r
4921   pt.y = HIWORD(lParam);\r
4922   x = EventToSquare(pt.x - boardRect.left);\r
4923   y = EventToSquare(pt.y - boardRect.top);\r
4924   if (!flipView && y >= 0) {\r
4925     y = BOARD_HEIGHT - 1 - y;\r
4926   }\r
4927   if (flipView && x >= 0) {\r
4928     x = BOARD_WIDTH - 1 - x;\r
4929   }\r
4930 \r
4931   switch (message) {\r
4932   case WM_LBUTTONDOWN:\r
4933     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4934         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4935         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4936         if(gameInfo.holdingsWidth && \r
4937                 (WhiteOnMove(currentMove) \r
4938                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4939                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4940             // click in right holdings, for determining promotion piece\r
4941             ChessSquare p = boards[currentMove][y][x];\r
4942             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4943             if(p != EmptySquare) {\r
4944                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4945                 fromX = fromY = -1;\r
4946                 break;\r
4947             }\r
4948         }\r
4949         DrawPosition(FALSE, boards[currentMove]);\r
4950         break;\r
4951     }\r
4952     ErrorPopDown();\r
4953     sameAgain = FALSE;\r
4954     if (y == -2) {\r
4955       /* Downclick vertically off board; check if on clock */\r
4956       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4957         if (gameMode == EditPosition) {\r
4958           SetWhiteToPlayEvent();\r
4959         } else if (gameMode == IcsPlayingBlack ||\r
4960                    gameMode == MachinePlaysWhite) {\r
4961           CallFlagEvent();\r
4962         } else if (gameMode == EditGame) {\r
4963           AdjustClock(flipClock, -1);\r
4964         }\r
4965       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4966         if (gameMode == EditPosition) {\r
4967           SetBlackToPlayEvent();\r
4968         } else if (gameMode == IcsPlayingWhite ||\r
4969                    gameMode == MachinePlaysBlack) {\r
4970           CallFlagEvent();\r
4971         } else if (gameMode == EditGame) {\r
4972           AdjustClock(!flipClock, -1);\r
4973         }\r
4974       }\r
4975       if (!appData.highlightLastMove) {\r
4976         ClearHighlights();\r
4977         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4978       }\r
4979       fromX = fromY = -1;\r
4980       dragInfo.start.x = dragInfo.start.y = -1;\r
4981       dragInfo.from = dragInfo.start;\r
4982       break;\r
4983     } else if (x < 0 || y < 0\r
4984       /* [HGM] block clicks between board and holdings */\r
4985               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4986               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4987               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4988         /* EditPosition, empty square, or different color piece;\r
4989            click-click move is possible */\r
4990                                ) {\r
4991       break;\r
4992     } else if (fromX == x && fromY == y) {\r
4993       /* Downclick on same square again */\r
4994       ClearHighlights();\r
4995       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4996       sameAgain = TRUE;  \r
4997     } else if (fromX != -1 &&\r
4998                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4999                                                                         ) {\r
5000       /* Downclick on different square. */\r
5001       /* [HGM] if on holdings file, should count as new first click ! */\r
5002       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5003         toX = x;\r
5004         toY = y;\r
5005         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5006            to make sure move is legal before showing promotion popup */\r
5007         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5008         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5009                 fromX = fromY = -1; \r
5010                 ClearHighlights();\r
5011                 DrawPosition(FALSE, boards[currentMove]);\r
5012                 break; \r
5013         } else \r
5014         if(moveType != ImpossibleMove) {\r
5015           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5016           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5017             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5018               appData.alwaysPromoteToQueen)) {\r
5019                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5020                   if (!appData.highlightLastMove) {\r
5021                       ClearHighlights();\r
5022                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5023                   }\r
5024           } else\r
5025           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5026                   SetHighlights(fromX, fromY, toX, toY);\r
5027                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5028                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5029                      If promotion to Q is legal, all are legal! */\r
5030                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5031                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5032                     // kludge to temporarily execute move on display, wthout promotng yet\r
5033                     promotionChoice = TRUE;\r
5034                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5035                     boards[currentMove][toY][toX] = p;\r
5036                     DrawPosition(FALSE, boards[currentMove]);\r
5037                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5038                     boards[currentMove][toY][toX] = q;\r
5039                   } else\r
5040                   PromotionPopup(hwnd);\r
5041           } else {       /* not a promotion */\r
5042              if (appData.animate || appData.highlightLastMove) {\r
5043                  SetHighlights(fromX, fromY, toX, toY);\r
5044              } else {\r
5045                  ClearHighlights();\r
5046              }\r
5047              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5048              fromX = fromY = -1;\r
5049              if (appData.animate && !appData.highlightLastMove) {\r
5050                   ClearHighlights();\r
5051                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5052              }\r
5053           }\r
5054           break;\r
5055         }\r
5056         if (gotPremove) {\r
5057             /* [HGM] it seemed that braces were missing here */\r
5058             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5059             fromX = fromY = -1;\r
5060             break;\r
5061         }\r
5062       }\r
5063       ClearHighlights();\r
5064       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5065     }\r
5066     /* First downclick, or restart on a square with same color piece */\r
5067     if (!frozen && OKToStartUserMove(x, y)) {\r
5068       fromX = x;\r
5069       fromY = y;\r
5070       dragInfo.lastpos = pt;\r
5071       dragInfo.from.x = fromX;\r
5072       dragInfo.from.y = fromY;\r
5073       dragInfo.start = dragInfo.from;\r
5074       SetCapture(hwndMain);\r
5075     } else {\r
5076       fromX = fromY = -1;\r
5077       dragInfo.start.x = dragInfo.start.y = -1;\r
5078       dragInfo.from = dragInfo.start;\r
5079       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5080     }\r
5081     break;\r
5082 \r
5083   case WM_LBUTTONUP:\r
5084     ReleaseCapture();\r
5085     if (fromX == -1) break;\r
5086     if (x == fromX && y == fromY) {\r
5087       dragInfo.from.x = dragInfo.from.y = -1;\r
5088       /* Upclick on same square */\r
5089       if (sameAgain) {\r
5090         /* Clicked same square twice: abort click-click move */\r
5091         fromX = fromY = -1;\r
5092         gotPremove = 0;\r
5093         ClearPremoveHighlights();\r
5094       } else {\r
5095         /* First square clicked: start click-click move */\r
5096         SetHighlights(fromX, fromY, -1, -1);\r
5097       }\r
5098       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5099     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5100       /* Errant click; ignore */\r
5101       break;\r
5102     } else {\r
5103       /* Finish drag move. */\r
5104     if (appData.debugMode) {\r
5105         fprintf(debugFP, "release\n");\r
5106     }\r
5107       dragInfo.from.x = dragInfo.from.y = -1;\r
5108       toX = x;\r
5109       toY = y;\r
5110       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5111       appData.animate = appData.animate && !appData.animateDragging;\r
5112       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5113       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5114                 fromX = fromY = -1; \r
5115                 ClearHighlights();\r
5116                 DrawPosition(FALSE, boards[currentMove]);\r
5117                 break; \r
5118       } else \r
5119       if(moveType != ImpossibleMove) {\r
5120           /* [HGM] use move type to determine if move is promotion.\r
5121              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5122           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5123             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5124               appData.alwaysPromoteToQueen)) \r
5125                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5126           else \r
5127           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5128                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5129                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5130                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5131                     // kludge to temporarily execute move on display, wthout promotng yet\r
5132                     promotionChoice = TRUE;\r
5133                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5134                     boards[currentMove][toY][toX] = p;\r
5135                     DrawPosition(FALSE, boards[currentMove]);\r
5136                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5137                     boards[currentMove][toY][toX] = q;\r
5138                     break;\r
5139                   } else\r
5140                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5141           } else {\r
5142             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5143                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5144                                         moveType == WhiteCapturesEnPassant || \r
5145                                         moveType == BlackCapturesEnPassant   ) )\r
5146                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5147             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5148           }\r
5149       }\r
5150       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5151       appData.animate = saveAnimate;\r
5152       fromX = fromY = -1;\r
5153       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5154         ClearHighlights();\r
5155       }\r
5156       if (appData.animate || appData.animateDragging ||\r
5157           appData.highlightDragging || gotPremove) {\r
5158         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5159       }\r
5160     }\r
5161     dragInfo.start.x = dragInfo.start.y = -1; \r
5162     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5163     break;\r
5164 \r
5165   case WM_MOUSEMOVE:\r
5166     if ((appData.animateDragging || appData.highlightDragging)\r
5167         && (wParam & MK_LBUTTON)\r
5168         && dragInfo.from.x >= 0) \r
5169     {\r
5170       BOOL full_repaint = FALSE;\r
5171 \r
5172       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5173       if (appData.animateDragging) {\r
5174         dragInfo.pos = pt;\r
5175       }\r
5176       if (appData.highlightDragging) {\r
5177         SetHighlights(fromX, fromY, x, y);\r
5178         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5179             full_repaint = TRUE;\r
5180         }\r
5181       }\r
5182       \r
5183       DrawPosition( full_repaint, NULL);\r
5184       \r
5185       dragInfo.lastpos = dragInfo.pos;\r
5186     }\r
5187     break;\r
5188 \r
5189   case WM_MOUSEWHEEL: // [DM]\r
5190     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5191        /* Mouse Wheel is being rolled forward\r
5192         * Play moves forward\r
5193         */\r
5194        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5195                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5196        /* Mouse Wheel is being rolled backward\r
5197         * Play moves backward\r
5198         */\r
5199        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5200                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5201     }\r
5202     break;\r
5203 \r
5204   case WM_MBUTTONDOWN:\r
5205   case WM_RBUTTONDOWN:\r
5206     ErrorPopDown();\r
5207     ReleaseCapture();\r
5208     fromX = fromY = -1;\r
5209     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5210     dragInfo.start.x = dragInfo.start.y = -1;\r
5211     dragInfo.from = dragInfo.start;\r
5212     dragInfo.lastpos = dragInfo.pos;\r
5213     if (appData.highlightDragging) {\r
5214       ClearHighlights();\r
5215     }\r
5216     if(y == -2) {\r
5217       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5218       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5219           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5220       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5221           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5222       }\r
5223     }\r
5224     DrawPosition(TRUE, NULL);\r
5225 \r
5226     switch (gameMode) {\r
5227     case EditPosition:\r
5228     case IcsExamining:\r
5229       if (x < 0 || y < 0) break;\r
5230       fromX = x;\r
5231       fromY = y;\r
5232       if (message == WM_MBUTTONDOWN) {\r
5233         buttonCount = 3;  /* even if system didn't think so */\r
5234         if (wParam & MK_SHIFT) \r
5235           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5236         else\r
5237           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5238       } else { /* message == WM_RBUTTONDOWN */\r
5239 #if 0\r
5240         if (buttonCount == 3) {\r
5241           if (wParam & MK_SHIFT) \r
5242             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5243           else\r
5244             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5245         } else {\r
5246           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5247         }\r
5248 #else\r
5249         /* Just have one menu, on the right button.  Windows users don't\r
5250            think to try the middle one, and sometimes other software steals\r
5251            it, or it doesn't really exist. */\r
5252         if(gameInfo.variant != VariantShogi)\r
5253             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5254         else\r
5255             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5256 #endif\r
5257       }\r
5258       break;\r
5259     case IcsPlayingWhite:\r
5260     case IcsPlayingBlack:\r
5261     case EditGame:\r
5262     case MachinePlaysWhite:\r
5263     case MachinePlaysBlack:\r
5264       if (appData.testLegality &&\r
5265           gameInfo.variant != VariantBughouse &&\r
5266           gameInfo.variant != VariantCrazyhouse) break;\r
5267       if (x < 0 || y < 0) break;\r
5268       fromX = x;\r
5269       fromY = y;\r
5270       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5271       SetupDropMenu(hmenu);\r
5272       MenuPopup(hwnd, pt, hmenu, -1);\r
5273       break;\r
5274     default:\r
5275       break;\r
5276     }\r
5277     break;\r
5278   }\r
5279 \r
5280   recursive--;\r
5281 }\r
5282 \r
5283 /* Preprocess messages for buttons in main window */\r
5284 LRESULT CALLBACK\r
5285 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5286 {\r
5287   int id = GetWindowLong(hwnd, GWL_ID);\r
5288   int i, dir;\r
5289 \r
5290   for (i=0; i<N_BUTTONS; i++) {\r
5291     if (buttonDesc[i].id == id) break;\r
5292   }\r
5293   if (i == N_BUTTONS) return 0;\r
5294   switch (message) {\r
5295   case WM_KEYDOWN:\r
5296     switch (wParam) {\r
5297     case VK_LEFT:\r
5298     case VK_RIGHT:\r
5299       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5300       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5301       return TRUE;\r
5302     }\r
5303     break;\r
5304   case WM_CHAR:\r
5305     switch (wParam) {\r
5306     case '\r':\r
5307       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5308       return TRUE;\r
5309     case '\t':\r
5310       if (appData.icsActive) {\r
5311         if (GetKeyState(VK_SHIFT) < 0) {\r
5312           /* shifted */\r
5313           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5314           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5315           SetFocus(h);\r
5316         } else {\r
5317           /* unshifted */\r
5318           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5319           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5320           SetFocus(h);\r
5321         }\r
5322         return TRUE;\r
5323       }\r
5324       break;\r
5325     default:\r
5326       if (appData.icsActive) {\r
5327         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5328         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5329         SetFocus(h);\r
5330         SendMessage(h, WM_CHAR, wParam, lParam);\r
5331         return TRUE;\r
5332       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5333         PopUpMoveDialog((char)wParam);\r
5334       }\r
5335       break;\r
5336     }\r
5337     break;\r
5338   }\r
5339   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5340 }\r
5341 \r
5342 /* Process messages for Promotion dialog box */\r
5343 LRESULT CALLBACK\r
5344 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5345 {\r
5346   char promoChar;\r
5347 \r
5348   switch (message) {\r
5349   case WM_INITDIALOG: /* message: initialize dialog box */\r
5350     /* Center the dialog over the application window */\r
5351     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5352     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5353       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5354        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5355                SW_SHOW : SW_HIDE);\r
5356     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5357     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5358        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5359          PieceToChar(WhiteAngel) != '~') ||\r
5360         (PieceToChar(BlackAngel) >= 'A' &&\r
5361          PieceToChar(BlackAngel) != '~')   ) ?\r
5362                SW_SHOW : SW_HIDE);\r
5363     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5364        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5365          PieceToChar(WhiteMarshall) != '~') ||\r
5366         (PieceToChar(BlackMarshall) >= 'A' &&\r
5367          PieceToChar(BlackMarshall) != '~')   ) ?\r
5368                SW_SHOW : SW_HIDE);\r
5369     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5370     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5371        gameInfo.variant != VariantShogi ?\r
5372                SW_SHOW : SW_HIDE);\r
5373     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5374        gameInfo.variant != VariantShogi ?\r
5375                SW_SHOW : SW_HIDE);\r
5376     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5377        gameInfo.variant == VariantShogi ?\r
5378                SW_SHOW : SW_HIDE);\r
5379     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5380        gameInfo.variant == VariantShogi ?\r
5381                SW_SHOW : SW_HIDE);\r
5382     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5383        gameInfo.variant == VariantSuper ?\r
5384                SW_SHOW : SW_HIDE);\r
5385     return TRUE;\r
5386 \r
5387   case WM_COMMAND: /* message: received a command */\r
5388     switch (LOWORD(wParam)) {\r
5389     case IDCANCEL:\r
5390       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5391       ClearHighlights();\r
5392       DrawPosition(FALSE, NULL);\r
5393       return TRUE;\r
5394     case PB_King:\r
5395       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5396       break;\r
5397     case PB_Queen:\r
5398       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5399       break;\r
5400     case PB_Rook:\r
5401       promoChar = PieceToChar(BlackRook);\r
5402       break;\r
5403     case PB_Bishop:\r
5404       promoChar = PieceToChar(BlackBishop);\r
5405       break;\r
5406     case PB_Chancellor:\r
5407       promoChar = PieceToChar(BlackMarshall);\r
5408       break;\r
5409     case PB_Archbishop:\r
5410       promoChar = PieceToChar(BlackAngel);\r
5411       break;\r
5412     case PB_Knight:\r
5413       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5414       break;\r
5415     default:\r
5416       return FALSE;\r
5417     }\r
5418     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5419     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5420        only show the popup when we are already sure the move is valid or\r
5421        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5422        will figure out it is a promotion from the promoChar. */\r
5423     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5424     if (!appData.highlightLastMove) {\r
5425       ClearHighlights();\r
5426       DrawPosition(FALSE, NULL);\r
5427     }\r
5428     return TRUE;\r
5429   }\r
5430   return FALSE;\r
5431 }\r
5432 \r
5433 /* Pop up promotion dialog */\r
5434 VOID\r
5435 PromotionPopup(HWND hwnd)\r
5436 {\r
5437   FARPROC lpProc;\r
5438 \r
5439   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5440   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5441     hwnd, (DLGPROC)lpProc);\r
5442   FreeProcInstance(lpProc);\r
5443 }\r
5444 \r
5445 /* Toggle ShowThinking */\r
5446 VOID\r
5447 ToggleShowThinking()\r
5448 {\r
5449   appData.showThinking = !appData.showThinking;\r
5450   ShowThinkingEvent();\r
5451 }\r
5452 \r
5453 VOID\r
5454 LoadGameDialog(HWND hwnd, char* title)\r
5455 {\r
5456   UINT number = 0;\r
5457   FILE *f;\r
5458   char fileTitle[MSG_SIZ];\r
5459   f = OpenFileDialog(hwnd, "rb", "",\r
5460                      appData.oldSaveStyle ? "gam" : "pgn",\r
5461                      GAME_FILT,\r
5462                      title, &number, fileTitle, NULL);\r
5463   if (f != NULL) {\r
5464     cmailMsgLoaded = FALSE;\r
5465     if (number == 0) {\r
5466       int error = GameListBuild(f);\r
5467       if (error) {\r
5468         DisplayError("Cannot build game list", error);\r
5469       } else if (!ListEmpty(&gameList) &&\r
5470                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5471         GameListPopUp(f, fileTitle);\r
5472         return;\r
5473       }\r
5474       GameListDestroy();\r
5475       number = 1;\r
5476     }\r
5477     LoadGame(f, number, fileTitle, FALSE);\r
5478   }\r
5479 }\r
5480 \r
5481 VOID\r
5482 ChangedConsoleFont()\r
5483 {\r
5484   CHARFORMAT cfmt;\r
5485   CHARRANGE tmpsel, sel;\r
5486   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5487   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5488   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5489   PARAFORMAT paraf;\r
5490 \r
5491   cfmt.cbSize = sizeof(CHARFORMAT);\r
5492   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5493   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5494   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5495    * size.  This was undocumented in the version of MSVC++ that I had\r
5496    * when I wrote the code, but is apparently documented now.\r
5497    */\r
5498   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5499   cfmt.bCharSet = f->lf.lfCharSet;\r
5500   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5501   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5502   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5503   /* Why are the following seemingly needed too? */\r
5504   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5505   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5506   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5507   tmpsel.cpMin = 0;\r
5508   tmpsel.cpMax = -1; /*999999?*/\r
5509   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5510   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5511   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5512    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5513    */\r
5514   paraf.cbSize = sizeof(paraf);\r
5515   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5516   paraf.dxStartIndent = 0;\r
5517   paraf.dxOffset = WRAP_INDENT;\r
5518   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5519   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5520 }\r
5521 \r
5522 /*---------------------------------------------------------------------------*\\r
5523  *\r
5524  * Window Proc for main window\r
5525  *\r
5526 \*---------------------------------------------------------------------------*/\r
5527 \r
5528 /* Process messages for main window, etc. */\r
5529 LRESULT CALLBACK\r
5530 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5531 {\r
5532   FARPROC lpProc;\r
5533   int wmId, wmEvent;\r
5534   char *defName;\r
5535   FILE *f;\r
5536   UINT number;\r
5537   char fileTitle[MSG_SIZ];\r
5538   char buf[MSG_SIZ];\r
5539   static SnapData sd;\r
5540 \r
5541   switch (message) {\r
5542 \r
5543   case WM_PAINT: /* message: repaint portion of window */\r
5544     PaintProc(hwnd);\r
5545     break;\r
5546 \r
5547   case WM_ERASEBKGND:\r
5548     if (IsIconic(hwnd)) {\r
5549       /* Cheat; change the message */\r
5550       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5551     } else {\r
5552       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5553     }\r
5554     break;\r
5555 \r
5556   case WM_LBUTTONDOWN:\r
5557   case WM_MBUTTONDOWN:\r
5558   case WM_RBUTTONDOWN:\r
5559   case WM_LBUTTONUP:\r
5560   case WM_MBUTTONUP:\r
5561   case WM_RBUTTONUP:\r
5562   case WM_MOUSEMOVE:\r
5563   case WM_MOUSEWHEEL:\r
5564     MouseEvent(hwnd, message, wParam, lParam);\r
5565     break;\r
5566 \r
5567   case WM_CHAR:\r
5568     \r
5569     if (appData.icsActive) {\r
5570       if (wParam == '\t') {\r
5571         if (GetKeyState(VK_SHIFT) < 0) {\r
5572           /* shifted */\r
5573           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5574           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5575           SetFocus(h);\r
5576         } else {\r
5577           /* unshifted */\r
5578           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5579           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5580           SetFocus(h);\r
5581         }\r
5582       } else {\r
5583         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5584         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5585         SetFocus(h);\r
5586         SendMessage(h, message, wParam, lParam);\r
5587       }\r
5588     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5589       PopUpMoveDialog((char)wParam);\r
5590     }\r
5591     break;\r
5592 \r
5593   case WM_PALETTECHANGED:\r
5594     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5595       int nnew;\r
5596       HDC hdc = GetDC(hwndMain);\r
5597       SelectPalette(hdc, hPal, TRUE);\r
5598       nnew = RealizePalette(hdc);\r
5599       if (nnew > 0) {\r
5600         paletteChanged = TRUE;\r
5601 #if 0\r
5602         UpdateColors(hdc);\r
5603 #else\r
5604         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5605 #endif\r
5606       }\r
5607       ReleaseDC(hwnd, hdc);\r
5608     }\r
5609     break;\r
5610 \r
5611   case WM_QUERYNEWPALETTE:\r
5612     if (!appData.monoMode /*&& paletteChanged*/) {\r
5613       int nnew;\r
5614       HDC hdc = GetDC(hwndMain);\r
5615       paletteChanged = FALSE;\r
5616       SelectPalette(hdc, hPal, FALSE);\r
5617       nnew = RealizePalette(hdc);\r
5618       if (nnew > 0) {\r
5619         InvalidateRect(hwnd, &boardRect, FALSE);\r
5620       }\r
5621       ReleaseDC(hwnd, hdc);\r
5622       return TRUE;\r
5623     }\r
5624     return FALSE;\r
5625 \r
5626   case WM_COMMAND: /* message: command from application menu */\r
5627     wmId    = LOWORD(wParam);\r
5628     wmEvent = HIWORD(wParam);\r
5629 \r
5630     switch (wmId) {\r
5631     case IDM_NewGame:\r
5632       ResetGameEvent();\r
5633       AnalysisPopDown();\r
5634       break;\r
5635 \r
5636     case IDM_NewGameFRC:\r
5637       if( NewGameFRC() == 0 ) {\r
5638         ResetGameEvent();\r
5639         AnalysisPopDown();\r
5640       }\r
5641       break;\r
5642 \r
5643     case IDM_NewVariant:\r
5644       NewVariantPopup(hwnd);\r
5645       break;\r
5646 \r
5647     case IDM_LoadGame:\r
5648       LoadGameDialog(hwnd, "Load Game from File");\r
5649       break;\r
5650 \r
5651     case IDM_LoadNextGame:\r
5652       ReloadGame(1);\r
5653       break;\r
5654 \r
5655     case IDM_LoadPrevGame:\r
5656       ReloadGame(-1);\r
5657       break;\r
5658 \r
5659     case IDM_ReloadGame:\r
5660       ReloadGame(0);\r
5661       break;\r
5662 \r
5663     case IDM_LoadPosition:\r
5664       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5665         Reset(FALSE, TRUE);\r
5666       }\r
5667       number = 1;\r
5668       f = OpenFileDialog(hwnd, "rb", "",\r
5669                          appData.oldSaveStyle ? "pos" : "fen",\r
5670                          POSITION_FILT,\r
5671                          "Load Position from File", &number, fileTitle, NULL);\r
5672       if (f != NULL) {\r
5673         LoadPosition(f, number, fileTitle);\r
5674       }\r
5675       break;\r
5676 \r
5677     case IDM_LoadNextPosition:\r
5678       ReloadPosition(1);\r
5679       break;\r
5680 \r
5681     case IDM_LoadPrevPosition:\r
5682       ReloadPosition(-1);\r
5683       break;\r
5684 \r
5685     case IDM_ReloadPosition:\r
5686       ReloadPosition(0);\r
5687       break;\r
5688 \r
5689     case IDM_SaveGame:\r
5690       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5691       f = OpenFileDialog(hwnd, "a", defName,\r
5692                          appData.oldSaveStyle ? "gam" : "pgn",\r
5693                          GAME_FILT,\r
5694                          "Save Game to File", NULL, fileTitle, NULL);\r
5695       if (f != NULL) {\r
5696         SaveGame(f, 0, "");\r
5697       }\r
5698       break;\r
5699 \r
5700     case IDM_SavePosition:\r
5701       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5702       f = OpenFileDialog(hwnd, "a", defName,\r
5703                          appData.oldSaveStyle ? "pos" : "fen",\r
5704                          POSITION_FILT,\r
5705                          "Save Position to File", NULL, fileTitle, NULL);\r
5706       if (f != NULL) {\r
5707         SavePosition(f, 0, "");\r
5708       }\r
5709       break;\r
5710 \r
5711     case IDM_SaveDiagram:\r
5712       defName = "diagram";\r
5713       f = OpenFileDialog(hwnd, "wb", defName,\r
5714                          "bmp",\r
5715                          DIAGRAM_FILT,\r
5716                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5717       if (f != NULL) {\r
5718         SaveDiagram(f);\r
5719       }\r
5720       break;\r
5721 \r
5722     case IDM_CopyGame:\r
5723       CopyGameToClipboard();\r
5724       break;\r
5725 \r
5726     case IDM_PasteGame:\r
5727       PasteGameFromClipboard();\r
5728       break;\r
5729 \r
5730     case IDM_CopyGameListToClipboard:\r
5731       CopyGameListToClipboard();\r
5732       break;\r
5733 \r
5734     /* [AS] Autodetect FEN or PGN data */\r
5735     case IDM_PasteAny:\r
5736       PasteGameOrFENFromClipboard();\r
5737       break;\r
5738 \r
5739     /* [AS] Move history */\r
5740     case IDM_ShowMoveHistory:\r
5741         if( MoveHistoryIsUp() ) {\r
5742             MoveHistoryPopDown();\r
5743         }\r
5744         else {\r
5745             MoveHistoryPopUp();\r
5746         }\r
5747         break;\r
5748 \r
5749     /* [AS] Eval graph */\r
5750     case IDM_ShowEvalGraph:\r
5751         if( EvalGraphIsUp() ) {\r
5752             EvalGraphPopDown();\r
5753         }\r
5754         else {\r
5755             EvalGraphPopUp();\r
5756         }\r
5757         break;\r
5758 \r
5759     /* [AS] Engine output */\r
5760     case IDM_ShowEngineOutput:\r
5761         if( EngineOutputIsUp() ) {\r
5762             EngineOutputPopDown();\r
5763         }\r
5764         else {\r
5765             EngineOutputPopUp();\r
5766         }\r
5767         break;\r
5768 \r
5769     /* [AS] User adjudication */\r
5770     case IDM_UserAdjudication_White:\r
5771         UserAdjudicationEvent( +1 );\r
5772         break;\r
5773 \r
5774     case IDM_UserAdjudication_Black:\r
5775         UserAdjudicationEvent( -1 );\r
5776         break;\r
5777 \r
5778     case IDM_UserAdjudication_Draw:\r
5779         UserAdjudicationEvent( 0 );\r
5780         break;\r
5781 \r
5782     /* [AS] Game list options dialog */\r
5783     case IDM_GameListOptions:\r
5784       GameListOptions();\r
5785       break;\r
5786 \r
5787     case IDM_CopyPosition:\r
5788       CopyFENToClipboard();\r
5789       break;\r
5790 \r
5791     case IDM_PastePosition:\r
5792       PasteFENFromClipboard();\r
5793       break;\r
5794 \r
5795     case IDM_MailMove:\r
5796       MailMoveEvent();\r
5797       break;\r
5798 \r
5799     case IDM_ReloadCMailMsg:\r
5800       Reset(TRUE, TRUE);\r
5801       ReloadCmailMsgEvent(FALSE);\r
5802       break;\r
5803 \r
5804     case IDM_Minimize:\r
5805       ShowWindow(hwnd, SW_MINIMIZE);\r
5806       break;\r
5807 \r
5808     case IDM_Exit:\r
5809       ExitEvent(0);\r
5810       break;\r
5811 \r
5812     case IDM_MachineWhite:\r
5813       MachineWhiteEvent();\r
5814       /*\r
5815        * refresh the tags dialog only if it's visible\r
5816        */\r
5817       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5818           char *tags;\r
5819           tags = PGNTags(&gameInfo);\r
5820           TagsPopUp(tags, CmailMsg());\r
5821           free(tags);\r
5822       }\r
5823       break;\r
5824 \r
5825     case IDM_MachineBlack:\r
5826       MachineBlackEvent();\r
5827       /*\r
5828        * refresh the tags dialog only if it's visible\r
5829        */\r
5830       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5831           char *tags;\r
5832           tags = PGNTags(&gameInfo);\r
5833           TagsPopUp(tags, CmailMsg());\r
5834           free(tags);\r
5835       }\r
5836       break;\r
5837 \r
5838     case IDM_TwoMachines:\r
5839       TwoMachinesEvent();\r
5840       /*\r
5841        * refresh the tags dialog only if it's visible\r
5842        */\r
5843       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5844           char *tags;\r
5845           tags = PGNTags(&gameInfo);\r
5846           TagsPopUp(tags, CmailMsg());\r
5847           free(tags);\r
5848       }\r
5849       break;\r
5850 \r
5851     case IDM_AnalysisMode:\r
5852       if (!first.analysisSupport) {\r
5853         sprintf(buf, "%s does not support analysis", first.tidy);\r
5854         DisplayError(buf, 0);\r
5855       } else {\r
5856         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5857         if (appData.icsActive) {\r
5858                if (gameMode != IcsObserving) {\r
5859                        sprintf(buf, "You are not observing a game");\r
5860                        DisplayError(buf, 0);\r
5861                        /* secure check */\r
5862                        if (appData.icsEngineAnalyze) {\r
5863                                if (appData.debugMode) \r
5864                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5865                                ExitAnalyzeMode();\r
5866                                ModeHighlight();\r
5867                                break;\r
5868                        }\r
5869                        break;\r
5870                } else {\r
5871                        /* if enable, user want disable icsEngineAnalyze */\r
5872                        if (appData.icsEngineAnalyze) {\r
5873                                ExitAnalyzeMode();\r
5874                                ModeHighlight();\r
5875                                break;\r
5876                        }\r
5877                        appData.icsEngineAnalyze = TRUE;\r
5878                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5879                }\r
5880         } \r
5881         if (!appData.showThinking) ToggleShowThinking();\r
5882         AnalyzeModeEvent();\r
5883       }\r
5884       break;\r
5885 \r
5886     case IDM_AnalyzeFile:\r
5887       if (!first.analysisSupport) {\r
5888         char buf[MSG_SIZ];\r
5889         sprintf(buf, "%s does not support analysis", first.tidy);\r
5890         DisplayError(buf, 0);\r
5891       } else {\r
5892         if (!appData.showThinking) ToggleShowThinking();\r
5893         AnalyzeFileEvent();\r
5894         LoadGameDialog(hwnd, "Analyze Game from File");\r
5895         AnalysisPeriodicEvent(1);\r
5896       }\r
5897       break;\r
5898 \r
5899     case IDM_IcsClient:\r
5900       IcsClientEvent();\r
5901       break;\r
5902 \r
5903     case IDM_EditGame:\r
5904       EditGameEvent();\r
5905       break;\r
5906 \r
5907     case IDM_EditPosition:\r
5908       EditPositionEvent();\r
5909       break;\r
5910 \r
5911     case IDM_Training:\r
5912       TrainingEvent();\r
5913       break;\r
5914 \r
5915     case IDM_ShowGameList:\r
5916       ShowGameListProc();\r
5917       break;\r
5918 \r
5919     case IDM_EditTags:\r
5920       EditTagsProc();\r
5921       break;\r
5922 \r
5923     case IDM_EditComment:\r
5924       if (commentDialogUp && editComment) {\r
5925         CommentPopDown();\r
5926       } else {\r
5927         EditCommentEvent();\r
5928       }\r
5929       break;\r
5930 \r
5931     case IDM_Pause:\r
5932       PauseEvent();\r
5933       break;\r
5934 \r
5935     case IDM_Accept:\r
5936       AcceptEvent();\r
5937       break;\r
5938 \r
5939     case IDM_Decline:\r
5940       DeclineEvent();\r
5941       break;\r
5942 \r
5943     case IDM_Rematch:\r
5944       RematchEvent();\r
5945       break;\r
5946 \r
5947     case IDM_CallFlag:\r
5948       CallFlagEvent();\r
5949       break;\r
5950 \r
5951     case IDM_Draw:\r
5952       DrawEvent();\r
5953       break;\r
5954 \r
5955     case IDM_Adjourn:\r
5956       AdjournEvent();\r
5957       break;\r
5958 \r
5959     case IDM_Abort:\r
5960       AbortEvent();\r
5961       break;\r
5962 \r
5963     case IDM_Resign:\r
5964       ResignEvent();\r
5965       break;\r
5966 \r
5967     case IDM_StopObserving:\r
5968       StopObservingEvent();\r
5969       break;\r
5970 \r
5971     case IDM_StopExamining:\r
5972       StopExaminingEvent();\r
5973       break;\r
5974 \r
5975     case IDM_TypeInMove:\r
5976       PopUpMoveDialog('\000');\r
5977       break;\r
5978 \r
5979     case IDM_TypeInName:\r
5980       PopUpNameDialog('\000');\r
5981       break;\r
5982 \r
5983     case IDM_Backward:\r
5984       BackwardEvent();\r
5985       SetFocus(hwndMain);\r
5986       break;\r
5987 \r
5988     case IDM_Forward:\r
5989       ForwardEvent();\r
5990       SetFocus(hwndMain);\r
5991       break;\r
5992 \r
5993     case IDM_ToStart:\r
5994       ToStartEvent();\r
5995       SetFocus(hwndMain);\r
5996       break;\r
5997 \r
5998     case IDM_ToEnd:\r
5999       ToEndEvent();\r
6000       SetFocus(hwndMain);\r
6001       break;\r
6002 \r
6003     case IDM_Revert:\r
6004       RevertEvent();\r
6005       break;\r
6006 \r
6007     case IDM_TruncateGame:\r
6008       TruncateGameEvent();\r
6009       break;\r
6010 \r
6011     case IDM_MoveNow:\r
6012       MoveNowEvent();\r
6013       break;\r
6014 \r
6015     case IDM_RetractMove:\r
6016       RetractMoveEvent();\r
6017       break;\r
6018 \r
6019     case IDM_FlipView:\r
6020       flipView = !flipView;\r
6021       DrawPosition(FALSE, NULL);\r
6022       break;\r
6023 \r
6024     case IDM_FlipClock:\r
6025       flipClock = !flipClock;\r
6026       DisplayBothClocks();\r
6027       DrawPosition(FALSE, NULL);\r
6028       break;\r
6029 \r
6030     case IDM_GeneralOptions:\r
6031       GeneralOptionsPopup(hwnd);\r
6032       DrawPosition(TRUE, NULL);\r
6033       break;\r
6034 \r
6035     case IDM_BoardOptions:\r
6036       BoardOptionsPopup(hwnd);\r
6037       break;\r
6038 \r
6039     case IDM_EnginePlayOptions:\r
6040       EnginePlayOptionsPopup(hwnd);\r
6041       break;\r
6042 \r
6043     case IDM_OptionsUCI:\r
6044       UciOptionsPopup(hwnd);\r
6045       break;\r
6046 \r
6047     case IDM_IcsOptions:\r
6048       IcsOptionsPopup(hwnd);\r
6049       break;\r
6050 \r
6051     case IDM_Fonts:\r
6052       FontsOptionsPopup(hwnd);\r
6053       break;\r
6054 \r
6055     case IDM_Sounds:\r
6056       SoundOptionsPopup(hwnd);\r
6057       break;\r
6058 \r
6059     case IDM_CommPort:\r
6060       CommPortOptionsPopup(hwnd);\r
6061       break;\r
6062 \r
6063     case IDM_LoadOptions:\r
6064       LoadOptionsPopup(hwnd);\r
6065       break;\r
6066 \r
6067     case IDM_SaveOptions:\r
6068       SaveOptionsPopup(hwnd);\r
6069       break;\r
6070 \r
6071     case IDM_TimeControl:\r
6072       TimeControlOptionsPopup(hwnd);\r
6073       break;\r
6074 \r
6075     case IDM_SaveSettings:\r
6076       SaveSettings(settingsFileName);\r
6077       break;\r
6078 \r
6079     case IDM_SaveSettingsOnExit:\r
6080       saveSettingsOnExit = !saveSettingsOnExit;\r
6081       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6082                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6083                                          MF_CHECKED : MF_UNCHECKED));\r
6084       break;\r
6085 \r
6086     case IDM_Hint:\r
6087       HintEvent();\r
6088       break;\r
6089 \r
6090     case IDM_Book:\r
6091       BookEvent();\r
6092       break;\r
6093 \r
6094     case IDM_AboutGame:\r
6095       AboutGameEvent();\r
6096       break;\r
6097 \r
6098     case IDM_Debug:\r
6099       appData.debugMode = !appData.debugMode;\r
6100       if (appData.debugMode) {\r
6101         char dir[MSG_SIZ];\r
6102         GetCurrentDirectory(MSG_SIZ, dir);\r
6103         SetCurrentDirectory(installDir);\r
6104         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6105         SetCurrentDirectory(dir);\r
6106         setbuf(debugFP, NULL);\r
6107       } else {\r
6108         fclose(debugFP);\r
6109         debugFP = NULL;\r
6110       }\r
6111       break;\r
6112 \r
6113     case IDM_HELPCONTENTS:\r
6114       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6115         MessageBox (GetFocus(),\r
6116                     "Unable to activate help",\r
6117                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6118       }\r
6119       break;\r
6120 \r
6121     case IDM_HELPSEARCH:\r
6122       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
6123         MessageBox (GetFocus(),\r
6124                     "Unable to activate help",\r
6125                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6126       }\r
6127       break;\r
6128 \r
6129     case IDM_HELPHELP:\r
6130       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6131         MessageBox (GetFocus(),\r
6132                     "Unable to activate help",\r
6133                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6134       }\r
6135       break;\r
6136 \r
6137     case IDM_ABOUT:\r
6138       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6139       DialogBox(hInst, \r
6140         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6141         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6142       FreeProcInstance(lpProc);\r
6143       break;\r
6144 \r
6145     case IDM_DirectCommand1:\r
6146       AskQuestionEvent("Direct Command",\r
6147                        "Send to chess program:", "", "1");\r
6148       break;\r
6149     case IDM_DirectCommand2:\r
6150       AskQuestionEvent("Direct Command",\r
6151                        "Send to second chess program:", "", "2");\r
6152       break;\r
6153 \r
6154     case EP_WhitePawn:\r
6155       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6156       fromX = fromY = -1;\r
6157       break;\r
6158 \r
6159     case EP_WhiteKnight:\r
6160       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6161       fromX = fromY = -1;\r
6162       break;\r
6163 \r
6164     case EP_WhiteBishop:\r
6165       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6166       fromX = fromY = -1;\r
6167       break;\r
6168 \r
6169     case EP_WhiteRook:\r
6170       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6171       fromX = fromY = -1;\r
6172       break;\r
6173 \r
6174     case EP_WhiteQueen:\r
6175       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6176       fromX = fromY = -1;\r
6177       break;\r
6178 \r
6179     case EP_WhiteFerz:\r
6180       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6181       fromX = fromY = -1;\r
6182       break;\r
6183 \r
6184     case EP_WhiteWazir:\r
6185       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6186       fromX = fromY = -1;\r
6187       break;\r
6188 \r
6189     case EP_WhiteAlfil:\r
6190       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6191       fromX = fromY = -1;\r
6192       break;\r
6193 \r
6194     case EP_WhiteCannon:\r
6195       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6196       fromX = fromY = -1;\r
6197       break;\r
6198 \r
6199     case EP_WhiteCardinal:\r
6200       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6201       fromX = fromY = -1;\r
6202       break;\r
6203 \r
6204     case EP_WhiteMarshall:\r
6205       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6206       fromX = fromY = -1;\r
6207       break;\r
6208 \r
6209     case EP_WhiteKing:\r
6210       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6211       fromX = fromY = -1;\r
6212       break;\r
6213 \r
6214     case EP_BlackPawn:\r
6215       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6216       fromX = fromY = -1;\r
6217       break;\r
6218 \r
6219     case EP_BlackKnight:\r
6220       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6221       fromX = fromY = -1;\r
6222       break;\r
6223 \r
6224     case EP_BlackBishop:\r
6225       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6226       fromX = fromY = -1;\r
6227       break;\r
6228 \r
6229     case EP_BlackRook:\r
6230       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6231       fromX = fromY = -1;\r
6232       break;\r
6233 \r
6234     case EP_BlackQueen:\r
6235       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6236       fromX = fromY = -1;\r
6237       break;\r
6238 \r
6239     case EP_BlackFerz:\r
6240       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6241       fromX = fromY = -1;\r
6242       break;\r
6243 \r
6244     case EP_BlackWazir:\r
6245       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6246       fromX = fromY = -1;\r
6247       break;\r
6248 \r
6249     case EP_BlackAlfil:\r
6250       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6251       fromX = fromY = -1;\r
6252       break;\r
6253 \r
6254     case EP_BlackCannon:\r
6255       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6256       fromX = fromY = -1;\r
6257       break;\r
6258 \r
6259     case EP_BlackCardinal:\r
6260       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6261       fromX = fromY = -1;\r
6262       break;\r
6263 \r
6264     case EP_BlackMarshall:\r
6265       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6266       fromX = fromY = -1;\r
6267       break;\r
6268 \r
6269     case EP_BlackKing:\r
6270       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6271       fromX = fromY = -1;\r
6272       break;\r
6273 \r
6274     case EP_EmptySquare:\r
6275       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6276       fromX = fromY = -1;\r
6277       break;\r
6278 \r
6279     case EP_ClearBoard:\r
6280       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6281       fromX = fromY = -1;\r
6282       break;\r
6283 \r
6284     case EP_White:\r
6285       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6286       fromX = fromY = -1;\r
6287       break;\r
6288 \r
6289     case EP_Black:\r
6290       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6291       fromX = fromY = -1;\r
6292       break;\r
6293 \r
6294     case EP_Promote:\r
6295       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6296       fromX = fromY = -1;\r
6297       break;\r
6298 \r
6299     case EP_Demote:\r
6300       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6301       fromX = fromY = -1;\r
6302       break;\r
6303 \r
6304     case DP_Pawn:\r
6305       DropMenuEvent(WhitePawn, fromX, fromY);\r
6306       fromX = fromY = -1;\r
6307       break;\r
6308 \r
6309     case DP_Knight:\r
6310       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6311       fromX = fromY = -1;\r
6312       break;\r
6313 \r
6314     case DP_Bishop:\r
6315       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6316       fromX = fromY = -1;\r
6317       break;\r
6318 \r
6319     case DP_Rook:\r
6320       DropMenuEvent(WhiteRook, fromX, fromY);\r
6321       fromX = fromY = -1;\r
6322       break;\r
6323 \r
6324     case DP_Queen:\r
6325       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6326       fromX = fromY = -1;\r
6327       break;\r
6328 \r
6329     default:\r
6330       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6331     }\r
6332     break;\r
6333 \r
6334   case WM_TIMER:\r
6335     switch (wParam) {\r
6336     case CLOCK_TIMER_ID:\r
6337       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6338       clockTimerEvent = 0;\r
6339       DecrementClocks(); /* call into back end */\r
6340       break;\r
6341     case LOAD_GAME_TIMER_ID:\r
6342       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6343       loadGameTimerEvent = 0;\r
6344       AutoPlayGameLoop(); /* call into back end */\r
6345       break;\r
6346     case ANALYSIS_TIMER_ID:\r
6347       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6348                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6349         AnalysisPeriodicEvent(0);\r
6350       } else {\r
6351         KillTimer(hwnd, analysisTimerEvent);\r
6352         analysisTimerEvent = 0;\r
6353       }\r
6354       break;\r
6355     case DELAYED_TIMER_ID:\r
6356       KillTimer(hwnd, delayedTimerEvent);\r
6357       delayedTimerEvent = 0;\r
6358       delayedTimerCallback();\r
6359       break;\r
6360     }\r
6361     break;\r
6362 \r
6363   case WM_USER_Input:\r
6364     InputEvent(hwnd, message, wParam, lParam);\r
6365     break;\r
6366 \r
6367   /* [AS] Also move "attached" child windows */\r
6368   case WM_WINDOWPOSCHANGING:\r
6369 \r
6370     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6371         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6372 \r
6373         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6374             /* Window is moving */\r
6375             RECT rcMain;\r
6376 \r
6377 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6378             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6379             rcMain.right  = boardX + winWidth;\r
6380             rcMain.top    = boardY;\r
6381             rcMain.bottom = boardY + winHeight;\r
6382             \r
6383             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6384             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6385             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6386             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6387             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6388             boardX = lpwp->x;\r
6389             boardY = lpwp->y;\r
6390         }\r
6391     }\r
6392     break;\r
6393 \r
6394   /* [AS] Snapping */\r
6395   case WM_ENTERSIZEMOVE:\r
6396     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6397     if (hwnd == hwndMain) {\r
6398       doingSizing = TRUE;\r
6399       lastSizing = 0;\r
6400     }\r
6401     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6402     break;\r
6403 \r
6404   case WM_SIZING:\r
6405     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6406     if (hwnd == hwndMain) {\r
6407       lastSizing = wParam;\r
6408     }\r
6409     break;\r
6410 \r
6411   case WM_MOVING:\r
6412     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6413       return OnMoving( &sd, hwnd, wParam, lParam );\r
6414 \r
6415   case WM_EXITSIZEMOVE:\r
6416     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6417     if (hwnd == hwndMain) {\r
6418       RECT client;\r
6419       doingSizing = FALSE;\r
6420       InvalidateRect(hwnd, &boardRect, FALSE);\r
6421       GetClientRect(hwnd, &client);\r
6422       ResizeBoard(client.right, client.bottom, lastSizing);\r
6423       lastSizing = 0;\r
6424       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6425     }\r
6426     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6427     break;\r
6428 \r
6429   case WM_DESTROY: /* message: window being destroyed */\r
6430     PostQuitMessage(0);\r
6431     break;\r
6432 \r
6433   case WM_CLOSE:\r
6434     if (hwnd == hwndMain) {\r
6435       ExitEvent(0);\r
6436     }\r
6437     break;\r
6438 \r
6439   default:      /* Passes it on if unprocessed */\r
6440     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6441   }\r
6442   return 0;\r
6443 }\r
6444 \r
6445 /*---------------------------------------------------------------------------*\\r
6446  *\r
6447  * Misc utility routines\r
6448  *\r
6449 \*---------------------------------------------------------------------------*/\r
6450 \r
6451 /*\r
6452  * Decent random number generator, at least not as bad as Windows\r
6453  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6454  */\r
6455 unsigned int randstate;\r
6456 \r
6457 int\r
6458 myrandom(void)\r
6459 {\r
6460   randstate = randstate * 1664525 + 1013904223;\r
6461   return (int) randstate & 0x7fffffff;\r
6462 }\r
6463 \r
6464 void\r
6465 mysrandom(unsigned int seed)\r
6466 {\r
6467   randstate = seed;\r
6468 }\r
6469 \r
6470 \r
6471 /* \r
6472  * returns TRUE if user selects a different color, FALSE otherwise \r
6473  */\r
6474 \r
6475 BOOL\r
6476 ChangeColor(HWND hwnd, COLORREF *which)\r
6477 {\r
6478   static BOOL firstTime = TRUE;\r
6479   static DWORD customColors[16];\r
6480   CHOOSECOLOR cc;\r
6481   COLORREF newcolor;\r
6482   int i;\r
6483   ColorClass ccl;\r
6484 \r
6485   if (firstTime) {\r
6486     /* Make initial colors in use available as custom colors */\r
6487     /* Should we put the compiled-in defaults here instead? */\r
6488     i = 0;\r
6489     customColors[i++] = lightSquareColor & 0xffffff;\r
6490     customColors[i++] = darkSquareColor & 0xffffff;\r
6491     customColors[i++] = whitePieceColor & 0xffffff;\r
6492     customColors[i++] = blackPieceColor & 0xffffff;\r
6493     customColors[i++] = highlightSquareColor & 0xffffff;\r
6494     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6495 \r
6496     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6497       customColors[i++] = textAttribs[ccl].color;\r
6498     }\r
6499     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6500     firstTime = FALSE;\r
6501   }\r
6502 \r
6503   cc.lStructSize = sizeof(cc);\r
6504   cc.hwndOwner = hwnd;\r
6505   cc.hInstance = NULL;\r
6506   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6507   cc.lpCustColors = (LPDWORD) customColors;\r
6508   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6509 \r
6510   if (!ChooseColor(&cc)) return FALSE;\r
6511 \r
6512   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6513   if (newcolor == *which) return FALSE;\r
6514   *which = newcolor;\r
6515   return TRUE;\r
6516 \r
6517   /*\r
6518   InitDrawingColors();\r
6519   InvalidateRect(hwnd, &boardRect, FALSE);\r
6520   */\r
6521 }\r
6522 \r
6523 BOOLEAN\r
6524 MyLoadSound(MySound *ms)\r
6525 {\r
6526   BOOL ok = FALSE;\r
6527   struct stat st;\r
6528   FILE *f;\r
6529 \r
6530   if (ms->data) free(ms->data);\r
6531   ms->data = NULL;\r
6532 \r
6533   switch (ms->name[0]) {\r
6534   case NULLCHAR:\r
6535     /* Silence */\r
6536     ok = TRUE;\r
6537     break;\r
6538   case '$':\r
6539     /* System sound from Control Panel.  Don't preload here. */\r
6540     ok = TRUE;\r
6541     break;\r
6542   case '!':\r
6543     if (ms->name[1] == NULLCHAR) {\r
6544       /* "!" alone = silence */\r
6545       ok = TRUE;\r
6546     } else {\r
6547       /* Builtin wave resource.  Error if not found. */\r
6548       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6549       if (h == NULL) break;\r
6550       ms->data = (void *)LoadResource(hInst, h);\r
6551       if (h == NULL) break;\r
6552       ok = TRUE;\r
6553     }\r
6554     break;\r
6555   default:\r
6556     /* .wav file.  Error if not found. */\r
6557     f = fopen(ms->name, "rb");\r
6558     if (f == NULL) break;\r
6559     if (fstat(fileno(f), &st) < 0) break;\r
6560     ms->data = malloc(st.st_size);\r
6561     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6562     fclose(f);\r
6563     ok = TRUE;\r
6564     break;\r
6565   }\r
6566   if (!ok) {\r
6567     char buf[MSG_SIZ];\r
6568     sprintf(buf, "Error loading sound %s", ms->name);\r
6569     DisplayError(buf, GetLastError());\r
6570   }\r
6571   return ok;\r
6572 }\r
6573 \r
6574 BOOLEAN\r
6575 MyPlaySound(MySound *ms)\r
6576 {\r
6577   BOOLEAN ok = FALSE;\r
6578   switch (ms->name[0]) {\r
6579   case NULLCHAR:\r
6580     /* Silence */\r
6581     ok = TRUE;\r
6582     break;\r
6583   case '$':\r
6584     /* System sound from Control Panel (deprecated feature).\r
6585        "$" alone or an unset sound name gets default beep (still in use). */\r
6586     if (ms->name[1]) {\r
6587       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6588     }\r
6589     if (!ok) ok = MessageBeep(MB_OK);\r
6590     break; \r
6591   case '!':\r
6592     /* Builtin wave resource, or "!" alone for silence */\r
6593     if (ms->name[1]) {\r
6594       if (ms->data == NULL) return FALSE;\r
6595       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6596     } else {\r
6597       ok = TRUE;\r
6598     }\r
6599     break;\r
6600   default:\r
6601     /* .wav file.  Error if not found. */\r
6602     if (ms->data == NULL) return FALSE;\r
6603     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6604     break;\r
6605   }\r
6606   /* Don't print an error: this can happen innocently if the sound driver\r
6607      is busy; for instance, if another instance of WinBoard is playing\r
6608      a sound at about the same time. */\r
6609 #if 0\r
6610   if (!ok) {\r
6611     char buf[MSG_SIZ];\r
6612     sprintf(buf, "Error playing sound %s", ms->name);\r
6613     DisplayError(buf, GetLastError());\r
6614   }\r
6615 #endif\r
6616   return ok;\r
6617 }\r
6618 \r
6619 \r
6620 LRESULT CALLBACK\r
6621 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6622 {\r
6623   BOOL ok;\r
6624   OPENFILENAME *ofn;\r
6625   static UINT *number; /* gross that this is static */\r
6626 \r
6627   switch (message) {\r
6628   case WM_INITDIALOG: /* message: initialize dialog box */\r
6629     /* Center the dialog over the application window */\r
6630     ofn = (OPENFILENAME *) lParam;\r
6631     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6632       number = (UINT *) ofn->lCustData;\r
6633       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6634     } else {\r
6635       number = NULL;\r
6636     }\r
6637     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6638     return FALSE;  /* Allow for further processing */\r
6639 \r
6640   case WM_COMMAND:\r
6641     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6642       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6643     }\r
6644     return FALSE;  /* Allow for further processing */\r
6645   }\r
6646   return FALSE;\r
6647 }\r
6648 \r
6649 UINT APIENTRY\r
6650 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6651 {\r
6652   static UINT *number;\r
6653   OPENFILENAME *ofname;\r
6654   OFNOTIFY *ofnot;\r
6655   switch (uiMsg) {\r
6656   case WM_INITDIALOG:\r
6657     ofname = (OPENFILENAME *)lParam;\r
6658     number = (UINT *)(ofname->lCustData);\r
6659     break;\r
6660   case WM_NOTIFY:\r
6661     ofnot = (OFNOTIFY *)lParam;\r
6662     if (ofnot->hdr.code == CDN_FILEOK) {\r
6663       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6664     }\r
6665     break;\r
6666   }\r
6667   return 0;\r
6668 }\r
6669 \r
6670 \r
6671 FILE *\r
6672 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6673                char *nameFilt, char *dlgTitle, UINT *number,\r
6674                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6675 {\r
6676   OPENFILENAME openFileName;\r
6677   char buf1[MSG_SIZ];\r
6678   FILE *f;\r
6679 \r
6680   if (fileName == NULL) fileName = buf1;\r
6681   if (defName == NULL) {\r
6682     strcpy(fileName, "*.");\r
6683     strcat(fileName, defExt);\r
6684   } else {\r
6685     strcpy(fileName, defName);\r
6686   }\r
6687   if (fileTitle) strcpy(fileTitle, "");\r
6688   if (number) *number = 0;\r
6689 \r
6690   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6691   openFileName.hwndOwner         = hwnd;\r
6692   openFileName.hInstance         = (HANDLE) hInst;\r
6693   openFileName.lpstrFilter       = nameFilt;\r
6694   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6695   openFileName.nMaxCustFilter    = 0L;\r
6696   openFileName.nFilterIndex      = 1L;\r
6697   openFileName.lpstrFile         = fileName;\r
6698   openFileName.nMaxFile          = MSG_SIZ;\r
6699   openFileName.lpstrFileTitle    = fileTitle;\r
6700   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6701   openFileName.lpstrInitialDir   = NULL;\r
6702   openFileName.lpstrTitle        = dlgTitle;\r
6703   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6704     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6705     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6706     | (oldDialog ? 0 : OFN_EXPLORER);\r
6707   openFileName.nFileOffset       = 0;\r
6708   openFileName.nFileExtension    = 0;\r
6709   openFileName.lpstrDefExt       = defExt;\r
6710   openFileName.lCustData         = (LONG) number;\r
6711   openFileName.lpfnHook          = oldDialog ?\r
6712     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6713   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6714 \r
6715   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6716                         GetOpenFileName(&openFileName)) {\r
6717     /* open the file */\r
6718     f = fopen(openFileName.lpstrFile, write);\r
6719     if (f == NULL) {\r
6720       MessageBox(hwnd, "File open failed", NULL,\r
6721                  MB_OK|MB_ICONEXCLAMATION);\r
6722       return NULL;\r
6723     }\r
6724   } else {\r
6725     int err = CommDlgExtendedError();\r
6726     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6727     return FALSE;\r
6728   }\r
6729   return f;\r
6730 }\r
6731 \r
6732 \r
6733 \r
6734 VOID APIENTRY\r
6735 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6736 {\r
6737   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6738 \r
6739   /*\r
6740    * Get the first pop-up menu in the menu template. This is the\r
6741    * menu that TrackPopupMenu displays.\r
6742    */\r
6743   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6744 \r
6745   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6746 \r
6747   /*\r
6748    * TrackPopup uses screen coordinates, so convert the\r
6749    * coordinates of the mouse click to screen coordinates.\r
6750    */\r
6751   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6752 \r
6753   /* Draw and track the floating pop-up menu. */\r
6754   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6755                  pt.x, pt.y, 0, hwnd, NULL);\r
6756 \r
6757   /* Destroy the menu.*/\r
6758   DestroyMenu(hmenu);\r
6759 }\r
6760    \r
6761 typedef struct {\r
6762   HWND hDlg, hText;\r
6763   int sizeX, sizeY, newSizeX, newSizeY;\r
6764   HDWP hdwp;\r
6765 } ResizeEditPlusButtonsClosure;\r
6766 \r
6767 BOOL CALLBACK\r
6768 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6769 {\r
6770   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6771   RECT rect;\r
6772   POINT pt;\r
6773 \r
6774   if (hChild == cl->hText) return TRUE;\r
6775   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6776   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6777   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6778   ScreenToClient(cl->hDlg, &pt);\r
6779   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6780     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6781   return TRUE;\r
6782 }\r
6783 \r
6784 /* Resize a dialog that has a (rich) edit field filling most of\r
6785    the top, with a row of buttons below */\r
6786 VOID\r
6787 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6788 {\r
6789   RECT rectText;\r
6790   int newTextHeight, newTextWidth;\r
6791   ResizeEditPlusButtonsClosure cl;\r
6792   \r
6793   /*if (IsIconic(hDlg)) return;*/\r
6794   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6795   \r
6796   cl.hdwp = BeginDeferWindowPos(8);\r
6797 \r
6798   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6799   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6800   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6801   if (newTextHeight < 0) {\r
6802     newSizeY += -newTextHeight;\r
6803     newTextHeight = 0;\r
6804   }\r
6805   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6806     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6807 \r
6808   cl.hDlg = hDlg;\r
6809   cl.hText = hText;\r
6810   cl.sizeX = sizeX;\r
6811   cl.sizeY = sizeY;\r
6812   cl.newSizeX = newSizeX;\r
6813   cl.newSizeY = newSizeY;\r
6814   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6815 \r
6816   EndDeferWindowPos(cl.hdwp);\r
6817 }\r
6818 \r
6819 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6820 {\r
6821     RECT    rChild, rParent;\r
6822     int     wChild, hChild, wParent, hParent;\r
6823     int     wScreen, hScreen, xNew, yNew;\r
6824     HDC     hdc;\r
6825 \r
6826     /* Get the Height and Width of the child window */\r
6827     GetWindowRect (hwndChild, &rChild);\r
6828     wChild = rChild.right - rChild.left;\r
6829     hChild = rChild.bottom - rChild.top;\r
6830 \r
6831     /* Get the Height and Width of the parent window */\r
6832     GetWindowRect (hwndParent, &rParent);\r
6833     wParent = rParent.right - rParent.left;\r
6834     hParent = rParent.bottom - rParent.top;\r
6835 \r
6836     /* Get the display limits */\r
6837     hdc = GetDC (hwndChild);\r
6838     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6839     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6840     ReleaseDC(hwndChild, hdc);\r
6841 \r
6842     /* Calculate new X position, then adjust for screen */\r
6843     xNew = rParent.left + ((wParent - wChild) /2);\r
6844     if (xNew < 0) {\r
6845         xNew = 0;\r
6846     } else if ((xNew+wChild) > wScreen) {\r
6847         xNew = wScreen - wChild;\r
6848     }\r
6849 \r
6850     /* Calculate new Y position, then adjust for screen */\r
6851     if( mode == 0 ) {\r
6852         yNew = rParent.top  + ((hParent - hChild) /2);\r
6853     }\r
6854     else {\r
6855         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6856     }\r
6857 \r
6858     if (yNew < 0) {\r
6859         yNew = 0;\r
6860     } else if ((yNew+hChild) > hScreen) {\r
6861         yNew = hScreen - hChild;\r
6862     }\r
6863 \r
6864     /* Set it, and return */\r
6865     return SetWindowPos (hwndChild, NULL,\r
6866                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6867 }\r
6868 \r
6869 /* Center one window over another */\r
6870 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6871 {\r
6872     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6873 }\r
6874 \r
6875 /*---------------------------------------------------------------------------*\\r
6876  *\r
6877  * Startup Dialog functions\r
6878  *\r
6879 \*---------------------------------------------------------------------------*/\r
6880 void\r
6881 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6882 {\r
6883   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6884 \r
6885   while (*cd != NULL) {\r
6886     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6887     cd++;\r
6888   }\r
6889 }\r
6890 \r
6891 void\r
6892 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6893 {\r
6894   char buf1[ARG_MAX];\r
6895   int len;\r
6896 \r
6897   if (str[0] == '@') {\r
6898     FILE* f = fopen(str + 1, "r");\r
6899     if (f == NULL) {\r
6900       DisplayFatalError(str + 1, errno, 2);\r
6901       return;\r
6902     }\r
6903     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6904     fclose(f);\r
6905     buf1[len] = NULLCHAR;\r
6906     str = buf1;\r
6907   }\r
6908 \r
6909   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6910 \r
6911   for (;;) {\r
6912     char buf[MSG_SIZ];\r
6913     char *end = strchr(str, '\n');\r
6914     if (end == NULL) return;\r
6915     memcpy(buf, str, end - str);\r
6916     buf[end - str] = NULLCHAR;\r
6917     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6918     str = end + 1;\r
6919   }\r
6920 }\r
6921 \r
6922 void\r
6923 SetStartupDialogEnables(HWND hDlg)\r
6924 {\r
6925   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6926     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6927     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6928   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6929     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6930   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6931     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6932   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6933     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6934   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6935     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6936     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6937     IsDlgButtonChecked(hDlg, OPT_View));\r
6938 }\r
6939 \r
6940 char *\r
6941 QuoteForFilename(char *filename)\r
6942 {\r
6943   int dquote, space;\r
6944   dquote = strchr(filename, '"') != NULL;\r
6945   space = strchr(filename, ' ') != NULL;\r
6946   if (dquote || space) {\r
6947     if (dquote) {\r
6948       return "'";\r
6949     } else {\r
6950       return "\"";\r
6951     }\r
6952   } else {\r
6953     return "";\r
6954   }\r
6955 }\r
6956 \r
6957 VOID\r
6958 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6959 {\r
6960   char buf[MSG_SIZ];\r
6961   char *q;\r
6962 \r
6963   InitComboStringsFromOption(hwndCombo, nthnames);\r
6964   q = QuoteForFilename(nthcp);\r
6965   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6966   if (*nthdir != NULLCHAR) {\r
6967     q = QuoteForFilename(nthdir);\r
6968     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6969   }\r
6970   if (*nthcp == NULLCHAR) {\r
6971     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6972   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6973     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6974     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6975   }\r
6976 }\r
6977 \r
6978 LRESULT CALLBACK\r
6979 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6980 {\r
6981   char buf[MSG_SIZ];\r
6982   HANDLE hwndCombo;\r
6983   char *p;\r
6984 \r
6985   switch (message) {\r
6986   case WM_INITDIALOG:\r
6987     /* Center the dialog */\r
6988     CenterWindow (hDlg, GetDesktopWindow());\r
6989     /* Initialize the dialog items */\r
6990     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6991                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6992                   firstChessProgramNames);\r
6993     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6994                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6995                   secondChessProgramNames);\r
6996     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6997     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6998     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6999     if (*appData.icsHelper != NULLCHAR) {\r
7000       char *q = QuoteForFilename(appData.icsHelper);\r
7001       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7002     }\r
7003     if (*appData.icsHost == NULLCHAR) {\r
7004       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7005       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7006     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7007       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7008       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7009     }\r
7010 \r
7011     if (appData.icsActive) {\r
7012       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7013     }\r
7014     else if (appData.noChessProgram) {\r
7015       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7016     }\r
7017     else {\r
7018       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7019     }\r
7020 \r
7021     SetStartupDialogEnables(hDlg);\r
7022     return TRUE;\r
7023 \r
7024   case WM_COMMAND:\r
7025     switch (LOWORD(wParam)) {\r
7026     case IDOK:\r
7027       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7028         strcpy(buf, "/fcp=");\r
7029         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7030         p = buf;\r
7031         ParseArgs(StringGet, &p);\r
7032         strcpy(buf, "/scp=");\r
7033         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7034         p = buf;\r
7035         ParseArgs(StringGet, &p);\r
7036         appData.noChessProgram = FALSE;\r
7037         appData.icsActive = FALSE;\r
7038       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7039         strcpy(buf, "/ics /icshost=");\r
7040         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7041         p = buf;\r
7042         ParseArgs(StringGet, &p);\r
7043         if (appData.zippyPlay) {\r
7044           strcpy(buf, "/fcp=");\r
7045           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7046           p = buf;\r
7047           ParseArgs(StringGet, &p);\r
7048         }\r
7049       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7050         appData.noChessProgram = TRUE;\r
7051         appData.icsActive = FALSE;\r
7052       } else {\r
7053         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7054                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7055         return TRUE;\r
7056       }\r
7057       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7058         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7059         p = buf;\r
7060         ParseArgs(StringGet, &p);\r
7061       }\r
7062       EndDialog(hDlg, TRUE);\r
7063       return TRUE;\r
7064 \r
7065     case IDCANCEL:\r
7066       ExitEvent(0);\r
7067       return TRUE;\r
7068 \r
7069     case IDM_HELPCONTENTS:\r
7070       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7071         MessageBox (GetFocus(),\r
7072                     "Unable to activate help",\r
7073                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7074       }\r
7075       break;\r
7076 \r
7077     default:\r
7078       SetStartupDialogEnables(hDlg);\r
7079       break;\r
7080     }\r
7081     break;\r
7082   }\r
7083   return FALSE;\r
7084 }\r
7085 \r
7086 /*---------------------------------------------------------------------------*\\r
7087  *\r
7088  * About box dialog functions\r
7089  *\r
7090 \*---------------------------------------------------------------------------*/\r
7091 \r
7092 /* Process messages for "About" dialog box */\r
7093 LRESULT CALLBACK\r
7094 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7095 {\r
7096   switch (message) {\r
7097   case WM_INITDIALOG: /* message: initialize dialog box */\r
7098     /* Center the dialog over the application window */\r
7099     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7100     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7101     return (TRUE);\r
7102 \r
7103   case WM_COMMAND: /* message: received a command */\r
7104     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7105         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7106       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7107       return (TRUE);\r
7108     }\r
7109     break;\r
7110   }\r
7111   return (FALSE);\r
7112 }\r
7113 \r
7114 /*---------------------------------------------------------------------------*\\r
7115  *\r
7116  * Comment Dialog functions\r
7117  *\r
7118 \*---------------------------------------------------------------------------*/\r
7119 \r
7120 LRESULT CALLBACK\r
7121 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7122 {\r
7123   static HANDLE hwndText = NULL;\r
7124   int len, newSizeX, newSizeY, flags;\r
7125   static int sizeX, sizeY;\r
7126   char *str;\r
7127   RECT rect;\r
7128   MINMAXINFO *mmi;\r
7129 \r
7130   switch (message) {\r
7131   case WM_INITDIALOG: /* message: initialize dialog box */\r
7132     /* Initialize the dialog items */\r
7133     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7134     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7135     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7136     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7137     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7138     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7139     SetWindowText(hDlg, commentTitle);\r
7140     if (editComment) {\r
7141       SetFocus(hwndText);\r
7142     } else {\r
7143       SetFocus(GetDlgItem(hDlg, IDOK));\r
7144     }\r
7145     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7146                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7147                 MAKELPARAM(FALSE, 0));\r
7148     /* Size and position the dialog */\r
7149     if (!commentDialog) {\r
7150       commentDialog = hDlg;\r
7151       flags = SWP_NOZORDER;\r
7152       GetClientRect(hDlg, &rect);\r
7153       sizeX = rect.right;\r
7154       sizeY = rect.bottom;\r
7155       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7156           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7157         WINDOWPLACEMENT wp;\r
7158         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7159         wp.length = sizeof(WINDOWPLACEMENT);\r
7160         wp.flags = 0;\r
7161         wp.showCmd = SW_SHOW;\r
7162         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7163         wp.rcNormalPosition.left = commentX;\r
7164         wp.rcNormalPosition.right = commentX + commentW;\r
7165         wp.rcNormalPosition.top = commentY;\r
7166         wp.rcNormalPosition.bottom = commentY + commentH;\r
7167         SetWindowPlacement(hDlg, &wp);\r
7168 \r
7169         GetClientRect(hDlg, &rect);\r
7170         newSizeX = rect.right;\r
7171         newSizeY = rect.bottom;\r
7172         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7173                               newSizeX, newSizeY);\r
7174         sizeX = newSizeX;\r
7175         sizeY = newSizeY;\r
7176       }\r
7177     }\r
7178     return FALSE;\r
7179 \r
7180   case WM_COMMAND: /* message: received a command */\r
7181     switch (LOWORD(wParam)) {\r
7182     case IDOK:\r
7183       if (editComment) {\r
7184         char *p, *q;\r
7185         /* Read changed options from the dialog box */\r
7186         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7187         len = GetWindowTextLength(hwndText);\r
7188         str = (char *) malloc(len + 1);\r
7189         GetWindowText(hwndText, str, len + 1);\r
7190         p = q = str;\r
7191         while (*q) {\r
7192           if (*q == '\r')\r
7193             q++;\r
7194           else\r
7195             *p++ = *q++;\r
7196         }\r
7197         *p = NULLCHAR;\r
7198         ReplaceComment(commentIndex, str);\r
7199         free(str);\r
7200       }\r
7201       CommentPopDown();\r
7202       return TRUE;\r
7203 \r
7204     case IDCANCEL:\r
7205     case OPT_CancelComment:\r
7206       CommentPopDown();\r
7207       return TRUE;\r
7208 \r
7209     case OPT_ClearComment:\r
7210       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7211       break;\r
7212 \r
7213     case OPT_EditComment:\r
7214       EditCommentEvent();\r
7215       return TRUE;\r
7216 \r
7217     default:\r
7218       break;\r
7219     }\r
7220     break;\r
7221 \r
7222   case WM_SIZE:\r
7223     newSizeX = LOWORD(lParam);\r
7224     newSizeY = HIWORD(lParam);\r
7225     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7226     sizeX = newSizeX;\r
7227     sizeY = newSizeY;\r
7228     break;\r
7229 \r
7230   case WM_GETMINMAXINFO:\r
7231     /* Prevent resizing window too small */\r
7232     mmi = (MINMAXINFO *) lParam;\r
7233     mmi->ptMinTrackSize.x = 100;\r
7234     mmi->ptMinTrackSize.y = 100;\r
7235     break;\r
7236   }\r
7237   return FALSE;\r
7238 }\r
7239 \r
7240 VOID\r
7241 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7242 {\r
7243   FARPROC lpProc;\r
7244   char *p, *q;\r
7245 \r
7246   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7247 \r
7248   if (str == NULL) str = "";\r
7249   p = (char *) malloc(2 * strlen(str) + 2);\r
7250   q = p;\r
7251   while (*str) {\r
7252     if (*str == '\n') *q++ = '\r';\r
7253     *q++ = *str++;\r
7254   }\r
7255   *q = NULLCHAR;\r
7256   if (commentText != NULL) free(commentText);\r
7257 \r
7258   commentIndex = index;\r
7259   commentTitle = title;\r
7260   commentText = p;\r
7261   editComment = edit;\r
7262 \r
7263   if (commentDialog) {\r
7264     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7265     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7266   } else {\r
7267     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7268     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7269                  hwndMain, (DLGPROC)lpProc);\r
7270     FreeProcInstance(lpProc);\r
7271   }\r
7272   commentDialogUp = TRUE;\r
7273 }\r
7274 \r
7275 \r
7276 /*---------------------------------------------------------------------------*\\r
7277  *\r
7278  * Type-in move dialog functions\r
7279  * \r
7280 \*---------------------------------------------------------------------------*/\r
7281 \r
7282 LRESULT CALLBACK\r
7283 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7284 {\r
7285   char move[MSG_SIZ];\r
7286   HWND hInput;\r
7287   ChessMove moveType;\r
7288   int fromX, fromY, toX, toY;\r
7289   char promoChar;\r
7290 \r
7291   switch (message) {\r
7292   case WM_INITDIALOG:\r
7293     move[0] = (char) lParam;\r
7294     move[1] = NULLCHAR;\r
7295     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7296     hInput = GetDlgItem(hDlg, OPT_Move);\r
7297     SetWindowText(hInput, move);\r
7298     SetFocus(hInput);\r
7299     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7300     return FALSE;\r
7301 \r
7302   case WM_COMMAND:\r
7303     switch (LOWORD(wParam)) {\r
7304     case IDOK:\r
7305       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7306         gameMode != Training) {\r
7307         DisplayMoveError("Displayed move is not current");\r
7308       } else {\r
7309         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7310         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7311           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7312           if (gameMode != Training)\r
7313               forwardMostMove = currentMove;\r
7314           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7315         } else {\r
7316           DisplayMoveError("Could not parse move");\r
7317         }\r
7318       }\r
7319       EndDialog(hDlg, TRUE);\r
7320       return TRUE;\r
7321     case IDCANCEL:\r
7322       EndDialog(hDlg, FALSE);\r
7323       return TRUE;\r
7324     default:\r
7325       break;\r
7326     }\r
7327     break;\r
7328   }\r
7329   return FALSE;\r
7330 }\r
7331 \r
7332 VOID\r
7333 PopUpMoveDialog(char firstchar)\r
7334 {\r
7335     FARPROC lpProc;\r
7336     \r
7337     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7338         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7339         gameMode == AnalyzeMode || gameMode == EditGame || \r
7340         gameMode == EditPosition || gameMode == IcsExamining ||\r
7341         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7342         gameMode == Training) {\r
7343       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7344       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7345         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7346       FreeProcInstance(lpProc);\r
7347     }\r
7348 }\r
7349 \r
7350 /*---------------------------------------------------------------------------*\\r
7351  *\r
7352  * Type-in name dialog functions\r
7353  * \r
7354 \*---------------------------------------------------------------------------*/\r
7355 \r
7356 LRESULT CALLBACK\r
7357 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7358 {\r
7359   char move[MSG_SIZ];\r
7360   HWND hInput;\r
7361 \r
7362   switch (message) {\r
7363   case WM_INITDIALOG:\r
7364     move[0] = (char) lParam;\r
7365     move[1] = NULLCHAR;\r
7366     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7367     hInput = GetDlgItem(hDlg, OPT_Name);\r
7368     SetWindowText(hInput, move);\r
7369     SetFocus(hInput);\r
7370     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7371     return FALSE;\r
7372 \r
7373   case WM_COMMAND:\r
7374     switch (LOWORD(wParam)) {\r
7375     case IDOK:\r
7376       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7377       appData.userName = strdup(move);\r
7378       SetUserLogo();\r
7379 \r
7380       EndDialog(hDlg, TRUE);\r
7381       return TRUE;\r
7382     case IDCANCEL:\r
7383       EndDialog(hDlg, FALSE);\r
7384       return TRUE;\r
7385     default:\r
7386       break;\r
7387     }\r
7388     break;\r
7389   }\r
7390   return FALSE;\r
7391 }\r
7392 \r
7393 VOID\r
7394 PopUpNameDialog(char firstchar)\r
7395 {\r
7396     FARPROC lpProc;\r
7397     \r
7398       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7399       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7400         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7401       FreeProcInstance(lpProc);\r
7402 }\r
7403 \r
7404 /*---------------------------------------------------------------------------*\\r
7405  *\r
7406  *  Error dialogs\r
7407  * \r
7408 \*---------------------------------------------------------------------------*/\r
7409 \r
7410 /* Nonmodal error box */\r
7411 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7412                              WPARAM wParam, LPARAM lParam);\r
7413 \r
7414 VOID\r
7415 ErrorPopUp(char *title, char *content)\r
7416 {\r
7417   FARPROC lpProc;\r
7418   char *p, *q;\r
7419   BOOLEAN modal = hwndMain == NULL;\r
7420 \r
7421   p = content;\r
7422   q = errorMessage;\r
7423   while (*p) {\r
7424     if (*p == '\n') {\r
7425       if (modal) {\r
7426         *q++ = ' ';\r
7427         p++;\r
7428       } else {\r
7429         *q++ = '\r';\r
7430         *q++ = *p++;\r
7431       }\r
7432     } else {\r
7433       *q++ = *p++;\r
7434     }\r
7435   }\r
7436   *q = NULLCHAR;\r
7437   strncpy(errorTitle, title, sizeof(errorTitle));\r
7438   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7439   \r
7440   if (modal) {\r
7441     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7442   } else {\r
7443     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7444     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7445                  hwndMain, (DLGPROC)lpProc);\r
7446     FreeProcInstance(lpProc);\r
7447   }\r
7448 }\r
7449 \r
7450 VOID\r
7451 ErrorPopDown()\r
7452 {\r
7453   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7454   if (errorDialog == NULL) return;\r
7455   DestroyWindow(errorDialog);\r
7456   errorDialog = NULL;\r
7457 }\r
7458 \r
7459 LRESULT CALLBACK\r
7460 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7461 {\r
7462   HANDLE hwndText;\r
7463   RECT rChild;\r
7464 \r
7465   switch (message) {\r
7466   case WM_INITDIALOG:\r
7467     GetWindowRect(hDlg, &rChild);\r
7468 \r
7469     /*\r
7470     SetWindowPos(hDlg, NULL, rChild.left,\r
7471       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7472       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7473     */\r
7474 \r
7475     /* \r
7476         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7477         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7478         and it doesn't work when you resize the dialog.\r
7479         For now, just give it a default position.\r
7480     */\r
7481     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7482 \r
7483     errorDialog = hDlg;\r
7484     SetWindowText(hDlg, errorTitle);\r
7485     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7486     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7487     return FALSE;\r
7488 \r
7489   case WM_COMMAND:\r
7490     switch (LOWORD(wParam)) {\r
7491     case IDOK:\r
7492     case IDCANCEL:\r
7493       if (errorDialog == hDlg) errorDialog = NULL;\r
7494       DestroyWindow(hDlg);\r
7495       return TRUE;\r
7496 \r
7497     default:\r
7498       break;\r
7499     }\r
7500     break;\r
7501   }\r
7502   return FALSE;\r
7503 }\r
7504 \r
7505 #ifdef GOTHIC\r
7506 HWND gothicDialog = NULL;\r
7507 \r
7508 LRESULT CALLBACK\r
7509 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7510 {\r
7511   HANDLE hwndText;\r
7512   RECT rChild;\r
7513   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7514 \r
7515   switch (message) {\r
7516   case WM_INITDIALOG:\r
7517     GetWindowRect(hDlg, &rChild);\r
7518 \r
7519     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7520                                                              SWP_NOZORDER);\r
7521 \r
7522     /* \r
7523         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7524         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7525         and it doesn't work when you resize the dialog.\r
7526         For now, just give it a default position.\r
7527     */\r
7528     gothicDialog = hDlg;\r
7529     SetWindowText(hDlg, errorTitle);\r
7530     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7531     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7532     return FALSE;\r
7533 \r
7534   case WM_COMMAND:\r
7535     switch (LOWORD(wParam)) {\r
7536     case IDOK:\r
7537     case IDCANCEL:\r
7538       if (errorDialog == hDlg) errorDialog = NULL;\r
7539       DestroyWindow(hDlg);\r
7540       return TRUE;\r
7541 \r
7542     default:\r
7543       break;\r
7544     }\r
7545     break;\r
7546   }\r
7547   return FALSE;\r
7548 }\r
7549 \r
7550 VOID\r
7551 GothicPopUp(char *title, VariantClass variant)\r
7552 {\r
7553   FARPROC lpProc;\r
7554   static char *lastTitle;\r
7555 \r
7556   strncpy(errorTitle, title, sizeof(errorTitle));\r
7557   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7558 \r
7559   if(lastTitle != title && gothicDialog != NULL) {\r
7560     DestroyWindow(gothicDialog);\r
7561     gothicDialog = NULL;\r
7562   }\r
7563   if(variant != VariantNormal && gothicDialog == NULL) {\r
7564     title = lastTitle;\r
7565     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7566     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7567                  hwndMain, (DLGPROC)lpProc);\r
7568     FreeProcInstance(lpProc);\r
7569   }\r
7570 }\r
7571 #endif\r
7572 \r
7573 /*---------------------------------------------------------------------------*\\r
7574  *\r
7575  *  Ics Interaction console functions\r
7576  *\r
7577 \*---------------------------------------------------------------------------*/\r
7578 \r
7579 #define HISTORY_SIZE 64\r
7580 static char *history[HISTORY_SIZE];\r
7581 int histIn = 0, histP = 0;\r
7582 \r
7583 VOID\r
7584 SaveInHistory(char *cmd)\r
7585 {\r
7586   if (history[histIn] != NULL) {\r
7587     free(history[histIn]);\r
7588     history[histIn] = NULL;\r
7589   }\r
7590   if (*cmd == NULLCHAR) return;\r
7591   history[histIn] = StrSave(cmd);\r
7592   histIn = (histIn + 1) % HISTORY_SIZE;\r
7593   if (history[histIn] != NULL) {\r
7594     free(history[histIn]);\r
7595     history[histIn] = NULL;\r
7596   }\r
7597   histP = histIn;\r
7598 }\r
7599 \r
7600 char *\r
7601 PrevInHistory(char *cmd)\r
7602 {\r
7603   int newhp;\r
7604   if (histP == histIn) {\r
7605     if (history[histIn] != NULL) free(history[histIn]);\r
7606     history[histIn] = StrSave(cmd);\r
7607   }\r
7608   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7609   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7610   histP = newhp;\r
7611   return history[histP];\r
7612 }\r
7613 \r
7614 char *\r
7615 NextInHistory()\r
7616 {\r
7617   if (histP == histIn) return NULL;\r
7618   histP = (histP + 1) % HISTORY_SIZE;\r
7619   return history[histP];\r
7620 }\r
7621 \r
7622 typedef struct {\r
7623   char *item;\r
7624   char *command;\r
7625   BOOLEAN getname;\r
7626   BOOLEAN immediate;\r
7627 } IcsTextMenuEntry;\r
7628 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7629 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7630 \r
7631 void\r
7632 ParseIcsTextMenu(char *icsTextMenuString)\r
7633 {\r
7634 //  int flags = 0;\r
7635   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7636   char *p = icsTextMenuString;\r
7637   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7638     free(e->item);\r
7639     e->item = NULL;\r
7640     if (e->command != NULL) {\r
7641       free(e->command);\r
7642       e->command = NULL;\r
7643     }\r
7644     e++;\r
7645   }\r
7646   e = icsTextMenuEntry;\r
7647   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7648     if (*p == ';' || *p == '\n') {\r
7649       e->item = strdup("-");\r
7650       e->command = NULL;\r
7651       p++;\r
7652     } else if (*p == '-') {\r
7653       e->item = strdup("-");\r
7654       e->command = NULL;\r
7655       p++;\r
7656       if (*p) p++;\r
7657     } else {\r
7658       char *q, *r, *s, *t;\r
7659       char c;\r
7660       q = strchr(p, ',');\r
7661       if (q == NULL) break;\r
7662       *q = NULLCHAR;\r
7663       r = strchr(q + 1, ',');\r
7664       if (r == NULL) break;\r
7665       *r = NULLCHAR;\r
7666       s = strchr(r + 1, ',');\r
7667       if (s == NULL) break;\r
7668       *s = NULLCHAR;\r
7669       c = ';';\r
7670       t = strchr(s + 1, c);\r
7671       if (t == NULL) {\r
7672         c = '\n';\r
7673         t = strchr(s + 1, c);\r
7674       }\r
7675       if (t != NULL) *t = NULLCHAR;\r
7676       e->item = strdup(p);\r
7677       e->command = strdup(q + 1);\r
7678       e->getname = *(r + 1) != '0';\r
7679       e->immediate = *(s + 1) != '0';\r
7680       *q = ',';\r
7681       *r = ',';\r
7682       *s = ',';\r
7683       if (t == NULL) break;\r
7684       *t = c;\r
7685       p = t + 1;\r
7686     }\r
7687     e++;\r
7688   } \r
7689 }\r
7690 \r
7691 HMENU\r
7692 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7693 {\r
7694   HMENU hmenu, h;\r
7695   int i = 0;\r
7696   hmenu = LoadMenu(hInst, "TextMenu");\r
7697   h = GetSubMenu(hmenu, 0);\r
7698   while (e->item) {\r
7699     if (strcmp(e->item, "-") == 0) {\r
7700       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7701     } else {\r
7702       if (e->item[0] == '|') {\r
7703         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7704                    IDM_CommandX + i, &e->item[1]);\r
7705       } else {\r
7706         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7707       }\r
7708     }\r
7709     e++;\r
7710     i++;\r
7711   } \r
7712   return hmenu;\r
7713 }\r
7714 \r
7715 WNDPROC consoleTextWindowProc;\r
7716 \r
7717 void\r
7718 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7719 {\r
7720   char buf[MSG_SIZ], name[MSG_SIZ];\r
7721   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7722   CHARRANGE sel;\r
7723 \r
7724   if (!getname) {\r
7725     SetWindowText(hInput, command);\r
7726     if (immediate) {\r
7727       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7728     } else {\r
7729       sel.cpMin = 999999;\r
7730       sel.cpMax = 999999;\r
7731       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7732       SetFocus(hInput);\r
7733     }\r
7734     return;\r
7735   }    \r
7736   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7737   if (sel.cpMin == sel.cpMax) {\r
7738     /* Expand to surrounding word */\r
7739     TEXTRANGE tr;\r
7740     do {\r
7741       tr.chrg.cpMax = sel.cpMin;\r
7742       tr.chrg.cpMin = --sel.cpMin;\r
7743       if (sel.cpMin < 0) break;\r
7744       tr.lpstrText = name;\r
7745       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7746     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7747     sel.cpMin++;\r
7748 \r
7749     do {\r
7750       tr.chrg.cpMin = sel.cpMax;\r
7751       tr.chrg.cpMax = ++sel.cpMax;\r
7752       tr.lpstrText = name;\r
7753       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7754     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7755     sel.cpMax--;\r
7756 \r
7757     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7758       MessageBeep(MB_ICONEXCLAMATION);\r
7759       return;\r
7760     }\r
7761     tr.chrg = sel;\r
7762     tr.lpstrText = name;\r
7763     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7764   } else {\r
7765     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7766       MessageBeep(MB_ICONEXCLAMATION);\r
7767       return;\r
7768     }\r
7769     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7770   }\r
7771   if (immediate) {\r
7772     sprintf(buf, "%s %s", command, name);\r
7773     SetWindowText(hInput, buf);\r
7774     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7775   } else {\r
7776     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7777     SetWindowText(hInput, buf);\r
7778     sel.cpMin = 999999;\r
7779     sel.cpMax = 999999;\r
7780     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7781     SetFocus(hInput);\r
7782   }\r
7783 }\r
7784 \r
7785 LRESULT CALLBACK \r
7786 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7787 {\r
7788   HWND hInput;\r
7789   CHARRANGE sel;\r
7790 \r
7791   switch (message) {\r
7792   case WM_KEYDOWN:\r
7793     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7794     switch (wParam) {\r
7795     case VK_PRIOR:\r
7796       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7797       return 0;\r
7798     case VK_NEXT:\r
7799       sel.cpMin = 999999;\r
7800       sel.cpMax = 999999;\r
7801       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7802       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7803       return 0;\r
7804     }\r
7805     break;\r
7806   case WM_CHAR:\r
7807     if (wParam == '\t') {\r
7808       if (GetKeyState(VK_SHIFT) < 0) {\r
7809         /* shifted */\r
7810         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7811         if (buttonDesc[0].hwnd) {\r
7812           SetFocus(buttonDesc[0].hwnd);\r
7813         } else {\r
7814           SetFocus(hwndMain);\r
7815         }\r
7816       } else {\r
7817         /* unshifted */\r
7818         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7819       }\r
7820     } else {\r
7821       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7822       SetFocus(hInput);\r
7823       SendMessage(hInput, message, wParam, lParam);\r
7824     }\r
7825     return 0;\r
7826   case WM_PASTE:\r
7827     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7828     SetFocus(hInput);\r
7829     return SendMessage(hInput, message, wParam, lParam);\r
7830   case WM_MBUTTONDOWN:\r
7831     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7832   case WM_RBUTTONDOWN:\r
7833     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7834       /* Move selection here if it was empty */\r
7835       POINT pt;\r
7836       pt.x = LOWORD(lParam);\r
7837       pt.y = HIWORD(lParam);\r
7838       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7839       if (sel.cpMin == sel.cpMax) {\r
7840         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7841         sel.cpMax = sel.cpMin;\r
7842         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7843       }\r
7844       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7845     }\r
7846     return 0;\r
7847   case WM_RBUTTONUP:\r
7848     if (GetKeyState(VK_SHIFT) & ~1) {\r
7849       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7850         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7851     } else {\r
7852       POINT pt;\r
7853       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7854       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7855       if (sel.cpMin == sel.cpMax) {\r
7856         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7857         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7858       }\r
7859       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7860         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7861       }\r
7862       pt.x = LOWORD(lParam);\r
7863       pt.y = HIWORD(lParam);\r
7864       MenuPopup(hwnd, pt, hmenu, -1);\r
7865     }\r
7866     return 0;\r
7867   case WM_COMMAND:\r
7868     switch (LOWORD(wParam)) {\r
7869     case IDM_QuickPaste:\r
7870       {\r
7871         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7872         if (sel.cpMin == sel.cpMax) {\r
7873           MessageBeep(MB_ICONEXCLAMATION);\r
7874           return 0;\r
7875         }\r
7876         SendMessage(hwnd, WM_COPY, 0, 0);\r
7877         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7878         SendMessage(hInput, WM_PASTE, 0, 0);\r
7879         SetFocus(hInput);\r
7880         return 0;\r
7881       }\r
7882     case IDM_Cut:\r
7883       SendMessage(hwnd, WM_CUT, 0, 0);\r
7884       return 0;\r
7885     case IDM_Paste:\r
7886       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7887       return 0;\r
7888     case IDM_Copy:\r
7889       SendMessage(hwnd, WM_COPY, 0, 0);\r
7890       return 0;\r
7891     default:\r
7892       {\r
7893         int i = LOWORD(wParam) - IDM_CommandX;\r
7894         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7895             icsTextMenuEntry[i].command != NULL) {\r
7896           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7897                    icsTextMenuEntry[i].getname,\r
7898                    icsTextMenuEntry[i].immediate);\r
7899           return 0;\r
7900         }\r
7901       }\r
7902       break;\r
7903     }\r
7904     break;\r
7905   }\r
7906   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7907 }\r
7908 \r
7909 WNDPROC consoleInputWindowProc;\r
7910 \r
7911 LRESULT CALLBACK\r
7912 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7913 {\r
7914   char buf[MSG_SIZ];\r
7915   char *p;\r
7916   static BOOL sendNextChar = FALSE;\r
7917   static BOOL quoteNextChar = FALSE;\r
7918   InputSource *is = consoleInputSource;\r
7919   CHARFORMAT cf;\r
7920   CHARRANGE sel;\r
7921 \r
7922   switch (message) {\r
7923   case WM_CHAR:\r
7924     if (!appData.localLineEditing || sendNextChar) {\r
7925       is->buf[0] = (CHAR) wParam;\r
7926       is->count = 1;\r
7927       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7928       sendNextChar = FALSE;\r
7929       return 0;\r
7930     }\r
7931     if (quoteNextChar) {\r
7932       buf[0] = (char) wParam;\r
7933       buf[1] = NULLCHAR;\r
7934       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7935       quoteNextChar = FALSE;\r
7936       return 0;\r
7937     }\r
7938     switch (wParam) {\r
7939     case '\r':   /* Enter key */\r
7940       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7941       if (consoleEcho) SaveInHistory(is->buf);\r
7942       is->buf[is->count++] = '\n';\r
7943       is->buf[is->count] = NULLCHAR;\r
7944       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7945       if (consoleEcho) {\r
7946         ConsoleOutput(is->buf, is->count, TRUE);\r
7947       } else if (appData.localLineEditing) {\r
7948         ConsoleOutput("\n", 1, TRUE);\r
7949       }\r
7950       /* fall thru */\r
7951     case '\033': /* Escape key */\r
7952       SetWindowText(hwnd, "");\r
7953       cf.cbSize = sizeof(CHARFORMAT);\r
7954       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7955       if (consoleEcho) {\r
7956         cf.crTextColor = textAttribs[ColorNormal].color;\r
7957       } else {\r
7958         cf.crTextColor = COLOR_ECHOOFF;\r
7959       }\r
7960       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7961       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7962       return 0;\r
7963     case '\t':   /* Tab key */\r
7964       if (GetKeyState(VK_SHIFT) < 0) {\r
7965         /* shifted */\r
7966         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7967       } else {\r
7968         /* unshifted */\r
7969         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7970         if (buttonDesc[0].hwnd) {\r
7971           SetFocus(buttonDesc[0].hwnd);\r
7972         } else {\r
7973           SetFocus(hwndMain);\r
7974         }\r
7975       }\r
7976       return 0;\r
7977     case '\023': /* Ctrl+S */\r
7978       sendNextChar = TRUE;\r
7979       return 0;\r
7980     case '\021': /* Ctrl+Q */\r
7981       quoteNextChar = TRUE;\r
7982       return 0;\r
7983     default:\r
7984       break;\r
7985     }\r
7986     break;\r
7987   case WM_KEYDOWN:\r
7988     switch (wParam) {\r
7989     case VK_UP:\r
7990       GetWindowText(hwnd, buf, MSG_SIZ);\r
7991       p = PrevInHistory(buf);\r
7992       if (p != NULL) {\r
7993         SetWindowText(hwnd, p);\r
7994         sel.cpMin = 999999;\r
7995         sel.cpMax = 999999;\r
7996         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7997         return 0;\r
7998       }\r
7999       break;\r
8000     case VK_DOWN:\r
8001       p = NextInHistory();\r
8002       if (p != NULL) {\r
8003         SetWindowText(hwnd, p);\r
8004         sel.cpMin = 999999;\r
8005         sel.cpMax = 999999;\r
8006         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8007         return 0;\r
8008       }\r
8009       break;\r
8010     case VK_HOME:\r
8011     case VK_END:\r
8012       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8013       /* fall thru */\r
8014     case VK_PRIOR:\r
8015     case VK_NEXT:\r
8016       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8017       return 0;\r
8018     }\r
8019     break;\r
8020   case WM_MBUTTONDOWN:\r
8021     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8022       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8023     break;\r
8024   case WM_RBUTTONUP:\r
8025     if (GetKeyState(VK_SHIFT) & ~1) {\r
8026       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8027         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8028     } else {\r
8029       POINT pt;\r
8030       HMENU hmenu;\r
8031       hmenu = LoadMenu(hInst, "InputMenu");\r
8032       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8033       if (sel.cpMin == sel.cpMax) {\r
8034         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8035         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8036       }\r
8037       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8038         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8039       }\r
8040       pt.x = LOWORD(lParam);\r
8041       pt.y = HIWORD(lParam);\r
8042       MenuPopup(hwnd, pt, hmenu, -1);\r
8043     }\r
8044     return 0;\r
8045   case WM_COMMAND:\r
8046     switch (LOWORD(wParam)) { \r
8047     case IDM_Undo:\r
8048       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8049       return 0;\r
8050     case IDM_SelectAll:\r
8051       sel.cpMin = 0;\r
8052       sel.cpMax = -1; /*999999?*/\r
8053       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8054       return 0;\r
8055     case IDM_Cut:\r
8056       SendMessage(hwnd, WM_CUT, 0, 0);\r
8057       return 0;\r
8058     case IDM_Paste:\r
8059       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8060       return 0;\r
8061     case IDM_Copy:\r
8062       SendMessage(hwnd, WM_COPY, 0, 0);\r
8063       return 0;\r
8064     }\r
8065     break;\r
8066   }\r
8067   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8068 }\r
8069 \r
8070 #define CO_MAX  100000\r
8071 #define CO_TRIM   1000\r
8072 \r
8073 LRESULT CALLBACK\r
8074 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8075 {\r
8076   static SnapData sd;\r
8077   static HWND hText, hInput /*, hFocus*/;\r
8078 //  InputSource *is = consoleInputSource;\r
8079   RECT rect;\r
8080   static int sizeX, sizeY;\r
8081   int newSizeX, newSizeY;\r
8082   MINMAXINFO *mmi;\r
8083 \r
8084   switch (message) {\r
8085   case WM_INITDIALOG: /* message: initialize dialog box */\r
8086     hwndConsole = hDlg;\r
8087     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8088     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8089     SetFocus(hInput);\r
8090     consoleTextWindowProc = (WNDPROC)\r
8091       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8092     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8093     consoleInputWindowProc = (WNDPROC)\r
8094       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8095     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8096     Colorize(ColorNormal, TRUE);\r
8097     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8098     ChangedConsoleFont();\r
8099     GetClientRect(hDlg, &rect);\r
8100     sizeX = rect.right;\r
8101     sizeY = rect.bottom;\r
8102     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8103         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8104       WINDOWPLACEMENT wp;\r
8105       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8106       wp.length = sizeof(WINDOWPLACEMENT);\r
8107       wp.flags = 0;\r
8108       wp.showCmd = SW_SHOW;\r
8109       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8110       wp.rcNormalPosition.left = wpConsole.x;\r
8111       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8112       wp.rcNormalPosition.top = wpConsole.y;\r
8113       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8114       SetWindowPlacement(hDlg, &wp);\r
8115     }\r
8116 #if 0 \r
8117    // [HGM] Chessknight's change 2004-07-13\r
8118    else { /* Determine Defaults */\r
8119        WINDOWPLACEMENT wp;\r
8120        wpConsole.x = winWidth + 1;\r
8121        wpConsole.y = boardY;\r
8122        wpConsoleW = screenWidth -  winWidth;\r
8123        wpConsoleH = winHeight;\r
8124        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8125        wp.length = sizeof(WINDOWPLACEMENT);\r
8126        wp.flags = 0;\r
8127        wp.showCmd = SW_SHOW;\r
8128        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8129        wp.rcNormalPosition.left = wpConsole.x;\r
8130        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8131        wp.rcNormalPosition.top = wpConsole.y;\r
8132        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8133        SetWindowPlacement(hDlg, &wp);\r
8134     }\r
8135 #endif\r
8136     return FALSE;\r
8137 \r
8138   case WM_SETFOCUS:\r
8139     SetFocus(hInput);\r
8140     return 0;\r
8141 \r
8142   case WM_CLOSE:\r
8143     ExitEvent(0);\r
8144     /* not reached */\r
8145     break;\r
8146 \r
8147   case WM_SIZE:\r
8148     if (IsIconic(hDlg)) break;\r
8149     newSizeX = LOWORD(lParam);\r
8150     newSizeY = HIWORD(lParam);\r
8151     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8152       RECT rectText, rectInput;\r
8153       POINT pt;\r
8154       int newTextHeight, newTextWidth;\r
8155       GetWindowRect(hText, &rectText);\r
8156       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8157       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8158       if (newTextHeight < 0) {\r
8159         newSizeY += -newTextHeight;\r
8160         newTextHeight = 0;\r
8161       }\r
8162       SetWindowPos(hText, NULL, 0, 0,\r
8163         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8164       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8165       pt.x = rectInput.left;\r
8166       pt.y = rectInput.top + newSizeY - sizeY;\r
8167       ScreenToClient(hDlg, &pt);\r
8168       SetWindowPos(hInput, NULL, \r
8169         pt.x, pt.y, /* needs client coords */   \r
8170         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8171         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8172     }\r
8173     sizeX = newSizeX;\r
8174     sizeY = newSizeY;\r
8175     break;\r
8176 \r
8177   case WM_GETMINMAXINFO:\r
8178     /* Prevent resizing window too small */\r
8179     mmi = (MINMAXINFO *) lParam;\r
8180     mmi->ptMinTrackSize.x = 100;\r
8181     mmi->ptMinTrackSize.y = 100;\r
8182     break;\r
8183 \r
8184   /* [AS] Snapping */\r
8185   case WM_ENTERSIZEMOVE:\r
8186     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8187 \r
8188   case WM_SIZING:\r
8189     return OnSizing( &sd, hDlg, wParam, lParam );\r
8190 \r
8191   case WM_MOVING:\r
8192     return OnMoving( &sd, hDlg, wParam, lParam );\r
8193 \r
8194   case WM_EXITSIZEMOVE:\r
8195     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8196   }\r
8197 \r
8198   return DefWindowProc(hDlg, message, wParam, lParam);\r
8199 }\r
8200 \r
8201 \r
8202 VOID\r
8203 ConsoleCreate()\r
8204 {\r
8205   HWND hCons;\r
8206   if (hwndConsole) return;\r
8207   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8208   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8209 }\r
8210 \r
8211 \r
8212 VOID\r
8213 ConsoleOutput(char* data, int length, int forceVisible)\r
8214 {\r
8215   HWND hText;\r
8216   int trim, exlen;\r
8217   char *p, *q;\r
8218   char buf[CO_MAX+1];\r
8219   POINT pEnd;\r
8220   RECT rect;\r
8221   static int delayLF = 0;\r
8222   CHARRANGE savesel, sel;\r
8223 \r
8224   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8225   p = data;\r
8226   q = buf;\r
8227   if (delayLF) {\r
8228     *q++ = '\r';\r
8229     *q++ = '\n';\r
8230     delayLF = 0;\r
8231   }\r
8232   while (length--) {\r
8233     if (*p == '\n') {\r
8234       if (*++p) {\r
8235         *q++ = '\r';\r
8236         *q++ = '\n';\r
8237       } else {\r
8238         delayLF = 1;\r
8239       }\r
8240     } else if (*p == '\007') {\r
8241        MyPlaySound(&sounds[(int)SoundBell]);\r
8242        p++;\r
8243     } else {\r
8244       *q++ = *p++;\r
8245     }\r
8246   }\r
8247   *q = NULLCHAR;\r
8248   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8249   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8250   /* Save current selection */\r
8251   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8252   exlen = GetWindowTextLength(hText);\r
8253   /* Find out whether current end of text is visible */\r
8254   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8255   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8256   /* Trim existing text if it's too long */\r
8257   if (exlen + (q - buf) > CO_MAX) {\r
8258     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8259     sel.cpMin = 0;\r
8260     sel.cpMax = trim;\r
8261     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8262     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8263     exlen -= trim;\r
8264     savesel.cpMin -= trim;\r
8265     savesel.cpMax -= trim;\r
8266     if (exlen < 0) exlen = 0;\r
8267     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8268     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8269   }\r
8270   /* Append the new text */\r
8271   sel.cpMin = exlen;\r
8272   sel.cpMax = exlen;\r
8273   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8274   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8275   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8276   if (forceVisible || exlen == 0 ||\r
8277       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8278        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8279     /* Scroll to make new end of text visible if old end of text\r
8280        was visible or new text is an echo of user typein */\r
8281     sel.cpMin = 9999999;\r
8282     sel.cpMax = 9999999;\r
8283     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8284     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8285     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8286     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8287   }\r
8288   if (savesel.cpMax == exlen || forceVisible) {\r
8289     /* Move insert point to new end of text if it was at the old\r
8290        end of text or if the new text is an echo of user typein */\r
8291     sel.cpMin = 9999999;\r
8292     sel.cpMax = 9999999;\r
8293     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8294   } else {\r
8295     /* Restore previous selection */\r
8296     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8297   }\r
8298   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8299 }\r
8300 \r
8301 /*---------*/\r
8302 \r
8303 \r
8304 void\r
8305 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8306 {\r
8307   char buf[100];\r
8308   char *str;\r
8309   COLORREF oldFg, oldBg;\r
8310   HFONT oldFont;\r
8311   RECT rect;\r
8312 \r
8313   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8314 \r
8315   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8316   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8317   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8318 \r
8319   rect.left = x;\r
8320   rect.right = x + squareSize;\r
8321   rect.top  = y;\r
8322   rect.bottom = y + squareSize;\r
8323   str = buf;\r
8324 \r
8325   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8326                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8327              y, ETO_CLIPPED|ETO_OPAQUE,\r
8328              &rect, str, strlen(str), NULL);\r
8329 \r
8330   (void) SetTextColor(hdc, oldFg);\r
8331   (void) SetBkColor(hdc, oldBg);\r
8332   (void) SelectObject(hdc, oldFont);\r
8333 }\r
8334 \r
8335 void\r
8336 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8337               RECT *rect, char *color, char *flagFell)\r
8338 {\r
8339   char buf[100];\r
8340   char *str;\r
8341   COLORREF oldFg, oldBg;\r
8342   HFONT oldFont;\r
8343 \r
8344   if (appData.clockMode) {\r
8345     if (tinyLayout)\r
8346       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8347     else\r
8348       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8349     str = buf;\r
8350   } else {\r
8351     str = color;\r
8352   }\r
8353 \r
8354   if (highlight) {\r
8355     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8356     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8357   } else {\r
8358     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8359     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8360   }\r
8361   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8362 \r
8363   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8364              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8365              rect, str, strlen(str), NULL);\r
8366   if(logoHeight > 0 && appData.clockMode) {\r
8367       RECT r;\r
8368       sprintf(buf, "%s %s", TimeString(timeRemaining), flagFell);\r
8369       r.top = rect->top + logoHeight/2;\r
8370       r.left = rect->left;\r
8371       r.right = rect->right;\r
8372       r.bottom = rect->bottom;\r
8373       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8374                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8375                  &r, str, strlen(str), NULL);\r
8376   }\r
8377   (void) SetTextColor(hdc, oldFg);\r
8378   (void) SetBkColor(hdc, oldBg);\r
8379   (void) SelectObject(hdc, oldFont);\r
8380 }\r
8381 \r
8382 \r
8383 int\r
8384 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8385            OVERLAPPED *ovl)\r
8386 {\r
8387   int ok, err;\r
8388 \r
8389   /* [AS]  */\r
8390   if( count <= 0 ) {\r
8391     if (appData.debugMode) {\r
8392       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8393     }\r
8394 \r
8395     return ERROR_INVALID_USER_BUFFER;\r
8396   }\r
8397 \r
8398   ResetEvent(ovl->hEvent);\r
8399   ovl->Offset = ovl->OffsetHigh = 0;\r
8400   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8401   if (ok) {\r
8402     err = NO_ERROR;\r
8403   } else {\r
8404     err = GetLastError();\r
8405     if (err == ERROR_IO_PENDING) {\r
8406       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8407       if (ok)\r
8408         err = NO_ERROR;\r
8409       else\r
8410         err = GetLastError();\r
8411     }\r
8412   }\r
8413   return err;\r
8414 }\r
8415 \r
8416 int\r
8417 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8418             OVERLAPPED *ovl)\r
8419 {\r
8420   int ok, err;\r
8421 \r
8422   ResetEvent(ovl->hEvent);\r
8423   ovl->Offset = ovl->OffsetHigh = 0;\r
8424   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8425   if (ok) {\r
8426     err = NO_ERROR;\r
8427   } else {\r
8428     err = GetLastError();\r
8429     if (err == ERROR_IO_PENDING) {\r
8430       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8431       if (ok)\r
8432         err = NO_ERROR;\r
8433       else\r
8434         err = GetLastError();\r
8435     }\r
8436   }\r
8437   return err;\r
8438 }\r
8439 \r
8440 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8441 void CheckForInputBufferFull( InputSource * is )\r
8442 {\r
8443     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8444         /* Look for end of line */\r
8445         char * p = is->buf;\r
8446         \r
8447         while( p < is->next && *p != '\n' ) {\r
8448             p++;\r
8449         }\r
8450 \r
8451         if( p >= is->next ) {\r
8452             if (appData.debugMode) {\r
8453                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8454             }\r
8455 \r
8456             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8457             is->count = (DWORD) -1;\r
8458             is->next = is->buf;\r
8459         }\r
8460     }\r
8461 }\r
8462 \r
8463 DWORD\r
8464 InputThread(LPVOID arg)\r
8465 {\r
8466   InputSource *is;\r
8467   OVERLAPPED ovl;\r
8468 \r
8469   is = (InputSource *) arg;\r
8470   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8471   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8472   while (is->hThread != NULL) {\r
8473     is->error = DoReadFile(is->hFile, is->next,\r
8474                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8475                            &is->count, &ovl);\r
8476     if (is->error == NO_ERROR) {\r
8477       is->next += is->count;\r
8478     } else {\r
8479       if (is->error == ERROR_BROKEN_PIPE) {\r
8480         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8481         is->count = 0;\r
8482       } else {\r
8483         is->count = (DWORD) -1;\r
8484         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8485         break; \r
8486       }\r
8487     }\r
8488 \r
8489     CheckForInputBufferFull( is );\r
8490 \r
8491     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8492 \r
8493     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8494 \r
8495     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8496   }\r
8497 \r
8498   CloseHandle(ovl.hEvent);\r
8499   CloseHandle(is->hFile);\r
8500 \r
8501   if (appData.debugMode) {\r
8502     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8503   }\r
8504 \r
8505   return 0;\r
8506 }\r
8507 \r
8508 \r
8509 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8510 DWORD\r
8511 NonOvlInputThread(LPVOID arg)\r
8512 {\r
8513   InputSource *is;\r
8514   char *p, *q;\r
8515   int i;\r
8516   char prev;\r
8517 \r
8518   is = (InputSource *) arg;\r
8519   while (is->hThread != NULL) {\r
8520     is->error = ReadFile(is->hFile, is->next,\r
8521                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8522                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8523     if (is->error == NO_ERROR) {\r
8524       /* Change CRLF to LF */\r
8525       if (is->next > is->buf) {\r
8526         p = is->next - 1;\r
8527         i = is->count + 1;\r
8528       } else {\r
8529         p = is->next;\r
8530         i = is->count;\r
8531       }\r
8532       q = p;\r
8533       prev = NULLCHAR;\r
8534       while (i > 0) {\r
8535         if (prev == '\r' && *p == '\n') {\r
8536           *(q-1) = '\n';\r
8537           is->count--;\r
8538         } else { \r
8539           *q++ = *p;\r
8540         }\r
8541         prev = *p++;\r
8542         i--;\r
8543       }\r
8544       *q = NULLCHAR;\r
8545       is->next = q;\r
8546     } else {\r
8547       if (is->error == ERROR_BROKEN_PIPE) {\r
8548         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8549         is->count = 0; \r
8550       } else {\r
8551         is->count = (DWORD) -1;\r
8552       }\r
8553     }\r
8554 \r
8555     CheckForInputBufferFull( is );\r
8556 \r
8557     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8558 \r
8559     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8560 \r
8561     if (is->count < 0) break;  /* Quit on error */\r
8562   }\r
8563   CloseHandle(is->hFile);\r
8564   return 0;\r
8565 }\r
8566 \r
8567 DWORD\r
8568 SocketInputThread(LPVOID arg)\r
8569 {\r
8570   InputSource *is;\r
8571 \r
8572   is = (InputSource *) arg;\r
8573   while (is->hThread != NULL) {\r
8574     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8575     if ((int)is->count == SOCKET_ERROR) {\r
8576       is->count = (DWORD) -1;\r
8577       is->error = WSAGetLastError();\r
8578     } else {\r
8579       is->error = NO_ERROR;\r
8580       is->next += is->count;\r
8581       if (is->count == 0 && is->second == is) {\r
8582         /* End of file on stderr; quit with no message */\r
8583         break;\r
8584       }\r
8585     }\r
8586     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8587 \r
8588     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8589 \r
8590     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8591   }\r
8592   return 0;\r
8593 }\r
8594 \r
8595 VOID\r
8596 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8597 {\r
8598   InputSource *is;\r
8599 \r
8600   is = (InputSource *) lParam;\r
8601   if (is->lineByLine) {\r
8602     /* Feed in lines one by one */\r
8603     char *p = is->buf;\r
8604     char *q = p;\r
8605     while (q < is->next) {\r
8606       if (*q++ == '\n') {\r
8607         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8608         p = q;\r
8609       }\r
8610     }\r
8611     \r
8612     /* Move any partial line to the start of the buffer */\r
8613     q = is->buf;\r
8614     while (p < is->next) {\r
8615       *q++ = *p++;\r
8616     }\r
8617     is->next = q;\r
8618 \r
8619     if (is->error != NO_ERROR || is->count == 0) {\r
8620       /* Notify backend of the error.  Note: If there was a partial\r
8621          line at the end, it is not flushed through. */\r
8622       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8623     }\r
8624   } else {\r
8625     /* Feed in the whole chunk of input at once */\r
8626     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8627     is->next = is->buf;\r
8628   }\r
8629 }\r
8630 \r
8631 /*---------------------------------------------------------------------------*\\r
8632  *\r
8633  *  Menu enables. Used when setting various modes.\r
8634  *\r
8635 \*---------------------------------------------------------------------------*/\r
8636 \r
8637 typedef struct {\r
8638   int item;\r
8639   int flags;\r
8640 } Enables;\r
8641 \r
8642 VOID\r
8643 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8644 {\r
8645   while (enab->item > 0) {\r
8646     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8647     enab++;\r
8648   }\r
8649 }\r
8650 \r
8651 Enables gnuEnables[] = {\r
8652   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8653   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8654   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8656   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8657   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8658   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8659   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8660   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8661   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8662   { -1, -1 }\r
8663 };\r
8664 \r
8665 Enables icsEnables[] = {\r
8666   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8667   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8668   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8669   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8670   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8671   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8672   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8673   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8674   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8675   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8676   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8677   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8678   { -1, -1 }\r
8679 };\r
8680 \r
8681 #ifdef ZIPPY\r
8682 Enables zippyEnables[] = {\r
8683   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8684   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8685   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8686   { -1, -1 }\r
8687 };\r
8688 #endif\r
8689 \r
8690 Enables ncpEnables[] = {\r
8691   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8692   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8693   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8694   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8695   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8696   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8697   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8698   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8699   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8700   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8701   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8702   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8703   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8704   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8705   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8706   { -1, -1 }\r
8707 };\r
8708 \r
8709 Enables trainingOnEnables[] = {\r
8710   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8711   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8712   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8713   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8714   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8715   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8716   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8717   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8718   { -1, -1 }\r
8719 };\r
8720 \r
8721 Enables trainingOffEnables[] = {\r
8722   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8723   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8724   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8725   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8726   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8727   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8728   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8729   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8730   { -1, -1 }\r
8731 };\r
8732 \r
8733 /* These modify either ncpEnables or gnuEnables */\r
8734 Enables cmailEnables[] = {\r
8735   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8736   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8737   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8738   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8739   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8740   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8741   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8742   { -1, -1 }\r
8743 };\r
8744 \r
8745 Enables machineThinkingEnables[] = {\r
8746   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8747   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8748   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8749   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8750   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8751   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8752   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8753   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8754   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8755   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8756   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8757   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8758   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8759   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8760   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8761   { -1, -1 }\r
8762 };\r
8763 \r
8764 Enables userThinkingEnables[] = {\r
8765   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8766   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8767   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8768   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8769   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8770   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8771   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8772   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8773   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8774   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8775   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8776   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8777   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8778   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8779   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8780   { -1, -1 }\r
8781 };\r
8782 \r
8783 /*---------------------------------------------------------------------------*\\r
8784  *\r
8785  *  Front-end interface functions exported by XBoard.\r
8786  *  Functions appear in same order as prototypes in frontend.h.\r
8787  * \r
8788 \*---------------------------------------------------------------------------*/\r
8789 VOID\r
8790 ModeHighlight()\r
8791 {\r
8792   static UINT prevChecked = 0;\r
8793   static int prevPausing = 0;\r
8794   UINT nowChecked;\r
8795 \r
8796   if (pausing != prevPausing) {\r
8797     prevPausing = pausing;\r
8798     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8799                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8800     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8801   }\r
8802 \r
8803   switch (gameMode) {\r
8804   case BeginningOfGame:\r
8805     if (appData.icsActive)\r
8806       nowChecked = IDM_IcsClient;\r
8807     else if (appData.noChessProgram)\r
8808       nowChecked = IDM_EditGame;\r
8809     else\r
8810       nowChecked = IDM_MachineBlack;\r
8811     break;\r
8812   case MachinePlaysBlack:\r
8813     nowChecked = IDM_MachineBlack;\r
8814     break;\r
8815   case MachinePlaysWhite:\r
8816     nowChecked = IDM_MachineWhite;\r
8817     break;\r
8818   case TwoMachinesPlay:\r
8819     nowChecked = IDM_TwoMachines;\r
8820     break;\r
8821   case AnalyzeMode:\r
8822     nowChecked = IDM_AnalysisMode;\r
8823     break;\r
8824   case AnalyzeFile:\r
8825     nowChecked = IDM_AnalyzeFile;\r
8826     break;\r
8827   case EditGame:\r
8828     nowChecked = IDM_EditGame;\r
8829     break;\r
8830   case PlayFromGameFile:\r
8831     nowChecked = IDM_LoadGame;\r
8832     break;\r
8833   case EditPosition:\r
8834     nowChecked = IDM_EditPosition;\r
8835     break;\r
8836   case Training:\r
8837     nowChecked = IDM_Training;\r
8838     break;\r
8839   case IcsPlayingWhite:\r
8840   case IcsPlayingBlack:\r
8841   case IcsObserving:\r
8842   case IcsIdle:\r
8843     nowChecked = IDM_IcsClient;\r
8844     break;\r
8845   default:\r
8846   case EndOfGame:\r
8847     nowChecked = 0;\r
8848     break;\r
8849   }\r
8850   if (prevChecked != 0)\r
8851     (void) CheckMenuItem(GetMenu(hwndMain),\r
8852                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8853   if (nowChecked != 0)\r
8854     (void) CheckMenuItem(GetMenu(hwndMain),\r
8855                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8856 \r
8857   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8858     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8859                           MF_BYCOMMAND|MF_ENABLED);\r
8860   } else {\r
8861     (void) EnableMenuItem(GetMenu(hwndMain), \r
8862                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8863   }\r
8864 \r
8865   prevChecked = nowChecked;\r
8866 \r
8867   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8868   if (appData.icsActive) {\r
8869        if (appData.icsEngineAnalyze) {\r
8870                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8871                        MF_BYCOMMAND|MF_CHECKED);\r
8872        } else {\r
8873                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8874                        MF_BYCOMMAND|MF_UNCHECKED);\r
8875        }\r
8876   }\r
8877 }\r
8878 \r
8879 VOID\r
8880 SetICSMode()\r
8881 {\r
8882   HMENU hmenu = GetMenu(hwndMain);\r
8883   SetMenuEnables(hmenu, icsEnables);\r
8884   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8885     MF_BYPOSITION|MF_ENABLED);\r
8886 #ifdef ZIPPY\r
8887   if (appData.zippyPlay) {\r
8888     SetMenuEnables(hmenu, zippyEnables);\r
8889     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8890          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8891           MF_BYCOMMAND|MF_ENABLED);\r
8892   }\r
8893 #endif\r
8894 }\r
8895 \r
8896 VOID\r
8897 SetGNUMode()\r
8898 {\r
8899   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8900 }\r
8901 \r
8902 VOID\r
8903 SetNCPMode()\r
8904 {\r
8905   HMENU hmenu = GetMenu(hwndMain);\r
8906   SetMenuEnables(hmenu, ncpEnables);\r
8907   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8908     MF_BYPOSITION|MF_GRAYED);\r
8909     DrawMenuBar(hwndMain);\r
8910 }\r
8911 \r
8912 VOID\r
8913 SetCmailMode()\r
8914 {\r
8915   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8916 }\r
8917 \r
8918 VOID \r
8919 SetTrainingModeOn()\r
8920 {\r
8921   int i;\r
8922   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8923   for (i = 0; i < N_BUTTONS; i++) {\r
8924     if (buttonDesc[i].hwnd != NULL)\r
8925       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8926   }\r
8927   CommentPopDown();\r
8928 }\r
8929 \r
8930 VOID SetTrainingModeOff()\r
8931 {\r
8932   int i;\r
8933   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8934   for (i = 0; i < N_BUTTONS; i++) {\r
8935     if (buttonDesc[i].hwnd != NULL)\r
8936       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8937   }\r
8938 }\r
8939 \r
8940 \r
8941 VOID\r
8942 SetUserThinkingEnables()\r
8943 {\r
8944   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8945 }\r
8946 \r
8947 VOID\r
8948 SetMachineThinkingEnables()\r
8949 {\r
8950   HMENU hMenu = GetMenu(hwndMain);\r
8951   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8952 \r
8953   SetMenuEnables(hMenu, machineThinkingEnables);\r
8954 \r
8955   if (gameMode == MachinePlaysBlack) {\r
8956     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8957   } else if (gameMode == MachinePlaysWhite) {\r
8958     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8959   } else if (gameMode == TwoMachinesPlay) {\r
8960     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8961   }\r
8962 }\r
8963 \r
8964 \r
8965 VOID\r
8966 DisplayTitle(char *str)\r
8967 {\r
8968   char title[MSG_SIZ], *host;\r
8969   if (str[0] != NULLCHAR) {\r
8970     strcpy(title, str);\r
8971   } else if (appData.icsActive) {\r
8972     if (appData.icsCommPort[0] != NULLCHAR)\r
8973       host = "ICS";\r
8974     else \r
8975       host = appData.icsHost;\r
8976     sprintf(title, "%s: %s", szTitle, host);\r
8977   } else if (appData.noChessProgram) {\r
8978     strcpy(title, szTitle);\r
8979   } else {\r
8980     strcpy(title, szTitle);\r
8981     strcat(title, ": ");\r
8982     strcat(title, first.tidy);\r
8983   }\r
8984   SetWindowText(hwndMain, title);\r
8985 }\r
8986 \r
8987 \r
8988 VOID\r
8989 DisplayMessage(char *str1, char *str2)\r
8990 {\r
8991   HDC hdc;\r
8992   HFONT oldFont;\r
8993   int remain = MESSAGE_TEXT_MAX - 1;\r
8994   int len;\r
8995 \r
8996   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8997   messageText[0] = NULLCHAR;\r
8998   if (*str1) {\r
8999     len = strlen(str1);\r
9000     if (len > remain) len = remain;\r
9001     strncpy(messageText, str1, len);\r
9002     messageText[len] = NULLCHAR;\r
9003     remain -= len;\r
9004   }\r
9005   if (*str2 && remain >= 2) {\r
9006     if (*str1) {\r
9007       strcat(messageText, "  ");\r
9008       remain -= 2;\r
9009     }\r
9010     len = strlen(str2);\r
9011     if (len > remain) len = remain;\r
9012     strncat(messageText, str2, len);\r
9013   }\r
9014   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9015 \r
9016   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9017   hdc = GetDC(hwndMain);\r
9018   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9019   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9020              &messageRect, messageText, strlen(messageText), NULL);\r
9021   (void) SelectObject(hdc, oldFont);\r
9022   (void) ReleaseDC(hwndMain, hdc);\r
9023 }\r
9024 \r
9025 VOID\r
9026 DisplayError(char *str, int error)\r
9027 {\r
9028   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9029   int len;\r
9030 \r
9031   if (error == 0) {\r
9032     strcpy(buf, str);\r
9033   } else {\r
9034     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9035                         NULL, error, LANG_NEUTRAL,\r
9036                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9037     if (len > 0) {\r
9038       sprintf(buf, "%s:\n%s", str, buf2);\r
9039     } else {\r
9040       ErrorMap *em = errmap;\r
9041       while (em->err != 0 && em->err != error) em++;\r
9042       if (em->err != 0) {\r
9043         sprintf(buf, "%s:\n%s", str, em->msg);\r
9044       } else {\r
9045         sprintf(buf, "%s:\nError code %d", str, error);\r
9046       }\r
9047     }\r
9048   }\r
9049   \r
9050   ErrorPopUp("Error", buf);\r
9051 }\r
9052 \r
9053 \r
9054 VOID\r
9055 DisplayMoveError(char *str)\r
9056 {\r
9057   fromX = fromY = -1;\r
9058   ClearHighlights();\r
9059   DrawPosition(FALSE, NULL);\r
9060   if (appData.popupMoveErrors) {\r
9061     ErrorPopUp("Error", str);\r
9062   } else {\r
9063     DisplayMessage(str, "");\r
9064     moveErrorMessageUp = TRUE;\r
9065   }\r
9066 }\r
9067 \r
9068 VOID\r
9069 DisplayFatalError(char *str, int error, int exitStatus)\r
9070 {\r
9071   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9072   int len;\r
9073   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9074 \r
9075   if (error != 0) {\r
9076     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9077                         NULL, error, LANG_NEUTRAL,\r
9078                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9079     if (len > 0) {\r
9080       sprintf(buf, "%s:\n%s", str, buf2);\r
9081     } else {\r
9082       ErrorMap *em = errmap;\r
9083       while (em->err != 0 && em->err != error) em++;\r
9084       if (em->err != 0) {\r
9085         sprintf(buf, "%s:\n%s", str, em->msg);\r
9086       } else {\r
9087         sprintf(buf, "%s:\nError code %d", str, error);\r
9088       }\r
9089     }\r
9090     str = buf;\r
9091   }\r
9092   if (appData.debugMode) {\r
9093     fprintf(debugFP, "%s: %s\n", label, str);\r
9094   }\r
9095   if (appData.popupExitMessage) {\r
9096     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9097                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9098   }\r
9099   ExitEvent(exitStatus);\r
9100 }\r
9101 \r
9102 \r
9103 VOID\r
9104 DisplayInformation(char *str)\r
9105 {\r
9106   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9107 }\r
9108 \r
9109 \r
9110 VOID\r
9111 DisplayNote(char *str)\r
9112 {\r
9113   ErrorPopUp("Note", str);\r
9114 }\r
9115 \r
9116 \r
9117 typedef struct {\r
9118   char *title, *question, *replyPrefix;\r
9119   ProcRef pr;\r
9120 } QuestionParams;\r
9121 \r
9122 LRESULT CALLBACK\r
9123 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9124 {\r
9125   static QuestionParams *qp;\r
9126   char reply[MSG_SIZ];\r
9127   int len, err;\r
9128 \r
9129   switch (message) {\r
9130   case WM_INITDIALOG:\r
9131     qp = (QuestionParams *) lParam;\r
9132     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9133     SetWindowText(hDlg, qp->title);\r
9134     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9135     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9136     return FALSE;\r
9137 \r
9138   case WM_COMMAND:\r
9139     switch (LOWORD(wParam)) {\r
9140     case IDOK:\r
9141       strcpy(reply, qp->replyPrefix);\r
9142       if (*reply) strcat(reply, " ");\r
9143       len = strlen(reply);\r
9144       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9145       strcat(reply, "\n");\r
9146       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9147       EndDialog(hDlg, TRUE);\r
9148       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9149       return TRUE;\r
9150     case IDCANCEL:\r
9151       EndDialog(hDlg, FALSE);\r
9152       return TRUE;\r
9153     default:\r
9154       break;\r
9155     }\r
9156     break;\r
9157   }\r
9158   return FALSE;\r
9159 }\r
9160 \r
9161 VOID\r
9162 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9163 {\r
9164     QuestionParams qp;\r
9165     FARPROC lpProc;\r
9166     \r
9167     qp.title = title;\r
9168     qp.question = question;\r
9169     qp.replyPrefix = replyPrefix;\r
9170     qp.pr = pr;\r
9171     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9172     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9173       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9174     FreeProcInstance(lpProc);\r
9175 }\r
9176 \r
9177 /* [AS] Pick FRC position */\r
9178 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9179 {\r
9180     static int * lpIndexFRC;\r
9181     BOOL index_is_ok;\r
9182     char buf[16];\r
9183 \r
9184     switch( message )\r
9185     {\r
9186     case WM_INITDIALOG:\r
9187         lpIndexFRC = (int *) lParam;\r
9188 \r
9189         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9190 \r
9191         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9192         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9193         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9194         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9195 \r
9196         break;\r
9197 \r
9198     case WM_COMMAND:\r
9199         switch( LOWORD(wParam) ) {\r
9200         case IDOK:\r
9201             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9202             EndDialog( hDlg, 0 );\r
9203             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9204             return TRUE;\r
9205         case IDCANCEL:\r
9206             EndDialog( hDlg, 1 );   \r
9207             return TRUE;\r
9208         case IDC_NFG_Edit:\r
9209             if( HIWORD(wParam) == EN_CHANGE ) {\r
9210                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9211 \r
9212                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9213             }\r
9214             return TRUE;\r
9215         case IDC_NFG_Random:\r
9216             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9217             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9218             return TRUE;\r
9219         }\r
9220 \r
9221         break;\r
9222     }\r
9223 \r
9224     return FALSE;\r
9225 }\r
9226 \r
9227 int NewGameFRC()\r
9228 {\r
9229     int result;\r
9230     int index = appData.defaultFrcPosition;\r
9231     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9232 \r
9233     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9234 \r
9235     if( result == 0 ) {\r
9236         appData.defaultFrcPosition = index;\r
9237     }\r
9238 \r
9239     return result;\r
9240 }\r
9241 \r
9242 /* [AS] Game list options */\r
9243 typedef struct {\r
9244     char id;\r
9245     char * name;\r
9246 } GLT_Item;\r
9247 \r
9248 static GLT_Item GLT_ItemInfo[] = {\r
9249     { GLT_EVENT,      "Event" },\r
9250     { GLT_SITE,       "Site" },\r
9251     { GLT_DATE,       "Date" },\r
9252     { GLT_ROUND,      "Round" },\r
9253     { GLT_PLAYERS,    "Players" },\r
9254     { GLT_RESULT,     "Result" },\r
9255     { GLT_WHITE_ELO,  "White Rating" },\r
9256     { GLT_BLACK_ELO,  "Black Rating" },\r
9257     { GLT_TIME_CONTROL,"Time Control" },\r
9258     { GLT_VARIANT,    "Variant" },\r
9259     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9260     { 0, 0 }\r
9261 };\r
9262 \r
9263 const char * GLT_FindItem( char id )\r
9264 {\r
9265     const char * result = 0;\r
9266 \r
9267     GLT_Item * list = GLT_ItemInfo;\r
9268 \r
9269     while( list->id != 0 ) {\r
9270         if( list->id == id ) {\r
9271             result = list->name;\r
9272             break;\r
9273         }\r
9274 \r
9275         list++;\r
9276     }\r
9277 \r
9278     return result;\r
9279 }\r
9280 \r
9281 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9282 {\r
9283     const char * name = GLT_FindItem( id );\r
9284 \r
9285     if( name != 0 ) {\r
9286         if( index >= 0 ) {\r
9287             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9288         }\r
9289         else {\r
9290             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9291         }\r
9292     }\r
9293 }\r
9294 \r
9295 void GLT_TagsToList( HWND hDlg, char * tags )\r
9296 {\r
9297     char * pc = tags;\r
9298 \r
9299     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9300 \r
9301     while( *pc ) {\r
9302         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9303         pc++;\r
9304     }\r
9305 \r
9306     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9307 \r
9308     pc = GLT_ALL_TAGS;\r
9309 \r
9310     while( *pc ) {\r
9311         if( strchr( tags, *pc ) == 0 ) {\r
9312             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9313         }\r
9314         pc++;\r
9315     }\r
9316 \r
9317     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9318 }\r
9319 \r
9320 char GLT_ListItemToTag( HWND hDlg, int index )\r
9321 {\r
9322     char result = '\0';\r
9323     char name[128];\r
9324 \r
9325     GLT_Item * list = GLT_ItemInfo;\r
9326 \r
9327     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9328         while( list->id != 0 ) {\r
9329             if( strcmp( list->name, name ) == 0 ) {\r
9330                 result = list->id;\r
9331                 break;\r
9332             }\r
9333 \r
9334             list++;\r
9335         }\r
9336     }\r
9337 \r
9338     return result;\r
9339 }\r
9340 \r
9341 void GLT_MoveSelection( HWND hDlg, int delta )\r
9342 {\r
9343     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9344     int idx2 = idx1 + delta;\r
9345     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9346 \r
9347     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9348         char buf[128];\r
9349 \r
9350         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9351         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9352         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9353         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9354     }\r
9355 }\r
9356 \r
9357 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9358 {\r
9359     static char glt[64];\r
9360     static char * lpUserGLT;\r
9361 \r
9362     switch( message )\r
9363     {\r
9364     case WM_INITDIALOG:\r
9365         lpUserGLT = (char *) lParam;\r
9366         \r
9367         strcpy( glt, lpUserGLT );\r
9368 \r
9369         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9370 \r
9371         /* Initialize list */\r
9372         GLT_TagsToList( hDlg, glt );\r
9373 \r
9374         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9375 \r
9376         break;\r
9377 \r
9378     case WM_COMMAND:\r
9379         switch( LOWORD(wParam) ) {\r
9380         case IDOK:\r
9381             {\r
9382                 char * pc = lpUserGLT;\r
9383                 int idx = 0;\r
9384 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9385                 char id;\r
9386 \r
9387                 do {\r
9388                     id = GLT_ListItemToTag( hDlg, idx );\r
9389 \r
9390                     *pc++ = id;\r
9391                     idx++;\r
9392                 } while( id != '\0' );\r
9393             }\r
9394             EndDialog( hDlg, 0 );\r
9395             return TRUE;\r
9396         case IDCANCEL:\r
9397             EndDialog( hDlg, 1 );\r
9398             return TRUE;\r
9399 \r
9400         case IDC_GLT_Default:\r
9401             strcpy( glt, GLT_DEFAULT_TAGS );\r
9402             GLT_TagsToList( hDlg, glt );\r
9403             return TRUE;\r
9404 \r
9405         case IDC_GLT_Restore:\r
9406             strcpy( glt, lpUserGLT );\r
9407             GLT_TagsToList( hDlg, glt );\r
9408             return TRUE;\r
9409 \r
9410         case IDC_GLT_Up:\r
9411             GLT_MoveSelection( hDlg, -1 );\r
9412             return TRUE;\r
9413 \r
9414         case IDC_GLT_Down:\r
9415             GLT_MoveSelection( hDlg, +1 );\r
9416             return TRUE;\r
9417         }\r
9418 \r
9419         break;\r
9420     }\r
9421 \r
9422     return FALSE;\r
9423 }\r
9424 \r
9425 int GameListOptions()\r
9426 {\r
9427     char glt[64];\r
9428     int result;\r
9429     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9430 \r
9431     strcpy( glt, appData.gameListTags );\r
9432 \r
9433     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9434 \r
9435     if( result == 0 ) {\r
9436         /* [AS] Memory leak here! */\r
9437         appData.gameListTags = strdup( glt ); \r
9438     }\r
9439 \r
9440     return result;\r
9441 }\r
9442 \r
9443 \r
9444 VOID\r
9445 DisplayIcsInteractionTitle(char *str)\r
9446 {\r
9447   char consoleTitle[MSG_SIZ];\r
9448 \r
9449   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9450   SetWindowText(hwndConsole, consoleTitle);\r
9451 }\r
9452 \r
9453 void\r
9454 DrawPosition(int fullRedraw, Board board)\r
9455 {\r
9456   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9457 }\r
9458 \r
9459 \r
9460 VOID\r
9461 ResetFrontEnd()\r
9462 {\r
9463   fromX = fromY = -1;\r
9464   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9465     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9466     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9467     dragInfo.lastpos = dragInfo.pos;\r
9468     dragInfo.start.x = dragInfo.start.y = -1;\r
9469     dragInfo.from = dragInfo.start;\r
9470     ReleaseCapture();\r
9471     DrawPosition(TRUE, NULL);\r
9472   }\r
9473 }\r
9474 \r
9475 \r
9476 VOID\r
9477 CommentPopUp(char *title, char *str)\r
9478 {\r
9479   HWND hwnd = GetActiveWindow();\r
9480   EitherCommentPopUp(0, title, str, FALSE);\r
9481   SetActiveWindow(hwnd);\r
9482 }\r
9483 \r
9484 VOID\r
9485 CommentPopDown(void)\r
9486 {\r
9487   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9488   if (commentDialog) {\r
9489     ShowWindow(commentDialog, SW_HIDE);\r
9490   }\r
9491   commentDialogUp = FALSE;\r
9492 }\r
9493 \r
9494 VOID\r
9495 EditCommentPopUp(int index, char *title, char *str)\r
9496 {\r
9497   EitherCommentPopUp(index, title, str, TRUE);\r
9498 }\r
9499 \r
9500 \r
9501 VOID\r
9502 RingBell()\r
9503 {\r
9504   MyPlaySound(&sounds[(int)SoundMove]);\r
9505 }\r
9506 \r
9507 VOID PlayIcsWinSound()\r
9508 {\r
9509   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9510 }\r
9511 \r
9512 VOID PlayIcsLossSound()\r
9513 {\r
9514   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9515 }\r
9516 \r
9517 VOID PlayIcsDrawSound()\r
9518 {\r
9519   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9520 }\r
9521 \r
9522 VOID PlayIcsUnfinishedSound()\r
9523 {\r
9524   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9525 }\r
9526 \r
9527 VOID\r
9528 PlayAlarmSound()\r
9529 {\r
9530   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9531 }\r
9532 \r
9533 \r
9534 VOID\r
9535 EchoOn()\r
9536 {\r
9537   HWND hInput;\r
9538   consoleEcho = TRUE;\r
9539   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9540   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9541   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9542 }\r
9543 \r
9544 \r
9545 VOID\r
9546 EchoOff()\r
9547 {\r
9548   CHARFORMAT cf;\r
9549   HWND hInput;\r
9550   consoleEcho = FALSE;\r
9551   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9552   /* This works OK: set text and background both to the same color */\r
9553   cf = consoleCF;\r
9554   cf.crTextColor = COLOR_ECHOOFF;\r
9555   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9556   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9557 }\r
9558 \r
9559 /* No Raw()...? */\r
9560 \r
9561 void Colorize(ColorClass cc, int continuation)\r
9562 {\r
9563   currentColorClass = cc;\r
9564   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9565   consoleCF.crTextColor = textAttribs[cc].color;\r
9566   consoleCF.dwEffects = textAttribs[cc].effects;\r
9567   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9568 }\r
9569 \r
9570 char *\r
9571 UserName()\r
9572 {\r
9573   static char buf[MSG_SIZ];\r
9574   DWORD bufsiz = MSG_SIZ;\r
9575 \r
9576   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9577         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9578   }\r
9579   if (!GetUserName(buf, &bufsiz)) {\r
9580     /*DisplayError("Error getting user name", GetLastError());*/\r
9581     strcpy(buf, "User");\r
9582   }\r
9583   return buf;\r
9584 }\r
9585 \r
9586 char *\r
9587 HostName()\r
9588 {\r
9589   static char buf[MSG_SIZ];\r
9590   DWORD bufsiz = MSG_SIZ;\r
9591 \r
9592   if (!GetComputerName(buf, &bufsiz)) {\r
9593     /*DisplayError("Error getting host name", GetLastError());*/\r
9594     strcpy(buf, "Unknown");\r
9595   }\r
9596   return buf;\r
9597 }\r
9598 \r
9599 \r
9600 int\r
9601 ClockTimerRunning()\r
9602 {\r
9603   return clockTimerEvent != 0;\r
9604 }\r
9605 \r
9606 int\r
9607 StopClockTimer()\r
9608 {\r
9609   if (clockTimerEvent == 0) return FALSE;\r
9610   KillTimer(hwndMain, clockTimerEvent);\r
9611   clockTimerEvent = 0;\r
9612   return TRUE;\r
9613 }\r
9614 \r
9615 void\r
9616 StartClockTimer(long millisec)\r
9617 {\r
9618   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9619                              (UINT) millisec, NULL);\r
9620 }\r
9621 \r
9622 void\r
9623 DisplayWhiteClock(long timeRemaining, int highlight)\r
9624 {\r
9625   HDC hdc;\r
9626   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9627 \r
9628   if(appData.noGUI) return;\r
9629   hdc = GetDC(hwndMain);\r
9630   if (!IsIconic(hwndMain)) {\r
9631     DisplayAClock(hdc, timeRemaining, highlight, \r
9632                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9633   }\r
9634   if (highlight && iconCurrent == iconBlack) {\r
9635     iconCurrent = iconWhite;\r
9636     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9637     if (IsIconic(hwndMain)) {\r
9638       DrawIcon(hdc, 2, 2, iconCurrent);\r
9639     }\r
9640   }\r
9641   (void) ReleaseDC(hwndMain, hdc);\r
9642   if (hwndConsole)\r
9643     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9644 }\r
9645 \r
9646 void\r
9647 DisplayBlackClock(long timeRemaining, int highlight)\r
9648 {\r
9649   HDC hdc;\r
9650   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9651 \r
9652   if(appData.noGUI) return;\r
9653   hdc = GetDC(hwndMain);\r
9654   if (!IsIconic(hwndMain)) {\r
9655     DisplayAClock(hdc, timeRemaining, highlight, \r
9656                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9657   }\r
9658   if (highlight && iconCurrent == iconWhite) {\r
9659     iconCurrent = iconBlack;\r
9660     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9661     if (IsIconic(hwndMain)) {\r
9662       DrawIcon(hdc, 2, 2, iconCurrent);\r
9663     }\r
9664   }\r
9665   (void) ReleaseDC(hwndMain, hdc);\r
9666   if (hwndConsole)\r
9667     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9668 }\r
9669 \r
9670 \r
9671 int\r
9672 LoadGameTimerRunning()\r
9673 {\r
9674   return loadGameTimerEvent != 0;\r
9675 }\r
9676 \r
9677 int\r
9678 StopLoadGameTimer()\r
9679 {\r
9680   if (loadGameTimerEvent == 0) return FALSE;\r
9681   KillTimer(hwndMain, loadGameTimerEvent);\r
9682   loadGameTimerEvent = 0;\r
9683   return TRUE;\r
9684 }\r
9685 \r
9686 void\r
9687 StartLoadGameTimer(long millisec)\r
9688 {\r
9689   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9690                                 (UINT) millisec, NULL);\r
9691 }\r
9692 \r
9693 void\r
9694 AutoSaveGame()\r
9695 {\r
9696   char *defName;\r
9697   FILE *f;\r
9698   char fileTitle[MSG_SIZ];\r
9699 \r
9700   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9701   f = OpenFileDialog(hwndMain, "a", defName,\r
9702                      appData.oldSaveStyle ? "gam" : "pgn",\r
9703                      GAME_FILT, \r
9704                      "Save Game to File", NULL, fileTitle, NULL);\r
9705   if (f != NULL) {\r
9706     SaveGame(f, 0, "");\r
9707     fclose(f);\r
9708   }\r
9709 }\r
9710 \r
9711 \r
9712 void\r
9713 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9714 {\r
9715   if (delayedTimerEvent != 0) {\r
9716     if (appData.debugMode) {\r
9717       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9718     }\r
9719     KillTimer(hwndMain, delayedTimerEvent);\r
9720     delayedTimerEvent = 0;\r
9721     delayedTimerCallback();\r
9722   }\r
9723   delayedTimerCallback = cb;\r
9724   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9725                                 (UINT) millisec, NULL);\r
9726 }\r
9727 \r
9728 DelayedEventCallback\r
9729 GetDelayedEvent()\r
9730 {\r
9731   if (delayedTimerEvent) {\r
9732     return delayedTimerCallback;\r
9733   } else {\r
9734     return NULL;\r
9735   }\r
9736 }\r
9737 \r
9738 void\r
9739 CancelDelayedEvent()\r
9740 {\r
9741   if (delayedTimerEvent) {\r
9742     KillTimer(hwndMain, delayedTimerEvent);\r
9743     delayedTimerEvent = 0;\r
9744   }\r
9745 }\r
9746 \r
9747 DWORD GetWin32Priority(int nice)\r
9748 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9749 /*\r
9750 REALTIME_PRIORITY_CLASS     0x00000100\r
9751 HIGH_PRIORITY_CLASS         0x00000080\r
9752 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9753 NORMAL_PRIORITY_CLASS       0x00000020\r
9754 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9755 IDLE_PRIORITY_CLASS         0x00000040\r
9756 */\r
9757         if (nice < -15) return 0x00000080;\r
9758         if (nice < 0)   return 0x00008000;\r
9759         if (nice == 0)  return 0x00000020;\r
9760         if (nice < 15)  return 0x00004000;\r
9761         return 0x00000040;\r
9762 }\r
9763 \r
9764 /* Start a child process running the given program.\r
9765    The process's standard output can be read from "from", and its\r
9766    standard input can be written to "to".\r
9767    Exit with fatal error if anything goes wrong.\r
9768    Returns an opaque pointer that can be used to destroy the process\r
9769    later.\r
9770 */\r
9771 int\r
9772 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9773 {\r
9774 #define BUFSIZE 4096\r
9775 \r
9776   HANDLE hChildStdinRd, hChildStdinWr,\r
9777     hChildStdoutRd, hChildStdoutWr;\r
9778   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9779   SECURITY_ATTRIBUTES saAttr;\r
9780   BOOL fSuccess;\r
9781   PROCESS_INFORMATION piProcInfo;\r
9782   STARTUPINFO siStartInfo;\r
9783   ChildProc *cp;\r
9784   char buf[MSG_SIZ];\r
9785   DWORD err;\r
9786 \r
9787   if (appData.debugMode) {\r
9788     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9789   }\r
9790 \r
9791   *pr = NoProc;\r
9792 \r
9793   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9794   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9795   saAttr.bInheritHandle = TRUE;\r
9796   saAttr.lpSecurityDescriptor = NULL;\r
9797 \r
9798   /*\r
9799    * The steps for redirecting child's STDOUT:\r
9800    *     1. Create anonymous pipe to be STDOUT for child.\r
9801    *     2. Create a noninheritable duplicate of read handle,\r
9802    *         and close the inheritable read handle.\r
9803    */\r
9804 \r
9805   /* Create a pipe for the child's STDOUT. */\r
9806   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9807     return GetLastError();\r
9808   }\r
9809 \r
9810   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9811   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9812                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9813                              FALSE,     /* not inherited */\r
9814                              DUPLICATE_SAME_ACCESS);\r
9815   if (! fSuccess) {\r
9816     return GetLastError();\r
9817   }\r
9818   CloseHandle(hChildStdoutRd);\r
9819 \r
9820   /*\r
9821    * The steps for redirecting child's STDIN:\r
9822    *     1. Create anonymous pipe to be STDIN for child.\r
9823    *     2. Create a noninheritable duplicate of write handle,\r
9824    *         and close the inheritable write handle.\r
9825    */\r
9826 \r
9827   /* Create a pipe for the child's STDIN. */\r
9828   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9829     return GetLastError();\r
9830   }\r
9831 \r
9832   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9833   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9834                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9835                              FALSE,     /* not inherited */\r
9836                              DUPLICATE_SAME_ACCESS);\r
9837   if (! fSuccess) {\r
9838     return GetLastError();\r
9839   }\r
9840   CloseHandle(hChildStdinWr);\r
9841 \r
9842   /* Arrange to (1) look in dir for the child .exe file, and\r
9843    * (2) have dir be the child's working directory.  Interpret\r
9844    * dir relative to the directory WinBoard loaded from. */\r
9845   GetCurrentDirectory(MSG_SIZ, buf);\r
9846   SetCurrentDirectory(installDir);\r
9847   SetCurrentDirectory(dir);\r
9848 \r
9849   /* Now create the child process. */\r
9850 \r
9851   siStartInfo.cb = sizeof(STARTUPINFO);\r
9852   siStartInfo.lpReserved = NULL;\r
9853   siStartInfo.lpDesktop = NULL;\r
9854   siStartInfo.lpTitle = NULL;\r
9855   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9856   siStartInfo.cbReserved2 = 0;\r
9857   siStartInfo.lpReserved2 = NULL;\r
9858   siStartInfo.hStdInput = hChildStdinRd;\r
9859   siStartInfo.hStdOutput = hChildStdoutWr;\r
9860   siStartInfo.hStdError = hChildStdoutWr;\r
9861 \r
9862   fSuccess = CreateProcess(NULL,\r
9863                            cmdLine,        /* command line */\r
9864                            NULL,           /* process security attributes */\r
9865                            NULL,           /* primary thread security attrs */\r
9866                            TRUE,           /* handles are inherited */\r
9867                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9868                            NULL,           /* use parent's environment */\r
9869                            NULL,\r
9870                            &siStartInfo, /* STARTUPINFO pointer */\r
9871                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9872 \r
9873   err = GetLastError();\r
9874   SetCurrentDirectory(buf); /* return to prev directory */\r
9875   if (! fSuccess) {\r
9876     return err;\r
9877   }\r
9878 \r
9879   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9880     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9881     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9882   }\r
9883 \r
9884   /* Close the handles we don't need in the parent */\r
9885   CloseHandle(piProcInfo.hThread);\r
9886   CloseHandle(hChildStdinRd);\r
9887   CloseHandle(hChildStdoutWr);\r
9888 \r
9889   /* Prepare return value */\r
9890   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9891   cp->kind = CPReal;\r
9892   cp->hProcess = piProcInfo.hProcess;\r
9893   cp->pid = piProcInfo.dwProcessId;\r
9894   cp->hFrom = hChildStdoutRdDup;\r
9895   cp->hTo = hChildStdinWrDup;\r
9896 \r
9897   *pr = (void *) cp;\r
9898 \r
9899   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9900      2000 where engines sometimes don't see the initial command(s)\r
9901      from WinBoard and hang.  I don't understand how that can happen,\r
9902      but the Sleep is harmless, so I've put it in.  Others have also\r
9903      reported what may be the same problem, so hopefully this will fix\r
9904      it for them too.  */\r
9905   Sleep(500);\r
9906 \r
9907   return NO_ERROR;\r
9908 }\r
9909 \r
9910 \r
9911 void\r
9912 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9913 {\r
9914   ChildProc *cp; int result;\r
9915 \r
9916   cp = (ChildProc *) pr;\r
9917   if (cp == NULL) return;\r
9918 \r
9919   switch (cp->kind) {\r
9920   case CPReal:\r
9921     /* TerminateProcess is considered harmful, so... */\r
9922     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9923     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9924     /* The following doesn't work because the chess program\r
9925        doesn't "have the same console" as WinBoard.  Maybe\r
9926        we could arrange for this even though neither WinBoard\r
9927        nor the chess program uses a console for stdio? */\r
9928     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9929 \r
9930     /* [AS] Special termination modes for misbehaving programs... */\r
9931     if( signal == 9 ) { \r
9932         result = TerminateProcess( cp->hProcess, 0 );\r
9933 \r
9934         if ( appData.debugMode) {\r
9935             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9936         }\r
9937     }\r
9938     else if( signal == 10 ) {\r
9939         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9940 \r
9941         if( dw != WAIT_OBJECT_0 ) {\r
9942             result = TerminateProcess( cp->hProcess, 0 );\r
9943 \r
9944             if ( appData.debugMode) {\r
9945                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9946             }\r
9947 \r
9948         }\r
9949     }\r
9950 \r
9951     CloseHandle(cp->hProcess);\r
9952     break;\r
9953 \r
9954   case CPComm:\r
9955     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9956     break;\r
9957 \r
9958   case CPSock:\r
9959     closesocket(cp->sock);\r
9960     WSACleanup();\r
9961     break;\r
9962 \r
9963   case CPRcmd:\r
9964     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9965     closesocket(cp->sock);\r
9966     closesocket(cp->sock2);\r
9967     WSACleanup();\r
9968     break;\r
9969   }\r
9970   free(cp);\r
9971 }\r
9972 \r
9973 void\r
9974 InterruptChildProcess(ProcRef pr)\r
9975 {\r
9976   ChildProc *cp;\r
9977 \r
9978   cp = (ChildProc *) pr;\r
9979   if (cp == NULL) return;\r
9980   switch (cp->kind) {\r
9981   case CPReal:\r
9982     /* The following doesn't work because the chess program\r
9983        doesn't "have the same console" as WinBoard.  Maybe\r
9984        we could arrange for this even though neither WinBoard\r
9985        nor the chess program uses a console for stdio */\r
9986     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9987     break;\r
9988 \r
9989   case CPComm:\r
9990   case CPSock:\r
9991     /* Can't interrupt */\r
9992     break;\r
9993 \r
9994   case CPRcmd:\r
9995     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9996     break;\r
9997   }\r
9998 }\r
9999 \r
10000 \r
10001 int\r
10002 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10003 {\r
10004   char cmdLine[MSG_SIZ];\r
10005 \r
10006   if (port[0] == NULLCHAR) {\r
10007     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10008   } else {\r
10009     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10010   }\r
10011   return StartChildProcess(cmdLine, "", pr);\r
10012 }\r
10013 \r
10014 \r
10015 /* Code to open TCP sockets */\r
10016 \r
10017 int\r
10018 OpenTCP(char *host, char *port, ProcRef *pr)\r
10019 {\r
10020   ChildProc *cp;\r
10021   int err;\r
10022   SOCKET s;\r
10023   struct sockaddr_in sa, mysa;\r
10024   struct hostent FAR *hp;\r
10025   unsigned short uport;\r
10026   WORD wVersionRequested;\r
10027   WSADATA wsaData;\r
10028 \r
10029   /* Initialize socket DLL */\r
10030   wVersionRequested = MAKEWORD(1, 1);\r
10031   err = WSAStartup(wVersionRequested, &wsaData);\r
10032   if (err != 0) return err;\r
10033 \r
10034   /* Make socket */\r
10035   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10036     err = WSAGetLastError();\r
10037     WSACleanup();\r
10038     return err;\r
10039   }\r
10040 \r
10041   /* Bind local address using (mostly) don't-care values.\r
10042    */\r
10043   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10044   mysa.sin_family = AF_INET;\r
10045   mysa.sin_addr.s_addr = INADDR_ANY;\r
10046   uport = (unsigned short) 0;\r
10047   mysa.sin_port = htons(uport);\r
10048   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10049       == SOCKET_ERROR) {\r
10050     err = WSAGetLastError();\r
10051     WSACleanup();\r
10052     return err;\r
10053   }\r
10054 \r
10055   /* Resolve remote host name */\r
10056   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10057   if (!(hp = gethostbyname(host))) {\r
10058     unsigned int b0, b1, b2, b3;\r
10059 \r
10060     err = WSAGetLastError();\r
10061 \r
10062     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10063       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10064       hp->h_addrtype = AF_INET;\r
10065       hp->h_length = 4;\r
10066       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10067       hp->h_addr_list[0] = (char *) malloc(4);\r
10068       hp->h_addr_list[0][0] = (char) b0;\r
10069       hp->h_addr_list[0][1] = (char) b1;\r
10070       hp->h_addr_list[0][2] = (char) b2;\r
10071       hp->h_addr_list[0][3] = (char) b3;\r
10072     } else {\r
10073       WSACleanup();\r
10074       return err;\r
10075     }\r
10076   }\r
10077   sa.sin_family = hp->h_addrtype;\r
10078   uport = (unsigned short) atoi(port);\r
10079   sa.sin_port = htons(uport);\r
10080   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10081 \r
10082   /* Make connection */\r
10083   if (connect(s, (struct sockaddr *) &sa,\r
10084               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10085     err = WSAGetLastError();\r
10086     WSACleanup();\r
10087     return err;\r
10088   }\r
10089 \r
10090   /* Prepare return value */\r
10091   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10092   cp->kind = CPSock;\r
10093   cp->sock = s;\r
10094   *pr = (ProcRef *) cp;\r
10095 \r
10096   return NO_ERROR;\r
10097 }\r
10098 \r
10099 int\r
10100 OpenCommPort(char *name, ProcRef *pr)\r
10101 {\r
10102   HANDLE h;\r
10103   COMMTIMEOUTS ct;\r
10104   ChildProc *cp;\r
10105   char fullname[MSG_SIZ];\r
10106 \r
10107   if (*name != '\\')\r
10108     sprintf(fullname, "\\\\.\\%s", name);\r
10109   else\r
10110     strcpy(fullname, name);\r
10111 \r
10112   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10113                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10114   if (h == (HANDLE) -1) {\r
10115     return GetLastError();\r
10116   }\r
10117   hCommPort = h;\r
10118 \r
10119   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10120 \r
10121   /* Accumulate characters until a 100ms pause, then parse */\r
10122   ct.ReadIntervalTimeout = 100;\r
10123   ct.ReadTotalTimeoutMultiplier = 0;\r
10124   ct.ReadTotalTimeoutConstant = 0;\r
10125   ct.WriteTotalTimeoutMultiplier = 0;\r
10126   ct.WriteTotalTimeoutConstant = 0;\r
10127   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10128 \r
10129   /* Prepare return value */\r
10130   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10131   cp->kind = CPComm;\r
10132   cp->hFrom = h;\r
10133   cp->hTo = h;\r
10134   *pr = (ProcRef *) cp;\r
10135 \r
10136   return NO_ERROR;\r
10137 }\r
10138 \r
10139 int\r
10140 OpenLoopback(ProcRef *pr)\r
10141 {\r
10142   DisplayFatalError("Not implemented", 0, 1);\r
10143   return NO_ERROR;\r
10144 }\r
10145 \r
10146 \r
10147 int\r
10148 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10149 {\r
10150   ChildProc *cp;\r
10151   int err;\r
10152   SOCKET s, s2, s3;\r
10153   struct sockaddr_in sa, mysa;\r
10154   struct hostent FAR *hp;\r
10155   unsigned short uport;\r
10156   WORD wVersionRequested;\r
10157   WSADATA wsaData;\r
10158   int fromPort;\r
10159   char stderrPortStr[MSG_SIZ];\r
10160 \r
10161   /* Initialize socket DLL */\r
10162   wVersionRequested = MAKEWORD(1, 1);\r
10163   err = WSAStartup(wVersionRequested, &wsaData);\r
10164   if (err != 0) return err;\r
10165 \r
10166   /* Resolve remote host name */\r
10167   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10168   if (!(hp = gethostbyname(host))) {\r
10169     unsigned int b0, b1, b2, b3;\r
10170 \r
10171     err = WSAGetLastError();\r
10172 \r
10173     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10174       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10175       hp->h_addrtype = AF_INET;\r
10176       hp->h_length = 4;\r
10177       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10178       hp->h_addr_list[0] = (char *) malloc(4);\r
10179       hp->h_addr_list[0][0] = (char) b0;\r
10180       hp->h_addr_list[0][1] = (char) b1;\r
10181       hp->h_addr_list[0][2] = (char) b2;\r
10182       hp->h_addr_list[0][3] = (char) b3;\r
10183     } else {\r
10184       WSACleanup();\r
10185       return err;\r
10186     }\r
10187   }\r
10188   sa.sin_family = hp->h_addrtype;\r
10189   uport = (unsigned short) 514;\r
10190   sa.sin_port = htons(uport);\r
10191   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10192 \r
10193   /* Bind local socket to unused "privileged" port address\r
10194    */\r
10195   s = INVALID_SOCKET;\r
10196   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10197   mysa.sin_family = AF_INET;\r
10198   mysa.sin_addr.s_addr = INADDR_ANY;\r
10199   for (fromPort = 1023;; fromPort--) {\r
10200     if (fromPort < 0) {\r
10201       WSACleanup();\r
10202       return WSAEADDRINUSE;\r
10203     }\r
10204     if (s == INVALID_SOCKET) {\r
10205       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10206         err = WSAGetLastError();\r
10207         WSACleanup();\r
10208         return err;\r
10209       }\r
10210     }\r
10211     uport = (unsigned short) fromPort;\r
10212     mysa.sin_port = htons(uport);\r
10213     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10214         == SOCKET_ERROR) {\r
10215       err = WSAGetLastError();\r
10216       if (err == WSAEADDRINUSE) continue;\r
10217       WSACleanup();\r
10218       return err;\r
10219     }\r
10220     if (connect(s, (struct sockaddr *) &sa,\r
10221       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10222       err = WSAGetLastError();\r
10223       if (err == WSAEADDRINUSE) {\r
10224         closesocket(s);\r
10225         s = -1;\r
10226         continue;\r
10227       }\r
10228       WSACleanup();\r
10229       return err;\r
10230     }\r
10231     break;\r
10232   }\r
10233 \r
10234   /* Bind stderr local socket to unused "privileged" port address\r
10235    */\r
10236   s2 = INVALID_SOCKET;\r
10237   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10238   mysa.sin_family = AF_INET;\r
10239   mysa.sin_addr.s_addr = INADDR_ANY;\r
10240   for (fromPort = 1023;; fromPort--) {\r
10241     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10242     if (fromPort < 0) {\r
10243       (void) closesocket(s);\r
10244       WSACleanup();\r
10245       return WSAEADDRINUSE;\r
10246     }\r
10247     if (s2 == INVALID_SOCKET) {\r
10248       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10249         err = WSAGetLastError();\r
10250         closesocket(s);\r
10251         WSACleanup();\r
10252         return err;\r
10253       }\r
10254     }\r
10255     uport = (unsigned short) fromPort;\r
10256     mysa.sin_port = htons(uport);\r
10257     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10258         == SOCKET_ERROR) {\r
10259       err = WSAGetLastError();\r
10260       if (err == WSAEADDRINUSE) continue;\r
10261       (void) closesocket(s);\r
10262       WSACleanup();\r
10263       return err;\r
10264     }\r
10265     if (listen(s2, 1) == SOCKET_ERROR) {\r
10266       err = WSAGetLastError();\r
10267       if (err == WSAEADDRINUSE) {\r
10268         closesocket(s2);\r
10269         s2 = INVALID_SOCKET;\r
10270         continue;\r
10271       }\r
10272       (void) closesocket(s);\r
10273       (void) closesocket(s2);\r
10274       WSACleanup();\r
10275       return err;\r
10276     }\r
10277     break;\r
10278   }\r
10279   prevStderrPort = fromPort; // remember port used\r
10280   sprintf(stderrPortStr, "%d", fromPort);\r
10281 \r
10282   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10283     err = WSAGetLastError();\r
10284     (void) closesocket(s);\r
10285     (void) closesocket(s2);\r
10286     WSACleanup();\r
10287     return err;\r
10288   }\r
10289 \r
10290   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10291     err = WSAGetLastError();\r
10292     (void) closesocket(s);\r
10293     (void) closesocket(s2);\r
10294     WSACleanup();\r
10295     return err;\r
10296   }\r
10297   if (*user == NULLCHAR) user = UserName();\r
10298   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10299     err = WSAGetLastError();\r
10300     (void) closesocket(s);\r
10301     (void) closesocket(s2);\r
10302     WSACleanup();\r
10303     return err;\r
10304   }\r
10305   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10306     err = WSAGetLastError();\r
10307     (void) closesocket(s);\r
10308     (void) closesocket(s2);\r
10309     WSACleanup();\r
10310     return err;\r
10311   }\r
10312 \r
10313   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10314     err = WSAGetLastError();\r
10315     (void) closesocket(s);\r
10316     (void) closesocket(s2);\r
10317     WSACleanup();\r
10318     return err;\r
10319   }\r
10320   (void) closesocket(s2);  /* Stop listening */\r
10321 \r
10322   /* Prepare return value */\r
10323   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10324   cp->kind = CPRcmd;\r
10325   cp->sock = s;\r
10326   cp->sock2 = s3;\r
10327   *pr = (ProcRef *) cp;\r
10328 \r
10329   return NO_ERROR;\r
10330 }\r
10331 \r
10332 \r
10333 InputSourceRef\r
10334 AddInputSource(ProcRef pr, int lineByLine,\r
10335                InputCallback func, VOIDSTAR closure)\r
10336 {\r
10337   InputSource *is, *is2 = NULL;\r
10338   ChildProc *cp = (ChildProc *) pr;\r
10339 \r
10340   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10341   is->lineByLine = lineByLine;\r
10342   is->func = func;\r
10343   is->closure = closure;\r
10344   is->second = NULL;\r
10345   is->next = is->buf;\r
10346   if (pr == NoProc) {\r
10347     is->kind = CPReal;\r
10348     consoleInputSource = is;\r
10349   } else {\r
10350     is->kind = cp->kind;\r
10351     /* \r
10352         [AS] Try to avoid a race condition if the thread is given control too early:\r
10353         we create all threads suspended so that the is->hThread variable can be\r
10354         safely assigned, then let the threads start with ResumeThread.\r
10355     */\r
10356     switch (cp->kind) {\r
10357     case CPReal:\r
10358       is->hFile = cp->hFrom;\r
10359       cp->hFrom = NULL; /* now owned by InputThread */\r
10360       is->hThread =\r
10361         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10362                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10363       break;\r
10364 \r
10365     case CPComm:\r
10366       is->hFile = cp->hFrom;\r
10367       cp->hFrom = NULL; /* now owned by InputThread */\r
10368       is->hThread =\r
10369         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10370                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10371       break;\r
10372 \r
10373     case CPSock:\r
10374       is->sock = cp->sock;\r
10375       is->hThread =\r
10376         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10377                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10378       break;\r
10379 \r
10380     case CPRcmd:\r
10381       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10382       *is2 = *is;\r
10383       is->sock = cp->sock;\r
10384       is->second = is2;\r
10385       is2->sock = cp->sock2;\r
10386       is2->second = is2;\r
10387       is->hThread =\r
10388         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10389                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10390       is2->hThread =\r
10391         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10392                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10393       break;\r
10394     }\r
10395 \r
10396     if( is->hThread != NULL ) {\r
10397         ResumeThread( is->hThread );\r
10398     }\r
10399 \r
10400     if( is2 != NULL && is2->hThread != NULL ) {\r
10401         ResumeThread( is2->hThread );\r
10402     }\r
10403   }\r
10404 \r
10405   return (InputSourceRef) is;\r
10406 }\r
10407 \r
10408 void\r
10409 RemoveInputSource(InputSourceRef isr)\r
10410 {\r
10411   InputSource *is;\r
10412 \r
10413   is = (InputSource *) isr;\r
10414   is->hThread = NULL;  /* tell thread to stop */\r
10415   CloseHandle(is->hThread);\r
10416   if (is->second != NULL) {\r
10417     is->second->hThread = NULL;\r
10418     CloseHandle(is->second->hThread);\r
10419   }\r
10420 }\r
10421 \r
10422 \r
10423 int\r
10424 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10425 {\r
10426   DWORD dOutCount;\r
10427   int outCount = SOCKET_ERROR;\r
10428   ChildProc *cp = (ChildProc *) pr;\r
10429   static OVERLAPPED ovl;\r
10430 \r
10431   if (pr == NoProc) {\r
10432     ConsoleOutput(message, count, FALSE);\r
10433     return count;\r
10434   } \r
10435 \r
10436   if (ovl.hEvent == NULL) {\r
10437     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10438   }\r
10439   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10440 \r
10441   switch (cp->kind) {\r
10442   case CPSock:\r
10443   case CPRcmd:\r
10444     outCount = send(cp->sock, message, count, 0);\r
10445     if (outCount == SOCKET_ERROR) {\r
10446       *outError = WSAGetLastError();\r
10447     } else {\r
10448       *outError = NO_ERROR;\r
10449     }\r
10450     break;\r
10451 \r
10452   case CPReal:\r
10453     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10454                   &dOutCount, NULL)) {\r
10455       *outError = NO_ERROR;\r
10456       outCount = (int) dOutCount;\r
10457     } else {\r
10458       *outError = GetLastError();\r
10459     }\r
10460     break;\r
10461 \r
10462   case CPComm:\r
10463     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10464                             &dOutCount, &ovl);\r
10465     if (*outError == NO_ERROR) {\r
10466       outCount = (int) dOutCount;\r
10467     }\r
10468     break;\r
10469   }\r
10470   return outCount;\r
10471 }\r
10472 \r
10473 int\r
10474 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10475                        long msdelay)\r
10476 {\r
10477   /* Ignore delay, not implemented for WinBoard */\r
10478   return OutputToProcess(pr, message, count, outError);\r
10479 }\r
10480 \r
10481 \r
10482 void\r
10483 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10484                         char *buf, int count, int error)\r
10485 {\r
10486   DisplayFatalError("Not implemented", 0, 1);\r
10487 }\r
10488 \r
10489 /* see wgamelist.c for Game List functions */\r
10490 /* see wedittags.c for Edit Tags functions */\r
10491 \r
10492 \r
10493 VOID\r
10494 ICSInitScript()\r
10495 {\r
10496   FILE *f;\r
10497   char buf[MSG_SIZ];\r
10498   char *dummy;\r
10499 \r
10500   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10501     f = fopen(buf, "r");\r
10502     if (f != NULL) {\r
10503       ProcessICSInitScript(f);\r
10504       fclose(f);\r
10505     }\r
10506   }\r
10507 }\r
10508 \r
10509 \r
10510 VOID\r
10511 StartAnalysisClock()\r
10512 {\r
10513   if (analysisTimerEvent) return;\r
10514   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10515                                         (UINT) 2000, NULL);\r
10516 }\r
10517 \r
10518 LRESULT CALLBACK\r
10519 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10520 {\r
10521   static HANDLE hwndText;\r
10522   RECT rect;\r
10523   static int sizeX, sizeY;\r
10524   int newSizeX, newSizeY, flags;\r
10525   MINMAXINFO *mmi;\r
10526 \r
10527   switch (message) {\r
10528   case WM_INITDIALOG: /* message: initialize dialog box */\r
10529     /* Initialize the dialog items */\r
10530     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10531     SetWindowText(hDlg, analysisTitle);\r
10532     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10533     /* Size and position the dialog */\r
10534     if (!analysisDialog) {\r
10535       analysisDialog = hDlg;\r
10536       flags = SWP_NOZORDER;\r
10537       GetClientRect(hDlg, &rect);\r
10538       sizeX = rect.right;\r
10539       sizeY = rect.bottom;\r
10540       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10541           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10542         WINDOWPLACEMENT wp;\r
10543         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10544         wp.length = sizeof(WINDOWPLACEMENT);\r
10545         wp.flags = 0;\r
10546         wp.showCmd = SW_SHOW;\r
10547         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10548         wp.rcNormalPosition.left = analysisX;\r
10549         wp.rcNormalPosition.right = analysisX + analysisW;\r
10550         wp.rcNormalPosition.top = analysisY;\r
10551         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10552         SetWindowPlacement(hDlg, &wp);\r
10553 \r
10554         GetClientRect(hDlg, &rect);\r
10555         newSizeX = rect.right;\r
10556         newSizeY = rect.bottom;\r
10557         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10558                               newSizeX, newSizeY);\r
10559         sizeX = newSizeX;\r
10560         sizeY = newSizeY;\r
10561       }\r
10562     }\r
10563     return FALSE;\r
10564 \r
10565   case WM_COMMAND: /* message: received a command */\r
10566     switch (LOWORD(wParam)) {\r
10567     case IDCANCEL:\r
10568       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10569           ExitAnalyzeMode();\r
10570           ModeHighlight();\r
10571           return TRUE;\r
10572       }\r
10573       EditGameEvent();\r
10574       return TRUE;\r
10575     default:\r
10576       break;\r
10577     }\r
10578     break;\r
10579 \r
10580   case WM_SIZE:\r
10581     newSizeX = LOWORD(lParam);\r
10582     newSizeY = HIWORD(lParam);\r
10583     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10584     sizeX = newSizeX;\r
10585     sizeY = newSizeY;\r
10586     break;\r
10587 \r
10588   case WM_GETMINMAXINFO:\r
10589     /* Prevent resizing window too small */\r
10590     mmi = (MINMAXINFO *) lParam;\r
10591     mmi->ptMinTrackSize.x = 100;\r
10592     mmi->ptMinTrackSize.y = 100;\r
10593     break;\r
10594   }\r
10595   return FALSE;\r
10596 }\r
10597 \r
10598 VOID\r
10599 AnalysisPopUp(char* title, char* str)\r
10600 {\r
10601   FARPROC lpProc;\r
10602   char *p, *q;\r
10603 \r
10604   /* [AS] */\r
10605   EngineOutputPopUp();\r
10606   return;\r
10607 \r
10608   if (str == NULL) str = "";\r
10609   p = (char *) malloc(2 * strlen(str) + 2);\r
10610   q = p;\r
10611   while (*str) {\r
10612     if (*str == '\n') *q++ = '\r';\r
10613     *q++ = *str++;\r
10614   }\r
10615   *q = NULLCHAR;\r
10616   if (analysisText != NULL) free(analysisText);\r
10617   analysisText = p;\r
10618 \r
10619   if (analysisDialog) {\r
10620     SetWindowText(analysisDialog, title);\r
10621     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10622     ShowWindow(analysisDialog, SW_SHOW);\r
10623   } else {\r
10624     analysisTitle = title;\r
10625     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10626     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10627                  hwndMain, (DLGPROC)lpProc);\r
10628     FreeProcInstance(lpProc);\r
10629   }\r
10630   analysisDialogUp = TRUE;  \r
10631 }\r
10632 \r
10633 VOID\r
10634 AnalysisPopDown()\r
10635 {\r
10636   if (analysisDialog) {\r
10637     ShowWindow(analysisDialog, SW_HIDE);\r
10638   }\r
10639   analysisDialogUp = FALSE;  \r
10640 }\r
10641 \r
10642 \r
10643 VOID\r
10644 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10645 {\r
10646   highlightInfo.sq[0].x = fromX;\r
10647   highlightInfo.sq[0].y = fromY;\r
10648   highlightInfo.sq[1].x = toX;\r
10649   highlightInfo.sq[1].y = toY;\r
10650 }\r
10651 \r
10652 VOID\r
10653 ClearHighlights()\r
10654 {\r
10655   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10656     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10657 }\r
10658 \r
10659 VOID\r
10660 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10661 {\r
10662   premoveHighlightInfo.sq[0].x = fromX;\r
10663   premoveHighlightInfo.sq[0].y = fromY;\r
10664   premoveHighlightInfo.sq[1].x = toX;\r
10665   premoveHighlightInfo.sq[1].y = toY;\r
10666 }\r
10667 \r
10668 VOID\r
10669 ClearPremoveHighlights()\r
10670 {\r
10671   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10672     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10673 }\r
10674 \r
10675 VOID\r
10676 ShutDownFrontEnd()\r
10677 {\r
10678   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10679   DeleteClipboardTempFiles();\r
10680 }\r
10681 \r
10682 void\r
10683 BoardToTop()\r
10684 {\r
10685     if (IsIconic(hwndMain))\r
10686       ShowWindow(hwndMain, SW_RESTORE);\r
10687 \r
10688     SetActiveWindow(hwndMain);\r
10689 }\r
10690 \r
10691 /*\r
10692  * Prototypes for animation support routines\r
10693  */\r
10694 static void ScreenSquare(int column, int row, POINT * pt);\r
10695 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10696      POINT frames[], int * nFrames);\r
10697 \r
10698 \r
10699 void\r
10700 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10701 {       // [HGM] atomic: animate blast wave\r
10702         int i;\r
10703 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10704         explodeInfo.fromX = fromX;\r
10705         explodeInfo.fromY = fromY;\r
10706         explodeInfo.toX = toX;\r
10707         explodeInfo.toY = toY;\r
10708         for(i=1; i<nFrames; i++) {\r
10709             explodeInfo.radius = (i*180)/(nFrames-1);\r
10710             DrawPosition(FALSE, NULL);\r
10711             Sleep(appData.animSpeed);\r
10712         }\r
10713         explodeInfo.radius = 0;\r
10714         DrawPosition(TRUE, NULL);\r
10715 }\r
10716 \r
10717 #define kFactor 4\r
10718 \r
10719 void\r
10720 AnimateMove(board, fromX, fromY, toX, toY)\r
10721      Board board;\r
10722      int fromX;\r
10723      int fromY;\r
10724      int toX;\r
10725      int toY;\r
10726 {\r
10727   ChessSquare piece;\r
10728   POINT start, finish, mid;\r
10729   POINT frames[kFactor * 2 + 1];\r
10730   int nFrames, n;\r
10731 \r
10732   if (!appData.animate) return;\r
10733   if (doingSizing) return;\r
10734   if (fromY < 0 || fromX < 0) return;\r
10735   piece = board[fromY][fromX];\r
10736   if (piece >= EmptySquare) return;\r
10737 \r
10738   ScreenSquare(fromX, fromY, &start);\r
10739   ScreenSquare(toX, toY, &finish);\r
10740 \r
10741   /* All pieces except knights move in straight line */\r
10742   if (piece != WhiteKnight && piece != BlackKnight) {\r
10743     mid.x = start.x + (finish.x - start.x) / 2;\r
10744     mid.y = start.y + (finish.y - start.y) / 2;\r
10745   } else {\r
10746     /* Knight: make diagonal movement then straight */\r
10747     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10748        mid.x = start.x + (finish.x - start.x) / 2;\r
10749        mid.y = finish.y;\r
10750      } else {\r
10751        mid.x = finish.x;\r
10752        mid.y = start.y + (finish.y - start.y) / 2;\r
10753      }\r
10754   }\r
10755   \r
10756   /* Don't use as many frames for very short moves */\r
10757   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10758     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10759   else\r
10760     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10761 \r
10762   animInfo.from.x = fromX;\r
10763   animInfo.from.y = fromY;\r
10764   animInfo.to.x = toX;\r
10765   animInfo.to.y = toY;\r
10766   animInfo.lastpos = start;\r
10767   animInfo.piece = piece;\r
10768   for (n = 0; n < nFrames; n++) {\r
10769     animInfo.pos = frames[n];\r
10770     DrawPosition(FALSE, NULL);\r
10771     animInfo.lastpos = animInfo.pos;\r
10772     Sleep(appData.animSpeed);\r
10773   }\r
10774   animInfo.pos = finish;\r
10775   DrawPosition(FALSE, NULL);\r
10776   animInfo.piece = EmptySquare;\r
10777   if(gameInfo.variant == VariantAtomic && \r
10778      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10779         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10780 }\r
10781 \r
10782 /*      Convert board position to corner of screen rect and color       */\r
10783 \r
10784 static void\r
10785 ScreenSquare(column, row, pt)\r
10786      int column; int row; POINT * pt;\r
10787 {\r
10788   if (flipView) {\r
10789     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10790     pt->y = lineGap + row * (squareSize + lineGap);\r
10791   } else {\r
10792     pt->x = lineGap + column * (squareSize + lineGap);\r
10793     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10794   }\r
10795 }\r
10796 \r
10797 /*      Generate a series of frame coords from start->mid->finish.\r
10798         The movement rate doubles until the half way point is\r
10799         reached, then halves back down to the final destination,\r
10800         which gives a nice slow in/out effect. The algorithmn\r
10801         may seem to generate too many intermediates for short\r
10802         moves, but remember that the purpose is to attract the\r
10803         viewers attention to the piece about to be moved and\r
10804         then to where it ends up. Too few frames would be less\r
10805         noticeable.                                             */\r
10806 \r
10807 static void\r
10808 Tween(start, mid, finish, factor, frames, nFrames)\r
10809      POINT * start; POINT * mid;\r
10810      POINT * finish; int factor;\r
10811      POINT frames[]; int * nFrames;\r
10812 {\r
10813   int n, fraction = 1, count = 0;\r
10814 \r
10815   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10816   for (n = 0; n < factor; n++)\r
10817     fraction *= 2;\r
10818   for (n = 0; n < factor; n++) {\r
10819     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10820     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10821     count ++;\r
10822     fraction = fraction / 2;\r
10823   }\r
10824   \r
10825   /* Midpoint */\r
10826   frames[count] = *mid;\r
10827   count ++;\r
10828   \r
10829   /* Slow out, stepping 1/2, then 1/4, ... */\r
10830   fraction = 2;\r
10831   for (n = 0; n < factor; n++) {\r
10832     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10833     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10834     count ++;\r
10835     fraction = fraction * 2;\r
10836   }\r
10837   *nFrames = count;\r
10838 }\r
10839 \r
10840 void\r
10841 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10842 {\r
10843 #if 0\r
10844     char buf[256];\r
10845 \r
10846     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10847         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10848 \r
10849     OutputDebugString( buf );\r
10850 #endif\r
10851 \r
10852     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10853 \r
10854     EvalGraphSet( first, last, current, pvInfoList );\r
10855 }\r
10856 \r
10857 void SetProgramStats( FrontEndProgramStats * stats )\r
10858 {\r
10859 #if 0\r
10860     char buf[1024];\r
10861 \r
10862     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10863         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10864 \r
10865     OutputDebugString( buf );\r
10866 #endif\r
10867 \r
10868     EngineOutputUpdate( stats );\r
10869 }\r