changes from H.G. Muller; version 4.3.15
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <time.h>\r
61 #include <malloc.h>\r
62 #include <sys/stat.h>\r
63 #include <fcntl.h>\r
64 #include <math.h>\r
65 #include <commdlg.h>\r
66 #include <dlgs.h>\r
67 #include <richedit.h>\r
68 #include <mmsystem.h>\r
69 \r
70 #if __GNUC__\r
71 #include <errno.h>\r
72 #include <string.h>\r
73 #endif\r
74 \r
75 #include "common.h"\r
76 #include "winboard.h"\r
77 #include "frontend.h"\r
78 #include "backend.h"\r
79 #include "moves.h"\r
80 #include "wclipbrd.h"\r
81 #include "wgamelist.h"\r
82 #include "wedittags.h"\r
83 #include "woptions.h"\r
84 #include "wsockerr.h"\r
85 #include "defaults.h"\r
86 \r
87 #include "wsnap.h"\r
88 \r
89 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
90 \r
91   int myrandom(void);\r
92   void mysrandom(unsigned int seed);\r
93 \r
94 extern int whiteFlag, blackFlag;\r
95 Boolean flipClock = FALSE;\r
96 \r
97 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
98 \r
99 typedef struct {\r
100   ChessSquare piece;  \r
101   POINT pos;      /* window coordinates of current pos */\r
102   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
103   POINT from;     /* board coordinates of the piece's orig pos */\r
104   POINT to;       /* board coordinates of the piece's new pos */\r
105 } AnimInfo;\r
106 \r
107 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
108 \r
109 typedef struct {\r
110   POINT start;    /* window coordinates of start pos */\r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114 } DragInfo;\r
115 \r
116 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
117 \r
118 typedef struct {\r
119   POINT sq[2];    /* board coordinates of from, to squares */\r
120 } HighlightInfo;\r
121 \r
122 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
123 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
124 \r
125 /* Window class names */\r
126 char szAppName[] = "WinBoard";\r
127 char szConsoleName[] = "WBConsole";\r
128 \r
129 /* Title bar text */\r
130 char szTitle[] = "WinBoard";\r
131 char szConsoleTitle[] = "ICS Interaction";\r
132 \r
133 char *programName;\r
134 char *settingsFileName;\r
135 BOOLEAN saveSettingsOnExit;\r
136 char installDir[MSG_SIZ];\r
137 \r
138 BoardSize boardSize;\r
139 BOOLEAN chessProgram;\r
140 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
141 static int squareSize, lineGap, minorSize;\r
142 static int winWidth, winHeight;\r
143 static RECT messageRect, whiteRect, blackRect;\r
144 static char messageText[MESSAGE_TEXT_MAX];\r
145 static int clockTimerEvent = 0;\r
146 static int loadGameTimerEvent = 0;\r
147 static int analysisTimerEvent = 0;\r
148 static DelayedEventCallback delayedTimerCallback;\r
149 static int delayedTimerEvent = 0;\r
150 static int buttonCount = 2;\r
151 char *icsTextMenuString;\r
152 char *icsNames;\r
153 char *firstChessProgramNames;\r
154 char *secondChessProgramNames;\r
155 \r
156 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
157 \r
158 #define PALETTESIZE 256\r
159 \r
160 HINSTANCE hInst;          /* current instance */\r
161 HWND hwndMain = NULL;        /* root window*/\r
162 HWND hwndConsole = NULL;\r
163 BOOLEAN alwaysOnTop = FALSE;\r
164 RECT boardRect;\r
165 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
166   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
167 HPALETTE hPal;\r
168 ColorClass currentColorClass;\r
169 \r
170 HWND hCommPort = NULL;    /* currently open comm port */\r
171 static HWND hwndPause;    /* pause button */\r
172 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
173 static HBRUSH lightSquareBrush, darkSquareBrush,\r
174   blackSquareBrush, /* [HGM] for band between board and holdings */\r
175   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
176 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
177 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
178 static HPEN gridPen = NULL;\r
179 static HPEN highlightPen = NULL;\r
180 static HPEN premovePen = NULL;\r
181 static NPLOGPALETTE pLogPal;\r
182 static BOOL paletteChanged = FALSE;\r
183 static HICON iconWhite, iconBlack, iconCurrent;\r
184 static int doingSizing = FALSE;\r
185 static int lastSizing = 0;\r
186 static int prevStderrPort;\r
187 \r
188 /* [AS] Support for background textures */\r
189 #define BACK_TEXTURE_MODE_DISABLED      0\r
190 #define BACK_TEXTURE_MODE_PLAIN         1\r
191 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
192 \r
193 static HBITMAP liteBackTexture = NULL;\r
194 static HBITMAP darkBackTexture = NULL;\r
195 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
196 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
197 static int backTextureSquareSize = 0;\r
198 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
199 \r
200 #if __GNUC__ && !defined(_winmajor)\r
201 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
202 #else\r
203 #define oldDialog (_winmajor < 4)\r
204 #endif\r
205 \r
206 char *defaultTextAttribs[] = \r
207 {\r
208   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
209   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
210   COLOR_NONE\r
211 };\r
212 \r
213 typedef struct {\r
214   char *name;\r
215   int squareSize;\r
216   int lineGap;\r
217   int smallLayout;\r
218   int tinyLayout;\r
219   int cliWidth, cliHeight;\r
220 } SizeInfo;\r
221 \r
222 SizeInfo sizeInfo[] = \r
223 {\r
224   { "tiny",     21, 0, 1, 1, 0, 0 },\r
225   { "teeny",    25, 1, 1, 1, 0, 0 },\r
226   { "dinky",    29, 1, 1, 1, 0, 0 },\r
227   { "petite",   33, 1, 1, 1, 0, 0 },\r
228   { "slim",     37, 2, 1, 0, 0, 0 },\r
229   { "small",    40, 2, 1, 0, 0, 0 },\r
230   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
231   { "middling", 49, 2, 0, 0, 0, 0 },\r
232   { "average",  54, 2, 0, 0, 0, 0 },\r
233   { "moderate", 58, 3, 0, 0, 0, 0 },\r
234   { "medium",   64, 3, 0, 0, 0, 0 },\r
235   { "bulky",    72, 3, 0, 0, 0, 0 },\r
236   { "large",    80, 3, 0, 0, 0, 0 },\r
237   { "big",      87, 3, 0, 0, 0, 0 },\r
238   { "huge",     95, 3, 0, 0, 0, 0 },\r
239   { "giant",    108, 3, 0, 0, 0, 0 },\r
240   { "colossal", 116, 4, 0, 0, 0, 0 },\r
241   { "titanic",  129, 4, 0, 0, 0, 0 },\r
242   { NULL, 0, 0, 0, 0, 0, 0 }\r
243 };\r
244 \r
245 #define MF(x) {x, {0, }, {0, }, 0}\r
246 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
247 {\r
248   { 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
249   { 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
250   { 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
251   { 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
252   { 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
253   { 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
254   { 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
255   { 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
256   { 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
257   { 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
258   { 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
259   { 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
260   { 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
261   { 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
262   { 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
263   { 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
264   { 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
265   { 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
266 };\r
267 \r
268 MyFont *font[NUM_SIZES][NUM_FONTS];\r
269 \r
270 typedef struct {\r
271   char *label;\r
272   int id;\r
273   HWND hwnd;\r
274   WNDPROC wndproc;\r
275 } MyButtonDesc;\r
276 \r
277 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
278 #define N_BUTTONS 5\r
279 \r
280 MyButtonDesc buttonDesc[N_BUTTONS] =\r
281 {\r
282   {"<<", IDM_ToStart, NULL, NULL},\r
283   {"<", IDM_Backward, NULL, NULL},\r
284   {"P", IDM_Pause, NULL, NULL},\r
285   {">", IDM_Forward, NULL, NULL},\r
286   {">>", IDM_ToEnd, NULL, NULL},\r
287 };\r
288 \r
289 int tinyLayout = 0, smallLayout = 0;\r
290 #define MENU_BAR_ITEMS 6\r
291 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
292   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
293   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
294 };\r
295 \r
296 \r
297 MySound sounds[(int)NSoundClasses];\r
298 MyTextAttribs textAttribs[(int)NColorClasses];\r
299 \r
300 MyColorizeAttribs colorizeAttribs[] = {\r
301   { (COLORREF)0, 0, "Shout Text" },\r
302   { (COLORREF)0, 0, "SShout/CShout" },\r
303   { (COLORREF)0, 0, "Channel 1 Text" },\r
304   { (COLORREF)0, 0, "Channel Text" },\r
305   { (COLORREF)0, 0, "Kibitz Text" },\r
306   { (COLORREF)0, 0, "Tell Text" },\r
307   { (COLORREF)0, 0, "Challenge Text" },\r
308   { (COLORREF)0, 0, "Request Text" },\r
309   { (COLORREF)0, 0, "Seek Text" },\r
310   { (COLORREF)0, 0, "Normal Text" },\r
311   { (COLORREF)0, 0, "None" }\r
312 };\r
313 \r
314 \r
315 \r
316 static char *commentTitle;\r
317 static char *commentText;\r
318 static int commentIndex;\r
319 static Boolean editComment = FALSE;\r
320 HWND commentDialog = NULL;\r
321 BOOLEAN commentDialogUp = FALSE;\r
322 static int commentX, commentY, commentH, commentW;\r
323 \r
324 static char *analysisTitle;\r
325 static char *analysisText;\r
326 HWND analysisDialog = NULL;\r
327 BOOLEAN analysisDialogUp = FALSE;\r
328 static int analysisX, analysisY, analysisH, analysisW;\r
329 \r
330 char errorTitle[MSG_SIZ];\r
331 char errorMessage[2*MSG_SIZ];\r
332 HWND errorDialog = NULL;\r
333 BOOLEAN moveErrorMessageUp = FALSE;\r
334 BOOLEAN consoleEcho = TRUE;\r
335 CHARFORMAT consoleCF;\r
336 COLORREF consoleBackgroundColor;\r
337 \r
338 char *programVersion;\r
339 \r
340 #define CPReal 1\r
341 #define CPComm 2\r
342 #define CPSock 3\r
343 #define CPRcmd 4\r
344 typedef int CPKind;\r
345 \r
346 typedef struct {\r
347   CPKind kind;\r
348   HANDLE hProcess;\r
349   DWORD pid;\r
350   HANDLE hTo;\r
351   HANDLE hFrom;\r
352   SOCKET sock;\r
353   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
354 } ChildProc;\r
355 \r
356 #define INPUT_SOURCE_BUF_SIZE 4096\r
357 \r
358 typedef struct _InputSource {\r
359   CPKind kind;\r
360   HANDLE hFile;\r
361   SOCKET sock;\r
362   int lineByLine;\r
363   HANDLE hThread;\r
364   DWORD id;\r
365   char buf[INPUT_SOURCE_BUF_SIZE];\r
366   char *next;\r
367   DWORD count;\r
368   int error;\r
369   InputCallback func;\r
370   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
371   VOIDSTAR closure;\r
372 } InputSource;\r
373 \r
374 InputSource *consoleInputSource;\r
375 \r
376 DCB dcb;\r
377 \r
378 /* forward */\r
379 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
380 VOID ConsoleCreate();\r
381 LRESULT CALLBACK\r
382   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
383 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
384 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
385 VOID ParseCommSettings(char *arg, DCB *dcb);\r
386 LRESULT CALLBACK\r
387   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
388 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
389 void ParseIcsTextMenu(char *icsTextMenuString);\r
390 VOID PopUpMoveDialog(char firstchar);\r
391 VOID PopUpNameDialog(char firstchar);\r
392 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
393 \r
394 /* [AS] */\r
395 int NewGameFRC();\r
396 int GameListOptions();\r
397 \r
398 HWND moveHistoryDialog = NULL;\r
399 BOOLEAN moveHistoryDialogUp = FALSE;\r
400 \r
401 WindowPlacement wpMoveHistory;\r
402 \r
403 HWND evalGraphDialog = NULL;\r
404 BOOLEAN evalGraphDialogUp = FALSE;\r
405 \r
406 WindowPlacement wpEvalGraph;\r
407 \r
408 HWND engineOutputDialog = NULL;\r
409 BOOLEAN engineOutputDialogUp = FALSE;\r
410 \r
411 WindowPlacement wpEngineOutput;\r
412 \r
413 VOID MoveHistoryPopUp();\r
414 VOID MoveHistoryPopDown();\r
415 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
416 BOOL MoveHistoryIsUp();\r
417 \r
418 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
419 VOID EvalGraphPopUp();\r
420 VOID EvalGraphPopDown();\r
421 BOOL EvalGraphIsUp();\r
422 \r
423 VOID EngineOutputPopUp();\r
424 VOID EngineOutputPopDown();\r
425 BOOL EngineOutputIsUp();\r
426 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
427 \r
428 VOID GothicPopUp(char *title, VariantClass variant);\r
429 /*\r
430  * Setting "frozen" should disable all user input other than deleting\r
431  * the window.  We do this while engines are initializing themselves.\r
432  */\r
433 static int frozen = 0;\r
434 static int oldMenuItemState[MENU_BAR_ITEMS];\r
435 void FreezeUI()\r
436 {\r
437   HMENU hmenu;\r
438   int i;\r
439 \r
440   if (frozen) return;\r
441   frozen = 1;\r
442   hmenu = GetMenu(hwndMain);\r
443   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
444     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
445   }\r
446   DrawMenuBar(hwndMain);\r
447 }\r
448 \r
449 /* Undo a FreezeUI */\r
450 void ThawUI()\r
451 {\r
452   HMENU hmenu;\r
453   int i;\r
454 \r
455   if (!frozen) return;\r
456   frozen = 0;\r
457   hmenu = GetMenu(hwndMain);\r
458   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
459     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
460   }\r
461   DrawMenuBar(hwndMain);\r
462 }\r
463 \r
464 /*---------------------------------------------------------------------------*\\r
465  *\r
466  * WinMain\r
467  *\r
468 \*---------------------------------------------------------------------------*/\r
469 \r
470 int APIENTRY\r
471 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
472         LPSTR lpCmdLine, int nCmdShow)\r
473 {\r
474   MSG msg;\r
475   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
476 \r
477   debugFP = stderr;\r
478 \r
479   LoadLibrary("RICHED32.DLL");\r
480   consoleCF.cbSize = sizeof(CHARFORMAT);\r
481 \r
482   if (!InitApplication(hInstance)) {\r
483     return (FALSE);\r
484   }\r
485   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
486     return (FALSE);\r
487   }\r
488 \r
489   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
490   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
491   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
492 \r
493   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
494 \r
495   while (GetMessage(&msg, /* message structure */\r
496                     NULL, /* handle of window receiving the message */\r
497                     0,    /* lowest message to examine */\r
498                     0))   /* highest message to examine */\r
499     {\r
500       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
501           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
502           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
503           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
504           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
505           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
506           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
507           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
508           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
509           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
510         TranslateMessage(&msg); /* Translates virtual key codes */\r
511         DispatchMessage(&msg);  /* Dispatches message to window */\r
512       }\r
513     }\r
514 \r
515 \r
516   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
517 }\r
518 \r
519 /*---------------------------------------------------------------------------*\\r
520  *\r
521  * Initialization functions\r
522  *\r
523 \*---------------------------------------------------------------------------*/\r
524 \r
525 BOOL\r
526 InitApplication(HINSTANCE hInstance)\r
527 {\r
528   WNDCLASS wc;\r
529 \r
530   /* Fill in window class structure with parameters that describe the */\r
531   /* main window. */\r
532 \r
533   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
534   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
535   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
536   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
537   wc.hInstance     = hInstance;         /* Owner of this class */\r
538   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
539   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
540   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
541   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
542   wc.lpszClassName = szAppName;                 /* Name to register as */\r
543 \r
544   /* Register the window class and return success/failure code. */\r
545   if (!RegisterClass(&wc)) return FALSE;\r
546 \r
547   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
548   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
549   wc.cbClsExtra    = 0;\r
550   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
551   wc.hInstance     = hInstance;\r
552   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
553   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
554   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
555   wc.lpszMenuName  = NULL;\r
556   wc.lpszClassName = szConsoleName;\r
557 \r
558   if (!RegisterClass(&wc)) return FALSE;\r
559   return TRUE;\r
560 }\r
561 \r
562 \r
563 /* Set by InitInstance, used by EnsureOnScreen */\r
564 int screenHeight, screenWidth;\r
565 \r
566 void\r
567 EnsureOnScreen(int *x, int *y)\r
568 {\r
569   int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
570   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
571   if (*x > screenWidth - 32) *x = 0;\r
572   if (*y > screenHeight - 32) *y = 0;\r
573   if (*x < 10) *x = 10;\r
574   if (*y < gap) *y = gap;\r
575 }\r
576 \r
577 BOOL\r
578 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
579 {\r
580   HWND hwnd; /* Main window handle. */\r
581   int ibs;\r
582   WINDOWPLACEMENT wp;\r
583   char *filepart;\r
584 \r
585   hInst = hInstance;    /* Store instance handle in our global variable */\r
586 \r
587   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
588     *filepart = NULLCHAR;\r
589   } else {\r
590     GetCurrentDirectory(MSG_SIZ, installDir);\r
591   }\r
592   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
593   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
594   if (appData.debugMode) {\r
595     debugFP = fopen(appData.nameOfDebugFile, "w");\r
596     setbuf(debugFP, NULL);\r
597   }\r
598 \r
599   InitBackEnd1();\r
600 \r
601 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
602 //  InitEngineUCI( installDir, &second );\r
603 \r
604   /* Create a main window for this application instance. */\r
605   hwnd = CreateWindow(szAppName, szTitle,\r
606                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
607                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
608                       NULL, NULL, hInstance, NULL);\r
609   hwndMain = hwnd;\r
610 \r
611   /* If window could not be created, return "failure" */\r
612   if (!hwnd) {\r
613     return (FALSE);\r
614   }\r
615 \r
616   iconWhite = LoadIcon(hInstance, "icon_white");\r
617   iconBlack = LoadIcon(hInstance, "icon_black");\r
618   iconCurrent = iconWhite;\r
619   InitDrawingColors();\r
620   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
621   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
622   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
623     /* Compute window size for each board size, and use the largest\r
624        size that fits on this screen as the default. */\r
625     InitDrawingSizes((BoardSize)ibs, 0);\r
626     if (boardSize == (BoardSize)-1 &&\r
627         winHeight <= screenHeight\r
628            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
629         && winWidth <= screenWidth) {\r
630       boardSize = (BoardSize)ibs;\r
631     }\r
632   }\r
633   InitDrawingSizes(boardSize, 0);\r
634   InitMenuChecks();\r
635   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
636 \r
637   /* [AS] Load textures if specified */\r
638   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
639   \r
640   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
641       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
642       liteBackTextureMode = appData.liteBackTextureMode;\r
643 \r
644       if (liteBackTexture == NULL && appData.debugMode) {\r
645           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
646       }\r
647   }\r
648   \r
649   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
650       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
651       darkBackTextureMode = appData.darkBackTextureMode;\r
652 \r
653       if (darkBackTexture == NULL && appData.debugMode) {\r
654           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
655       }\r
656   }\r
657 \r
658   mysrandom( (unsigned) time(NULL) );\r
659 \r
660   /* Make a console window if needed */\r
661   if (appData.icsActive) {\r
662     ConsoleCreate();\r
663   }\r
664 \r
665   /* [AS] Restore layout */\r
666   if( wpMoveHistory.visible ) {\r
667       MoveHistoryPopUp();\r
668   }\r
669 \r
670   if( wpEvalGraph.visible ) {\r
671       EvalGraphPopUp();\r
672   }\r
673 \r
674   if( wpEngineOutput.visible ) {\r
675       EngineOutputPopUp();\r
676   }\r
677 \r
678   InitBackEnd2();\r
679 \r
680   /* Make the window visible; update its client area; and return "success" */\r
681   EnsureOnScreen(&boardX, &boardY);\r
682   wp.length = sizeof(WINDOWPLACEMENT);\r
683   wp.flags = 0;\r
684   wp.showCmd = nCmdShow;\r
685   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
686   wp.rcNormalPosition.left = boardX;\r
687   wp.rcNormalPosition.right = boardX + winWidth;\r
688   wp.rcNormalPosition.top = boardY;\r
689   wp.rcNormalPosition.bottom = boardY + winHeight;\r
690   SetWindowPlacement(hwndMain, &wp);\r
691 \r
692   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
693                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
694 \r
695 #if 0\r
696   /* [AS] Disable the FRC stuff if not playing the proper variant */\r
697   if( gameInfo.variant != VariantFischeRandom ) {\r
698       EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );\r
699   }\r
700 #endif\r
701   if (hwndConsole) {\r
702 #if AOT_CONSOLE\r
703     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
704                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
705 #endif\r
706     ShowWindow(hwndConsole, nCmdShow);\r
707   }\r
708   UpdateWindow(hwnd);\r
709 \r
710   return TRUE;\r
711 \r
712 }\r
713 \r
714 \r
715 typedef enum {\r
716   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
717   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
718   ArgSettingsFilename\r
719 } ArgType;\r
720 \r
721 typedef struct {\r
722   char *argName;\r
723   ArgType argType;\r
724   /***\r
725   union {\r
726     String *pString;       // ArgString\r
727     int *pInt;             // ArgInt\r
728     float *pFloat;         // ArgFloat\r
729     Boolean *pBoolean;     // ArgBoolean\r
730     COLORREF *pColor;      // ArgColor\r
731     ColorClass cc;         // ArgAttribs\r
732     String *pFilename;     // ArgFilename\r
733     BoardSize *pBoardSize; // ArgBoardSize\r
734     int whichFont;         // ArgFont\r
735     DCB *pDCB;             // ArgCommSettings\r
736     String *pFilename;     // ArgSettingsFilename\r
737   } argLoc;\r
738   ***/\r
739   LPVOID argLoc;\r
740   BOOL save;\r
741 } ArgDescriptor;\r
742 \r
743 int junk;\r
744 ArgDescriptor argDescriptors[] = {\r
745   /* positional arguments */\r
746   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
747   { "", ArgNone, NULL },\r
748   /* keyword arguments */\r
749   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
750   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
751   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
752   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
753   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
754   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
755   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
756   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
757   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
758   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
759   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
760   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
761   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
762   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
763   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
764   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
765   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
766   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
767     FALSE },\r
768   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
769     FALSE },\r
770   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
771     FALSE },\r
772   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
773   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
774     FALSE },\r
775   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
776   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
777   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
778   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
779   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
780   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
781   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
782   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
783   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
784   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
785   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
786   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
787   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
788   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
789   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
790   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
791   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
792   /*!!bitmapDirectory?*/\r
793   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
794   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
795   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
796   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
797   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
798   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
799   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
800   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
801   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
802   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
803   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
804   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
805   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
806   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
807   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
808   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
809   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
810   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
811   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
812   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
813   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
814   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
815   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
816   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
817   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
818   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
819   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
820   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
821   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
822   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
823   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
824   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
825   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
826   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
827   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
828   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
829   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
830   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
831   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
832   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
833   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
834   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
835   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
836   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
837   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
838   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
839   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
840   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
841   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
842   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
843   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
844   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
845   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
846   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
847   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
848   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
849   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
850   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
851   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
852   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
853   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
854   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
855   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
856   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
857   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
858   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
859   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
860   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
861   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
862   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
863   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
864   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
865   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
866   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
867   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
868   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
869   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
870   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
871   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
872   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
873   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
874   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
875   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
876   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
877   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
878   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
879   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
880   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
881   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
882   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
883   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
884   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
885   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
886   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
887     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
888   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
889   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
890   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
891   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
892   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
893   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
894   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
895   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
896     TRUE }, /* must come after all fonts */\r
897   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
898   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
899     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
900   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
901   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
902   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
903   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
904   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
905   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
906   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
907   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
908   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
909   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
910   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
911   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
912   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
913   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
914   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
915   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
916   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
917   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
918   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
919   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
920   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
921   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
922   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
923   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
924   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
925   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
926   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
927   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
928 #if 0\r
929   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
930   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
931 #endif\r
932   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
933   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
934   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
935   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
936   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
937   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
938   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
939   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
940   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
941   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
942   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
943   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
944   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
945   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
946   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
947   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
948   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
949   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
950   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
951   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
952   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
953   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
954   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
955   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
956   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
957   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
958   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
959   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
960   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
961   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
962   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
963   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
964   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
965   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
966   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
967   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
968   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
969   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
970   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
971   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
972   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
973   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
974   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
975   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
976   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
977   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
978   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
979   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
980   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
981   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
982   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
983   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
984   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
985   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
986   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
987   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
988   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
989   { "highlightLastMove", ArgBoolean,\r
990     (LPVOID) &appData.highlightLastMove, TRUE },\r
991   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
992   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
993   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
994   { "highlightDragging", ArgBoolean,\r
995     (LPVOID) &appData.highlightDragging, TRUE },\r
996   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
997   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
998   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
999   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1000   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1001   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1002   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1003   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1004   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1005   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1006   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1007   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1008   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1009   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1010   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1011   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1012   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1013   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1014   { "soundShout", ArgFilename,\r
1015     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1016   { "soundSShout", ArgFilename,\r
1017     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1018   { "soundChannel1", ArgFilename,\r
1019     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1020   { "soundChannel", ArgFilename,\r
1021     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1022   { "soundKibitz", ArgFilename,\r
1023     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1024   { "soundTell", ArgFilename,\r
1025     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1026   { "soundChallenge", ArgFilename,\r
1027     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1028   { "soundRequest", ArgFilename,\r
1029     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1030   { "soundSeek", ArgFilename,\r
1031     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1032   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1033   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1034   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1035   { "soundIcsLoss", ArgFilename, \r
1036     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1037   { "soundIcsDraw", ArgFilename, \r
1038     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1039   { "soundIcsUnfinished", ArgFilename, \r
1040     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1041   { "soundIcsAlarm", ArgFilename, \r
1042     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1043   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1044   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1045   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1046   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1047   { "reuseChessPrograms", ArgBoolean,\r
1048     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1049   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1050   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1051   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1052   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1053   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1054   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1055   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1056   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
1057   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
1058   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
1059   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
1060   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
1061   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
1062   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
1063   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
1064   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
1065   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
1066   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1067   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1068   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
1069   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
1070   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1071   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1072   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
1073   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
1074   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
1075   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
1076   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1077   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1078   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1079   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1080   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1081   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1082   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1083   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1084   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1085   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1086     TRUE },\r
1087   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1088     TRUE },\r
1089   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1090   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1091   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1092   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1093   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1094   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1095   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1096   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1097   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1098   /* [AS] New features */\r
1099   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1100   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1101   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1102   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1103   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1104   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1105   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1106   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1107   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1108   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1109   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1110   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1111   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1112   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1113   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1114   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1115   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1116   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1117   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1118   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1119   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1120   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1121   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1122   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1123   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1124   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1125   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1126   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1127   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1128   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1129   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1130   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1131   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1132   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1133   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1134   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1135   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1136   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1137   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1138   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1139   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1140   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1141   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1142   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1143   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1144   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1145   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1146   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1147 \r
1148   /* [AS] Layout stuff */\r
1149   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1150   { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE },\r
1151   { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE },\r
1152   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1153   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1154 \r
1155   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1156   { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE },\r
1157   { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE },\r
1158   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1159   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1160 \r
1161   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1162   { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE },\r
1163   { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE },\r
1164   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1165   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1166 \r
1167   /* [HGM] board-size, adjudication and misc. options */\r
1168   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1169   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1170   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1171   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1172   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1173   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1174   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1175   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1176   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1177   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1178   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1179   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1180   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1181   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1182   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1183   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1184   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1185   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1186   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1187   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1188   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1189   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1190   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1191 \r
1192 #ifdef ZIPPY\r
1193   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1194   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1195   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1196   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1197   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1198   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1199   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1200   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1201   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1202   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1203   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1204   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1205   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1206     FALSE },\r
1207   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1208   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1209   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1210   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1211   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1212   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1213   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1214     FALSE },\r
1215   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1216   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1217   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1218   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1219   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1220   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1221   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1222   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1223   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1224   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1225   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1226   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1227   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1228   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1229   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1230   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1231   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1232   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1233 #endif\r
1234   /* [HGM] options for broadcasting and time odds */\r
1235   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1236   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1237   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1238   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1239   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1240   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1241   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1242   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1243   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1244   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1245   { NULL, ArgNone, NULL, FALSE }\r
1246 };\r
1247 \r
1248 \r
1249 /* Kludge for indirection files on command line */\r
1250 char* lastIndirectionFilename;\r
1251 ArgDescriptor argDescriptorIndirection =\r
1252 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1253 \r
1254 \r
1255 VOID\r
1256 ExitArgError(char *msg, char *badArg)\r
1257 {\r
1258   char buf[MSG_SIZ];\r
1259 \r
1260   sprintf(buf, "%s %s", msg, badArg);\r
1261   DisplayFatalError(buf, 0, 2);\r
1262   exit(2);\r
1263 }\r
1264 \r
1265 /* Command line font name parser.  NULL name means do nothing.\r
1266    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1267    For backward compatibility, syntax without the colon is also\r
1268    accepted, but font names with digits in them won't work in that case.\r
1269 */\r
1270 VOID\r
1271 ParseFontName(char *name, MyFontParams *mfp)\r
1272 {\r
1273   char *p, *q;\r
1274   if (name == NULL) return;\r
1275   p = name;\r
1276   q = strchr(p, ':');\r
1277   if (q) {\r
1278     if (q - p >= sizeof(mfp->faceName))\r
1279       ExitArgError("Font name too long:", name);\r
1280     memcpy(mfp->faceName, p, q - p);\r
1281     mfp->faceName[q - p] = NULLCHAR;\r
1282     p = q + 1;\r
1283   } else {\r
1284     q = mfp->faceName;\r
1285     while (*p && !isdigit(*p)) {\r
1286       *q++ = *p++;\r
1287       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1288         ExitArgError("Font name too long:", name);\r
1289     }\r
1290     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1291     *q = NULLCHAR;\r
1292   }\r
1293   if (!*p) ExitArgError("Font point size missing:", name);\r
1294   mfp->pointSize = (float) atof(p);\r
1295   mfp->bold = (strchr(p, 'b') != NULL);\r
1296   mfp->italic = (strchr(p, 'i') != NULL);\r
1297   mfp->underline = (strchr(p, 'u') != NULL);\r
1298   mfp->strikeout = (strchr(p, 's') != NULL);\r
1299 }\r
1300 \r
1301 /* Color name parser.\r
1302    X version accepts X color names, but this one\r
1303    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1304 COLORREF\r
1305 ParseColorName(char *name)\r
1306 {\r
1307   int red, green, blue, count;\r
1308   char buf[MSG_SIZ];\r
1309 \r
1310   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1311   if (count != 3) {\r
1312     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1313       &red, &green, &blue);\r
1314   }\r
1315   if (count != 3) {\r
1316     sprintf(buf, "Can't parse color name %s", name);\r
1317     DisplayError(buf, 0);\r
1318     return RGB(0, 0, 0);\r
1319   }\r
1320   return PALETTERGB(red, green, blue);\r
1321 }\r
1322 \r
1323 \r
1324 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1325 {\r
1326   char *e = argValue;\r
1327   int eff = 0;\r
1328 \r
1329   while (*e) {\r
1330     if (*e == 'b')      eff |= CFE_BOLD;\r
1331     else if (*e == 'i') eff |= CFE_ITALIC;\r
1332     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1333     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1334     else if (*e == '#' || isdigit(*e)) break;\r
1335     e++;\r
1336   }\r
1337   *effects = eff;\r
1338   *color   = ParseColorName(e);\r
1339 }\r
1340 \r
1341 \r
1342 BoardSize\r
1343 ParseBoardSize(char *name)\r
1344 {\r
1345   BoardSize bs = SizeTiny;\r
1346   while (sizeInfo[bs].name != NULL) {\r
1347     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1348     bs++;\r
1349   }\r
1350   ExitArgError("Unrecognized board size value", name);\r
1351   return bs; /* not reached */\r
1352 }\r
1353 \r
1354 \r
1355 char\r
1356 StringGet(void *getClosure)\r
1357 {\r
1358   char **p = (char **) getClosure;\r
1359   return *((*p)++);\r
1360 }\r
1361 \r
1362 char\r
1363 FileGet(void *getClosure)\r
1364 {\r
1365   int c;\r
1366   FILE* f = (FILE*) getClosure;\r
1367 \r
1368   c = getc(f);\r
1369   if (c == EOF)\r
1370     return NULLCHAR;\r
1371   else\r
1372     return (char) c;\r
1373 }\r
1374 \r
1375 /* Parse settings file named "name". If file found, return the\r
1376    full name in fullname and return TRUE; else return FALSE */\r
1377 BOOLEAN\r
1378 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1379 {\r
1380   char *dummy;\r
1381   FILE *f;\r
1382 \r
1383   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1384     f = fopen(fullname, "r");\r
1385     if (f != NULL) {\r
1386       ParseArgs(FileGet, f);\r
1387       fclose(f);\r
1388       return TRUE;\r
1389     }\r
1390   }\r
1391   return FALSE;\r
1392 }\r
1393 \r
1394 VOID\r
1395 ParseArgs(GetFunc get, void *cl)\r
1396 {\r
1397   char argName[ARG_MAX];\r
1398   char argValue[ARG_MAX];\r
1399   ArgDescriptor *ad;\r
1400   char start;\r
1401   char *q;\r
1402   int i, octval;\r
1403   char ch;\r
1404   int posarg = 0;\r
1405 \r
1406   ch = get(cl);\r
1407   for (;;) {\r
1408     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1409     if (ch == NULLCHAR) break;\r
1410     if (ch == ';') {\r
1411       /* Comment to end of line */\r
1412       ch = get(cl);\r
1413       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1414       continue;\r
1415     } else if (ch == '/' || ch == '-') {\r
1416       /* Switch */\r
1417       q = argName;\r
1418       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1419              ch != '\n' && ch != '\t') {\r
1420         *q++ = ch;\r
1421         ch = get(cl);\r
1422       }\r
1423       *q = NULLCHAR;\r
1424 \r
1425       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1426         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1427 \r
1428       if (ad->argName == NULL)\r
1429         ExitArgError("Unrecognized argument", argName);\r
1430 \r
1431     } else if (ch == '@') {\r
1432       /* Indirection file */\r
1433       ad = &argDescriptorIndirection;\r
1434       ch = get(cl);\r
1435     } else {\r
1436       /* Positional argument */\r
1437       ad = &argDescriptors[posarg++];\r
1438       strcpy(argName, ad->argName);\r
1439     }\r
1440 \r
1441     if (ad->argType == ArgTrue) {\r
1442       *(Boolean *) ad->argLoc = TRUE;\r
1443       continue;\r
1444     }\r
1445     if (ad->argType == ArgFalse) {\r
1446       *(Boolean *) ad->argLoc = FALSE;\r
1447       continue;\r
1448     }\r
1449 \r
1450     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1451     if (ch == NULLCHAR || ch == '\n') {\r
1452       ExitArgError("No value provided for argument", argName);\r
1453     }\r
1454     q = argValue;\r
1455     if (ch == '{') {\r
1456       // Quoting with { }.  No characters have to (or can) be escaped.\r
1457       // Thus the string cannot contain a '}' character.\r
1458       start = ch;\r
1459       ch = get(cl);\r
1460       while (start) {\r
1461         switch (ch) {\r
1462         case NULLCHAR:\r
1463           start = NULLCHAR;\r
1464           break;\r
1465           \r
1466         case '}':\r
1467           ch = get(cl);\r
1468           start = NULLCHAR;\r
1469           break;\r
1470 \r
1471         default:\r
1472           *q++ = ch;\r
1473           ch = get(cl);\r
1474           break;\r
1475         }\r
1476       }   \r
1477     } else if (ch == '\'' || ch == '"') {\r
1478       // Quoting with ' ' or " ", with \ as escape character.\r
1479       // Inconvenient for long strings that may contain Windows filenames.\r
1480       start = ch;\r
1481       ch = get(cl);\r
1482       while (start) {\r
1483         switch (ch) {\r
1484         case NULLCHAR:\r
1485           start = NULLCHAR;\r
1486           break;\r
1487 \r
1488         default:\r
1489         not_special:\r
1490           *q++ = ch;\r
1491           ch = get(cl);\r
1492           break;\r
1493 \r
1494         case '\'':\r
1495         case '\"':\r
1496           if (ch == start) {\r
1497             ch = get(cl);\r
1498             start = NULLCHAR;\r
1499             break;\r
1500           } else {\r
1501             goto not_special;\r
1502           }\r
1503 \r
1504         case '\\':\r
1505           if (ad->argType == ArgFilename\r
1506               || ad->argType == ArgSettingsFilename) {\r
1507               goto not_special;\r
1508           }\r
1509           ch = get(cl);\r
1510           switch (ch) {\r
1511           case NULLCHAR:\r
1512             ExitArgError("Incomplete \\ escape in value for", argName);\r
1513             break;\r
1514           case 'n':\r
1515             *q++ = '\n';\r
1516             ch = get(cl);\r
1517             break;\r
1518           case 'r':\r
1519             *q++ = '\r';\r
1520             ch = get(cl);\r
1521             break;\r
1522           case 't':\r
1523             *q++ = '\t';\r
1524             ch = get(cl);\r
1525             break;\r
1526           case 'b':\r
1527             *q++ = '\b';\r
1528             ch = get(cl);\r
1529             break;\r
1530           case 'f':\r
1531             *q++ = '\f';\r
1532             ch = get(cl);\r
1533             break;\r
1534           default:\r
1535             octval = 0;\r
1536             for (i = 0; i < 3; i++) {\r
1537               if (ch >= '0' && ch <= '7') {\r
1538                 octval = octval*8 + (ch - '0');\r
1539                 ch = get(cl);\r
1540               } else {\r
1541                 break;\r
1542               }\r
1543             }\r
1544             if (i > 0) {\r
1545               *q++ = (char) octval;\r
1546             } else {\r
1547               *q++ = ch;\r
1548               ch = get(cl);\r
1549             }\r
1550             break;\r
1551           }\r
1552           break;\r
1553         }\r
1554       }\r
1555     } else {\r
1556       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1557         *q++ = ch;\r
1558         ch = get(cl);\r
1559       }\r
1560     }\r
1561     *q = NULLCHAR;\r
1562 \r
1563     switch (ad->argType) {\r
1564     case ArgInt:\r
1565       *(int *) ad->argLoc = atoi(argValue);\r
1566       break;\r
1567 \r
1568     case ArgFloat:\r
1569       *(float *) ad->argLoc = (float) atof(argValue);\r
1570       break;\r
1571 \r
1572     case ArgString:\r
1573     case ArgFilename:\r
1574       *(char **) ad->argLoc = strdup(argValue);\r
1575       break;\r
1576 \r
1577     case ArgSettingsFilename:\r
1578       {\r
1579         char fullname[MSG_SIZ];\r
1580         if (ParseSettingsFile(argValue, fullname)) {\r
1581           if (ad->argLoc != NULL) {\r
1582             *(char **) ad->argLoc = strdup(fullname);\r
1583           }\r
1584         } else {\r
1585           if (ad->argLoc != NULL) {\r
1586           } else {\r
1587             ExitArgError("Failed to open indirection file", argValue);\r
1588           }\r
1589         }\r
1590       }\r
1591       break;\r
1592 \r
1593     case ArgBoolean:\r
1594       switch (argValue[0]) {\r
1595       case 't':\r
1596       case 'T':\r
1597         *(Boolean *) ad->argLoc = TRUE;\r
1598         break;\r
1599       case 'f':\r
1600       case 'F':\r
1601         *(Boolean *) ad->argLoc = FALSE;\r
1602         break;\r
1603       default:\r
1604         ExitArgError("Unrecognized boolean argument value", argValue);\r
1605         break;\r
1606       }\r
1607       break;\r
1608 \r
1609     case ArgColor:\r
1610       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1611       break;\r
1612 \r
1613     case ArgAttribs: {\r
1614       ColorClass cc = (ColorClass)ad->argLoc;\r
1615       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1616       }\r
1617       break;\r
1618       \r
1619     case ArgBoardSize:\r
1620       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1621       break;\r
1622 \r
1623     case ArgFont:\r
1624       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1625       break;\r
1626 \r
1627     case ArgCommSettings:\r
1628       ParseCommSettings(argValue, &dcb);\r
1629       break;\r
1630 \r
1631     case ArgNone:\r
1632       ExitArgError("Unrecognized argument", argValue);\r
1633       break;\r
1634     }\r
1635   }\r
1636 }\r
1637 \r
1638 VOID\r
1639 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1640 {\r
1641   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1642   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1643   DeleteDC(hdc);\r
1644   lf->lfWidth = 0;\r
1645   lf->lfEscapement = 0;\r
1646   lf->lfOrientation = 0;\r
1647   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1648   lf->lfItalic = mfp->italic;\r
1649   lf->lfUnderline = mfp->underline;\r
1650   lf->lfStrikeOut = mfp->strikeout;\r
1651   lf->lfCharSet = DEFAULT_CHARSET;\r
1652   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1653   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1654   lf->lfQuality = DEFAULT_QUALITY;\r
1655   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1656   strcpy(lf->lfFaceName, mfp->faceName);\r
1657 }\r
1658 \r
1659 VOID\r
1660 CreateFontInMF(MyFont *mf)\r
1661 {\r
1662   LFfromMFP(&mf->lf, &mf->mfp);\r
1663   if (mf->hf) DeleteObject(mf->hf);\r
1664   mf->hf = CreateFontIndirect(&mf->lf);\r
1665 }\r
1666 \r
1667 VOID\r
1668 SetDefaultTextAttribs()\r
1669 {\r
1670   ColorClass cc;\r
1671   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1672     ParseAttribs(&textAttribs[cc].color, \r
1673                  &textAttribs[cc].effects, \r
1674                  defaultTextAttribs[cc]);\r
1675   }\r
1676 }\r
1677 \r
1678 VOID\r
1679 SetDefaultSounds()\r
1680 {\r
1681   ColorClass cc;\r
1682   SoundClass sc;\r
1683   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1684     textAttribs[cc].sound.name = strdup("");\r
1685     textAttribs[cc].sound.data = NULL;\r
1686   }\r
1687   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1688     sounds[sc].name = strdup("");\r
1689     sounds[sc].data = NULL;\r
1690   }\r
1691   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1692 }\r
1693 \r
1694 VOID\r
1695 LoadAllSounds()\r
1696 {\r
1697   ColorClass cc;\r
1698   SoundClass sc;\r
1699   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1700     MyLoadSound(&textAttribs[cc].sound);\r
1701   }\r
1702   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1703     MyLoadSound(&sounds[sc]);\r
1704   }\r
1705 }\r
1706 \r
1707 VOID\r
1708 InitAppData(LPSTR lpCmdLine)\r
1709 {\r
1710   int i, j;\r
1711   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1712   char *dummy, *p;\r
1713 \r
1714   programName = szAppName;\r
1715 \r
1716   /* Initialize to defaults */\r
1717   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1718   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1719   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1720   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1721   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1722   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1723   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1724   SetDefaultTextAttribs();\r
1725   SetDefaultSounds();\r
1726   appData.movesPerSession = MOVES_PER_SESSION;\r
1727   appData.initString = INIT_STRING;\r
1728   appData.secondInitString = INIT_STRING;\r
1729   appData.firstComputerString = COMPUTER_STRING;\r
1730   appData.secondComputerString = COMPUTER_STRING;\r
1731   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1732   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1733   appData.firstPlaysBlack = FALSE;\r
1734   appData.noChessProgram = FALSE;\r
1735   chessProgram = FALSE;\r
1736   appData.firstHost = FIRST_HOST;\r
1737   appData.secondHost = SECOND_HOST;\r
1738   appData.firstDirectory = FIRST_DIRECTORY;\r
1739   appData.secondDirectory = SECOND_DIRECTORY;\r
1740   appData.bitmapDirectory = "";\r
1741   appData.remoteShell = REMOTE_SHELL;\r
1742   appData.remoteUser = "";\r
1743   appData.timeDelay = TIME_DELAY;\r
1744   appData.timeControl = TIME_CONTROL;\r
1745   appData.timeIncrement = TIME_INCREMENT;\r
1746   appData.icsActive = FALSE;\r
1747   appData.icsHost = "";\r
1748   appData.icsPort = ICS_PORT;\r
1749   appData.icsCommPort = ICS_COMM_PORT;\r
1750   appData.icsLogon = ICS_LOGON;\r
1751   appData.icsHelper = "";\r
1752   appData.useTelnet = FALSE;\r
1753   appData.telnetProgram = TELNET_PROGRAM;\r
1754   appData.gateway = "";\r
1755   appData.loadGameFile = "";\r
1756   appData.loadGameIndex = 0;\r
1757   appData.saveGameFile = "";\r
1758   appData.autoSaveGames = FALSE;\r
1759   appData.loadPositionFile = "";\r
1760   appData.loadPositionIndex = 1;\r
1761   appData.savePositionFile = "";\r
1762   appData.matchMode = FALSE;\r
1763   appData.matchGames = 0;\r
1764   appData.monoMode = FALSE;\r
1765   appData.debugMode = FALSE;\r
1766   appData.clockMode = TRUE;\r
1767   boardSize = (BoardSize) -1; /* determine by screen size */\r
1768   appData.Iconic = FALSE; /*unused*/\r
1769   appData.searchTime = "";\r
1770   appData.searchDepth = 0;\r
1771   appData.showCoords = FALSE;\r
1772   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1773   appData.autoCallFlag = FALSE;\r
1774   appData.flipView = FALSE;\r
1775   appData.autoFlipView = TRUE;\r
1776   appData.cmailGameName = "";\r
1777   appData.alwaysPromoteToQueen = FALSE;\r
1778   appData.oldSaveStyle = FALSE;\r
1779   appData.quietPlay = FALSE;\r
1780   appData.showThinking = FALSE;\r
1781   appData.ponderNextMove = TRUE;\r
1782   appData.periodicUpdates = TRUE;\r
1783   appData.popupExitMessage = TRUE;\r
1784   appData.popupMoveErrors = FALSE;\r
1785   appData.autoObserve = FALSE;\r
1786   appData.autoComment = FALSE;\r
1787   appData.animate = TRUE;\r
1788   appData.animSpeed = 10;\r
1789   appData.animateDragging = TRUE;\r
1790   appData.highlightLastMove = TRUE;\r
1791   appData.getMoveList = TRUE;\r
1792   appData.testLegality = TRUE;\r
1793   appData.premove = TRUE;\r
1794   appData.premoveWhite = FALSE;\r
1795   appData.premoveWhiteText = "";\r
1796   appData.premoveBlack = FALSE;\r
1797   appData.premoveBlackText = "";\r
1798   appData.icsAlarm = TRUE;\r
1799   appData.icsAlarmTime = 5000;\r
1800   appData.autoRaiseBoard = TRUE;\r
1801   appData.localLineEditing = TRUE;\r
1802   appData.colorize = TRUE;\r
1803   appData.reuseFirst = TRUE;\r
1804   appData.reuseSecond = TRUE;\r
1805   appData.blindfold = FALSE;\r
1806   dcb.DCBlength = sizeof(DCB);\r
1807   dcb.BaudRate = 9600;\r
1808   dcb.fBinary = TRUE;\r
1809   dcb.fParity = FALSE;\r
1810   dcb.fOutxCtsFlow = FALSE;\r
1811   dcb.fOutxDsrFlow = FALSE;\r
1812   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1813   dcb.fDsrSensitivity = FALSE;\r
1814   dcb.fTXContinueOnXoff = TRUE;\r
1815   dcb.fOutX = FALSE;\r
1816   dcb.fInX = FALSE;\r
1817   dcb.fNull = FALSE;\r
1818   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1819   dcb.fAbortOnError = FALSE;\r
1820   dcb.wReserved = 0;\r
1821   dcb.ByteSize = 7;\r
1822   dcb.Parity = SPACEPARITY;\r
1823   dcb.StopBits = ONESTOPBIT;\r
1824   settingsFileName = SETTINGS_FILE;\r
1825   saveSettingsOnExit = TRUE;\r
1826   boardX = CW_USEDEFAULT;\r
1827   boardY = CW_USEDEFAULT;\r
1828   consoleX = CW_USEDEFAULT; \r
1829   consoleY = CW_USEDEFAULT; \r
1830   consoleW = CW_USEDEFAULT;\r
1831   consoleH = CW_USEDEFAULT;\r
1832   analysisX = CW_USEDEFAULT; \r
1833   analysisY = CW_USEDEFAULT; \r
1834   analysisW = CW_USEDEFAULT;\r
1835   analysisH = CW_USEDEFAULT;\r
1836   commentX = CW_USEDEFAULT; \r
1837   commentY = CW_USEDEFAULT; \r
1838   commentW = CW_USEDEFAULT;\r
1839   commentH = CW_USEDEFAULT;\r
1840   editTagsX = CW_USEDEFAULT; \r
1841   editTagsY = CW_USEDEFAULT; \r
1842   editTagsW = CW_USEDEFAULT;\r
1843   editTagsH = CW_USEDEFAULT;\r
1844   gameListX = CW_USEDEFAULT; \r
1845   gameListY = CW_USEDEFAULT; \r
1846   gameListW = CW_USEDEFAULT;\r
1847   gameListH = CW_USEDEFAULT;\r
1848   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1849   icsNames = ICS_NAMES;\r
1850   firstChessProgramNames = FCP_NAMES;\r
1851   secondChessProgramNames = SCP_NAMES;\r
1852   appData.initialMode = "";\r
1853   appData.variant = "normal";\r
1854   appData.firstProtocolVersion = PROTOVER;\r
1855   appData.secondProtocolVersion = PROTOVER;\r
1856   appData.showButtonBar = TRUE;\r
1857 \r
1858    /* [AS] New properties (see comments in header file) */\r
1859   appData.firstScoreIsAbsolute = FALSE;\r
1860   appData.secondScoreIsAbsolute = FALSE;\r
1861   appData.saveExtendedInfoInPGN = FALSE;\r
1862   appData.hideThinkingFromHuman = FALSE;\r
1863   appData.liteBackTextureFile = "";\r
1864   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1865   appData.darkBackTextureFile = "";\r
1866   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
1867   appData.renderPiecesWithFont = "";\r
1868   appData.fontToPieceTable = "";\r
1869   appData.fontBackColorWhite = 0;\r
1870   appData.fontForeColorWhite = 0;\r
1871   appData.fontBackColorBlack = 0;\r
1872   appData.fontForeColorBlack = 0;\r
1873   appData.fontPieceSize = 80;\r
1874   appData.overrideLineGap = 1;\r
1875   appData.adjudicateLossThreshold = 0;\r
1876   appData.delayBeforeQuit = 0;\r
1877   appData.delayAfterQuit = 0;\r
1878   appData.nameOfDebugFile = "winboard.debug";\r
1879   appData.pgnEventHeader = "Computer Chess Game";\r
1880   appData.defaultFrcPosition = -1;\r
1881   appData.gameListTags = GLT_DEFAULT_TAGS;\r
1882   appData.saveOutOfBookInfo = TRUE;\r
1883   appData.showEvalInMoveHistory = TRUE;\r
1884   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
1885   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
1886   appData.highlightMoveWithArrow = FALSE;\r
1887   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
1888   appData.useStickyWindows = TRUE;\r
1889   appData.adjudicateDrawMoves = 0;\r
1890   appData.autoDisplayComment = TRUE;\r
1891   appData.autoDisplayTags = TRUE;\r
1892   appData.firstIsUCI = FALSE;\r
1893   appData.secondIsUCI = FALSE;\r
1894   appData.firstHasOwnBookUCI = TRUE;\r
1895   appData.secondHasOwnBookUCI = TRUE;\r
1896   appData.polyglotDir = "";\r
1897   appData.usePolyglotBook = FALSE;\r
1898   appData.polyglotBook = "";\r
1899   appData.defaultHashSize = 64;\r
1900   appData.defaultCacheSizeEGTB = 4;\r
1901   appData.defaultPathEGTB = "c:\\egtb";\r
1902 \r
1903   InitWindowPlacement( &wpMoveHistory );\r
1904   InitWindowPlacement( &wpEvalGraph );\r
1905   InitWindowPlacement( &wpEngineOutput );\r
1906 \r
1907   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
1908   appData.NrFiles      = -1;\r
1909   appData.NrRanks      = -1;\r
1910   appData.holdingsSize = -1;\r
1911   appData.testClaims   = FALSE;\r
1912   appData.checkMates   = FALSE;\r
1913   appData.materialDraws= FALSE;\r
1914   appData.trivialDraws = FALSE;\r
1915   appData.ruleMoves    = 51;\r
1916   appData.drawRepeats  = 6;\r
1917   appData.matchPause   = 10000;\r
1918   appData.alphaRank    = FALSE;\r
1919   appData.allWhite     = FALSE;\r
1920   appData.upsideDown   = FALSE;\r
1921   appData.serverPause  = 15;\r
1922   appData.serverMovesName   = NULL;\r
1923   appData.suppressLoadMoves = FALSE;\r
1924   appData.firstTimeOdds  = 1;\r
1925   appData.secondTimeOdds = 1;\r
1926   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
1927   appData.secondAccumulateTC = 1;\r
1928   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
1929   appData.secondNPS = -1;\r
1930   appData.engineComments = 1;\r
1931   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
1932   appData.egtFormats = "";\r
1933 \r
1934 #ifdef ZIPPY\r
1935   appData.zippyTalk = ZIPPY_TALK;\r
1936   appData.zippyPlay = ZIPPY_PLAY;\r
1937   appData.zippyLines = ZIPPY_LINES;\r
1938   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1939   appData.zippyPassword = ZIPPY_PASSWORD;\r
1940   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1941   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1942   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1943   appData.zippyUseI = ZIPPY_USE_I;\r
1944   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1945   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1946   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1947   appData.zippyGameStart = ZIPPY_GAME_START;\r
1948   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1949   appData.zippyAbort = ZIPPY_ABORT;\r
1950   appData.zippyVariants = ZIPPY_VARIANTS;\r
1951   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1952   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1953 #endif\r
1954 \r
1955   /* Point font array elements to structures and\r
1956      parse default font names */\r
1957   for (i=0; i<NUM_FONTS; i++) {\r
1958     for (j=0; j<NUM_SIZES; j++) {\r
1959       font[j][i] = &fontRec[j][i];\r
1960       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1961     }\r
1962   }\r
1963   \r
1964   /* Parse default settings file if any */\r
1965   if (ParseSettingsFile(settingsFileName, buf)) {\r
1966     settingsFileName = strdup(buf);\r
1967   }\r
1968 \r
1969   /* Parse command line */\r
1970   ParseArgs(StringGet, &lpCmdLine);\r
1971 \r
1972   /* [HGM] make sure board size is acceptable */\r
1973   if(appData.NrFiles > BOARD_SIZE ||\r
1974      appData.NrRanks > BOARD_SIZE   )\r
1975       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
1976 \r
1977   /* [HGM] After parsing the options from the .ini file, and overruling them\r
1978    * with options from the command line, we now make an even higher priority\r
1979    * overrule by WB options attached to the engine command line. This so that\r
1980    * tournament managers can use WB options (such as /timeOdds) that follow\r
1981    * the engines.\r
1982    */\r
1983   if(appData.firstChessProgram != NULL) {\r
1984       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
1985       static char *f = "first";\r
1986       char buf[MSG_SIZ], *q = buf;\r
1987       if(p != NULL) { // engine command line contains WinBoard options\r
1988           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
1989           ParseArgs(StringGet, &q);\r
1990           p[-1] = 0; // cut them offengine command line\r
1991       }\r
1992   }\r
1993   // now do same for second chess program\r
1994   if(appData.secondChessProgram != NULL) {\r
1995       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
1996       static char *s = "second";\r
1997       char buf[MSG_SIZ], *q = buf;\r
1998       if(p != NULL) { // engine command line contains WinBoard options\r
1999           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2000           ParseArgs(StringGet, &q);\r
2001           p[-1] = 0; // cut them offengine command line\r
2002       }\r
2003   }\r
2004 \r
2005 \r
2006   /* Propagate options that affect others */\r
2007   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2008   if (appData.icsActive || appData.noChessProgram) {\r
2009      chessProgram = FALSE;  /* not local chess program mode */\r
2010   }\r
2011 \r
2012   /* Open startup dialog if needed */\r
2013   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2014       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2015       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2016                         *appData.secondChessProgram == NULLCHAR))) {\r
2017     FARPROC lpProc;\r
2018     \r
2019     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2020     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2021     FreeProcInstance(lpProc);\r
2022   }\r
2023 \r
2024   /* Make sure save files land in the right (?) directory */\r
2025   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2026     appData.saveGameFile = strdup(buf);\r
2027   }\r
2028   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2029     appData.savePositionFile = strdup(buf);\r
2030   }\r
2031 \r
2032   /* Finish initialization for fonts and sounds */\r
2033   for (i=0; i<NUM_FONTS; i++) {\r
2034     for (j=0; j<NUM_SIZES; j++) {\r
2035       CreateFontInMF(font[j][i]);\r
2036     }\r
2037   }\r
2038   /* xboard, and older WinBoards, controlled the move sound with the\r
2039      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2040      always turn the option on (so that the backend will call us),\r
2041      then let the user turn the sound off by setting it to silence if\r
2042      desired.  To accommodate old winboard.ini files saved by old\r
2043      versions of WinBoard, we also turn off the sound if the option\r
2044      was initially set to false. */\r
2045   if (!appData.ringBellAfterMoves) {\r
2046     sounds[(int)SoundMove].name = strdup("");\r
2047     appData.ringBellAfterMoves = TRUE;\r
2048   }\r
2049   GetCurrentDirectory(MSG_SIZ, currDir);\r
2050   SetCurrentDirectory(installDir);\r
2051   LoadAllSounds();\r
2052   SetCurrentDirectory(currDir);\r
2053 \r
2054   p = icsTextMenuString;\r
2055   if (p[0] == '@') {\r
2056     FILE* f = fopen(p + 1, "r");\r
2057     if (f == NULL) {\r
2058       DisplayFatalError(p + 1, errno, 2);\r
2059       return;\r
2060     }\r
2061     i = fread(buf, 1, sizeof(buf)-1, f);\r
2062     fclose(f);\r
2063     buf[i] = NULLCHAR;\r
2064     p = buf;\r
2065   }\r
2066   ParseIcsTextMenu(strdup(p));\r
2067 }\r
2068 \r
2069 \r
2070 VOID\r
2071 InitMenuChecks()\r
2072 {\r
2073   HMENU hmenu = GetMenu(hwndMain);\r
2074 \r
2075   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2076                         MF_BYCOMMAND|((appData.icsActive &&\r
2077                                        *appData.icsCommPort != NULLCHAR) ?\r
2078                                       MF_ENABLED : MF_GRAYED));\r
2079   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2080                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2081                                      MF_CHECKED : MF_UNCHECKED));\r
2082 }\r
2083 \r
2084 \r
2085 VOID\r
2086 SaveSettings(char* name)\r
2087 {\r
2088   FILE *f;\r
2089   ArgDescriptor *ad;\r
2090   WINDOWPLACEMENT wp;\r
2091   char dir[MSG_SIZ];\r
2092 \r
2093   if (!hwndMain) return;\r
2094 \r
2095   GetCurrentDirectory(MSG_SIZ, dir);\r
2096   SetCurrentDirectory(installDir);\r
2097   f = fopen(name, "w");\r
2098   SetCurrentDirectory(dir);\r
2099   if (f == NULL) {\r
2100     DisplayError(name, errno);\r
2101     return;\r
2102   }\r
2103   fprintf(f, ";\n");\r
2104   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2105   fprintf(f, ";\n");\r
2106   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2107   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2108   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2109   fprintf(f, ";\n");\r
2110 \r
2111   wp.length = sizeof(WINDOWPLACEMENT);\r
2112   GetWindowPlacement(hwndMain, &wp);\r
2113   boardX = wp.rcNormalPosition.left;\r
2114   boardY = wp.rcNormalPosition.top;\r
2115 \r
2116   if (hwndConsole) {\r
2117     GetWindowPlacement(hwndConsole, &wp);\r
2118     consoleX = wp.rcNormalPosition.left;\r
2119     consoleY = wp.rcNormalPosition.top;\r
2120     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2121     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2122   }\r
2123 \r
2124   if (analysisDialog) {\r
2125     GetWindowPlacement(analysisDialog, &wp);\r
2126     analysisX = wp.rcNormalPosition.left;\r
2127     analysisY = wp.rcNormalPosition.top;\r
2128     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2129     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2130   }\r
2131 \r
2132   if (commentDialog) {\r
2133     GetWindowPlacement(commentDialog, &wp);\r
2134     commentX = wp.rcNormalPosition.left;\r
2135     commentY = wp.rcNormalPosition.top;\r
2136     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2137     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2138   }\r
2139 \r
2140   if (editTagsDialog) {\r
2141     GetWindowPlacement(editTagsDialog, &wp);\r
2142     editTagsX = wp.rcNormalPosition.left;\r
2143     editTagsY = wp.rcNormalPosition.top;\r
2144     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2145     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2146   }\r
2147 \r
2148   if (gameListDialog) {\r
2149     GetWindowPlacement(gameListDialog, &wp);\r
2150     gameListX = wp.rcNormalPosition.left;\r
2151     gameListY = wp.rcNormalPosition.top;\r
2152     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2153     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2154   }\r
2155 \r
2156   /* [AS] Move history */\r
2157   wpMoveHistory.visible = MoveHistoryIsUp();\r
2158   \r
2159   if( moveHistoryDialog ) {\r
2160     GetWindowPlacement(moveHistoryDialog, &wp);\r
2161     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2162     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2163     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2164     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2165   }\r
2166 \r
2167   /* [AS] Eval graph */\r
2168   wpEvalGraph.visible = EvalGraphIsUp();\r
2169 \r
2170   if( evalGraphDialog ) {\r
2171     GetWindowPlacement(evalGraphDialog, &wp);\r
2172     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2173     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2174     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2175     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2176   }\r
2177 \r
2178   /* [AS] Engine output */\r
2179   wpEngineOutput.visible = EngineOutputIsUp();\r
2180 \r
2181   if( engineOutputDialog ) {\r
2182     GetWindowPlacement(engineOutputDialog, &wp);\r
2183     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2184     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2185     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2186     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2187   }\r
2188 \r
2189   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2190     if (!ad->save) continue;\r
2191     switch (ad->argType) {\r
2192     case ArgString:\r
2193       {\r
2194         char *p = *(char **)ad->argLoc;\r
2195         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2196           /* Quote multiline values or \-containing values\r
2197              with { } if possible */\r
2198           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2199         } else {\r
2200           /* Else quote with " " */\r
2201           fprintf(f, "/%s=\"", ad->argName);\r
2202           while (*p) {\r
2203             if (*p == '\n') fprintf(f, "\n");\r
2204             else if (*p == '\r') fprintf(f, "\\r");\r
2205             else if (*p == '\t') fprintf(f, "\\t");\r
2206             else if (*p == '\b') fprintf(f, "\\b");\r
2207             else if (*p == '\f') fprintf(f, "\\f");\r
2208             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2209             else if (*p == '\"') fprintf(f, "\\\"");\r
2210             else if (*p == '\\') fprintf(f, "\\\\");\r
2211             else putc(*p, f);\r
2212             p++;\r
2213           }\r
2214           fprintf(f, "\"\n");\r
2215         }\r
2216       }\r
2217       break;\r
2218     case ArgInt:\r
2219       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2220       break;\r
2221     case ArgFloat:\r
2222       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2223       break;\r
2224     case ArgBoolean:\r
2225       fprintf(f, "/%s=%s\n", ad->argName, \r
2226         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2227       break;\r
2228     case ArgTrue:\r
2229       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2230       break;\r
2231     case ArgFalse:\r
2232       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2233       break;\r
2234     case ArgColor:\r
2235       {\r
2236         COLORREF color = *(COLORREF *)ad->argLoc;\r
2237         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
2238           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2239       }\r
2240       break;\r
2241     case ArgAttribs:\r
2242       {\r
2243         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2244         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
2245           (ta->effects & CFE_BOLD) ? "b" : "",\r
2246           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2247           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2248           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2249           (ta->effects) ? " " : "",\r
2250           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2251       }\r
2252       break;\r
2253     case ArgFilename:\r
2254       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2255         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2256       } else {\r
2257         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2258       }\r
2259       break;\r
2260     case ArgBoardSize:\r
2261       fprintf(f, "/%s=%s\n", ad->argName,\r
2262               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2263       break;\r
2264     case ArgFont:\r
2265       {\r
2266         int bs;\r
2267         for (bs=0; bs<NUM_SIZES; bs++) {\r
2268           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2269           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2270           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2271             ad->argName, mfp->faceName, mfp->pointSize,\r
2272             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2273             mfp->bold ? "b" : "",\r
2274             mfp->italic ? "i" : "",\r
2275             mfp->underline ? "u" : "",\r
2276             mfp->strikeout ? "s" : "");\r
2277         }\r
2278       }\r
2279       break;\r
2280     case ArgCommSettings:\r
2281       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2282     }\r
2283   }\r
2284   fclose(f);\r
2285 }\r
2286 \r
2287 \r
2288 \r
2289 /*---------------------------------------------------------------------------*\\r
2290  *\r
2291  * GDI board drawing routines\r
2292  *\r
2293 \*---------------------------------------------------------------------------*/\r
2294 \r
2295 /* [AS] Draw square using background texture */\r
2296 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2297 {\r
2298     XFORM   x;\r
2299 \r
2300     if( mode == 0 ) {\r
2301         return; /* Should never happen! */\r
2302     }\r
2303 \r
2304     SetGraphicsMode( dst, GM_ADVANCED );\r
2305 \r
2306     switch( mode ) {\r
2307     case 1:\r
2308         /* Identity */\r
2309         break;\r
2310     case 2:\r
2311         /* X reflection */\r
2312         x.eM11 = -1.0;\r
2313         x.eM12 = 0;\r
2314         x.eM21 = 0;\r
2315         x.eM22 = 1.0;\r
2316         x.eDx = (FLOAT) dw + dx - 1;\r
2317         x.eDy = 0;\r
2318         dx = 0;\r
2319         SetWorldTransform( dst, &x );\r
2320         break;\r
2321     case 3:\r
2322         /* Y reflection */\r
2323         x.eM11 = 1.0;\r
2324         x.eM12 = 0;\r
2325         x.eM21 = 0;\r
2326         x.eM22 = -1.0;\r
2327         x.eDx = 0;\r
2328         x.eDy = (FLOAT) dh + dy - 1;\r
2329         dy = 0;\r
2330         SetWorldTransform( dst, &x );\r
2331         break;\r
2332     case 4:\r
2333         /* X/Y flip */\r
2334         x.eM11 = 0;\r
2335         x.eM12 = 1.0;\r
2336         x.eM21 = 1.0;\r
2337         x.eM22 = 0;\r
2338         x.eDx = (FLOAT) dx;\r
2339         x.eDy = (FLOAT) dy;\r
2340         dx = 0;\r
2341         dy = 0;\r
2342         SetWorldTransform( dst, &x );\r
2343         break;\r
2344     }\r
2345 \r
2346     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2347 \r
2348     x.eM11 = 1.0;\r
2349     x.eM12 = 0;\r
2350     x.eM21 = 0;\r
2351     x.eM22 = 1.0;\r
2352     x.eDx = 0;\r
2353     x.eDy = 0;\r
2354     SetWorldTransform( dst, &x );\r
2355 \r
2356     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2357 }\r
2358 \r
2359 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2360 enum {\r
2361     PM_WP = (int) WhitePawn, \r
2362     PM_WN = (int) WhiteKnight, \r
2363     PM_WB = (int) WhiteBishop, \r
2364     PM_WR = (int) WhiteRook, \r
2365     PM_WQ = (int) WhiteQueen, \r
2366     PM_WF = (int) WhiteFerz, \r
2367     PM_WW = (int) WhiteWazir, \r
2368     PM_WE = (int) WhiteAlfil, \r
2369     PM_WM = (int) WhiteMan, \r
2370     PM_WO = (int) WhiteCannon, \r
2371     PM_WU = (int) WhiteUnicorn, \r
2372     PM_WH = (int) WhiteNightrider, \r
2373     PM_WA = (int) WhiteAngel, \r
2374     PM_WC = (int) WhiteMarshall, \r
2375     PM_WAB = (int) WhiteCardinal, \r
2376     PM_WD = (int) WhiteDragon, \r
2377     PM_WL = (int) WhiteLance, \r
2378     PM_WS = (int) WhiteCobra, \r
2379     PM_WV = (int) WhiteFalcon, \r
2380     PM_WSG = (int) WhiteSilver, \r
2381     PM_WG = (int) WhiteGrasshopper, \r
2382     PM_WK = (int) WhiteKing,\r
2383     PM_BP = (int) BlackPawn, \r
2384     PM_BN = (int) BlackKnight, \r
2385     PM_BB = (int) BlackBishop, \r
2386     PM_BR = (int) BlackRook, \r
2387     PM_BQ = (int) BlackQueen, \r
2388     PM_BF = (int) BlackFerz, \r
2389     PM_BW = (int) BlackWazir, \r
2390     PM_BE = (int) BlackAlfil, \r
2391     PM_BM = (int) BlackMan,\r
2392     PM_BO = (int) BlackCannon, \r
2393     PM_BU = (int) BlackUnicorn, \r
2394     PM_BH = (int) BlackNightrider, \r
2395     PM_BA = (int) BlackAngel, \r
2396     PM_BC = (int) BlackMarshall, \r
2397     PM_BG = (int) BlackGrasshopper, \r
2398     PM_BAB = (int) BlackCardinal,\r
2399     PM_BD = (int) BlackDragon,\r
2400     PM_BL = (int) BlackLance,\r
2401     PM_BS = (int) BlackCobra,\r
2402     PM_BV = (int) BlackFalcon,\r
2403     PM_BSG = (int) BlackSilver,\r
2404     PM_BK = (int) BlackKing\r
2405 };\r
2406 \r
2407 static HFONT hPieceFont = NULL;\r
2408 static HBITMAP hPieceMask[(int) EmptySquare];\r
2409 static HBITMAP hPieceFace[(int) EmptySquare];\r
2410 static int fontBitmapSquareSize = 0;\r
2411 static char pieceToFontChar[(int) EmptySquare] =\r
2412                               { 'p', 'n', 'b', 'r', 'q', \r
2413                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2414                       'k', 'o', 'm', 'v', 't', 'w', \r
2415                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2416                                                               'l' };\r
2417 \r
2418 extern BOOL SetCharTable( char *table, const char * map );\r
2419 /* [HGM] moved to backend.c */\r
2420 \r
2421 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2422 {\r
2423     HBRUSH hbrush;\r
2424     BYTE r1 = GetRValue( color );\r
2425     BYTE g1 = GetGValue( color );\r
2426     BYTE b1 = GetBValue( color );\r
2427     BYTE r2 = r1 / 2;\r
2428     BYTE g2 = g1 / 2;\r
2429     BYTE b2 = b1 / 2;\r
2430     RECT rc;\r
2431 \r
2432     /* Create a uniform background first */\r
2433     hbrush = CreateSolidBrush( color );\r
2434     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2435     FillRect( hdc, &rc, hbrush );\r
2436     DeleteObject( hbrush );\r
2437     \r
2438     if( mode == 1 ) {\r
2439         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2440         int steps = squareSize / 2;\r
2441         int i;\r
2442 \r
2443         for( i=0; i<steps; i++ ) {\r
2444             BYTE r = r1 - (r1-r2) * i / steps;\r
2445             BYTE g = g1 - (g1-g2) * i / steps;\r
2446             BYTE b = b1 - (b1-b2) * i / steps;\r
2447 \r
2448             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2449             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2450             FillRect( hdc, &rc, hbrush );\r
2451             DeleteObject(hbrush);\r
2452         }\r
2453     }\r
2454     else if( mode == 2 ) {\r
2455         /* Diagonal gradient, good more or less for every piece */\r
2456         POINT triangle[3];\r
2457         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2458         HBRUSH hbrush_old;\r
2459         int steps = squareSize;\r
2460         int i;\r
2461 \r
2462         triangle[0].x = squareSize - steps;\r
2463         triangle[0].y = squareSize;\r
2464         triangle[1].x = squareSize;\r
2465         triangle[1].y = squareSize;\r
2466         triangle[2].x = squareSize;\r
2467         triangle[2].y = squareSize - steps;\r
2468 \r
2469         for( i=0; i<steps; i++ ) {\r
2470             BYTE r = r1 - (r1-r2) * i / steps;\r
2471             BYTE g = g1 - (g1-g2) * i / steps;\r
2472             BYTE b = b1 - (b1-b2) * i / steps;\r
2473 \r
2474             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2475             hbrush_old = SelectObject( hdc, hbrush );\r
2476             Polygon( hdc, triangle, 3 );\r
2477             SelectObject( hdc, hbrush_old );\r
2478             DeleteObject(hbrush);\r
2479             triangle[0].x++;\r
2480             triangle[2].y++;\r
2481         }\r
2482 \r
2483         SelectObject( hdc, hpen );\r
2484     }\r
2485 }\r
2486 \r
2487 /*\r
2488     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2489     seems to work ok. The main problem here is to find the "inside" of a chess\r
2490     piece: follow the steps as explained below.\r
2491 */\r
2492 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2493 {\r
2494     HBITMAP hbm;\r
2495     HBITMAP hbm_old;\r
2496     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2497     RECT rc;\r
2498     SIZE sz;\r
2499     POINT pt;\r
2500     int backColor = whitePieceColor; \r
2501     int foreColor = blackPieceColor;\r
2502     \r
2503     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2504         backColor = appData.fontBackColorWhite;\r
2505         foreColor = appData.fontForeColorWhite;\r
2506     }\r
2507     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2508         backColor = appData.fontBackColorBlack;\r
2509         foreColor = appData.fontForeColorBlack;\r
2510     }\r
2511 \r
2512     /* Mask */\r
2513     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2514 \r
2515     hbm_old = SelectObject( hdc, hbm );\r
2516 \r
2517     rc.left = 0;\r
2518     rc.top = 0;\r
2519     rc.right = squareSize;\r
2520     rc.bottom = squareSize;\r
2521 \r
2522     /* Step 1: background is now black */\r
2523     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2524 \r
2525     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2526 \r
2527     pt.x = (squareSize - sz.cx) / 2;\r
2528     pt.y = (squareSize - sz.cy) / 2;\r
2529 \r
2530     SetBkMode( hdc, TRANSPARENT );\r
2531     SetTextColor( hdc, chroma );\r
2532     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2533     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2534 \r
2535     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2536     /* Step 3: the area outside the piece is filled with white */\r
2537 //    FloodFill( hdc, 0, 0, chroma );\r
2538     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2539     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2540     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2541     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2542     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2543     /* \r
2544         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2545         but if the start point is not inside the piece we're lost!\r
2546         There should be a better way to do this... if we could create a region or path\r
2547         from the fill operation we would be fine for example.\r
2548     */\r
2549 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2550     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2551 \r
2552     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2553         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2554         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2555 \r
2556         SelectObject( dc2, bm2 );\r
2557         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2558         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2559         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2560         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2561         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2562 \r
2563         DeleteDC( dc2 );\r
2564         DeleteObject( bm2 );\r
2565     }\r
2566 \r
2567     SetTextColor( hdc, 0 );\r
2568     /* \r
2569         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2570         draw the piece again in black for safety.\r
2571     */\r
2572     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2573 \r
2574     SelectObject( hdc, hbm_old );\r
2575 \r
2576     if( hPieceMask[index] != NULL ) {\r
2577         DeleteObject( hPieceMask[index] );\r
2578     }\r
2579 \r
2580     hPieceMask[index] = hbm;\r
2581 \r
2582     /* Face */\r
2583     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2584 \r
2585     SelectObject( hdc, hbm );\r
2586 \r
2587     {\r
2588         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2589         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2590         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2591 \r
2592         SelectObject( dc1, hPieceMask[index] );\r
2593         SelectObject( dc2, bm2 );\r
2594         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2595         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2596         \r
2597         /* \r
2598             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2599             the piece background and deletes (makes transparent) the rest.\r
2600             Thanks to that mask, we are free to paint the background with the greates\r
2601             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2602             We use this, to make gradients and give the pieces a "roundish" look.\r
2603         */\r
2604         SetPieceBackground( hdc, backColor, 2 );\r
2605         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2606 \r
2607         DeleteDC( dc2 );\r
2608         DeleteDC( dc1 );\r
2609         DeleteObject( bm2 );\r
2610     }\r
2611 \r
2612     SetTextColor( hdc, foreColor );\r
2613     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );\r
2614 \r
2615     SelectObject( hdc, hbm_old );\r
2616 \r
2617     if( hPieceFace[index] != NULL ) {\r
2618         DeleteObject( hPieceFace[index] );\r
2619     }\r
2620 \r
2621     hPieceFace[index] = hbm;\r
2622 }\r
2623 \r
2624 static int TranslatePieceToFontPiece( int piece )\r
2625 {\r
2626     switch( piece ) {\r
2627     case BlackPawn:\r
2628         return PM_BP;\r
2629     case BlackKnight:\r
2630         return PM_BN;\r
2631     case BlackBishop:\r
2632         return PM_BB;\r
2633     case BlackRook:\r
2634         return PM_BR;\r
2635     case BlackQueen:\r
2636         return PM_BQ;\r
2637     case BlackKing:\r
2638         return PM_BK;\r
2639     case WhitePawn:\r
2640         return PM_WP;\r
2641     case WhiteKnight:\r
2642         return PM_WN;\r
2643     case WhiteBishop:\r
2644         return PM_WB;\r
2645     case WhiteRook:\r
2646         return PM_WR;\r
2647     case WhiteQueen:\r
2648         return PM_WQ;\r
2649     case WhiteKing:\r
2650         return PM_WK;\r
2651 \r
2652     case BlackAngel:\r
2653         return PM_BA;\r
2654     case BlackMarshall:\r
2655         return PM_BC;\r
2656     case BlackFerz:\r
2657         return PM_BF;\r
2658     case BlackNightrider:\r
2659         return PM_BH;\r
2660     case BlackAlfil:\r
2661         return PM_BE;\r
2662     case BlackWazir:\r
2663         return PM_BW;\r
2664     case BlackUnicorn:\r
2665         return PM_BU;\r
2666     case BlackCannon:\r
2667         return PM_BO;\r
2668     case BlackGrasshopper:\r
2669         return PM_BG;\r
2670     case BlackMan:\r
2671         return PM_BM;\r
2672     case BlackSilver:\r
2673         return PM_BSG;\r
2674     case BlackLance:\r
2675         return PM_BL;\r
2676     case BlackFalcon:\r
2677         return PM_BV;\r
2678     case BlackCobra:\r
2679         return PM_BS;\r
2680     case BlackCardinal:\r
2681         return PM_BAB;\r
2682     case BlackDragon:\r
2683         return PM_BD;\r
2684 \r
2685     case WhiteAngel:\r
2686         return PM_WA;\r
2687     case WhiteMarshall:\r
2688         return PM_WC;\r
2689     case WhiteFerz:\r
2690         return PM_WF;\r
2691     case WhiteNightrider:\r
2692         return PM_WH;\r
2693     case WhiteAlfil:\r
2694         return PM_WE;\r
2695     case WhiteWazir:\r
2696         return PM_WW;\r
2697     case WhiteUnicorn:\r
2698         return PM_WU;\r
2699     case WhiteCannon:\r
2700         return PM_WO;\r
2701     case WhiteGrasshopper:\r
2702         return PM_WG;\r
2703     case WhiteMan:\r
2704         return PM_WM;\r
2705     case WhiteSilver:\r
2706         return PM_WSG;\r
2707     case WhiteLance:\r
2708         return PM_WL;\r
2709     case WhiteFalcon:\r
2710         return PM_WV;\r
2711     case WhiteCobra:\r
2712         return PM_WS;\r
2713     case WhiteCardinal:\r
2714         return PM_WAB;\r
2715     case WhiteDragon:\r
2716         return PM_WD;\r
2717     }\r
2718 \r
2719     return 0;\r
2720 }\r
2721 \r
2722 void CreatePiecesFromFont()\r
2723 {\r
2724     LOGFONT lf;\r
2725     HDC hdc_window = NULL;\r
2726     HDC hdc = NULL;\r
2727     HFONT hfont_old;\r
2728     int fontHeight;\r
2729     int i;\r
2730 \r
2731     if( fontBitmapSquareSize < 0 ) {\r
2732         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2733         return;\r
2734     }\r
2735 \r
2736     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2737         fontBitmapSquareSize = -1;\r
2738         return;\r
2739     }\r
2740 \r
2741     if( fontBitmapSquareSize != squareSize ) {\r
2742         hdc_window = GetDC( hwndMain );\r
2743         hdc = CreateCompatibleDC( hdc_window );\r
2744 \r
2745         if( hPieceFont != NULL ) {\r
2746             DeleteObject( hPieceFont );\r
2747         }\r
2748         else {\r
2749             for( i=0; i<=(int)BlackKing; i++ ) {\r
2750                 hPieceMask[i] = NULL;\r
2751                 hPieceFace[i] = NULL;\r
2752             }\r
2753         }\r
2754 \r
2755         fontHeight = 75;\r
2756 \r
2757         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2758             fontHeight = appData.fontPieceSize;\r
2759         }\r
2760 \r
2761         fontHeight = (fontHeight * squareSize) / 100;\r
2762 \r
2763         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2764         lf.lfWidth = 0;\r
2765         lf.lfEscapement = 0;\r
2766         lf.lfOrientation = 0;\r
2767         lf.lfWeight = FW_NORMAL;\r
2768         lf.lfItalic = 0;\r
2769         lf.lfUnderline = 0;\r
2770         lf.lfStrikeOut = 0;\r
2771         lf.lfCharSet = DEFAULT_CHARSET;\r
2772         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2773         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2774         lf.lfQuality = PROOF_QUALITY;\r
2775         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2776         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2777         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2778 \r
2779         hPieceFont = CreateFontIndirect( &lf );\r
2780 \r
2781         if( hPieceFont == NULL ) {\r
2782             fontBitmapSquareSize = -2;\r
2783         }\r
2784         else {\r
2785             /* Setup font-to-piece character table */\r
2786             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2787                 /* No (or wrong) global settings, try to detect the font */\r
2788                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2789                     /* Alpha */\r
2790                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2791                 }\r
2792                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2793                     /* DiagramTT* family */\r
2794                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2795                 }\r
2796                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
2797                     /* Fairy symbols */\r
2798                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
2799                 }\r
2800                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
2801                     /* Good Companion (Some characters get warped as literal :-( */\r
2802                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
2803                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
2804                     SetCharTable(pieceToFontChar, s);\r
2805                 }\r
2806                 else {\r
2807                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
2808                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
2809                 }\r
2810             }\r
2811 \r
2812             /* Create bitmaps */\r
2813             hfont_old = SelectObject( hdc, hPieceFont );\r
2814 #if 0\r
2815             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
2816             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
2817             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
2818             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
2819             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
2820             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
2821             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
2822             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
2823             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
2824             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
2825             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
2826             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
2827 \r
2828             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
2829             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
2830             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
2831             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
2832             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
2833             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
2834             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
2835             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
2836             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
2837             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
2838             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
2839             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
2840             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
2841             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
2842             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
2843             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
2844             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
2845             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
2846             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
2847             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
2848             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
2849             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
2850             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
2851             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
2852             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
2853             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
2854             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
2855             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
2856             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
2857             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
2858             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
2859             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
2860 #else\r
2861             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
2862                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
2863                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
2864 #endif\r
2865             SelectObject( hdc, hfont_old );\r
2866 \r
2867             fontBitmapSquareSize = squareSize;\r
2868         }\r
2869     }\r
2870 \r
2871     if( hdc != NULL ) {\r
2872         DeleteDC( hdc );\r
2873     }\r
2874 \r
2875     if( hdc_window != NULL ) {\r
2876         ReleaseDC( hwndMain, hdc_window );\r
2877     }\r
2878 }\r
2879 \r
2880 HBITMAP\r
2881 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
2882 {\r
2883   char name[128];\r
2884 \r
2885   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
2886   if (gameInfo.event &&\r
2887       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
2888       strcmp(name, "k80s") == 0) {\r
2889     strcpy(name, "tim");\r
2890   }\r
2891   return LoadBitmap(hinst, name);\r
2892 }\r
2893 \r
2894 \r
2895 /* Insert a color into the program's logical palette\r
2896    structure.  This code assumes the given color is\r
2897    the result of the RGB or PALETTERGB macro, and it\r
2898    knows how those macros work (which is documented).\r
2899 */\r
2900 VOID\r
2901 InsertInPalette(COLORREF color)\r
2902 {\r
2903   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
2904 \r
2905   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
2906     DisplayFatalError("Too many colors", 0, 1);\r
2907     pLogPal->palNumEntries--;\r
2908     return;\r
2909   }\r
2910 \r
2911   pe->peFlags = (char) 0;\r
2912   pe->peRed = (char) (0xFF & color);\r
2913   pe->peGreen = (char) (0xFF & (color >> 8));\r
2914   pe->peBlue = (char) (0xFF & (color >> 16));\r
2915   return;\r
2916 }\r
2917 \r
2918 \r
2919 VOID\r
2920 InitDrawingColors()\r
2921 {\r
2922   if (pLogPal == NULL) {\r
2923     /* Allocate enough memory for a logical palette with\r
2924      * PALETTESIZE entries and set the size and version fields\r
2925      * of the logical palette structure.\r
2926      */\r
2927     pLogPal = (NPLOGPALETTE)\r
2928       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2929                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2930     pLogPal->palVersion    = 0x300;\r
2931   }\r
2932   pLogPal->palNumEntries = 0;\r
2933 \r
2934   InsertInPalette(lightSquareColor);\r
2935   InsertInPalette(darkSquareColor);\r
2936   InsertInPalette(whitePieceColor);\r
2937   InsertInPalette(blackPieceColor);\r
2938   InsertInPalette(highlightSquareColor);\r
2939   InsertInPalette(premoveHighlightColor);\r
2940 \r
2941   /*  create a logical color palette according the information\r
2942    *  in the LOGPALETTE structure.\r
2943    */\r
2944   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2945 \r
2946   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2947   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
2948   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2949   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2950   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2951   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2952 \r
2953   /* [AS] Force rendering of the font-based pieces */\r
2954   if( fontBitmapSquareSize > 0 ) {\r
2955     fontBitmapSquareSize = 0;\r
2956   }\r
2957 }\r
2958 \r
2959 \r
2960 int\r
2961 BoardWidth(int boardSize, int n)\r
2962 { /* [HGM] argument n added to allow different width and height */\r
2963   int lineGap = sizeInfo[boardSize].lineGap;\r
2964 \r
2965   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
2966       lineGap = appData.overrideLineGap;\r
2967   }\r
2968 \r
2969   return (n + 1) * lineGap +\r
2970           n * sizeInfo[boardSize].squareSize;\r
2971 }\r
2972 \r
2973 /* Respond to board resize by dragging edge */\r
2974 VOID\r
2975 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2976 {\r
2977   BoardSize newSize = NUM_SIZES - 1;\r
2978   static int recurse = 0;\r
2979   if (IsIconic(hwndMain)) return;\r
2980   if (recurse > 0) return;\r
2981   recurse++;\r
2982   while (newSize > 0) {\r
2983         InitDrawingSizes(newSize, 0);\r
2984         if(newSizeX >= sizeInfo[newSize].cliWidth ||\r
2985            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
2986     newSize--;\r
2987   } \r
2988   boardSize = newSize;\r
2989   InitDrawingSizes(boardSize, flags);\r
2990   recurse--;\r
2991 }\r
2992 \r
2993 \r
2994 \r
2995 VOID\r
2996 InitDrawingSizes(BoardSize boardSize, int flags)\r
2997 {\r
2998   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
2999   ChessSquare piece;\r
3000   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3001   HDC hdc;\r
3002   SIZE clockSize, messageSize;\r
3003   HFONT oldFont;\r
3004   char buf[MSG_SIZ];\r
3005   char *str;\r
3006   HMENU hmenu = GetMenu(hwndMain);\r
3007   RECT crect, wrect;\r
3008   int offby;\r
3009   LOGBRUSH logbrush;\r
3010 \r
3011   /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */\r
3012   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3013 \r
3014   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3015   smallLayout = sizeInfo[boardSize].smallLayout;\r
3016   squareSize = sizeInfo[boardSize].squareSize;\r
3017   lineGap = sizeInfo[boardSize].lineGap;\r
3018   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3019 \r
3020   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3021       lineGap = appData.overrideLineGap;\r
3022   }\r
3023 \r
3024   if (tinyLayout != oldTinyLayout) {\r
3025     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3026     if (tinyLayout) {\r
3027       style &= ~WS_SYSMENU;\r
3028       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3029                  "&Minimize\tCtrl+F4");\r
3030     } else {\r
3031       style |= WS_SYSMENU;\r
3032       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3033     }\r
3034     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3035 \r
3036     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3037       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3038         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3039     }\r
3040     DrawMenuBar(hwndMain);\r
3041   }\r
3042 \r
3043   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3044   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3045 \r
3046   /* Get text area sizes */\r
3047   hdc = GetDC(hwndMain);\r
3048   if (appData.clockMode) {\r
3049     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3050   } else {\r
3051     sprintf(buf, "White");\r
3052   }\r
3053   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3054   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3055   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3056   str = "We only care about the height here";\r
3057   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3058   SelectObject(hdc, oldFont);\r
3059   ReleaseDC(hwndMain, hdc);\r
3060 \r
3061   /* Compute where everything goes */\r
3062   whiteRect.left = OUTER_MARGIN;\r
3063   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3064   whiteRect.top = OUTER_MARGIN;\r
3065   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3066 \r
3067   blackRect.left = whiteRect.right + INNER_MARGIN;\r
3068   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3069   blackRect.top = whiteRect.top;\r
3070   blackRect.bottom = whiteRect.bottom;\r
3071 \r
3072   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
3073   if (appData.showButtonBar) {\r
3074     messageRect.right = blackRect.right\r
3075       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3076   } else {\r
3077     messageRect.right = blackRect.right;\r
3078   }\r
3079   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3080   messageRect.bottom = messageRect.top + messageSize.cy;\r
3081 \r
3082   boardRect.left = whiteRect.left;\r
3083   boardRect.right = boardRect.left + boardWidth;\r
3084   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3085   boardRect.bottom = boardRect.top + boardHeight;\r
3086 \r
3087   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3088   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3089   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3090   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3091     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3092   GetWindowRect(hwndMain, &wrect);\r
3093   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3094                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3095   /* compensate if menu bar wrapped */\r
3096   GetClientRect(hwndMain, &crect);\r
3097   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3098   winHeight += offby;\r
3099   switch (flags) {\r
3100   case WMSZ_TOPLEFT:\r
3101     SetWindowPos(hwndMain, NULL, \r
3102                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3103                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3104     break;\r
3105 \r
3106   case WMSZ_TOPRIGHT:\r
3107   case WMSZ_TOP:\r
3108     SetWindowPos(hwndMain, NULL, \r
3109                  wrect.left, wrect.bottom - winHeight, \r
3110                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3111     break;\r
3112 \r
3113   case WMSZ_BOTTOMLEFT:\r
3114   case WMSZ_LEFT:\r
3115     SetWindowPos(hwndMain, NULL, \r
3116                  wrect.right - winWidth, wrect.top, \r
3117                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3118     break;\r
3119 \r
3120   case WMSZ_BOTTOMRIGHT:\r
3121   case WMSZ_BOTTOM:\r
3122   case WMSZ_RIGHT:\r
3123   default:\r
3124     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3125                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3126     break;\r
3127   }\r
3128 \r
3129   hwndPause = NULL;\r
3130   for (i = 0; i < N_BUTTONS; i++) {\r
3131     if (buttonDesc[i].hwnd != NULL) {\r
3132       DestroyWindow(buttonDesc[i].hwnd);\r
3133       buttonDesc[i].hwnd = NULL;\r
3134     }\r
3135     if (appData.showButtonBar) {\r
3136       buttonDesc[i].hwnd =\r
3137         CreateWindow("BUTTON", buttonDesc[i].label,\r
3138                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3139                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3140                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3141                      (HMENU) buttonDesc[i].id,\r
3142                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3143       if (tinyLayout) {\r
3144         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3145                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3146                     MAKELPARAM(FALSE, 0));\r
3147       }\r
3148       if (buttonDesc[i].id == IDM_Pause)\r
3149         hwndPause = buttonDesc[i].hwnd;\r
3150       buttonDesc[i].wndproc = (WNDPROC)\r
3151         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3152     }\r
3153   }\r
3154   if (gridPen != NULL) DeleteObject(gridPen);\r
3155   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3156   if (premovePen != NULL) DeleteObject(premovePen);\r
3157   if (lineGap != 0) {\r
3158     logbrush.lbStyle = BS_SOLID;\r
3159     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3160     gridPen =\r
3161       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3162                    lineGap, &logbrush, 0, NULL);\r
3163     logbrush.lbColor = highlightSquareColor;\r
3164     highlightPen =\r
3165       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3166                    lineGap, &logbrush, 0, NULL);\r
3167 \r
3168     logbrush.lbColor = premoveHighlightColor; \r
3169     premovePen =\r
3170       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3171                    lineGap, &logbrush, 0, NULL);\r
3172 \r
3173     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3174     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3175       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3176       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3177         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3178       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3179         BOARD_WIDTH * (squareSize + lineGap);\r
3180         lineGap / 2 + (i * (squareSize + lineGap));\r
3181       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3182     }\r
3183     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3184       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3185       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3186         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3187         lineGap / 2 + (i * (squareSize + lineGap));\r
3188       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3189         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3190       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3191     }\r
3192   }\r
3193 \r
3194   /* [HGM] Licensing requirement */\r
3195 #ifdef GOTHIC\r
3196   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3197 #endif\r
3198 #ifdef FALCON\r
3199   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3200 #endif\r
3201   GothicPopUp( "", VariantNormal);\r
3202 \r
3203 \r
3204 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3205   oldBoardSize = boardSize;\r
3206   oldTinyLayout = tinyLayout;\r
3207 \r
3208   /* Load piece bitmaps for this board size */\r
3209   for (i=0; i<=2; i++) {\r
3210     for (piece = WhitePawn;\r
3211          (int) piece < (int) BlackPawn;\r
3212          piece = (ChessSquare) ((int) piece + 1)) {\r
3213       if (pieceBitmap[i][piece] != NULL)\r
3214         DeleteObject(pieceBitmap[i][piece]);\r
3215     }\r
3216   }\r
3217 \r
3218   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3219   // Orthodox Chess pieces\r
3220   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3221   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3222   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3223   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3224   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3225   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3226   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3227   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3228   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3229   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3230   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3231   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3232   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3233   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3234   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3235   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3236     // in Shogi, Hijack the unused Queen for Lance\r
3237     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3238     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3239     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3240   } else {\r
3241     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3242     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3243     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3244   }\r
3245 \r
3246   if(squareSize <= 72 && squareSize >= 33) { \r
3247     /* A & C are available in most sizes now */\r
3248     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3249       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3250       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3251       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3252       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3253       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3254       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3255       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3256       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3257       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3258       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3259       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3260       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3261     } else { // Smirf-like\r
3262       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3263       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3264       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3265     }\r
3266     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3267       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3268       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3269       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3270     } else { // WinBoard standard\r
3271       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3272       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3273       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3274     }\r
3275   }\r
3276 \r
3277 \r
3278   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3279     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3280     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3281     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3282     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3283     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3284     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3285     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3286     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3287     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3288     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3289     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3290     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3291     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3292     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3293     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3294     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3295     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3296     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3297     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3298     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3299     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3300     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3301     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3302     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3303     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3304     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3305     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3306     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3307     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3308     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3309 \r
3310     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3311       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3312       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3313       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3314       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3315       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3316       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3317       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3318       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3319       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3320       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3321       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3322       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3323     } else {\r
3324       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3325       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3326       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3327       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3328       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3329       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3330       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3331       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3332       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3333       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3334       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3335       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3336     }\r
3337 \r
3338   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3339     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3340     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3341     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3342     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3343     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3344     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3345     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3346     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3347     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3348     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3349     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3350     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3351     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3352     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3353   }\r
3354 \r
3355 \r
3356   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3357   /* special Shogi support in this size */\r
3358   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3359       for (piece = WhitePawn;\r
3360            (int) piece < (int) BlackPawn;\r
3361            piece = (ChessSquare) ((int) piece + 1)) {\r
3362         if (pieceBitmap[i][piece] != NULL)\r
3363           DeleteObject(pieceBitmap[i][piece]);\r
3364       }\r
3365     }\r
3366   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3367   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3368   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3369   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3370   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3371   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3372   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3373   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3374   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3375   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3376   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3377   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3378   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3379   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3380   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3381   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3382   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3383   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3384   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3385   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3386   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3387   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3388   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3389   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3390   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3391   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3392   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3393   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3394   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3395   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3396   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3397   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3398   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3399   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3400   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3401   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3402   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3403   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3404   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3405   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3406   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3407   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3408   minorSize = 0;\r
3409   }\r
3410 }\r
3411 \r
3412 HBITMAP\r
3413 PieceBitmap(ChessSquare p, int kind)\r
3414 {\r
3415   if ((int) p >= (int) BlackPawn)\r
3416     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3417 \r
3418   return pieceBitmap[kind][(int) p];\r
3419 }\r
3420 \r
3421 /***************************************************************/\r
3422 \r
3423 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3424 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3425 /*\r
3426 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3427 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3428 */\r
3429 \r
3430 VOID\r
3431 SquareToPos(int row, int column, int * x, int * y)\r
3432 {\r
3433   if (flipView) {\r
3434     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3435     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3436   } else {\r
3437     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3438     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3439   }\r
3440 }\r
3441 \r
3442 VOID\r
3443 DrawCoordsOnDC(HDC hdc)\r
3444 {\r
3445   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
3446   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
3447   char str[2] = { NULLCHAR, NULLCHAR };\r
3448   int oldMode, oldAlign, x, y, start, i;\r
3449   HFONT oldFont;\r
3450   HBRUSH oldBrush;\r
3451 \r
3452   if (!appData.showCoords)\r
3453     return;\r
3454 \r
3455   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3456 \r
3457   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3458   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3459   oldAlign = GetTextAlign(hdc);\r
3460   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3461 \r
3462   y = boardRect.top + lineGap;\r
3463   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3464 \r
3465   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3466   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3467     str[0] = files[start + i];\r
3468     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3469     y += squareSize + lineGap;\r
3470   }\r
3471 \r
3472   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3473 \r
3474   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3475   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3476     str[0] = ranks[start + i];\r
3477     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3478     x += squareSize + lineGap;\r
3479   }    \r
3480 \r
3481   SelectObject(hdc, oldBrush);\r
3482   SetBkMode(hdc, oldMode);\r
3483   SetTextAlign(hdc, oldAlign);\r
3484   SelectObject(hdc, oldFont);\r
3485 }\r
3486 \r
3487 VOID\r
3488 DrawGridOnDC(HDC hdc)\r
3489 {\r
3490   HPEN oldPen;\r
3491  \r
3492   if (lineGap != 0) {\r
3493     oldPen = SelectObject(hdc, gridPen);\r
3494     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3495     SelectObject(hdc, oldPen);\r
3496   }\r
3497 }\r
3498 \r
3499 #define HIGHLIGHT_PEN 0\r
3500 #define PREMOVE_PEN   1\r
3501 \r
3502 VOID\r
3503 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3504 {\r
3505   int x1, y1;\r
3506   HPEN oldPen, hPen;\r
3507   if (lineGap == 0) return;\r
3508   if (flipView) {\r
3509     x1 = boardRect.left +\r
3510       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3511     y1 = boardRect.top +\r
3512       lineGap/2 + y * (squareSize + lineGap);\r
3513   } else {\r
3514     x1 = boardRect.left +\r
3515       lineGap/2 + x * (squareSize + lineGap);\r
3516     y1 = boardRect.top +\r
3517       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3518   }\r
3519   hPen = pen ? premovePen : highlightPen;\r
3520   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3521   MoveToEx(hdc, x1, y1, NULL);\r
3522   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3523   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3524   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3525   LineTo(hdc, x1, y1);\r
3526   SelectObject(hdc, oldPen);\r
3527 }\r
3528 \r
3529 VOID\r
3530 DrawHighlightsOnDC(HDC hdc)\r
3531 {\r
3532   int i;\r
3533   for (i=0; i<2; i++) {\r
3534     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3535       DrawHighlightOnDC(hdc, TRUE,\r
3536                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3537                         HIGHLIGHT_PEN);\r
3538   }\r
3539   for (i=0; i<2; i++) {\r
3540     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3541         premoveHighlightInfo.sq[i].y >= 0) {\r
3542         DrawHighlightOnDC(hdc, TRUE,\r
3543                           premoveHighlightInfo.sq[i].x, \r
3544                           premoveHighlightInfo.sq[i].y,\r
3545                           PREMOVE_PEN);\r
3546     }\r
3547   }\r
3548 }\r
3549 \r
3550 /* Note: sqcolor is used only in monoMode */\r
3551 /* Note that this code is largely duplicated in woptions.c,\r
3552    function DrawSampleSquare, so that needs to be updated too */\r
3553 VOID\r
3554 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3555 {\r
3556   HBITMAP oldBitmap;\r
3557   HBRUSH oldBrush;\r
3558   int tmpSize;\r
3559 \r
3560   if (appData.blindfold) return;\r
3561 \r
3562   /* [AS] Use font-based pieces if needed */\r
3563   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3564     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3565     CreatePiecesFromFont();\r
3566 \r
3567     if( fontBitmapSquareSize == squareSize ) {\r
3568         int index = TranslatePieceToFontPiece( piece );\r
3569 \r
3570         SelectObject( tmphdc, hPieceMask[ index ] );\r
3571 \r
3572         BitBlt( hdc,\r
3573             x, y,\r
3574             squareSize, squareSize,\r
3575             tmphdc,\r
3576             0, 0,\r
3577             SRCAND );\r
3578 \r
3579         SelectObject( tmphdc, hPieceFace[ index ] );\r
3580 \r
3581         BitBlt( hdc,\r
3582             x, y,\r
3583             squareSize, squareSize,\r
3584             tmphdc,\r
3585             0, 0,\r
3586             SRCPAINT );\r
3587 \r
3588         return;\r
3589     }\r
3590   }\r
3591 \r
3592   if (appData.monoMode) {\r
3593     SelectObject(tmphdc, PieceBitmap(piece, \r
3594       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3595     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3596            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3597   } else {\r
3598     tmpSize = squareSize;\r
3599     if(minorSize &&\r
3600         (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper ||\r
3601          piece >= (int)BlackNightrider && piece <= BlackGrasshopper)  ) {\r
3602       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3603       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3604       x += (squareSize - minorSize)>>1;\r
3605       y += squareSize - minorSize - 2;\r
3606       tmpSize = minorSize;\r
3607     }\r
3608     if (color || appData.allWhite ) {\r
3609       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3610       if( color )\r
3611               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3612       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3613       if(appData.upsideDown && color==flipView)\r
3614         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3615       else\r
3616         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3617 #if 0\r
3618       /* Use black piece color for outline of white pieces */\r
3619       /* Not sure this looks really good (though xboard does it).\r
3620          Maybe better to have another selectable color, default black */\r
3621       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3622       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3623       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3624 #else\r
3625       /* Use black for outline of white pieces */\r
3626       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3627       if(appData.upsideDown && color==flipView)\r
3628         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3629       else\r
3630         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3631 #endif\r
3632     } else {\r
3633 #if 0\r
3634       /* Use white piece color for details of black pieces */\r
3635       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3636          WHITE_PIECE ones aren't always the right shape. */\r
3637       /* Not sure this looks really good (though xboard does it).\r
3638          Maybe better to have another selectable color, default medium gray? */\r
3639       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3640       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3641       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3642       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3643       SelectObject(hdc, blackPieceBrush);\r
3644       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3645 #else\r
3646       /* Use square color for details of black pieces */\r
3647       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3648       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3649       if(appData.upsideDown && !flipView)\r
3650         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3651       else\r
3652         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3653 #endif\r
3654     }\r
3655     SelectObject(hdc, oldBrush);\r
3656     SelectObject(tmphdc, oldBitmap);\r
3657   }\r
3658 }\r
3659 \r
3660 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3661 int GetBackTextureMode( int algo )\r
3662 {\r
3663     int result = BACK_TEXTURE_MODE_DISABLED;\r
3664 \r
3665     switch( algo ) \r
3666     {\r
3667         case BACK_TEXTURE_MODE_PLAIN:\r
3668             result = 1; /* Always use identity map */\r
3669             break;\r
3670         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3671             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3672             break;\r
3673     }\r
3674 \r
3675     return result;\r
3676 }\r
3677 \r
3678 /* \r
3679     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3680     to handle redraws cleanly (as random numbers would always be different).\r
3681 */\r
3682 VOID RebuildTextureSquareInfo()\r
3683 {\r
3684     BITMAP bi;\r
3685     int lite_w = 0;\r
3686     int lite_h = 0;\r
3687     int dark_w = 0;\r
3688     int dark_h = 0;\r
3689     int row;\r
3690     int col;\r
3691 \r
3692     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3693 \r
3694     if( liteBackTexture != NULL ) {\r
3695         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3696             lite_w = bi.bmWidth;\r
3697             lite_h = bi.bmHeight;\r
3698         }\r
3699     }\r
3700 \r
3701     if( darkBackTexture != NULL ) {\r
3702         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3703             dark_w = bi.bmWidth;\r
3704             dark_h = bi.bmHeight;\r
3705         }\r
3706     }\r
3707 \r
3708     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3709         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3710             if( (col + row) & 1 ) {\r
3711                 /* Lite square */\r
3712                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3713                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH;\r
3714                     backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT;\r
3715                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3716                 }\r
3717             }\r
3718             else {\r
3719                 /* Dark square */\r
3720                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3721                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_WIDTH;\r
3722                     backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT;\r
3723                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3724                 }\r
3725             }\r
3726         }\r
3727     }\r
3728 }\r
3729 \r
3730 /* [AS] Arrow highlighting support */\r
3731 \r
3732 static int A_WIDTH = 5; /* Width of arrow body */\r
3733 \r
3734 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3735 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3736 \r
3737 static double Sqr( double x )\r
3738 {\r
3739     return x*x;\r
3740 }\r
3741 \r
3742 static int Round( double x )\r
3743 {\r
3744     return (int) (x + 0.5);\r
3745 }\r
3746 \r
3747 /* Draw an arrow between two points using current settings */\r
3748 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3749 {\r
3750     POINT arrow[7];\r
3751     double dx, dy, j, k, x, y;\r
3752 \r
3753     if( d_x == s_x ) {\r
3754         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3755 \r
3756         arrow[0].x = s_x + A_WIDTH;\r
3757         arrow[0].y = s_y;\r
3758 \r
3759         arrow[1].x = s_x + A_WIDTH;\r
3760         arrow[1].y = d_y - h;\r
3761 \r
3762         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3763         arrow[2].y = d_y - h;\r
3764 \r
3765         arrow[3].x = d_x;\r
3766         arrow[3].y = d_y;\r
3767 \r
3768         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3769         arrow[4].y = d_y - h;\r
3770 \r
3771         arrow[5].x = s_x - A_WIDTH;\r
3772         arrow[5].y = d_y - h;\r
3773 \r
3774         arrow[6].x = s_x - A_WIDTH;\r
3775         arrow[6].y = s_y;\r
3776     }\r
3777     else if( d_y == s_y ) {\r
3778         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3779 \r
3780         arrow[0].x = s_x;\r
3781         arrow[0].y = s_y + A_WIDTH;\r
3782 \r
3783         arrow[1].x = d_x - w;\r
3784         arrow[1].y = s_y + A_WIDTH;\r
3785 \r
3786         arrow[2].x = d_x - w;\r
3787         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3788 \r
3789         arrow[3].x = d_x;\r
3790         arrow[3].y = d_y;\r
3791 \r
3792         arrow[4].x = d_x - w;\r
3793         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3794 \r
3795         arrow[5].x = d_x - w;\r
3796         arrow[5].y = s_y - A_WIDTH;\r
3797 \r
3798         arrow[6].x = s_x;\r
3799         arrow[6].y = s_y - A_WIDTH;\r
3800     }\r
3801     else {\r
3802         /* [AS] Needed a lot of paper for this! :-) */\r
3803         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
3804         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
3805   \r
3806         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
3807 \r
3808         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
3809 \r
3810         x = s_x;\r
3811         y = s_y;\r
3812 \r
3813         arrow[0].x = Round(x - j);\r
3814         arrow[0].y = Round(y + j*dx);\r
3815 \r
3816         arrow[1].x = Round(x + j);\r
3817         arrow[1].y = Round(y - j*dx);\r
3818 \r
3819         if( d_x > s_x ) {\r
3820             x = (double) d_x - k;\r
3821             y = (double) d_y - k*dy;\r
3822         }\r
3823         else {\r
3824             x = (double) d_x + k;\r
3825             y = (double) d_y + k*dy;\r
3826         }\r
3827 \r
3828         arrow[2].x = Round(x + j);\r
3829         arrow[2].y = Round(y - j*dx);\r
3830 \r
3831         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
3832         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
3833 \r
3834         arrow[4].x = d_x;\r
3835         arrow[4].y = d_y;\r
3836 \r
3837         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
3838         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
3839 \r
3840         arrow[6].x = Round(x - j);\r
3841         arrow[6].y = Round(y + j*dx);\r
3842     }\r
3843 \r
3844     Polygon( hdc, arrow, 7 );\r
3845 }\r
3846 \r
3847 /* [AS] Draw an arrow between two squares */\r
3848 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
3849 {\r
3850     int s_x, s_y, d_x, d_y;\r
3851     HPEN hpen;\r
3852     HPEN holdpen;\r
3853     HBRUSH hbrush;\r
3854     HBRUSH holdbrush;\r
3855     LOGBRUSH stLB;\r
3856 \r
3857     if( s_col == d_col && s_row == d_row ) {\r
3858         return;\r
3859     }\r
3860 \r
3861     /* Get source and destination points */\r
3862     SquareToPos( s_row, s_col, &s_x, &s_y);\r
3863     SquareToPos( d_row, d_col, &d_x, &d_y);\r
3864 \r
3865     if( d_y > s_y ) {\r
3866         d_y += squareSize / 4;\r
3867     }\r
3868     else if( d_y < s_y ) {\r
3869         d_y += 3 * squareSize / 4;\r
3870     }\r
3871     else {\r
3872         d_y += squareSize / 2;\r
3873     }\r
3874 \r
3875     if( d_x > s_x ) {\r
3876         d_x += squareSize / 4;\r
3877     }\r
3878     else if( d_x < s_x ) {\r
3879         d_x += 3 * squareSize / 4;\r
3880     }\r
3881     else {\r
3882         d_x += squareSize / 2;\r
3883     }\r
3884 \r
3885     s_x += squareSize / 2;\r
3886     s_y += squareSize / 2;\r
3887 \r
3888     /* Adjust width */\r
3889     A_WIDTH = squareSize / 14;\r
3890 \r
3891     /* Draw */\r
3892     stLB.lbStyle = BS_SOLID;\r
3893     stLB.lbColor = appData.highlightArrowColor;\r
3894     stLB.lbHatch = 0;\r
3895 \r
3896     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
3897     holdpen = SelectObject( hdc, hpen );\r
3898     hbrush = CreateBrushIndirect( &stLB );\r
3899     holdbrush = SelectObject( hdc, hbrush );\r
3900 \r
3901     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
3902 \r
3903     SelectObject( hdc, holdpen );\r
3904     SelectObject( hdc, holdbrush );\r
3905     DeleteObject( hpen );\r
3906     DeleteObject( hbrush );\r
3907 }\r
3908 \r
3909 BOOL HasHighlightInfo()\r
3910 {\r
3911     BOOL result = FALSE;\r
3912 \r
3913     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
3914         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
3915     {\r
3916         result = TRUE;\r
3917     }\r
3918 \r
3919     return result;\r
3920 }\r
3921 \r
3922 BOOL IsDrawArrowEnabled()\r
3923 {\r
3924     BOOL result = FALSE;\r
3925 \r
3926     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
3927         result = TRUE;\r
3928     }\r
3929 \r
3930     return result;\r
3931 }\r
3932 \r
3933 VOID DrawArrowHighlight( HDC hdc )\r
3934 {\r
3935     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
3936         DrawArrowBetweenSquares( hdc,\r
3937             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
3938             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
3939     }\r
3940 }\r
3941 \r
3942 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
3943 {\r
3944     HRGN result = NULL;\r
3945 \r
3946     if( HasHighlightInfo() ) {\r
3947         int x1, y1, x2, y2;\r
3948         int sx, sy, dx, dy;\r
3949 \r
3950         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
3951         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
3952 \r
3953         sx = MIN( x1, x2 );\r
3954         sy = MIN( y1, y2 );\r
3955         dx = MAX( x1, x2 ) + squareSize;\r
3956         dy = MAX( y1, y2 ) + squareSize;\r
3957 \r
3958         result = CreateRectRgn( sx, sy, dx, dy );\r
3959     }\r
3960 \r
3961     return result;\r
3962 }\r
3963 \r
3964 /*\r
3965     Warning: this function modifies the behavior of several other functions. \r
3966     \r
3967     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
3968     needed. Unfortunately, the decision whether or not to perform a full or partial\r
3969     repaint is scattered all over the place, which is not good for features such as\r
3970     "arrow highlighting" that require a full repaint of the board.\r
3971 \r
3972     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
3973     user interaction, when speed is not so important) but especially to avoid errors\r
3974     in the displayed graphics.\r
3975 \r
3976     In such patched places, I always try refer to this function so there is a single\r
3977     place to maintain knowledge.\r
3978     \r
3979     To restore the original behavior, just return FALSE unconditionally.\r
3980 */\r
3981 BOOL IsFullRepaintPreferrable()\r
3982 {\r
3983     BOOL result = FALSE;\r
3984 \r
3985     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
3986         /* Arrow may appear on the board */\r
3987         result = TRUE;\r
3988     }\r
3989 \r
3990     return result;\r
3991 }\r
3992 \r
3993 /* \r
3994     This function is called by DrawPosition to know whether a full repaint must\r
3995     be forced or not.\r
3996 \r
3997     Only DrawPosition may directly call this function, which makes use of \r
3998     some state information. Other function should call DrawPosition specifying \r
3999     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4000 */\r
4001 BOOL DrawPositionNeedsFullRepaint()\r
4002 {\r
4003     BOOL result = FALSE;\r
4004 \r
4005     /* \r
4006         Probably a slightly better policy would be to trigger a full repaint\r
4007         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4008         but animation is fast enough that it's difficult to notice.\r
4009     */\r
4010     if( animInfo.piece == EmptySquare ) {\r
4011         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4012             result = TRUE;\r
4013         }\r
4014     }\r
4015 \r
4016     return result;\r
4017 }\r
4018 \r
4019 VOID\r
4020 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4021 {\r
4022   int row, column, x, y, square_color, piece_color;\r
4023   ChessSquare piece;\r
4024   HBRUSH oldBrush;\r
4025   HDC texture_hdc = NULL;\r
4026 \r
4027   /* [AS] Initialize background textures if needed */\r
4028   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4029       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4030       if( backTextureSquareSize != squareSize \r
4031        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4032           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4033           backTextureSquareSize = squareSize;\r
4034           RebuildTextureSquareInfo();\r
4035       }\r
4036 \r
4037       texture_hdc = CreateCompatibleDC( hdc );\r
4038   }\r
4039 \r
4040   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4041     for (column = 0; column < BOARD_WIDTH; column++) {\r
4042   \r
4043       SquareToPos(row, column, &x, &y);\r
4044 \r
4045       piece = board[row][column];\r
4046 \r
4047       square_color = ((column + row) % 2) == 1;\r
4048       if(!strcmp(appData.variant, "xiangqi") ) {\r
4049           square_color = !InPalace(row, column);\r
4050           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4051           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4052       }\r
4053       piece_color = (int) piece < (int) BlackPawn;\r
4054 \r
4055 \r
4056       /* [HGM] holdings file: light square or black */\r
4057       if(column == BOARD_LEFT-2) {\r
4058             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4059                 square_color = 1;\r
4060             else {\r
4061                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4062                 continue;\r
4063             }\r
4064       } else\r
4065       if(column == BOARD_RGHT + 1 ) {\r
4066             if( row < gameInfo.holdingsSize )\r
4067                 square_color = 1;\r
4068             else {\r
4069                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4070                 continue;\r
4071             }\r
4072       }\r
4073       if(column == BOARD_LEFT-1 ) /* left align */\r
4074             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4075       else if( column == BOARD_RGHT) /* right align */\r
4076             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4077       else\r
4078       if (appData.monoMode) {\r
4079         if (piece == EmptySquare) {\r
4080           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4081                  square_color ? WHITENESS : BLACKNESS);\r
4082         } else {\r
4083           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4084         }\r
4085       } \r
4086       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4087           /* [AS] Draw the square using a texture bitmap */\r
4088           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4089 \r
4090           DrawTile( x, y, \r
4091               squareSize, squareSize, \r
4092               hdc, \r
4093               texture_hdc,\r
4094               backTextureSquareInfo[row][column].mode,\r
4095               backTextureSquareInfo[row][column].x,\r
4096               backTextureSquareInfo[row][column].y );\r
4097 \r
4098           SelectObject( texture_hdc, hbm );\r
4099 \r
4100           if (piece != EmptySquare) {\r
4101               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4102           }\r
4103       }\r
4104       else {\r
4105         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4106 \r
4107         oldBrush = SelectObject(hdc, brush );\r
4108         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4109         SelectObject(hdc, oldBrush);\r
4110         if (piece != EmptySquare)\r
4111           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4112       }\r
4113     }\r
4114   }\r
4115 \r
4116   if( texture_hdc != NULL ) {\r
4117     DeleteDC( texture_hdc );\r
4118   }\r
4119 }\r
4120 \r
4121 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4122 void fputDW(FILE *f, int x)\r
4123 {\r
4124         fputc(x     & 255, f);\r
4125         fputc(x>>8  & 255, f);\r
4126         fputc(x>>16 & 255, f);\r
4127         fputc(x>>24 & 255, f);\r
4128 }\r
4129 \r
4130 #define MAX_CLIPS 200   /* more than enough */\r
4131 \r
4132 VOID\r
4133 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4134 {\r
4135   static Board lastReq, lastDrawn;\r
4136   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4137   static int lastDrawnFlipView = 0;\r
4138   static int lastReqValid = 0, lastDrawnValid = 0;\r
4139   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4140   HDC tmphdc;\r
4141   HDC hdcmem;\r
4142   HBITMAP bufferBitmap;\r
4143   HBITMAP oldBitmap;\r
4144   RECT Rect;\r
4145   HRGN clips[MAX_CLIPS];\r
4146   ChessSquare dragged_piece = EmptySquare;\r
4147 \r
4148   /* I'm undecided on this - this function figures out whether a full\r
4149    * repaint is necessary on its own, so there's no real reason to have the\r
4150    * caller tell it that.  I think this can safely be set to FALSE - but\r
4151    * if we trust the callers not to request full repaints unnessesarily, then\r
4152    * we could skip some clipping work.  In other words, only request a full\r
4153    * redraw when the majority of pieces have changed positions (ie. flip, \r
4154    * gamestart and similar)  --Hawk\r
4155    */\r
4156   Boolean fullrepaint = repaint;\r
4157 \r
4158   if( DrawPositionNeedsFullRepaint() ) {\r
4159       fullrepaint = TRUE;\r
4160   }\r
4161 \r
4162 #if 0\r
4163   if( fullrepaint ) {\r
4164       static int repaint_count = 0;\r
4165       char buf[128];\r
4166 \r
4167       repaint_count++;\r
4168       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4169       OutputDebugString( buf );\r
4170   }\r
4171 #endif\r
4172 \r
4173   if (board == NULL) {\r
4174     if (!lastReqValid) {\r
4175       return;\r
4176     }\r
4177     board = lastReq;\r
4178   } else {\r
4179     CopyBoard(lastReq, board);\r
4180     lastReqValid = 1;\r
4181   }\r
4182 \r
4183   if (doingSizing) {\r
4184     return;\r
4185   }\r
4186 \r
4187   if (IsIconic(hwndMain)) {\r
4188     return;\r
4189   }\r
4190 \r
4191   if (hdc == NULL) {\r
4192     hdc = GetDC(hwndMain);\r
4193     if (!appData.monoMode) {\r
4194       SelectPalette(hdc, hPal, FALSE);\r
4195       RealizePalette(hdc);\r
4196     }\r
4197     releaseDC = TRUE;\r
4198   } else {\r
4199     releaseDC = FALSE;\r
4200   }\r
4201 \r
4202 #if 0\r
4203   fprintf(debugFP, "*******************************\n"\r
4204                    "repaint = %s\n"\r
4205                    "dragInfo.from (%d,%d)\n"\r
4206                    "dragInfo.start (%d,%d)\n"\r
4207                    "dragInfo.pos (%d,%d)\n"\r
4208                    "dragInfo.lastpos (%d,%d)\n", \r
4209                     repaint ? "TRUE" : "FALSE",\r
4210                     dragInfo.from.x, dragInfo.from.y, \r
4211                     dragInfo.start.x, dragInfo.start.y,\r
4212                     dragInfo.pos.x, dragInfo.pos.y,\r
4213                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4214   fprintf(debugFP, "prev:  ");\r
4215   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4216     for (column = 0; column < BOARD_WIDTH; column++) {\r
4217       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4218     }\r
4219   }\r
4220   fprintf(debugFP, "\n");\r
4221   fprintf(debugFP, "board: ");\r
4222   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4223     for (column = 0; column < BOARD_WIDTH; column++) {\r
4224       fprintf(debugFP, "%d ", board[row][column]);\r
4225     }\r
4226   }\r
4227   fprintf(debugFP, "\n");\r
4228   fflush(debugFP);\r
4229 #endif\r
4230 \r
4231   /* Create some work-DCs */\r
4232   hdcmem = CreateCompatibleDC(hdc);\r
4233   tmphdc = CreateCompatibleDC(hdc);\r
4234 \r
4235   /* If dragging is in progress, we temporarely remove the piece */\r
4236   /* [HGM] or temporarily decrease count if stacked              */\r
4237   /*       !! Moved to before board compare !!                   */\r
4238   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4239     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4240     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4241             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4242         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4243     } else \r
4244     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4245             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4246         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4247     } else \r
4248         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4249   }\r
4250 \r
4251   /* Figure out which squares need updating by comparing the \r
4252    * newest board with the last drawn board and checking if\r
4253    * flipping has changed.\r
4254    */\r
4255   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4256     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4257       for (column = 0; column < BOARD_WIDTH; column++) {\r
4258         if (lastDrawn[row][column] != board[row][column]) {\r
4259           SquareToPos(row, column, &x, &y);\r
4260           clips[num_clips++] =\r
4261             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4262         }\r
4263       }\r
4264     }\r
4265     for (i=0; i<2; i++) {\r
4266       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4267           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4268         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4269             lastDrawnHighlight.sq[i].y >= 0) {\r
4270           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4271                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4272           clips[num_clips++] =\r
4273             CreateRectRgn(x - lineGap, y - lineGap, \r
4274                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4275         }\r
4276         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4277           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4278           clips[num_clips++] =\r
4279             CreateRectRgn(x - lineGap, y - lineGap, \r
4280                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4281         }\r
4282       }\r
4283     }\r
4284     for (i=0; i<2; i++) {\r
4285       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4286           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4287         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4288             lastDrawnPremove.sq[i].y >= 0) {\r
4289           SquareToPos(lastDrawnPremove.sq[i].y,\r
4290                       lastDrawnPremove.sq[i].x, &x, &y);\r
4291           clips[num_clips++] =\r
4292             CreateRectRgn(x - lineGap, y - lineGap, \r
4293                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4294         }\r
4295         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4296             premoveHighlightInfo.sq[i].y >= 0) {\r
4297           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4298                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4299           clips[num_clips++] =\r
4300             CreateRectRgn(x - lineGap, y - lineGap, \r
4301                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4302         }\r
4303       }\r
4304     }\r
4305   } else {\r
4306     fullrepaint = TRUE;\r
4307   }\r
4308 \r
4309   /* Create a buffer bitmap - this is the actual bitmap\r
4310    * being written to.  When all the work is done, we can\r
4311    * copy it to the real DC (the screen).  This avoids\r
4312    * the problems with flickering.\r
4313    */\r
4314   GetClientRect(hwndMain, &Rect);\r
4315   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4316                                         Rect.bottom-Rect.top+1);\r
4317   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4318   if (!appData.monoMode) {\r
4319     SelectPalette(hdcmem, hPal, FALSE);\r
4320   }\r
4321 \r
4322   /* Create clips for dragging */\r
4323   if (!fullrepaint) {\r
4324     if (dragInfo.from.x >= 0) {\r
4325       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4326       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4327     }\r
4328     if (dragInfo.start.x >= 0) {\r
4329       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4330       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4331     }\r
4332     if (dragInfo.pos.x >= 0) {\r
4333       x = dragInfo.pos.x - squareSize / 2;\r
4334       y = dragInfo.pos.y - squareSize / 2;\r
4335       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4336     }\r
4337     if (dragInfo.lastpos.x >= 0) {\r
4338       x = dragInfo.lastpos.x - squareSize / 2;\r
4339       y = dragInfo.lastpos.y - squareSize / 2;\r
4340       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4341     }\r
4342   }\r
4343 \r
4344   /* Are we animating a move?  \r
4345    * If so, \r
4346    *   - remove the piece from the board (temporarely)\r
4347    *   - calculate the clipping region\r
4348    */\r
4349   if (!fullrepaint) {\r
4350     if (animInfo.piece != EmptySquare) {\r
4351       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4352       x = boardRect.left + animInfo.lastpos.x;\r
4353       y = boardRect.top + animInfo.lastpos.y;\r
4354       x2 = boardRect.left + animInfo.pos.x;\r
4355       y2 = boardRect.top + animInfo.pos.y;\r
4356       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4357       /* Slight kludge.  The real problem is that after AnimateMove is\r
4358          done, the position on the screen does not match lastDrawn.\r
4359          This currently causes trouble only on e.p. captures in\r
4360          atomic, where the piece moves to an empty square and then\r
4361          explodes.  The old and new positions both had an empty square\r
4362          at the destination, but animation has drawn a piece there and\r
4363          we have to remember to erase it. */\r
4364       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4365     }\r
4366   }\r
4367 \r
4368   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4369   if (num_clips == 0)\r
4370     fullrepaint = TRUE;\r
4371 \r
4372   /* Set clipping on the memory DC */\r
4373   if (!fullrepaint) {\r
4374     SelectClipRgn(hdcmem, clips[0]);\r
4375     for (x = 1; x < num_clips; x++) {\r
4376       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4377         abort();  // this should never ever happen!\r
4378     }\r
4379   }\r
4380 \r
4381   /* Do all the drawing to the memory DC */\r
4382   DrawGridOnDC(hdcmem);\r
4383   DrawHighlightsOnDC(hdcmem);\r
4384   DrawBoardOnDC(hdcmem, board, tmphdc);\r
4385 \r
4386   if( appData.highlightMoveWithArrow ) {\r
4387     DrawArrowHighlight(hdcmem);\r
4388   }\r
4389 \r
4390   DrawCoordsOnDC(hdcmem);\r
4391 \r
4392   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4393                  /* to make sure lastDrawn contains what is actually drawn */\r
4394 \r
4395   /* Put the dragged piece back into place and draw it (out of place!) */\r
4396     if (dragged_piece != EmptySquare) {\r
4397     /* [HGM] or restack */\r
4398     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4399                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4400     else\r
4401     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4402                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4403     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4404     x = dragInfo.pos.x - squareSize / 2;\r
4405     y = dragInfo.pos.y - squareSize / 2;\r
4406     DrawPieceOnDC(hdcmem, dragged_piece,\r
4407                   ((int) dragged_piece < (int) BlackPawn), \r
4408                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4409   }   \r
4410   \r
4411   /* Put the animated piece back into place and draw it */\r
4412   if (animInfo.piece != EmptySquare) {\r
4413     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4414     x = boardRect.left + animInfo.pos.x;\r
4415     y = boardRect.top + animInfo.pos.y;\r
4416     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4417                   ((int) animInfo.piece < (int) BlackPawn),\r
4418                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4419   }\r
4420 \r
4421   /* Release the bufferBitmap by selecting in the old bitmap \r
4422    * and delete the memory DC\r
4423    */\r
4424   SelectObject(hdcmem, oldBitmap);\r
4425   DeleteDC(hdcmem);\r
4426 \r
4427   /* Set clipping on the target DC */\r
4428   if (!fullrepaint) {\r
4429     SelectClipRgn(hdc, clips[0]);\r
4430     for (x = 1; x < num_clips; x++) {\r
4431       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4432         abort();   // this should never ever happen!\r
4433     } \r
4434   }\r
4435 \r
4436   /* Copy the new bitmap onto the screen in one go.\r
4437    * This way we avoid any flickering\r
4438    */\r
4439   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4440   BitBlt(hdc, boardRect.left, boardRect.top,\r
4441          boardRect.right - boardRect.left,\r
4442          boardRect.bottom - boardRect.top,\r
4443          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4444   if(saveDiagFlag) { \r
4445     BITMAP b; int i, j, m, w, wb, fac=0; char pData[1000000]; \r
4446     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4447 \r
4448     GetObject(bufferBitmap, sizeof(b), &b);\r
4449     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4450         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4451         bih.biWidth = b.bmWidth;\r
4452         bih.biHeight = b.bmHeight;\r
4453         bih.biPlanes = 1;\r
4454         bih.biBitCount = b.bmBitsPixel;\r
4455         bih.biCompression = 0;\r
4456         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4457         bih.biXPelsPerMeter = 0;\r
4458         bih.biYPelsPerMeter = 0;\r
4459         bih.biClrUsed = 0;\r
4460         bih.biClrImportant = 0;\r
4461 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4462 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4463         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4464 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4465 \r
4466 #if 1\r
4467         wb = b.bmWidthBytes;\r
4468         // count colors\r
4469         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4470                 int k = ((int*) pData)[i];\r
4471                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4472                 if(j >= 16) break;\r
4473                 color[j] = k;\r
4474                 if(j >= nrColors) nrColors = j+1;\r
4475         }\r
4476         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4477                 INT p = 0;\r
4478                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4479                     for(w=0; w<(wb>>2); w+=2) {\r
4480                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4481                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4482                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4483                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4484                         pData[p++] = m | j<<4;\r
4485                     }\r
4486                     while(p&3) pData[p++] = 0;\r
4487                 }\r
4488                 fac = 3;\r
4489                 wb = (wb+31>>5)<<2;\r
4490         }\r
4491         // write BITMAPFILEHEADER\r
4492         fprintf(diagFile, "BM");\r
4493         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4494         fputDW(diagFile, 0);\r
4495         fputDW(diagFile, 0x36 + (fac?64:0));\r
4496         // write BITMAPINFOHEADER\r
4497         fputDW(diagFile, 40);\r
4498         fputDW(diagFile, b.bmWidth);\r
4499         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4500         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4501         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4502         fputDW(diagFile, 0);\r
4503         fputDW(diagFile, 0);\r
4504         fputDW(diagFile, 0);\r
4505         fputDW(diagFile, 0);\r
4506         fputDW(diagFile, 0);\r
4507         fputDW(diagFile, 0);\r
4508         // write color table\r
4509         if(fac)\r
4510         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4511         // write bitmap data\r
4512         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4513                 fputc(pData[i], diagFile);\r
4514 #endif\r
4515      }\r
4516   }\r
4517 \r
4518   SelectObject(tmphdc, oldBitmap);\r
4519 \r
4520   /* Massive cleanup */\r
4521   for (x = 0; x < num_clips; x++)\r
4522     DeleteObject(clips[x]);\r
4523 \r
4524   DeleteDC(tmphdc);\r
4525   DeleteObject(bufferBitmap);\r
4526 \r
4527   if (releaseDC) \r
4528     ReleaseDC(hwndMain, hdc);\r
4529   \r
4530   if (lastDrawnFlipView != flipView) {\r
4531     if (flipView)\r
4532       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4533     else\r
4534       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4535   }\r
4536 \r
4537 /*  CopyBoard(lastDrawn, board);*/\r
4538   lastDrawnHighlight = highlightInfo;\r
4539   lastDrawnPremove   = premoveHighlightInfo;\r
4540   lastDrawnFlipView = flipView;\r
4541   lastDrawnValid = 1;\r
4542 }\r
4543 \r
4544 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4545 int\r
4546 SaveDiagram(f)\r
4547      FILE *f;\r
4548 {\r
4549     time_t tm;\r
4550     char *fen;\r
4551 \r
4552     saveDiagFlag = 1; diagFile = f;\r
4553     HDCDrawPosition(NULL, TRUE, NULL);\r
4554 \r
4555     saveDiagFlag = 0;\r
4556 \r
4557 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4558     \r
4559     fclose(f);\r
4560     return TRUE;\r
4561 }\r
4562 \r
4563 \r
4564 /*---------------------------------------------------------------------------*\\r
4565 | CLIENT PAINT PROCEDURE\r
4566 |   This is the main event-handler for the WM_PAINT message.\r
4567 |\r
4568 \*---------------------------------------------------------------------------*/\r
4569 VOID\r
4570 PaintProc(HWND hwnd)\r
4571 {\r
4572   HDC         hdc;\r
4573   PAINTSTRUCT ps;\r
4574   HFONT       oldFont;\r
4575 \r
4576   if(hdc = BeginPaint(hwnd, &ps)) {\r
4577     if (IsIconic(hwnd)) {\r
4578       DrawIcon(hdc, 2, 2, iconCurrent);\r
4579     } else {\r
4580       if (!appData.monoMode) {\r
4581         SelectPalette(hdc, hPal, FALSE);\r
4582         RealizePalette(hdc);\r
4583       }\r
4584       HDCDrawPosition(hdc, 1, NULL);\r
4585       oldFont =\r
4586         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4587       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4588                  ETO_CLIPPED|ETO_OPAQUE,\r
4589                  &messageRect, messageText, strlen(messageText), NULL);\r
4590       SelectObject(hdc, oldFont);\r
4591       DisplayBothClocks();\r
4592     }\r
4593     EndPaint(hwnd,&ps);\r
4594   }\r
4595 \r
4596   return;\r
4597 }\r
4598 \r
4599 \r
4600 /*\r
4601  * If the user selects on a border boundary, return -1; if off the board,\r
4602  *   return -2.  Otherwise map the event coordinate to the square.\r
4603  * The offset boardRect.left or boardRect.top must already have been\r
4604  *   subtracted from x.\r
4605  */\r
4606 int\r
4607 EventToSquare(int x)\r
4608 {\r
4609   if (x <= 0)\r
4610     return -2;\r
4611   if (x < lineGap)\r
4612     return -1;\r
4613   x -= lineGap;\r
4614   if ((x % (squareSize + lineGap)) >= squareSize)\r
4615     return -1;\r
4616   x /= (squareSize + lineGap);\r
4617   if (x >= BOARD_SIZE)\r
4618     return -2;\r
4619   return x;\r
4620 }\r
4621 \r
4622 typedef struct {\r
4623   char piece;\r
4624   int command;\r
4625   char* name;\r
4626 } DropEnable;\r
4627 \r
4628 DropEnable dropEnables[] = {\r
4629   { 'P', DP_Pawn, "Pawn" },\r
4630   { 'N', DP_Knight, "Knight" },\r
4631   { 'B', DP_Bishop, "Bishop" },\r
4632   { 'R', DP_Rook, "Rook" },\r
4633   { 'Q', DP_Queen, "Queen" },\r
4634 };\r
4635 \r
4636 VOID\r
4637 SetupDropMenu(HMENU hmenu)\r
4638 {\r
4639   int i, count, enable;\r
4640   char *p;\r
4641   extern char white_holding[], black_holding[];\r
4642   char item[MSG_SIZ];\r
4643 \r
4644   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4645     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4646                dropEnables[i].piece);\r
4647     count = 0;\r
4648     while (p && *p++ == dropEnables[i].piece) count++;\r
4649     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4650     enable = count > 0 || !appData.testLegality\r
4651       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4652                       && !appData.icsActive);\r
4653     ModifyMenu(hmenu, dropEnables[i].command,\r
4654                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4655                dropEnables[i].command, item);\r
4656   }\r
4657 }\r
4658 \r
4659 static int fromX = -1, fromY = -1, toX, toY;\r
4660 \r
4661 /* Event handler for mouse messages */\r
4662 VOID\r
4663 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4664 {\r
4665   int x, y;\r
4666   POINT pt;\r
4667   static int recursive = 0;\r
4668   HMENU hmenu;\r
4669   BOOLEAN needsRedraw = FALSE;\r
4670   BOOLEAN saveAnimate;\r
4671   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4672   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4673   ChessMove moveType;\r
4674 \r
4675   if (recursive) {\r
4676     if (message == WM_MBUTTONUP) {\r
4677       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4678          to the middle button: we simulate pressing the left button too!\r
4679          */\r
4680       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4681       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4682     }\r
4683     return;\r
4684   }\r
4685   recursive++;\r
4686   \r
4687   pt.x = LOWORD(lParam);\r
4688   pt.y = HIWORD(lParam);\r
4689   x = EventToSquare(pt.x - boardRect.left);\r
4690   y = EventToSquare(pt.y - boardRect.top);\r
4691   if (!flipView && y >= 0) {\r
4692     y = BOARD_HEIGHT - 1 - y;\r
4693   }\r
4694   if (flipView && x >= 0) {\r
4695     x = BOARD_WIDTH - 1 - x;\r
4696   }\r
4697 \r
4698   switch (message) {\r
4699   case WM_LBUTTONDOWN:\r
4700     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4701         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4702         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4703         if(gameInfo.holdingsWidth && \r
4704                 (WhiteOnMove(currentMove) \r
4705                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4706                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4707             // click in right holdings, for determining promotion piece\r
4708             ChessSquare p = boards[currentMove][y][x];\r
4709             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4710             if(p != EmptySquare) {\r
4711                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4712                 fromX = fromY = -1;\r
4713                 break;\r
4714             }\r
4715         }\r
4716         DrawPosition(FALSE, boards[currentMove]);\r
4717         break;\r
4718     }\r
4719     ErrorPopDown();\r
4720     sameAgain = FALSE;\r
4721     if (y == -2) {\r
4722       /* Downclick vertically off board; check if on clock */\r
4723       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4724         if (gameMode == EditPosition) {\r
4725           SetWhiteToPlayEvent();\r
4726         } else if (gameMode == IcsPlayingBlack ||\r
4727                    gameMode == MachinePlaysWhite) {\r
4728           CallFlagEvent();\r
4729         } else if (gameMode == EditGame) {\r
4730           AdjustClock(flipClock, -1);\r
4731         }\r
4732       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4733         if (gameMode == EditPosition) {\r
4734           SetBlackToPlayEvent();\r
4735         } else if (gameMode == IcsPlayingWhite ||\r
4736                    gameMode == MachinePlaysBlack) {\r
4737           CallFlagEvent();\r
4738         } else if (gameMode == EditGame) {\r
4739           AdjustClock(!flipClock, -1);\r
4740         }\r
4741       }\r
4742       if (!appData.highlightLastMove) {\r
4743         ClearHighlights();\r
4744         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4745       }\r
4746       fromX = fromY = -1;\r
4747       dragInfo.start.x = dragInfo.start.y = -1;\r
4748       dragInfo.from = dragInfo.start;\r
4749       break;\r
4750     } else if (x < 0 || y < 0\r
4751       /* [HGM] block clicks between board and holdings */\r
4752               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4753               || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize\r
4754               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize\r
4755         /* EditPosition, empty square, or different color piece;\r
4756            click-click move is possible */\r
4757                                ) {\r
4758       break;\r
4759     } else if (fromX == x && fromY == y) {\r
4760       /* Downclick on same square again */\r
4761       ClearHighlights();\r
4762       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4763       sameAgain = TRUE;  \r
4764     } else if (fromX != -1 &&\r
4765                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4766                                                                         ) {\r
4767       /* Downclick on different square. */\r
4768       /* [HGM] if on holdings file, should count as new first click ! */\r
4769       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
4770         toX = x;\r
4771         toY = y;\r
4772         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
4773            to make sure move is legal before showing promotion popup */\r
4774         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4775         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4776                 fromX = fromY = -1; \r
4777                 ClearHighlights();\r
4778                 DrawPosition(FALSE, boards[currentMove]);\r
4779                 break; \r
4780         } else \r
4781         if(moveType != ImpossibleMove) {\r
4782           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
4783           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4784              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4785               appData.alwaysPromoteToQueen) {\r
4786                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4787                   if (!appData.highlightLastMove) {\r
4788                       ClearHighlights();\r
4789                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4790                   }\r
4791           } else\r
4792           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4793                   SetHighlights(fromX, fromY, toX, toY);\r
4794                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4795                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
4796                      If promotion to Q is legal, all are legal! */\r
4797                   if(gameInfo.variant == VariantSuper)\r
4798                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4799                     // kludge to temporarily execute move on display, wthout promotng yet\r
4800                     promotionChoice = TRUE;\r
4801                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4802                     boards[currentMove][toY][toX] = p;\r
4803                     DrawPosition(FALSE, boards[currentMove]);\r
4804                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4805                     boards[currentMove][toY][toX] = q;\r
4806                   } else\r
4807                   PromotionPopup(hwnd);\r
4808           } else {       /* not a promotion */\r
4809              if (appData.animate || appData.highlightLastMove) {\r
4810                  SetHighlights(fromX, fromY, toX, toY);\r
4811              } else {\r
4812                  ClearHighlights();\r
4813              }\r
4814              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4815              fromX = fromY = -1;\r
4816              if (appData.animate && !appData.highlightLastMove) {\r
4817                   ClearHighlights();\r
4818                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
4819              }\r
4820           }\r
4821           break;\r
4822         }\r
4823         if (gotPremove) {\r
4824             /* [HGM] it seemed that braces were missing here */\r
4825             SetPremoveHighlights(fromX, fromY, toX, toY);\r
4826             fromX = fromY = -1;\r
4827             break;\r
4828         }\r
4829       }\r
4830       ClearHighlights();\r
4831       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4832     }\r
4833     /* First downclick, or restart on a square with same color piece */\r
4834     if (!frozen && OKToStartUserMove(x, y)) {\r
4835       fromX = x;\r
4836       fromY = y;\r
4837       dragInfo.lastpos = pt;\r
4838       dragInfo.from.x = fromX;\r
4839       dragInfo.from.y = fromY;\r
4840       dragInfo.start = dragInfo.from;\r
4841       SetCapture(hwndMain);\r
4842     } else {\r
4843       fromX = fromY = -1;\r
4844       dragInfo.start.x = dragInfo.start.y = -1;\r
4845       dragInfo.from = dragInfo.start;\r
4846       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4847     }\r
4848     break;\r
4849 \r
4850   case WM_LBUTTONUP:\r
4851     ReleaseCapture();\r
4852     if (fromX == -1) break;\r
4853     if (x == fromX && y == fromY) {\r
4854       dragInfo.from.x = dragInfo.from.y = -1;\r
4855       /* Upclick on same square */\r
4856       if (sameAgain) {\r
4857         /* Clicked same square twice: abort click-click move */\r
4858         fromX = fromY = -1;\r
4859         gotPremove = 0;\r
4860         ClearPremoveHighlights();\r
4861       } else {\r
4862         /* First square clicked: start click-click move */\r
4863         SetHighlights(fromX, fromY, -1, -1);\r
4864       }\r
4865       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4866     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
4867       /* Errant click; ignore */\r
4868       break;\r
4869     } else {\r
4870       /* Finish drag move. */\r
4871     if (appData.debugMode) {\r
4872         fprintf(debugFP, "release\n");\r
4873     }\r
4874       dragInfo.from.x = dragInfo.from.y = -1;\r
4875       toX = x;\r
4876       toY = y;\r
4877       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
4878       appData.animate = appData.animate && !appData.animateDragging;\r
4879       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
4880       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
4881                 fromX = fromY = -1; \r
4882                 ClearHighlights();\r
4883                 DrawPosition(FALSE, boards[currentMove]);\r
4884                 break; \r
4885       } else \r
4886       if(moveType != ImpossibleMove) {\r
4887           /* [HGM] use move type to determine if move is promotion.\r
4888              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
4889           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
4890              (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
4891               appData.alwaysPromoteToQueen) \r
4892                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
4893           else \r
4894           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
4895                DrawPosition(forceFullRepaint || FALSE, NULL);\r
4896                   if(gameInfo.variant == VariantSuper)\r
4897                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
4898                     // kludge to temporarily execute move on display, wthout promotng yet\r
4899                     promotionChoice = TRUE;\r
4900                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
4901                     boards[currentMove][toY][toX] = p;\r
4902                     DrawPosition(FALSE, boards[currentMove]);\r
4903                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
4904                     boards[currentMove][toY][toX] = q;\r
4905                     break;\r
4906                   } else\r
4907                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
4908         } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
4909       }\r
4910       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
4911       appData.animate = saveAnimate;\r
4912       fromX = fromY = -1;\r
4913       if (appData.highlightDragging && !appData.highlightLastMove) {\r
4914         ClearHighlights();\r
4915       }\r
4916       if (appData.animate || appData.animateDragging ||\r
4917           appData.highlightDragging || gotPremove) {\r
4918         DrawPosition(forceFullRepaint || FALSE, NULL);\r
4919       }\r
4920     }\r
4921     dragInfo.start.x = dragInfo.start.y = -1; \r
4922     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4923     break;\r
4924 \r
4925   case WM_MOUSEMOVE:\r
4926     if ((appData.animateDragging || appData.highlightDragging)\r
4927         && (wParam & MK_LBUTTON)\r
4928         && dragInfo.from.x >= 0) \r
4929     {\r
4930       BOOL full_repaint = FALSE;\r
4931 \r
4932       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
4933       if (appData.animateDragging) {\r
4934         dragInfo.pos = pt;\r
4935       }\r
4936       if (appData.highlightDragging) {\r
4937         SetHighlights(fromX, fromY, x, y);\r
4938         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4939             full_repaint = TRUE;\r
4940         }\r
4941       }\r
4942       \r
4943       DrawPosition( full_repaint, NULL);\r
4944       \r
4945       dragInfo.lastpos = dragInfo.pos;\r
4946     }\r
4947     break;\r
4948 \r
4949   case WM_MBUTTONDOWN:\r
4950   case WM_RBUTTONDOWN:\r
4951     ErrorPopDown();\r
4952     ReleaseCapture();\r
4953     fromX = fromY = -1;\r
4954     dragInfo.pos.x = dragInfo.pos.y = -1;\r
4955     dragInfo.start.x = dragInfo.start.y = -1;\r
4956     dragInfo.from = dragInfo.start;\r
4957     dragInfo.lastpos = dragInfo.pos;\r
4958     if (appData.highlightDragging) {\r
4959       ClearHighlights();\r
4960     }\r
4961     if(y == -2) {\r
4962       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
4963       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4964           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
4965       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4966           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
4967       }\r
4968     }\r
4969     DrawPosition(TRUE, NULL);\r
4970 \r
4971     switch (gameMode) {\r
4972     case EditPosition:\r
4973     case IcsExamining:\r
4974       if (x < 0 || y < 0) break;\r
4975       fromX = x;\r
4976       fromY = y;\r
4977       if (message == WM_MBUTTONDOWN) {\r
4978         buttonCount = 3;  /* even if system didn't think so */\r
4979         if (wParam & MK_SHIFT) \r
4980           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4981         else\r
4982           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4983       } else { /* message == WM_RBUTTONDOWN */\r
4984 #if 0\r
4985         if (buttonCount == 3) {\r
4986           if (wParam & MK_SHIFT) \r
4987             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
4988           else\r
4989             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
4990         } else {\r
4991           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4992         }\r
4993 #else\r
4994         /* Just have one menu, on the right button.  Windows users don't\r
4995            think to try the middle one, and sometimes other software steals\r
4996            it, or it doesn't really exist. */\r
4997         if(gameInfo.variant != VariantShogi)\r
4998             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
4999         else\r
5000             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5001 #endif\r
5002       }\r
5003       break;\r
5004     case IcsPlayingWhite:\r
5005     case IcsPlayingBlack:\r
5006     case EditGame:\r
5007     case MachinePlaysWhite:\r
5008     case MachinePlaysBlack:\r
5009       if (appData.testLegality &&\r
5010           gameInfo.variant != VariantBughouse &&\r
5011           gameInfo.variant != VariantCrazyhouse) break;\r
5012       if (x < 0 || y < 0) break;\r
5013       fromX = x;\r
5014       fromY = y;\r
5015       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5016       SetupDropMenu(hmenu);\r
5017       MenuPopup(hwnd, pt, hmenu, -1);\r
5018       break;\r
5019     default:\r
5020       break;\r
5021     }\r
5022     break;\r
5023   }\r
5024 \r
5025   recursive--;\r
5026 }\r
5027 \r
5028 /* Preprocess messages for buttons in main window */\r
5029 LRESULT CALLBACK\r
5030 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5031 {\r
5032   int id = GetWindowLong(hwnd, GWL_ID);\r
5033   int i, dir;\r
5034 \r
5035   for (i=0; i<N_BUTTONS; i++) {\r
5036     if (buttonDesc[i].id == id) break;\r
5037   }\r
5038   if (i == N_BUTTONS) return 0;\r
5039   switch (message) {\r
5040   case WM_KEYDOWN:\r
5041     switch (wParam) {\r
5042     case VK_LEFT:\r
5043     case VK_RIGHT:\r
5044       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5045       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5046       return TRUE;\r
5047     }\r
5048     break;\r
5049   case WM_CHAR:\r
5050     switch (wParam) {\r
5051     case '\r':\r
5052       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5053       return TRUE;\r
5054     case '\t':\r
5055       if (appData.icsActive) {\r
5056         if (GetKeyState(VK_SHIFT) < 0) {\r
5057           /* shifted */\r
5058           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5059           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5060           SetFocus(h);\r
5061         } else {\r
5062           /* unshifted */\r
5063           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5064           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5065           SetFocus(h);\r
5066         }\r
5067         return TRUE;\r
5068       }\r
5069       break;\r
5070     default:\r
5071       if (appData.icsActive) {\r
5072         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5073         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5074         SetFocus(h);\r
5075         SendMessage(h, WM_CHAR, wParam, lParam);\r
5076         return TRUE;\r
5077       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5078         PopUpMoveDialog((char)wParam);\r
5079       }\r
5080       break;\r
5081     }\r
5082     break;\r
5083   }\r
5084   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5085 }\r
5086 \r
5087 /* Process messages for Promotion dialog box */\r
5088 LRESULT CALLBACK\r
5089 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5090 {\r
5091   char promoChar;\r
5092 \r
5093   switch (message) {\r
5094   case WM_INITDIALOG: /* message: initialize dialog box */\r
5095     /* Center the dialog over the application window */\r
5096     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5097     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5098       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5099        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5100                SW_SHOW : SW_HIDE);\r
5101     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5102     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5103        (PieceToChar(WhiteAngel) >= 'A' &&\r
5104         PieceToChar(WhiteAngel) != '~' ||\r
5105         PieceToChar(BlackAngel) >= 'A' &&\r
5106         PieceToChar(BlackAngel) != '~'   ) ?\r
5107                SW_SHOW : SW_HIDE);\r
5108     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5109        (PieceToChar(WhiteMarshall) >= 'A' &&\r
5110         PieceToChar(WhiteMarshall) != '~' ||\r
5111         PieceToChar(BlackMarshall) >= 'A' &&\r
5112         PieceToChar(BlackMarshall) != '~'   ) ?\r
5113                SW_SHOW : SW_HIDE);\r
5114     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5115     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5116        gameInfo.variant != VariantShogi ?\r
5117                SW_SHOW : SW_HIDE);\r
5118     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5119        gameInfo.variant != VariantShogi ?\r
5120                SW_SHOW : SW_HIDE);\r
5121     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5122        gameInfo.variant == VariantShogi ?\r
5123                SW_SHOW : SW_HIDE);\r
5124     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5125        gameInfo.variant == VariantShogi ?\r
5126                SW_SHOW : SW_HIDE);\r
5127     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5128        gameInfo.variant == VariantSuper ?\r
5129                SW_SHOW : SW_HIDE);\r
5130     return TRUE;\r
5131 \r
5132   case WM_COMMAND: /* message: received a command */\r
5133     switch (LOWORD(wParam)) {\r
5134     case IDCANCEL:\r
5135       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5136       ClearHighlights();\r
5137       DrawPosition(FALSE, NULL);\r
5138       return TRUE;\r
5139     case PB_King:\r
5140       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5141       break;\r
5142     case PB_Queen:\r
5143       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5144       break;\r
5145     case PB_Rook:\r
5146       promoChar = PieceToChar(BlackRook);\r
5147       break;\r
5148     case PB_Bishop:\r
5149       promoChar = PieceToChar(BlackBishop);\r
5150       break;\r
5151     case PB_Chancellor:\r
5152       promoChar = PieceToChar(BlackMarshall);\r
5153       break;\r
5154     case PB_Archbishop:\r
5155       promoChar = PieceToChar(BlackAngel);\r
5156       break;\r
5157     case PB_Knight:\r
5158       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5159       break;\r
5160     default:\r
5161       return FALSE;\r
5162     }\r
5163     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5164     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5165        only show the popup when we are already sure the move is valid or\r
5166        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5167        will figure out it is a promotion from the promoChar. */\r
5168     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5169     if (!appData.highlightLastMove) {\r
5170       ClearHighlights();\r
5171       DrawPosition(FALSE, NULL);\r
5172     }\r
5173     return TRUE;\r
5174   }\r
5175   return FALSE;\r
5176 }\r
5177 \r
5178 /* Pop up promotion dialog */\r
5179 VOID\r
5180 PromotionPopup(HWND hwnd)\r
5181 {\r
5182   FARPROC lpProc;\r
5183 \r
5184   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5185   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5186     hwnd, (DLGPROC)lpProc);\r
5187   FreeProcInstance(lpProc);\r
5188 }\r
5189 \r
5190 /* Toggle ShowThinking */\r
5191 VOID\r
5192 ToggleShowThinking()\r
5193 {\r
5194   appData.showThinking = !appData.showThinking;\r
5195   ShowThinkingEvent();
5196 }\r
5197 \r
5198 VOID\r
5199 LoadGameDialog(HWND hwnd, char* title)\r
5200 {\r
5201   UINT number = 0;\r
5202   FILE *f;\r
5203   char fileTitle[MSG_SIZ];\r
5204   f = OpenFileDialog(hwnd, "rb", "",\r
5205                      appData.oldSaveStyle ? "gam" : "pgn",\r
5206                      GAME_FILT,\r
5207                      title, &number, fileTitle, NULL);\r
5208   if (f != NULL) {\r
5209     cmailMsgLoaded = FALSE;\r
5210     if (number == 0) {\r
5211       int error = GameListBuild(f);\r
5212       if (error) {\r
5213         DisplayError("Cannot build game list", error);\r
5214       } else if (!ListEmpty(&gameList) &&\r
5215                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5216         GameListPopUp(f, fileTitle);\r
5217         return;\r
5218       }\r
5219       GameListDestroy();\r
5220       number = 1;\r
5221     }\r
5222     LoadGame(f, number, fileTitle, FALSE);\r
5223   }\r
5224 }\r
5225 \r
5226 VOID\r
5227 ChangedConsoleFont()\r
5228 {\r
5229   CHARFORMAT cfmt;\r
5230   CHARRANGE tmpsel, sel;\r
5231   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5232   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5233   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5234   PARAFORMAT paraf;\r
5235 \r
5236   cfmt.cbSize = sizeof(CHARFORMAT);\r
5237   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5238   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5239   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5240    * size.  This was undocumented in the version of MSVC++ that I had\r
5241    * when I wrote the code, but is apparently documented now.\r
5242    */\r
5243   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5244   cfmt.bCharSet = f->lf.lfCharSet;\r
5245   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5246   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5247   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5248   /* Why are the following seemingly needed too? */\r
5249   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5250   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5251   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5252   tmpsel.cpMin = 0;\r
5253   tmpsel.cpMax = -1; /*999999?*/\r
5254   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5255   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5256   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5257    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5258    */\r
5259   paraf.cbSize = sizeof(paraf);\r
5260   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5261   paraf.dxStartIndent = 0;\r
5262   paraf.dxOffset = WRAP_INDENT;\r
5263   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5264   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5265 }\r
5266 \r
5267 /*---------------------------------------------------------------------------*\\r
5268  *\r
5269  * Window Proc for main window\r
5270  *\r
5271 \*---------------------------------------------------------------------------*/\r
5272 \r
5273 /* Process messages for main window, etc. */\r
5274 LRESULT CALLBACK\r
5275 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5276 {\r
5277   FARPROC lpProc;\r
5278   int wmId, wmEvent;\r
5279   char *defName;\r
5280   FILE *f;\r
5281   UINT number;\r
5282   char fileTitle[MSG_SIZ];\r
5283   static SnapData sd;\r
5284 \r
5285   switch (message) {\r
5286 \r
5287   case WM_PAINT: /* message: repaint portion of window */\r
5288     PaintProc(hwnd);\r
5289     break;\r
5290 \r
5291   case WM_ERASEBKGND:\r
5292     if (IsIconic(hwnd)) {\r
5293       /* Cheat; change the message */\r
5294       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5295     } else {\r
5296       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5297     }\r
5298     break;\r
5299 \r
5300   case WM_LBUTTONDOWN:\r
5301   case WM_MBUTTONDOWN:\r
5302   case WM_RBUTTONDOWN:\r
5303   case WM_LBUTTONUP:\r
5304   case WM_MBUTTONUP:\r
5305   case WM_RBUTTONUP:\r
5306   case WM_MOUSEMOVE:\r
5307     MouseEvent(hwnd, message, wParam, lParam);\r
5308     break;\r
5309 \r
5310   case WM_CHAR:\r
5311     \r
5312     if (appData.icsActive) {\r
5313       if (wParam == '\t') {\r
5314         if (GetKeyState(VK_SHIFT) < 0) {\r
5315           /* shifted */\r
5316           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5317           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5318           SetFocus(h);\r
5319         } else {\r
5320           /* unshifted */\r
5321           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5322           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5323           SetFocus(h);\r
5324         }\r
5325       } else {\r
5326         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5327         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5328         SetFocus(h);\r
5329         SendMessage(h, message, wParam, lParam);\r
5330       }\r
5331     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5332       PopUpMoveDialog((char)wParam);\r
5333     }\r
5334     break;\r
5335 \r
5336   case WM_PALETTECHANGED:\r
5337     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5338       int nnew;\r
5339       HDC hdc = GetDC(hwndMain);\r
5340       SelectPalette(hdc, hPal, TRUE);\r
5341       nnew = RealizePalette(hdc);\r
5342       if (nnew > 0) {\r
5343         paletteChanged = TRUE;\r
5344 #if 0\r
5345         UpdateColors(hdc);\r
5346 #else\r
5347         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5348 #endif\r
5349       }\r
5350       ReleaseDC(hwnd, hdc);\r
5351     }\r
5352     break;\r
5353 \r
5354   case WM_QUERYNEWPALETTE:\r
5355     if (!appData.monoMode /*&& paletteChanged*/) {\r
5356       int nnew;\r
5357       HDC hdc = GetDC(hwndMain);\r
5358       paletteChanged = FALSE;\r
5359       SelectPalette(hdc, hPal, FALSE);\r
5360       nnew = RealizePalette(hdc);\r
5361       if (nnew > 0) {\r
5362         InvalidateRect(hwnd, &boardRect, FALSE);\r
5363       }\r
5364       ReleaseDC(hwnd, hdc);\r
5365       return TRUE;\r
5366     }\r
5367     return FALSE;\r
5368 \r
5369   case WM_COMMAND: /* message: command from application menu */\r
5370     wmId    = LOWORD(wParam);\r
5371     wmEvent = HIWORD(wParam);\r
5372 \r
5373     switch (wmId) {\r
5374     case IDM_NewGame:\r
5375       ResetGameEvent();\r
5376       AnalysisPopDown();\r
5377       break;\r
5378 \r
5379     case IDM_NewGameFRC:\r
5380       if( NewGameFRC() == 0 ) {\r
5381         ResetGameEvent();\r
5382         AnalysisPopDown();\r
5383       }\r
5384       break;\r
5385 \r
5386     case IDM_NewVariant:\r
5387       NewVariantPopup(hwnd);\r
5388       break;\r
5389 \r
5390     case IDM_LoadGame:\r
5391       LoadGameDialog(hwnd, "Load Game from File");\r
5392       break;\r
5393 \r
5394     case IDM_LoadNextGame:\r
5395       ReloadGame(1);\r
5396       break;\r
5397 \r
5398     case IDM_LoadPrevGame:\r
5399       ReloadGame(-1);\r
5400       break;\r
5401 \r
5402     case IDM_ReloadGame:\r
5403       ReloadGame(0);\r
5404       break;\r
5405 \r
5406     case IDM_LoadPosition:\r
5407       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5408         Reset(FALSE, TRUE);\r
5409       }\r
5410       number = 1;\r
5411       f = OpenFileDialog(hwnd, "rb", "",\r
5412                          appData.oldSaveStyle ? "pos" : "fen",\r
5413                          POSITION_FILT,\r
5414                          "Load Position from File", &number, fileTitle, NULL);\r
5415       if (f != NULL) {\r
5416         LoadPosition(f, number, fileTitle);\r
5417       }\r
5418       break;\r
5419 \r
5420     case IDM_LoadNextPosition:\r
5421       ReloadPosition(1);\r
5422       break;\r
5423 \r
5424     case IDM_LoadPrevPosition:\r
5425       ReloadPosition(-1);\r
5426       break;\r
5427 \r
5428     case IDM_ReloadPosition:\r
5429       ReloadPosition(0);\r
5430       break;\r
5431 \r
5432     case IDM_SaveGame:\r
5433       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5434       f = OpenFileDialog(hwnd, "a", defName,\r
5435                          appData.oldSaveStyle ? "gam" : "pgn",\r
5436                          GAME_FILT,\r
5437                          "Save Game to File", NULL, fileTitle, NULL);\r
5438       if (f != NULL) {\r
5439         SaveGame(f, 0, "");\r
5440       }\r
5441       break;\r
5442 \r
5443     case IDM_SavePosition:\r
5444       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5445       f = OpenFileDialog(hwnd, "a", defName,\r
5446                          appData.oldSaveStyle ? "pos" : "fen",\r
5447                          POSITION_FILT,\r
5448                          "Save Position to File", NULL, fileTitle, NULL);\r
5449       if (f != NULL) {\r
5450         SavePosition(f, 0, "");\r
5451       }\r
5452       break;\r
5453 \r
5454     case IDM_SaveDiagram:\r
5455       defName = "diagram";\r
5456       f = OpenFileDialog(hwnd, "wb", defName,\r
5457                          "bmp",\r
5458                          DIAGRAM_FILT,\r
5459                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5460       if (f != NULL) {\r
5461         SaveDiagram(f);\r
5462       }\r
5463       break;\r
5464 \r
5465     case IDM_CopyGame:\r
5466       CopyGameToClipboard();\r
5467       break;\r
5468 \r
5469     case IDM_PasteGame:\r
5470       PasteGameFromClipboard();\r
5471       break;\r
5472 \r
5473     case IDM_CopyGameListToClipboard:\r
5474       CopyGameListToClipboard();\r
5475       break;\r
5476 \r
5477     /* [AS] Autodetect FEN or PGN data */\r
5478     case IDM_PasteAny:\r
5479       PasteGameOrFENFromClipboard();\r
5480       break;\r
5481 \r
5482     /* [AS] Move history */\r
5483     case IDM_ShowMoveHistory:\r
5484         if( MoveHistoryIsUp() ) {\r
5485             MoveHistoryPopDown();\r
5486         }\r
5487         else {\r
5488             MoveHistoryPopUp();\r
5489         }\r
5490         break;\r
5491 \r
5492     /* [AS] Eval graph */\r
5493     case IDM_ShowEvalGraph:\r
5494         if( EvalGraphIsUp() ) {\r
5495             EvalGraphPopDown();\r
5496         }\r
5497         else {\r
5498             EvalGraphPopUp();\r
5499         }\r
5500         break;\r
5501 \r
5502     /* [AS] Engine output */\r
5503     case IDM_ShowEngineOutput:\r
5504         if( EngineOutputIsUp() ) {\r
5505             EngineOutputPopDown();\r
5506         }\r
5507         else {\r
5508             EngineOutputPopUp();\r
5509         }
5510         break;\r
5511 \r
5512     /* [AS] User adjudication */\r
5513     case IDM_UserAdjudication_White:\r
5514         UserAdjudicationEvent( +1 );\r
5515         break;\r
5516 \r
5517     case IDM_UserAdjudication_Black:\r
5518         UserAdjudicationEvent( -1 );\r
5519         break;\r
5520 \r
5521     case IDM_UserAdjudication_Draw:\r
5522         UserAdjudicationEvent( 0 );\r
5523         break;\r
5524 \r
5525     /* [AS] Game list options dialog */\r
5526     case IDM_GameListOptions:\r
5527       GameListOptions();\r
5528       break;\r
5529 \r
5530     case IDM_CopyPosition:\r
5531       CopyFENToClipboard();\r
5532       break;\r
5533 \r
5534     case IDM_PastePosition:\r
5535       PasteFENFromClipboard();\r
5536       break;\r
5537 \r
5538     case IDM_MailMove:\r
5539       MailMoveEvent();\r
5540       break;\r
5541 \r
5542     case IDM_ReloadCMailMsg:\r
5543       Reset(TRUE, TRUE);\r
5544       ReloadCmailMsgEvent(FALSE);\r
5545       break;\r
5546 \r
5547     case IDM_Minimize:\r
5548       ShowWindow(hwnd, SW_MINIMIZE);\r
5549       break;\r
5550 \r
5551     case IDM_Exit:\r
5552       ExitEvent(0);\r
5553       break;\r
5554 \r
5555     case IDM_MachineWhite:\r
5556       MachineWhiteEvent();\r
5557       /*\r
5558        * refresh the tags dialog only if it's visible\r
5559        */\r
5560       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5561           char *tags;\r
5562           tags = PGNTags(&gameInfo);\r
5563           TagsPopUp(tags, CmailMsg());\r
5564           free(tags);\r
5565       }\r
5566       break;\r
5567 \r
5568     case IDM_MachineBlack:\r
5569       MachineBlackEvent();\r
5570       /*\r
5571        * refresh the tags dialog only if it's visible\r
5572        */\r
5573       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5574           char *tags;\r
5575           tags = PGNTags(&gameInfo);\r
5576           TagsPopUp(tags, CmailMsg());\r
5577           free(tags);\r
5578       }\r
5579       break;\r
5580 \r
5581     case IDM_TwoMachines:\r
5582       TwoMachinesEvent();\r
5583       /*\r
5584        * refresh the tags dialog only if it's visible\r
5585        */\r
5586       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5587           char *tags;\r
5588           tags = PGNTags(&gameInfo);\r
5589           TagsPopUp(tags, CmailMsg());\r
5590           free(tags);\r
5591       }\r
5592       break;\r
5593 \r
5594     case IDM_AnalysisMode:\r
5595       if (!first.analysisSupport) {\r
5596         char buf[MSG_SIZ];\r
5597         sprintf(buf, "%s does not support analysis", first.tidy);\r
5598         DisplayError(buf, 0);\r
5599       } else {\r
5600         if (!appData.showThinking) ToggleShowThinking();\r
5601         AnalyzeModeEvent();\r
5602       }\r
5603       break;\r
5604 \r
5605     case IDM_AnalyzeFile:\r
5606       if (!first.analysisSupport) {\r
5607         char buf[MSG_SIZ];\r
5608         sprintf(buf, "%s does not support analysis", first.tidy);\r
5609         DisplayError(buf, 0);\r
5610       } else {\r
5611         if (!appData.showThinking) ToggleShowThinking();\r
5612         AnalyzeFileEvent();\r
5613         LoadGameDialog(hwnd, "Analyze Game from File");\r
5614         AnalysisPeriodicEvent(1);\r
5615       }\r
5616       break;\r
5617 \r
5618     case IDM_IcsClient:\r
5619       IcsClientEvent();\r
5620       break;\r
5621 \r
5622     case IDM_EditGame:\r
5623       EditGameEvent();\r
5624       break;\r
5625 \r
5626     case IDM_EditPosition:\r
5627       EditPositionEvent();\r
5628       break;\r
5629 \r
5630     case IDM_Training:\r
5631       TrainingEvent();\r
5632       break;\r
5633 \r
5634     case IDM_ShowGameList:\r
5635       ShowGameListProc();\r
5636       break;\r
5637 \r
5638     case IDM_EditTags:\r
5639       EditTagsProc();\r
5640       break;\r
5641 \r
5642     case IDM_EditComment:\r
5643       if (commentDialogUp && editComment) {\r
5644         CommentPopDown();\r
5645       } else {\r
5646         EditCommentEvent();\r
5647       }\r
5648       break;\r
5649 \r
5650     case IDM_Pause:\r
5651       PauseEvent();\r
5652       break;\r
5653 \r
5654     case IDM_Accept:\r
5655       AcceptEvent();\r
5656       break;\r
5657 \r
5658     case IDM_Decline:\r
5659       DeclineEvent();\r
5660       break;\r
5661 \r
5662     case IDM_Rematch:\r
5663       RematchEvent();\r
5664       break;\r
5665 \r
5666     case IDM_CallFlag:\r
5667       CallFlagEvent();\r
5668       break;\r
5669 \r
5670     case IDM_Draw:\r
5671       DrawEvent();\r
5672       break;\r
5673 \r
5674     case IDM_Adjourn:\r
5675       AdjournEvent();\r
5676       break;\r
5677 \r
5678     case IDM_Abort:\r
5679       AbortEvent();\r
5680       break;\r
5681 \r
5682     case IDM_Resign:\r
5683       ResignEvent();\r
5684       break;\r
5685 \r
5686     case IDM_StopObserving:\r
5687       StopObservingEvent();\r
5688       break;\r
5689 \r
5690     case IDM_StopExamining:\r
5691       StopExaminingEvent();\r
5692       break;\r
5693 \r
5694     case IDM_TypeInMove:\r
5695       PopUpMoveDialog('\000');\r
5696       break;\r
5697 \r
5698     case IDM_TypeInName:\r
5699       PopUpNameDialog('\000');\r
5700       break;\r
5701 \r
5702     case IDM_Backward:\r
5703       BackwardEvent();\r
5704       SetFocus(hwndMain);\r
5705       break;\r
5706 \r
5707     case IDM_Forward:\r
5708       ForwardEvent();\r
5709       SetFocus(hwndMain);\r
5710       break;\r
5711 \r
5712     case IDM_ToStart:\r
5713       ToStartEvent();\r
5714       SetFocus(hwndMain);\r
5715       break;\r
5716 \r
5717     case IDM_ToEnd:\r
5718       ToEndEvent();\r
5719       SetFocus(hwndMain);\r
5720       break;\r
5721 \r
5722     case IDM_Revert:\r
5723       RevertEvent();\r
5724       break;\r
5725 \r
5726     case IDM_TruncateGame:\r
5727       TruncateGameEvent();\r
5728       break;\r
5729 \r
5730     case IDM_MoveNow:\r
5731       MoveNowEvent();\r
5732       break;\r
5733 \r
5734     case IDM_RetractMove:\r
5735       RetractMoveEvent();\r
5736       break;\r
5737 \r
5738     case IDM_FlipView:\r
5739       flipView = !flipView;\r
5740       DrawPosition(FALSE, NULL);\r
5741       break;\r
5742 \r
5743     case IDM_FlipClock:\r
5744       flipClock = !flipClock;\r
5745       DisplayBothClocks();\r
5746       break;\r
5747 \r
5748     case IDM_GeneralOptions:\r
5749       GeneralOptionsPopup(hwnd);\r
5750       DrawPosition(TRUE, NULL);\r
5751       break;\r
5752 \r
5753     case IDM_BoardOptions:\r
5754       BoardOptionsPopup(hwnd);\r
5755       break;\r
5756 \r
5757     case IDM_EnginePlayOptions:\r
5758       EnginePlayOptionsPopup(hwnd);\r
5759       break;\r
5760 \r
5761     case IDM_OptionsUCI:\r
5762       UciOptionsPopup(hwnd);\r
5763       break;\r
5764 \r
5765     case IDM_IcsOptions:\r
5766       IcsOptionsPopup(hwnd);\r
5767       break;\r
5768 \r
5769     case IDM_Fonts:\r
5770       FontsOptionsPopup(hwnd);\r
5771       break;\r
5772 \r
5773     case IDM_Sounds:\r
5774       SoundOptionsPopup(hwnd);\r
5775       break;\r
5776 \r
5777     case IDM_CommPort:\r
5778       CommPortOptionsPopup(hwnd);\r
5779       break;\r
5780 \r
5781     case IDM_LoadOptions:\r
5782       LoadOptionsPopup(hwnd);\r
5783       break;\r
5784 \r
5785     case IDM_SaveOptions:\r
5786       SaveOptionsPopup(hwnd);\r
5787       break;\r
5788 \r
5789     case IDM_TimeControl:\r
5790       TimeControlOptionsPopup(hwnd);\r
5791       break;\r
5792 \r
5793     case IDM_SaveSettings:\r
5794       SaveSettings(settingsFileName);\r
5795       break;\r
5796 \r
5797     case IDM_SaveSettingsOnExit:\r
5798       saveSettingsOnExit = !saveSettingsOnExit;\r
5799       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5800                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5801                                          MF_CHECKED : MF_UNCHECKED));\r
5802       break;\r
5803 \r
5804     case IDM_Hint:\r
5805       HintEvent();\r
5806       break;\r
5807 \r
5808     case IDM_Book:\r
5809       BookEvent();\r
5810       break;\r
5811 \r
5812     case IDM_AboutGame:\r
5813       AboutGameEvent();\r
5814       break;\r
5815 \r
5816     case IDM_Debug:\r
5817       appData.debugMode = !appData.debugMode;\r
5818       if (appData.debugMode) {\r
5819         char dir[MSG_SIZ];\r
5820         GetCurrentDirectory(MSG_SIZ, dir);\r
5821         SetCurrentDirectory(installDir);\r
5822         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5823         SetCurrentDirectory(dir);\r
5824         setbuf(debugFP, NULL);\r
5825       } else {\r
5826         fclose(debugFP);\r
5827         debugFP = NULL;\r
5828       }\r
5829       break;\r
5830 \r
5831     case IDM_HELPCONTENTS:\r
5832       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
5833         MessageBox (GetFocus(),\r
5834                     "Unable to activate help",\r
5835                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5836       }\r
5837       break;\r
5838 \r
5839     case IDM_HELPSEARCH:\r
5840       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
5841         MessageBox (GetFocus(),\r
5842                     "Unable to activate help",\r
5843                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5844       }\r
5845       break;\r
5846 \r
5847     case IDM_HELPHELP:\r
5848       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
5849         MessageBox (GetFocus(),\r
5850                     "Unable to activate help",\r
5851                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5852       }\r
5853       break;\r
5854 \r
5855     case IDM_ABOUT:\r
5856       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
5857       DialogBox(hInst, \r
5858         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
5859         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
5860       FreeProcInstance(lpProc);\r
5861       break;\r
5862 \r
5863     case IDM_DirectCommand1:\r
5864       AskQuestionEvent("Direct Command",\r
5865                        "Send to chess program:", "", "1");\r
5866       break;\r
5867     case IDM_DirectCommand2:\r
5868       AskQuestionEvent("Direct Command",\r
5869                        "Send to second chess program:", "", "2");\r
5870       break;\r
5871 \r
5872     case EP_WhitePawn:\r
5873       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
5874       fromX = fromY = -1;\r
5875       break;\r
5876 \r
5877     case EP_WhiteKnight:\r
5878       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
5879       fromX = fromY = -1;\r
5880       break;\r
5881 \r
5882     case EP_WhiteBishop:\r
5883       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
5884       fromX = fromY = -1;\r
5885       break;\r
5886 \r
5887     case EP_WhiteRook:\r
5888       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
5889       fromX = fromY = -1;\r
5890       break;\r
5891 \r
5892     case EP_WhiteQueen:\r
5893       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
5894       fromX = fromY = -1;\r
5895       break;\r
5896 \r
5897     case EP_WhiteFerz:\r
5898       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
5899       fromX = fromY = -1;\r
5900       break;\r
5901 \r
5902     case EP_WhiteWazir:\r
5903       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
5904       fromX = fromY = -1;\r
5905       break;\r
5906 \r
5907     case EP_WhiteAlfil:\r
5908       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
5909       fromX = fromY = -1;\r
5910       break;\r
5911 \r
5912     case EP_WhiteCannon:\r
5913       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
5914       fromX = fromY = -1;\r
5915       break;\r
5916 \r
5917     case EP_WhiteCardinal:\r
5918       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
5919       fromX = fromY = -1;\r
5920       break;\r
5921 \r
5922     case EP_WhiteMarshall:\r
5923       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
5924       fromX = fromY = -1;\r
5925       break;\r
5926 \r
5927     case EP_WhiteKing:\r
5928       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
5929       fromX = fromY = -1;\r
5930       break;\r
5931 \r
5932     case EP_BlackPawn:\r
5933       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
5934       fromX = fromY = -1;\r
5935       break;\r
5936 \r
5937     case EP_BlackKnight:\r
5938       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
5939       fromX = fromY = -1;\r
5940       break;\r
5941 \r
5942     case EP_BlackBishop:\r
5943       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
5944       fromX = fromY = -1;\r
5945       break;\r
5946 \r
5947     case EP_BlackRook:\r
5948       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
5949       fromX = fromY = -1;\r
5950       break;\r
5951 \r
5952     case EP_BlackQueen:\r
5953       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
5954       fromX = fromY = -1;\r
5955       break;\r
5956 \r
5957     case EP_BlackFerz:\r
5958       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
5959       fromX = fromY = -1;\r
5960       break;\r
5961 \r
5962     case EP_BlackWazir:\r
5963       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
5964       fromX = fromY = -1;\r
5965       break;\r
5966 \r
5967     case EP_BlackAlfil:\r
5968       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
5969       fromX = fromY = -1;\r
5970       break;\r
5971 \r
5972     case EP_BlackCannon:\r
5973       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
5974       fromX = fromY = -1;\r
5975       break;\r
5976 \r
5977     case EP_BlackCardinal:\r
5978       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
5979       fromX = fromY = -1;\r
5980       break;\r
5981 \r
5982     case EP_BlackMarshall:\r
5983       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
5984       fromX = fromY = -1;\r
5985       break;\r
5986 \r
5987     case EP_BlackKing:\r
5988       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
5989       fromX = fromY = -1;\r
5990       break;\r
5991 \r
5992     case EP_EmptySquare:\r
5993       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
5994       fromX = fromY = -1;\r
5995       break;\r
5996 \r
5997     case EP_ClearBoard:\r
5998       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
5999       fromX = fromY = -1;\r
6000       break;\r
6001 \r
6002     case EP_White:\r
6003       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6004       fromX = fromY = -1;\r
6005       break;\r
6006 \r
6007     case EP_Black:\r
6008       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6009       fromX = fromY = -1;\r
6010       break;\r
6011 \r
6012     case EP_Promote:\r
6013       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6014       fromX = fromY = -1;\r
6015       break;\r
6016 \r
6017     case EP_Demote:\r
6018       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6019       fromX = fromY = -1;\r
6020       break;\r
6021 \r
6022     case DP_Pawn:\r
6023       DropMenuEvent(WhitePawn, fromX, fromY);\r
6024       fromX = fromY = -1;\r
6025       break;\r
6026 \r
6027     case DP_Knight:\r
6028       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6029       fromX = fromY = -1;\r
6030       break;\r
6031 \r
6032     case DP_Bishop:\r
6033       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6034       fromX = fromY = -1;\r
6035       break;\r
6036 \r
6037     case DP_Rook:\r
6038       DropMenuEvent(WhiteRook, fromX, fromY);\r
6039       fromX = fromY = -1;\r
6040       break;\r
6041 \r
6042     case DP_Queen:\r
6043       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6044       fromX = fromY = -1;\r
6045       break;\r
6046 \r
6047     default:\r
6048       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6049     }\r
6050     break;\r
6051 \r
6052   case WM_TIMER:\r
6053     switch (wParam) {\r
6054     case CLOCK_TIMER_ID:\r
6055       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6056       clockTimerEvent = 0;\r
6057       DecrementClocks(); /* call into back end */\r
6058       break;\r
6059     case LOAD_GAME_TIMER_ID:\r
6060       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6061       loadGameTimerEvent = 0;\r
6062       AutoPlayGameLoop(); /* call into back end */\r
6063       break;\r
6064     case ANALYSIS_TIMER_ID:\r
6065       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
6066           appData.periodicUpdates) {\r
6067         AnalysisPeriodicEvent(0);\r
6068       } else {\r
6069         KillTimer(hwnd, analysisTimerEvent);\r
6070         analysisTimerEvent = 0;\r
6071       }\r
6072       break;\r
6073     case DELAYED_TIMER_ID:\r
6074       KillTimer(hwnd, delayedTimerEvent);\r
6075       delayedTimerEvent = 0;\r
6076       delayedTimerCallback();\r
6077       break;\r
6078     }\r
6079     break;\r
6080 \r
6081   case WM_USER_Input:\r
6082     InputEvent(hwnd, message, wParam, lParam);\r
6083     break;\r
6084 \r
6085   /* [AS] Also move "attached" child windows */\r
6086   case WM_WINDOWPOSCHANGING:\r
6087     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6088         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6089 \r
6090         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6091             /* Window is moving */\r
6092             RECT rcMain;\r
6093 \r
6094             GetWindowRect( hwnd, &rcMain );\r
6095             \r
6096             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6097             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6098             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6099         }\r
6100     }\r
6101     break;\r
6102 \r
6103   /* [AS] Snapping */\r
6104   case WM_ENTERSIZEMOVE:\r
6105     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6106     if (hwnd == hwndMain) {\r
6107       doingSizing = TRUE;\r
6108       lastSizing = 0;\r
6109     }\r
6110     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6111     break;\r
6112 \r
6113   case WM_SIZING:\r
6114     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6115     if (hwnd == hwndMain) {\r
6116       lastSizing = wParam;\r
6117     }\r
6118     break;\r
6119 \r
6120   case WM_MOVING:\r
6121     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6122       return OnMoving( &sd, hwnd, wParam, lParam );\r
6123 \r
6124   case WM_EXITSIZEMOVE:\r
6125     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6126     if (hwnd == hwndMain) {\r
6127       RECT client;\r
6128       doingSizing = FALSE;\r
6129       InvalidateRect(hwnd, &boardRect, FALSE);\r
6130       GetClientRect(hwnd, &client);\r
6131       ResizeBoard(client.right, client.bottom, lastSizing);\r
6132       lastSizing = 0;\r
6133       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6134     }\r
6135     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6136     break;\r
6137 \r
6138   case WM_DESTROY: /* message: window being destroyed */\r
6139     PostQuitMessage(0);\r
6140     break;\r
6141 \r
6142   case WM_CLOSE:\r
6143     if (hwnd == hwndMain) {\r
6144       ExitEvent(0);\r
6145     }\r
6146     break;\r
6147 \r
6148   default:      /* Passes it on if unprocessed */\r
6149     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6150   }\r
6151   return 0;\r
6152 }\r
6153 \r
6154 /*---------------------------------------------------------------------------*\\r
6155  *\r
6156  * Misc utility routines\r
6157  *\r
6158 \*---------------------------------------------------------------------------*/\r
6159 \r
6160 /*\r
6161  * Decent random number generator, at least not as bad as Windows\r
6162  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6163  */\r
6164 unsigned int randstate;\r
6165 \r
6166 int\r
6167 myrandom(void)\r
6168 {\r
6169   randstate = randstate * 1664525 + 1013904223;\r
6170   return (int) randstate & 0x7fffffff;\r
6171 }\r
6172 \r
6173 void\r
6174 mysrandom(unsigned int seed)\r
6175 {\r
6176   randstate = seed;\r
6177 }\r
6178 \r
6179 \r
6180 /* \r
6181  * returns TRUE if user selects a different color, FALSE otherwise \r
6182  */\r
6183 \r
6184 BOOL\r
6185 ChangeColor(HWND hwnd, COLORREF *which)\r
6186 {\r
6187   static BOOL firstTime = TRUE;\r
6188   static DWORD customColors[16];\r
6189   CHOOSECOLOR cc;\r
6190   COLORREF newcolor;\r
6191   int i;\r
6192   ColorClass ccl;\r
6193 \r
6194   if (firstTime) {\r
6195     /* Make initial colors in use available as custom colors */\r
6196     /* Should we put the compiled-in defaults here instead? */\r
6197     i = 0;\r
6198     customColors[i++] = lightSquareColor & 0xffffff;\r
6199     customColors[i++] = darkSquareColor & 0xffffff;\r
6200     customColors[i++] = whitePieceColor & 0xffffff;\r
6201     customColors[i++] = blackPieceColor & 0xffffff;\r
6202     customColors[i++] = highlightSquareColor & 0xffffff;\r
6203     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6204 \r
6205     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6206       customColors[i++] = textAttribs[ccl].color;\r
6207     }\r
6208     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6209     firstTime = FALSE;\r
6210   }\r
6211 \r
6212   cc.lStructSize = sizeof(cc);\r
6213   cc.hwndOwner = hwnd;\r
6214   cc.hInstance = NULL;\r
6215   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6216   cc.lpCustColors = (LPDWORD) customColors;\r
6217   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6218 \r
6219   if (!ChooseColor(&cc)) return FALSE;\r
6220 \r
6221   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6222   if (newcolor == *which) return FALSE;\r
6223   *which = newcolor;\r
6224   return TRUE;\r
6225 \r
6226   /*\r
6227   InitDrawingColors();\r
6228   InvalidateRect(hwnd, &boardRect, FALSE);\r
6229   */\r
6230 }\r
6231 \r
6232 BOOLEAN\r
6233 MyLoadSound(MySound *ms)\r
6234 {\r
6235   BOOL ok = FALSE;\r
6236   struct stat st;\r
6237   FILE *f;\r
6238 \r
6239   if (ms->data) free(ms->data);\r
6240   ms->data = NULL;\r
6241 \r
6242   switch (ms->name[0]) {\r
6243   case NULLCHAR:\r
6244     /* Silence */\r
6245     ok = TRUE;\r
6246     break;\r
6247   case '$':\r
6248     /* System sound from Control Panel.  Don't preload here. */\r
6249     ok = TRUE;\r
6250     break;\r
6251   case '!':\r
6252     if (ms->name[1] == NULLCHAR) {\r
6253       /* "!" alone = silence */\r
6254       ok = TRUE;\r
6255     } else {\r
6256       /* Builtin wave resource.  Error if not found. */\r
6257       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6258       if (h == NULL) break;\r
6259       ms->data = (void *)LoadResource(hInst, h);\r
6260       if (h == NULL) break;\r
6261       ok = TRUE;\r
6262     }\r
6263     break;\r
6264   default:\r
6265     /* .wav file.  Error if not found. */\r
6266     f = fopen(ms->name, "rb");\r
6267     if (f == NULL) break;\r
6268     if (fstat(fileno(f), &st) < 0) break;\r
6269     ms->data = malloc(st.st_size);\r
6270     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6271     fclose(f);\r
6272     ok = TRUE;\r
6273     break;\r
6274   }\r
6275   if (!ok) {\r
6276     char buf[MSG_SIZ];\r
6277     sprintf(buf, "Error loading sound %s", ms->name);\r
6278     DisplayError(buf, GetLastError());\r
6279   }\r
6280   return ok;\r
6281 }\r
6282 \r
6283 BOOLEAN\r
6284 MyPlaySound(MySound *ms)\r
6285 {\r
6286   BOOLEAN ok = FALSE;\r
6287   switch (ms->name[0]) {\r
6288   case NULLCHAR:\r
6289     /* Silence */\r
6290     ok = TRUE;\r
6291     break;\r
6292   case '$':\r
6293     /* System sound from Control Panel (deprecated feature).\r
6294        "$" alone or an unset sound name gets default beep (still in use). */\r
6295     if (ms->name[1]) {\r
6296       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6297     }\r
6298     if (!ok) ok = MessageBeep(MB_OK);\r
6299     break; \r
6300   case '!':\r
6301     /* Builtin wave resource, or "!" alone for silence */\r
6302     if (ms->name[1]) {\r
6303       if (ms->data == NULL) return FALSE;\r
6304       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6305     } else {\r
6306       ok = TRUE;\r
6307     }\r
6308     break;\r
6309   default:\r
6310     /* .wav file.  Error if not found. */\r
6311     if (ms->data == NULL) return FALSE;\r
6312     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6313     break;\r
6314   }\r
6315   /* Don't print an error: this can happen innocently if the sound driver\r
6316      is busy; for instance, if another instance of WinBoard is playing\r
6317      a sound at about the same time. */\r
6318 #if 0\r
6319   if (!ok) {\r
6320     char buf[MSG_SIZ];\r
6321     sprintf(buf, "Error playing sound %s", ms->name);\r
6322     DisplayError(buf, GetLastError());\r
6323   }\r
6324 #endif\r
6325   return ok;\r
6326 }\r
6327 \r
6328 \r
6329 LRESULT CALLBACK\r
6330 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6331 {\r
6332   BOOL ok;\r
6333   OPENFILENAME *ofn;\r
6334   static UINT *number; /* gross that this is static */\r
6335 \r
6336   switch (message) {\r
6337   case WM_INITDIALOG: /* message: initialize dialog box */\r
6338     /* Center the dialog over the application window */\r
6339     ofn = (OPENFILENAME *) lParam;\r
6340     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6341       number = (UINT *) ofn->lCustData;\r
6342       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6343     } else {\r
6344       number = NULL;\r
6345     }\r
6346     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6347     return FALSE;  /* Allow for further processing */\r
6348 \r
6349   case WM_COMMAND:\r
6350     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6351       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6352     }\r
6353     return FALSE;  /* Allow for further processing */\r
6354   }\r
6355   return FALSE;\r
6356 }\r
6357 \r
6358 UINT APIENTRY\r
6359 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6360 {\r
6361   static UINT *number;\r
6362   OPENFILENAME *ofname;\r
6363   OFNOTIFY *ofnot;\r
6364   switch (uiMsg) {\r
6365   case WM_INITDIALOG:\r
6366     ofname = (OPENFILENAME *)lParam;\r
6367     number = (UINT *)(ofname->lCustData);\r
6368     break;\r
6369   case WM_NOTIFY:\r
6370     ofnot = (OFNOTIFY *)lParam;\r
6371     if (ofnot->hdr.code == CDN_FILEOK) {\r
6372       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6373     }\r
6374     break;\r
6375   }\r
6376   return 0;\r
6377 }\r
6378 \r
6379 \r
6380 FILE *\r
6381 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6382                char *nameFilt, char *dlgTitle, UINT *number,\r
6383                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6384 {\r
6385   OPENFILENAME openFileName;\r
6386   char buf1[MSG_SIZ];\r
6387   FILE *f;\r
6388 \r
6389   if (fileName == NULL) fileName = buf1;\r
6390   if (defName == NULL) {\r
6391     strcpy(fileName, "*.");\r
6392     strcat(fileName, defExt);\r
6393   } else {\r
6394     strcpy(fileName, defName);\r
6395   }\r
6396   if (fileTitle) strcpy(fileTitle, "");\r
6397   if (number) *number = 0;\r
6398 \r
6399   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6400   openFileName.hwndOwner         = hwnd;\r
6401   openFileName.hInstance         = (HANDLE) hInst;\r
6402   openFileName.lpstrFilter       = nameFilt;\r
6403   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6404   openFileName.nMaxCustFilter    = 0L;\r
6405   openFileName.nFilterIndex      = 1L;\r
6406   openFileName.lpstrFile         = fileName;\r
6407   openFileName.nMaxFile          = MSG_SIZ;\r
6408   openFileName.lpstrFileTitle    = fileTitle;\r
6409   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6410   openFileName.lpstrInitialDir   = NULL;\r
6411   openFileName.lpstrTitle        = dlgTitle;\r
6412   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6413     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6414     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6415     | (oldDialog ? 0 : OFN_EXPLORER);\r
6416   openFileName.nFileOffset       = 0;\r
6417   openFileName.nFileExtension    = 0;\r
6418   openFileName.lpstrDefExt       = defExt;\r
6419   openFileName.lCustData         = (LONG) number;\r
6420   openFileName.lpfnHook          = oldDialog ?\r
6421     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6422   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6423 \r
6424   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6425                         GetOpenFileName(&openFileName)) {\r
6426     /* open the file */\r
6427     f = fopen(openFileName.lpstrFile, write);\r
6428     if (f == NULL) {\r
6429       MessageBox(hwnd, "File open failed", NULL,\r
6430                  MB_OK|MB_ICONEXCLAMATION);\r
6431       return NULL;\r
6432     }\r
6433   } else {\r
6434     int err = CommDlgExtendedError();\r
6435     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6436     return FALSE;\r
6437   }\r
6438   return f;\r
6439 }\r
6440 \r
6441 \r
6442 \r
6443 VOID APIENTRY\r
6444 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6445 {\r
6446   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6447 \r
6448   /*\r
6449    * Get the first pop-up menu in the menu template. This is the\r
6450    * menu that TrackPopupMenu displays.\r
6451    */\r
6452   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6453 \r
6454   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6455 \r
6456   /*\r
6457    * TrackPopup uses screen coordinates, so convert the\r
6458    * coordinates of the mouse click to screen coordinates.\r
6459    */\r
6460   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6461 \r
6462   /* Draw and track the floating pop-up menu. */\r
6463   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6464                  pt.x, pt.y, 0, hwnd, NULL);\r
6465 \r
6466   /* Destroy the menu.*/\r
6467   DestroyMenu(hmenu);\r
6468 }\r
6469    \r
6470 typedef struct {\r
6471   HWND hDlg, hText;\r
6472   int sizeX, sizeY, newSizeX, newSizeY;\r
6473   HDWP hdwp;\r
6474 } ResizeEditPlusButtonsClosure;\r
6475 \r
6476 BOOL CALLBACK\r
6477 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6478 {\r
6479   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6480   RECT rect;\r
6481   POINT pt;\r
6482 \r
6483   if (hChild == cl->hText) return TRUE;\r
6484   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6485   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6486   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6487   ScreenToClient(cl->hDlg, &pt);\r
6488   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6489     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6490   return TRUE;\r
6491 }\r
6492 \r
6493 /* Resize a dialog that has a (rich) edit field filling most of\r
6494    the top, with a row of buttons below */\r
6495 VOID\r
6496 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6497 {\r
6498   RECT rectText;\r
6499   int newTextHeight, newTextWidth;\r
6500   ResizeEditPlusButtonsClosure cl;\r
6501   \r
6502   /*if (IsIconic(hDlg)) return;*/\r
6503   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6504   \r
6505   cl.hdwp = BeginDeferWindowPos(8);\r
6506 \r
6507   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6508   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6509   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6510   if (newTextHeight < 0) {\r
6511     newSizeY += -newTextHeight;\r
6512     newTextHeight = 0;\r
6513   }\r
6514   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6515     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6516 \r
6517   cl.hDlg = hDlg;\r
6518   cl.hText = hText;\r
6519   cl.sizeX = sizeX;\r
6520   cl.sizeY = sizeY;\r
6521   cl.newSizeX = newSizeX;\r
6522   cl.newSizeY = newSizeY;\r
6523   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6524 \r
6525   EndDeferWindowPos(cl.hdwp);\r
6526 }\r
6527 \r
6528 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6529 {\r
6530     RECT    rChild, rParent;\r
6531     int     wChild, hChild, wParent, hParent;\r
6532     int     wScreen, hScreen, xNew, yNew;\r
6533     HDC     hdc;\r
6534 \r
6535     /* Get the Height and Width of the child window */\r
6536     GetWindowRect (hwndChild, &rChild);\r
6537     wChild = rChild.right - rChild.left;\r
6538     hChild = rChild.bottom - rChild.top;\r
6539 \r
6540     /* Get the Height and Width of the parent window */\r
6541     GetWindowRect (hwndParent, &rParent);\r
6542     wParent = rParent.right - rParent.left;\r
6543     hParent = rParent.bottom - rParent.top;\r
6544 \r
6545     /* Get the display limits */\r
6546     hdc = GetDC (hwndChild);\r
6547     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6548     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6549     ReleaseDC(hwndChild, hdc);\r
6550 \r
6551     /* Calculate new X position, then adjust for screen */\r
6552     xNew = rParent.left + ((wParent - wChild) /2);\r
6553     if (xNew < 0) {\r
6554         xNew = 0;\r
6555     } else if ((xNew+wChild) > wScreen) {\r
6556         xNew = wScreen - wChild;\r
6557     }\r
6558 \r
6559     /* Calculate new Y position, then adjust for screen */\r
6560     if( mode == 0 ) {\r
6561         yNew = rParent.top  + ((hParent - hChild) /2);\r
6562     }\r
6563     else {\r
6564         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6565     }\r
6566 \r
6567     if (yNew < 0) {\r
6568         yNew = 0;\r
6569     } else if ((yNew+hChild) > hScreen) {\r
6570         yNew = hScreen - hChild;\r
6571     }\r
6572 \r
6573     /* Set it, and return */\r
6574     return SetWindowPos (hwndChild, NULL,\r
6575                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6576 }\r
6577 \r
6578 /* Center one window over another */\r
6579 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6580 {\r
6581     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6582 }\r
6583 \r
6584 /*---------------------------------------------------------------------------*\\r
6585  *\r
6586  * Startup Dialog functions\r
6587  *\r
6588 \*---------------------------------------------------------------------------*/\r
6589 void\r
6590 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6591 {\r
6592   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6593 \r
6594   while (*cd != NULL) {\r
6595     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6596     cd++;\r
6597   }\r
6598 }\r
6599 \r
6600 void\r
6601 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6602 {\r
6603   char buf1[ARG_MAX];\r
6604   int len;\r
6605 \r
6606   if (str[0] == '@') {\r
6607     FILE* f = fopen(str + 1, "r");\r
6608     if (f == NULL) {\r
6609       DisplayFatalError(str + 1, errno, 2);\r
6610       return;\r
6611     }\r
6612     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6613     fclose(f);\r
6614     buf1[len] = NULLCHAR;\r
6615     str = buf1;\r
6616   }\r
6617 \r
6618   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6619 \r
6620   for (;;) {\r
6621     char buf[MSG_SIZ];\r
6622     char *end = strchr(str, '\n');\r
6623     if (end == NULL) return;\r
6624     memcpy(buf, str, end - str);\r
6625     buf[end - str] = NULLCHAR;\r
6626     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6627     str = end + 1;\r
6628   }\r
6629 }\r
6630 \r
6631 void\r
6632 SetStartupDialogEnables(HWND hDlg)\r
6633 {\r
6634   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6635     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6636     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6637   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6638     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6639   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6640     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6641   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6642     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6643   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6644     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6645     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6646     IsDlgButtonChecked(hDlg, OPT_View));\r
6647 }\r
6648 \r
6649 char *\r
6650 QuoteForFilename(char *filename)\r
6651 {\r
6652   int dquote, space;\r
6653   dquote = strchr(filename, '"') != NULL;\r
6654   space = strchr(filename, ' ') != NULL;\r
6655   if (dquote || space) {\r
6656     if (dquote) {\r
6657       return "'";\r
6658     } else {\r
6659       return "\"";\r
6660     }\r
6661   } else {\r
6662     return "";\r
6663   }\r
6664 }\r
6665 \r
6666 VOID\r
6667 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6668 {\r
6669   char buf[MSG_SIZ];\r
6670   char *q;\r
6671 \r
6672   InitComboStringsFromOption(hwndCombo, nthnames);\r
6673   q = QuoteForFilename(nthcp);\r
6674   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6675   if (*nthdir != NULLCHAR) {\r
6676     q = QuoteForFilename(nthdir);\r
6677     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6678   }\r
6679   if (*nthcp == NULLCHAR) {\r
6680     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6681   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6682     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6683     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6684   }\r
6685 }\r
6686 \r
6687 LRESULT CALLBACK\r
6688 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6689 {\r
6690   char buf[MSG_SIZ];\r
6691   HANDLE hwndCombo;\r
6692   char *p;\r
6693 \r
6694   switch (message) {\r
6695   case WM_INITDIALOG:\r
6696     /* Center the dialog */\r
6697     CenterWindow (hDlg, GetDesktopWindow());\r
6698     /* Initialize the dialog items */\r
6699     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6700                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6701                   firstChessProgramNames);\r
6702     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6703                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6704                   secondChessProgramNames);\r
6705     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6706     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6707     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6708     if (*appData.icsHelper != NULLCHAR) {\r
6709       char *q = QuoteForFilename(appData.icsHelper);\r
6710       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6711     }\r
6712     if (*appData.icsHost == NULLCHAR) {\r
6713       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6714       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6715     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6716       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6717       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6718     }\r
6719 \r
6720     if (appData.icsActive) {\r
6721       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6722     }\r
6723     else if (appData.noChessProgram) {\r
6724       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6725     }\r
6726     else {\r
6727       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6728     }\r
6729 \r
6730     SetStartupDialogEnables(hDlg);\r
6731     return TRUE;\r
6732 \r
6733   case WM_COMMAND:\r
6734     switch (LOWORD(wParam)) {\r
6735     case IDOK:\r
6736       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6737         strcpy(buf, "/fcp=");\r
6738         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6739         p = buf;\r
6740         ParseArgs(StringGet, &p);\r
6741         strcpy(buf, "/scp=");\r
6742         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6743         p = buf;\r
6744         ParseArgs(StringGet, &p);\r
6745         appData.noChessProgram = FALSE;\r
6746         appData.icsActive = FALSE;\r
6747       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6748         strcpy(buf, "/ics /icshost=");\r
6749         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6750         p = buf;\r
6751         ParseArgs(StringGet, &p);\r
6752         if (appData.zippyPlay) {\r
6753           strcpy(buf, "/fcp=");\r
6754           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6755           p = buf;\r
6756           ParseArgs(StringGet, &p);\r
6757         }\r
6758       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6759         appData.noChessProgram = TRUE;\r
6760         appData.icsActive = FALSE;\r
6761       } else {\r
6762         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6763                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6764         return TRUE;\r
6765       }\r
6766       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6767         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6768         p = buf;\r
6769         ParseArgs(StringGet, &p);\r
6770       }\r
6771       EndDialog(hDlg, TRUE);\r
6772       return TRUE;\r
6773 \r
6774     case IDCANCEL:\r
6775       ExitEvent(0);\r
6776       return TRUE;\r
6777 \r
6778     case IDM_HELPCONTENTS:\r
6779       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6780         MessageBox (GetFocus(),\r
6781                     "Unable to activate help",\r
6782                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6783       }\r
6784       break;\r
6785 \r
6786     default:\r
6787       SetStartupDialogEnables(hDlg);\r
6788       break;\r
6789     }\r
6790     break;\r
6791   }\r
6792   return FALSE;\r
6793 }\r
6794 \r
6795 /*---------------------------------------------------------------------------*\\r
6796  *\r
6797  * About box dialog functions\r
6798  *\r
6799 \*---------------------------------------------------------------------------*/\r
6800 \r
6801 /* Process messages for "About" dialog box */\r
6802 LRESULT CALLBACK\r
6803 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6804 {\r
6805   switch (message) {\r
6806   case WM_INITDIALOG: /* message: initialize dialog box */\r
6807     /* Center the dialog over the application window */\r
6808     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6809     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6810     return (TRUE);\r
6811 \r
6812   case WM_COMMAND: /* message: received a command */\r
6813     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6814         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6815       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6816       return (TRUE);\r
6817     }\r
6818     break;\r
6819   }\r
6820   return (FALSE);\r
6821 }\r
6822 \r
6823 /*---------------------------------------------------------------------------*\\r
6824  *\r
6825  * Comment Dialog functions\r
6826  *\r
6827 \*---------------------------------------------------------------------------*/\r
6828 \r
6829 LRESULT CALLBACK\r
6830 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6831 {\r
6832   static HANDLE hwndText = NULL;\r
6833   int len, newSizeX, newSizeY, flags;\r
6834   static int sizeX, sizeY;\r
6835   char *str;\r
6836   RECT rect;\r
6837   MINMAXINFO *mmi;\r
6838 \r
6839   switch (message) {\r
6840   case WM_INITDIALOG: /* message: initialize dialog box */\r
6841     /* Initialize the dialog items */\r
6842     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6843     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
6844     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
6845     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
6846     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
6847     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
6848     SetWindowText(hDlg, commentTitle);\r
6849     if (editComment) {\r
6850       SetFocus(hwndText);\r
6851     } else {\r
6852       SetFocus(GetDlgItem(hDlg, IDOK));\r
6853     }\r
6854     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
6855                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
6856                 MAKELPARAM(FALSE, 0));\r
6857     /* Size and position the dialog */\r
6858     if (!commentDialog) {\r
6859       commentDialog = hDlg;\r
6860       flags = SWP_NOZORDER;\r
6861       GetClientRect(hDlg, &rect);\r
6862       sizeX = rect.right;\r
6863       sizeY = rect.bottom;\r
6864       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
6865           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
6866         WINDOWPLACEMENT wp;\r
6867         EnsureOnScreen(&commentX, &commentY);\r
6868         wp.length = sizeof(WINDOWPLACEMENT);\r
6869         wp.flags = 0;\r
6870         wp.showCmd = SW_SHOW;\r
6871         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
6872         wp.rcNormalPosition.left = commentX;\r
6873         wp.rcNormalPosition.right = commentX + commentW;\r
6874         wp.rcNormalPosition.top = commentY;\r
6875         wp.rcNormalPosition.bottom = commentY + commentH;\r
6876         SetWindowPlacement(hDlg, &wp);\r
6877 \r
6878         GetClientRect(hDlg, &rect);\r
6879         newSizeX = rect.right;\r
6880         newSizeY = rect.bottom;\r
6881         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
6882                               newSizeX, newSizeY);\r
6883         sizeX = newSizeX;\r
6884         sizeY = newSizeY;\r
6885       }\r
6886     }\r
6887     return FALSE;\r
6888 \r
6889   case WM_COMMAND: /* message: received a command */\r
6890     switch (LOWORD(wParam)) {\r
6891     case IDOK:\r
6892       if (editComment) {\r
6893         char *p, *q;\r
6894         /* Read changed options from the dialog box */\r
6895         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
6896         len = GetWindowTextLength(hwndText);\r
6897         str = (char *) malloc(len + 1);\r
6898         GetWindowText(hwndText, str, len + 1);\r
6899         p = q = str;\r
6900         while (*q) {\r
6901           if (*q == '\r')\r
6902             q++;\r
6903           else\r
6904             *p++ = *q++;\r
6905         }\r
6906         *p = NULLCHAR;\r
6907         ReplaceComment(commentIndex, str);\r
6908         free(str);\r
6909       }\r
6910       CommentPopDown();\r
6911       return TRUE;\r
6912 \r
6913     case IDCANCEL:\r
6914     case OPT_CancelComment:\r
6915       CommentPopDown();\r
6916       return TRUE;\r
6917 \r
6918     case OPT_ClearComment:\r
6919       SetDlgItemText(hDlg, OPT_CommentText, "");\r
6920       break;\r
6921 \r
6922     case OPT_EditComment:\r
6923       EditCommentEvent();\r
6924       return TRUE;\r
6925 \r
6926     default:\r
6927       break;\r
6928     }\r
6929     break;\r
6930 \r
6931   case WM_SIZE:\r
6932     newSizeX = LOWORD(lParam);\r
6933     newSizeY = HIWORD(lParam);\r
6934     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
6935     sizeX = newSizeX;\r
6936     sizeY = newSizeY;\r
6937     break;\r
6938 \r
6939   case WM_GETMINMAXINFO:\r
6940     /* Prevent resizing window too small */\r
6941     mmi = (MINMAXINFO *) lParam;\r
6942     mmi->ptMinTrackSize.x = 100;\r
6943     mmi->ptMinTrackSize.y = 100;\r
6944     break;\r
6945   }\r
6946   return FALSE;\r
6947 }\r
6948 \r
6949 VOID\r
6950 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
6951 {\r
6952   FARPROC lpProc;\r
6953   char *p, *q;\r
6954 \r
6955   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
6956 \r
6957   if (str == NULL) str = "";\r
6958   p = (char *) malloc(2 * strlen(str) + 2);\r
6959   q = p;\r
6960   while (*str) {\r
6961     if (*str == '\n') *q++ = '\r';\r
6962     *q++ = *str++;\r
6963   }\r
6964   *q = NULLCHAR;\r
6965   if (commentText != NULL) free(commentText);\r
6966 \r
6967   commentIndex = index;\r
6968   commentTitle = title;\r
6969   commentText = p;\r
6970   editComment = edit;\r
6971 \r
6972   if (commentDialog) {\r
6973     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
6974     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
6975   } else {\r
6976     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
6977     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
6978                  hwndMain, (DLGPROC)lpProc);\r
6979     FreeProcInstance(lpProc);\r
6980   }\r
6981   commentDialogUp = TRUE;\r
6982 }\r
6983 \r
6984 \r
6985 /*---------------------------------------------------------------------------*\\r
6986  *\r
6987  * Type-in move dialog functions\r
6988  * \r
6989 \*---------------------------------------------------------------------------*/\r
6990 \r
6991 LRESULT CALLBACK\r
6992 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6993 {\r
6994   char move[MSG_SIZ];\r
6995   HWND hInput;\r
6996   ChessMove moveType;\r
6997   int fromX, fromY, toX, toY;\r
6998   char promoChar;\r
6999 \r
7000   switch (message) {\r
7001   case WM_INITDIALOG:\r
7002     move[0] = (char) lParam;\r
7003     move[1] = NULLCHAR;\r
7004     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7005     hInput = GetDlgItem(hDlg, OPT_Move);\r
7006     SetWindowText(hInput, move);\r
7007     SetFocus(hInput);\r
7008     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7009     return FALSE;\r
7010 \r
7011   case WM_COMMAND:\r
7012     switch (LOWORD(wParam)) {\r
7013     case IDOK:\r
7014       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7015         gameMode != Training) {\r
7016         DisplayMoveError("Displayed move is not current");\r
7017       } else {\r
7018         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7019         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7020           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7021           if (gameMode != Training)\r
7022               forwardMostMove = currentMove;\r
7023           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7024         } else {\r
7025           DisplayMoveError("Could not parse move");\r
7026         }\r
7027       }\r
7028       EndDialog(hDlg, TRUE);\r
7029       return TRUE;\r
7030     case IDCANCEL:\r
7031       EndDialog(hDlg, FALSE);\r
7032       return TRUE;\r
7033     default:\r
7034       break;\r
7035     }\r
7036     break;\r
7037   }\r
7038   return FALSE;\r
7039 }\r
7040 \r
7041 VOID\r
7042 PopUpMoveDialog(char firstchar)\r
7043 {\r
7044     FARPROC lpProc;\r
7045     \r
7046     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7047         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7048         gameMode == AnalyzeMode || gameMode == EditGame || \r
7049         gameMode == EditPosition || gameMode == IcsExamining ||\r
7050         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7051         gameMode == Training) {\r
7052       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7053       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7054         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7055       FreeProcInstance(lpProc);\r
7056     }\r
7057 }\r
7058 \r
7059 /*---------------------------------------------------------------------------*\\r
7060  *\r
7061  * Type-in name dialog functions\r
7062  * \r
7063 \*---------------------------------------------------------------------------*/\r
7064 \r
7065 LRESULT CALLBACK\r
7066 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7067 {\r
7068   char move[MSG_SIZ];\r
7069   HWND hInput;\r
7070 \r
7071   switch (message) {\r
7072   case WM_INITDIALOG:\r
7073     move[0] = (char) lParam;\r
7074     move[1] = NULLCHAR;\r
7075     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7076     hInput = GetDlgItem(hDlg, OPT_Name);\r
7077     SetWindowText(hInput, move);\r
7078     SetFocus(hInput);\r
7079     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7080     return FALSE;\r
7081 \r
7082   case WM_COMMAND:\r
7083     switch (LOWORD(wParam)) {\r
7084     case IDOK:\r
7085       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7086       appData.userName = strdup(move);\r
7087 \r
7088       EndDialog(hDlg, TRUE);\r
7089       return TRUE;\r
7090     case IDCANCEL:\r
7091       EndDialog(hDlg, FALSE);\r
7092       return TRUE;\r
7093     default:\r
7094       break;\r
7095     }\r
7096     break;\r
7097   }\r
7098   return FALSE;\r
7099 }\r
7100 \r
7101 VOID\r
7102 PopUpNameDialog(char firstchar)\r
7103 {\r
7104     FARPROC lpProc;\r
7105     \r
7106       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7107       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7108         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7109       FreeProcInstance(lpProc);\r
7110 }\r
7111 \r
7112 /*---------------------------------------------------------------------------*\\r
7113  *\r
7114  *  Error dialogs\r
7115  * \r
7116 \*---------------------------------------------------------------------------*/\r
7117 \r
7118 /* Nonmodal error box */\r
7119 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7120                              WPARAM wParam, LPARAM lParam);\r
7121 \r
7122 VOID\r
7123 ErrorPopUp(char *title, char *content)\r
7124 {\r
7125   FARPROC lpProc;\r
7126   char *p, *q;\r
7127   BOOLEAN modal = hwndMain == NULL;\r
7128 \r
7129   p = content;\r
7130   q = errorMessage;\r
7131   while (*p) {\r
7132     if (*p == '\n') {\r
7133       if (modal) {\r
7134         *q++ = ' ';\r
7135         p++;\r
7136       } else {\r
7137         *q++ = '\r';\r
7138         *q++ = *p++;\r
7139       }\r
7140     } else {\r
7141       *q++ = *p++;\r
7142     }\r
7143   }\r
7144   *q = NULLCHAR;\r
7145   strncpy(errorTitle, title, sizeof(errorTitle));\r
7146   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7147   \r
7148   if (modal) {\r
7149     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7150   } else {\r
7151     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7152     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7153                  hwndMain, (DLGPROC)lpProc);\r
7154     FreeProcInstance(lpProc);\r
7155   }\r
7156 }\r
7157 \r
7158 VOID\r
7159 ErrorPopDown()\r
7160 {\r
7161   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7162   if (errorDialog == NULL) return;\r
7163   DestroyWindow(errorDialog);\r
7164   errorDialog = NULL;\r
7165 }\r
7166 \r
7167 LRESULT CALLBACK\r
7168 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7169 {\r
7170   HANDLE hwndText;\r
7171   RECT rChild;\r
7172 \r
7173   switch (message) {\r
7174   case WM_INITDIALOG:\r
7175     GetWindowRect(hDlg, &rChild);\r
7176 \r
7177     /*\r
7178     SetWindowPos(hDlg, NULL, rChild.left,\r
7179       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7180       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7181     */\r
7182 \r
7183     /* \r
7184         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7185         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7186         and it doesn't work when you resize the dialog.\r
7187         For now, just give it a default position.\r
7188     */\r
7189     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7190 \r
7191     errorDialog = hDlg;\r
7192     SetWindowText(hDlg, errorTitle);\r
7193     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7194     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7195     return FALSE;\r
7196 \r
7197   case WM_COMMAND:\r
7198     switch (LOWORD(wParam)) {\r
7199     case IDOK:\r
7200     case IDCANCEL:\r
7201       if (errorDialog == hDlg) errorDialog = NULL;\r
7202       DestroyWindow(hDlg);\r
7203       return TRUE;\r
7204 \r
7205     default:\r
7206       break;\r
7207     }\r
7208     break;\r
7209   }\r
7210   return FALSE;\r
7211 }\r
7212 \r
7213 #ifdef GOTHIC\r
7214 HWND gothicDialog = NULL;\r
7215 \r
7216 LRESULT CALLBACK\r
7217 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7218 {\r
7219   HANDLE hwndText;\r
7220   RECT rChild;\r
7221   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7222 \r
7223   switch (message) {\r
7224   case WM_INITDIALOG:\r
7225     GetWindowRect(hDlg, &rChild);\r
7226 \r
7227     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7228                                                              SWP_NOZORDER);\r
7229 \r
7230     /* \r
7231         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7232         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7233         and it doesn't work when you resize the dialog.\r
7234         For now, just give it a default position.\r
7235     */\r
7236     gothicDialog = hDlg;\r
7237     SetWindowText(hDlg, errorTitle);\r
7238     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7239     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7240     return FALSE;\r
7241 \r
7242   case WM_COMMAND:\r
7243     switch (LOWORD(wParam)) {\r
7244     case IDOK:\r
7245     case IDCANCEL:\r
7246       if (errorDialog == hDlg) errorDialog = NULL;\r
7247       DestroyWindow(hDlg);\r
7248       return TRUE;\r
7249 \r
7250     default:\r
7251       break;\r
7252     }\r
7253     break;\r
7254   }\r
7255   return FALSE;\r
7256 }\r
7257 \r
7258 VOID\r
7259 GothicPopUp(char *title, VariantClass variant)\r
7260 {\r
7261   FARPROC lpProc;\r
7262   char *p, *q;\r
7263   BOOLEAN modal = hwndMain == NULL;\r
7264   static char *lastTitle;\r
7265 \r
7266   strncpy(errorTitle, title, sizeof(errorTitle));\r
7267   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7268 \r
7269   if(lastTitle != title && gothicDialog != NULL) {\r
7270     DestroyWindow(gothicDialog);\r
7271     gothicDialog = NULL;\r
7272   }\r
7273   if(variant != VariantNormal && gothicDialog == NULL) {\r
7274     title = lastTitle;\r
7275     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7276     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7277                  hwndMain, (DLGPROC)lpProc);\r
7278     FreeProcInstance(lpProc);\r
7279   }\r
7280 }\r
7281 #endif\r
7282 \r
7283 /*---------------------------------------------------------------------------*\\r
7284  *\r
7285  *  Ics Interaction console functions\r
7286  *\r
7287 \*---------------------------------------------------------------------------*/\r
7288 \r
7289 #define HISTORY_SIZE 64\r
7290 static char *history[HISTORY_SIZE];\r
7291 int histIn = 0, histP = 0;\r
7292 \r
7293 VOID\r
7294 SaveInHistory(char *cmd)\r
7295 {\r
7296   if (history[histIn] != NULL) {\r
7297     free(history[histIn]);\r
7298     history[histIn] = NULL;\r
7299   }\r
7300   if (*cmd == NULLCHAR) return;\r
7301   history[histIn] = StrSave(cmd);\r
7302   histIn = (histIn + 1) % HISTORY_SIZE;\r
7303   if (history[histIn] != NULL) {\r
7304     free(history[histIn]);\r
7305     history[histIn] = NULL;\r
7306   }\r
7307   histP = histIn;\r
7308 }\r
7309 \r
7310 char *\r
7311 PrevInHistory(char *cmd)\r
7312 {\r
7313   int newhp;\r
7314   if (histP == histIn) {\r
7315     if (history[histIn] != NULL) free(history[histIn]);\r
7316     history[histIn] = StrSave(cmd);\r
7317   }\r
7318   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7319   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7320   histP = newhp;\r
7321   return history[histP];\r
7322 }\r
7323 \r
7324 char *\r
7325 NextInHistory()\r
7326 {\r
7327   if (histP == histIn) return NULL;\r
7328   histP = (histP + 1) % HISTORY_SIZE;\r
7329   return history[histP];\r
7330 }\r
7331 \r
7332 typedef struct {\r
7333   char *item;\r
7334   char *command;\r
7335   BOOLEAN getname;\r
7336   BOOLEAN immediate;\r
7337 } IcsTextMenuEntry;\r
7338 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7339 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7340 \r
7341 void\r
7342 ParseIcsTextMenu(char *icsTextMenuString)\r
7343 {\r
7344   int flags = 0;\r
7345   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7346   char *p = icsTextMenuString;\r
7347   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7348     free(e->item);\r
7349     e->item = NULL;\r
7350     if (e->command != NULL) {\r
7351       free(e->command);\r
7352       e->command = NULL;\r
7353     }\r
7354     e++;\r
7355   }\r
7356   e = icsTextMenuEntry;\r
7357   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7358     if (*p == ';' || *p == '\n') {\r
7359       e->item = strdup("-");\r
7360       e->command = NULL;\r
7361       p++;\r
7362     } else if (*p == '-') {\r
7363       e->item = strdup("-");\r
7364       e->command = NULL;\r
7365       p++;\r
7366       if (*p) p++;\r
7367     } else {\r
7368       char *q, *r, *s, *t;\r
7369       char c;\r
7370       q = strchr(p, ',');\r
7371       if (q == NULL) break;\r
7372       *q = NULLCHAR;\r
7373       r = strchr(q + 1, ',');\r
7374       if (r == NULL) break;\r
7375       *r = NULLCHAR;\r
7376       s = strchr(r + 1, ',');\r
7377       if (s == NULL) break;\r
7378       *s = NULLCHAR;\r
7379       c = ';';\r
7380       t = strchr(s + 1, c);\r
7381       if (t == NULL) {\r
7382         c = '\n';\r
7383         t = strchr(s + 1, c);\r
7384       }\r
7385       if (t != NULL) *t = NULLCHAR;\r
7386       e->item = strdup(p);\r
7387       e->command = strdup(q + 1);\r
7388       e->getname = *(r + 1) != '0';\r
7389       e->immediate = *(s + 1) != '0';\r
7390       *q = ',';\r
7391       *r = ',';\r
7392       *s = ',';\r
7393       if (t == NULL) break;\r
7394       *t = c;\r
7395       p = t + 1;\r
7396     }\r
7397     e++;\r
7398   } \r
7399 }\r
7400 \r
7401 HMENU\r
7402 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7403 {\r
7404   HMENU hmenu, h;\r
7405   int i = 0;\r
7406   hmenu = LoadMenu(hInst, "TextMenu");\r
7407   h = GetSubMenu(hmenu, 0);\r
7408   while (e->item) {\r
7409     if (strcmp(e->item, "-") == 0) {\r
7410       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7411     } else {\r
7412       if (e->item[0] == '|') {\r
7413         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7414                    IDM_CommandX + i, &e->item[1]);\r
7415       } else {\r
7416         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7417       }\r
7418     }\r
7419     e++;\r
7420     i++;\r
7421   } \r
7422   return hmenu;\r
7423 }\r
7424 \r
7425 WNDPROC consoleTextWindowProc;\r
7426 \r
7427 void\r
7428 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7429 {\r
7430   char buf[MSG_SIZ], name[MSG_SIZ];\r
7431   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7432   CHARRANGE sel;\r
7433 \r
7434   if (!getname) {\r
7435     SetWindowText(hInput, command);\r
7436     if (immediate) {\r
7437       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7438     } else {\r
7439       sel.cpMin = 999999;\r
7440       sel.cpMax = 999999;\r
7441       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7442       SetFocus(hInput);\r
7443     }\r
7444     return;\r
7445   }    \r
7446   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7447   if (sel.cpMin == sel.cpMax) {\r
7448     /* Expand to surrounding word */\r
7449     TEXTRANGE tr;\r
7450     do {\r
7451       tr.chrg.cpMax = sel.cpMin;\r
7452       tr.chrg.cpMin = --sel.cpMin;\r
7453       if (sel.cpMin < 0) break;\r
7454       tr.lpstrText = name;\r
7455       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7456     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7457     sel.cpMin++;\r
7458 \r
7459     do {\r
7460       tr.chrg.cpMin = sel.cpMax;\r
7461       tr.chrg.cpMax = ++sel.cpMax;\r
7462       tr.lpstrText = name;\r
7463       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7464     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7465     sel.cpMax--;\r
7466 \r
7467     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7468       MessageBeep(MB_ICONEXCLAMATION);\r
7469       return;\r
7470     }\r
7471     tr.chrg = sel;\r
7472     tr.lpstrText = name;\r
7473     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7474   } else {\r
7475     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7476       MessageBeep(MB_ICONEXCLAMATION);\r
7477       return;\r
7478     }\r
7479     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7480   }\r
7481   if (immediate) {\r
7482     sprintf(buf, "%s %s", command, name);\r
7483     SetWindowText(hInput, buf);\r
7484     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7485   } else {\r
7486     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7487     SetWindowText(hInput, buf);\r
7488     sel.cpMin = 999999;\r
7489     sel.cpMax = 999999;\r
7490     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7491     SetFocus(hInput);\r
7492   }\r
7493 }\r
7494 \r
7495 LRESULT CALLBACK \r
7496 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7497 {\r
7498   HWND hInput;\r
7499   CHARRANGE sel;\r
7500 \r
7501   switch (message) {\r
7502   case WM_KEYDOWN:\r
7503     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7504     switch (wParam) {\r
7505     case VK_PRIOR:\r
7506       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7507       return 0;\r
7508     case VK_NEXT:\r
7509       sel.cpMin = 999999;\r
7510       sel.cpMax = 999999;\r
7511       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7512       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7513       return 0;\r
7514     }\r
7515     break;\r
7516   case WM_CHAR:\r
7517     if (wParam == '\t') {\r
7518       if (GetKeyState(VK_SHIFT) < 0) {\r
7519         /* shifted */\r
7520         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7521         if (buttonDesc[0].hwnd) {\r
7522           SetFocus(buttonDesc[0].hwnd);\r
7523         } else {\r
7524           SetFocus(hwndMain);\r
7525         }\r
7526       } else {\r
7527         /* unshifted */\r
7528         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7529       }\r
7530     } else {\r
7531       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7532       SetFocus(hInput);\r
7533       SendMessage(hInput, message, wParam, lParam);\r
7534     }\r
7535     return 0;\r
7536   case WM_PASTE:\r
7537     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7538     SetFocus(hInput);\r
7539     return SendMessage(hInput, message, wParam, lParam);\r
7540   case WM_MBUTTONDOWN:\r
7541     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7542   case WM_RBUTTONDOWN:\r
7543     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7544       /* Move selection here if it was empty */\r
7545       POINT pt;\r
7546       pt.x = LOWORD(lParam);\r
7547       pt.y = HIWORD(lParam);\r
7548       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7549       if (sel.cpMin == sel.cpMax) {\r
7550         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7551         sel.cpMax = sel.cpMin;\r
7552         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7553       }\r
7554       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7555     }\r
7556     return 0;\r
7557   case WM_RBUTTONUP:\r
7558     if (GetKeyState(VK_SHIFT) & ~1) {\r
7559       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7560         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7561     } else {\r
7562       POINT pt;\r
7563       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7564       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7565       if (sel.cpMin == sel.cpMax) {\r
7566         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7567         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7568       }\r
7569       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7570         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7571       }\r
7572       pt.x = LOWORD(lParam);\r
7573       pt.y = HIWORD(lParam);\r
7574       MenuPopup(hwnd, pt, hmenu, -1);\r
7575     }\r
7576     return 0;\r
7577   case WM_COMMAND:\r
7578     switch (LOWORD(wParam)) {\r
7579     case IDM_QuickPaste:\r
7580       {\r
7581         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7582         if (sel.cpMin == sel.cpMax) {\r
7583           MessageBeep(MB_ICONEXCLAMATION);\r
7584           return 0;\r
7585         }\r
7586         SendMessage(hwnd, WM_COPY, 0, 0);\r
7587         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7588         SendMessage(hInput, WM_PASTE, 0, 0);\r
7589         SetFocus(hInput);\r
7590         return 0;\r
7591       }\r
7592     case IDM_Cut:\r
7593       SendMessage(hwnd, WM_CUT, 0, 0);\r
7594       return 0;\r
7595     case IDM_Paste:\r
7596       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7597       return 0;\r
7598     case IDM_Copy:\r
7599       SendMessage(hwnd, WM_COPY, 0, 0);\r
7600       return 0;\r
7601     default:\r
7602       {\r
7603         int i = LOWORD(wParam) - IDM_CommandX;\r
7604         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7605             icsTextMenuEntry[i].command != NULL) {\r
7606           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7607                    icsTextMenuEntry[i].getname,\r
7608                    icsTextMenuEntry[i].immediate);\r
7609           return 0;\r
7610         }\r
7611       }\r
7612       break;\r
7613     }\r
7614     break;\r
7615   }\r
7616   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7617 }\r
7618 \r
7619 WNDPROC consoleInputWindowProc;\r
7620 \r
7621 LRESULT CALLBACK\r
7622 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7623 {\r
7624   char buf[MSG_SIZ];\r
7625   char *p;\r
7626   static BOOL sendNextChar = FALSE;\r
7627   static BOOL quoteNextChar = FALSE;\r
7628   InputSource *is = consoleInputSource;\r
7629   CHARFORMAT cf;\r
7630   CHARRANGE sel;\r
7631 \r
7632   switch (message) {\r
7633   case WM_CHAR:\r
7634     if (!appData.localLineEditing || sendNextChar) {\r
7635       is->buf[0] = (CHAR) wParam;\r
7636       is->count = 1;\r
7637       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7638       sendNextChar = FALSE;\r
7639       return 0;\r
7640     }\r
7641     if (quoteNextChar) {\r
7642       buf[0] = (char) wParam;\r
7643       buf[1] = NULLCHAR;\r
7644       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7645       quoteNextChar = FALSE;\r
7646       return 0;\r
7647     }\r
7648     switch (wParam) {\r
7649     case '\r':   /* Enter key */\r
7650       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7651       if (consoleEcho) SaveInHistory(is->buf);\r
7652       is->buf[is->count++] = '\n';\r
7653       is->buf[is->count] = NULLCHAR;\r
7654       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7655       if (consoleEcho) {\r
7656         ConsoleOutput(is->buf, is->count, TRUE);\r
7657       } else if (appData.localLineEditing) {\r
7658         ConsoleOutput("\n", 1, TRUE);\r
7659       }\r
7660       /* fall thru */\r
7661     case '\033': /* Escape key */\r
7662       SetWindowText(hwnd, "");\r
7663       cf.cbSize = sizeof(CHARFORMAT);\r
7664       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7665       if (consoleEcho) {\r
7666         cf.crTextColor = textAttribs[ColorNormal].color;\r
7667       } else {\r
7668         cf.crTextColor = COLOR_ECHOOFF;\r
7669       }\r
7670       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7671       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7672       return 0;\r
7673     case '\t':   /* Tab key */\r
7674       if (GetKeyState(VK_SHIFT) < 0) {\r
7675         /* shifted */\r
7676         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7677       } else {\r
7678         /* unshifted */\r
7679         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7680         if (buttonDesc[0].hwnd) {\r
7681           SetFocus(buttonDesc[0].hwnd);\r
7682         } else {\r
7683           SetFocus(hwndMain);\r
7684         }\r
7685       }\r
7686       return 0;\r
7687     case '\023': /* Ctrl+S */\r
7688       sendNextChar = TRUE;\r
7689       return 0;\r
7690     case '\021': /* Ctrl+Q */\r
7691       quoteNextChar = TRUE;\r
7692       return 0;\r
7693     default:\r
7694       break;\r
7695     }\r
7696     break;\r
7697   case WM_KEYDOWN:\r
7698     switch (wParam) {\r
7699     case VK_UP:\r
7700       GetWindowText(hwnd, buf, MSG_SIZ);\r
7701       p = PrevInHistory(buf);\r
7702       if (p != NULL) {\r
7703         SetWindowText(hwnd, p);\r
7704         sel.cpMin = 999999;\r
7705         sel.cpMax = 999999;\r
7706         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7707         return 0;\r
7708       }\r
7709       break;\r
7710     case VK_DOWN:\r
7711       p = NextInHistory();\r
7712       if (p != NULL) {\r
7713         SetWindowText(hwnd, p);\r
7714         sel.cpMin = 999999;\r
7715         sel.cpMax = 999999;\r
7716         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7717         return 0;\r
7718       }\r
7719       break;\r
7720     case VK_HOME:\r
7721     case VK_END:\r
7722       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7723       /* fall thru */\r
7724     case VK_PRIOR:\r
7725     case VK_NEXT:\r
7726       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7727       return 0;\r
7728     }\r
7729     break;\r
7730   case WM_MBUTTONDOWN:\r
7731     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7732       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7733     break;\r
7734   case WM_RBUTTONUP:\r
7735     if (GetKeyState(VK_SHIFT) & ~1) {\r
7736       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7737         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7738     } else {\r
7739       POINT pt;\r
7740       HMENU hmenu;\r
7741       hmenu = LoadMenu(hInst, "InputMenu");\r
7742       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7743       if (sel.cpMin == sel.cpMax) {\r
7744         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7745         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7746       }\r
7747       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7748         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7749       }\r
7750       pt.x = LOWORD(lParam);\r
7751       pt.y = HIWORD(lParam);\r
7752       MenuPopup(hwnd, pt, hmenu, -1);\r
7753     }\r
7754     return 0;\r
7755   case WM_COMMAND:\r
7756     switch (LOWORD(wParam)) { \r
7757     case IDM_Undo:\r
7758       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7759       return 0;\r
7760     case IDM_SelectAll:\r
7761       sel.cpMin = 0;\r
7762       sel.cpMax = -1; /*999999?*/\r
7763       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7764       return 0;\r
7765     case IDM_Cut:\r
7766       SendMessage(hwnd, WM_CUT, 0, 0);\r
7767       return 0;\r
7768     case IDM_Paste:\r
7769       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7770       return 0;\r
7771     case IDM_Copy:\r
7772       SendMessage(hwnd, WM_COPY, 0, 0);\r
7773       return 0;\r
7774     }\r
7775     break;\r
7776   }\r
7777   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7778 }\r
7779 \r
7780 #define CO_MAX  100000\r
7781 #define CO_TRIM   1000\r
7782 \r
7783 LRESULT CALLBACK\r
7784 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7785 {\r
7786   static SnapData sd;\r
7787   static HWND hText, hInput, hFocus;\r
7788   InputSource *is = consoleInputSource;\r
7789   RECT rect;\r
7790   static int sizeX, sizeY;\r
7791   int newSizeX, newSizeY;\r
7792   MINMAXINFO *mmi;\r
7793 \r
7794   switch (message) {\r
7795   case WM_INITDIALOG: /* message: initialize dialog box */\r
7796     hwndConsole = hDlg;\r
7797     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7798     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7799     SetFocus(hInput);\r
7800     consoleTextWindowProc = (WNDPROC)\r
7801       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
7802     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7803     consoleInputWindowProc = (WNDPROC)\r
7804       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
7805     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
7806     Colorize(ColorNormal, TRUE);\r
7807     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
7808     ChangedConsoleFont();\r
7809     GetClientRect(hDlg, &rect);\r
7810     sizeX = rect.right;\r
7811     sizeY = rect.bottom;\r
7812     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
7813         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
7814       WINDOWPLACEMENT wp;\r
7815       EnsureOnScreen(&consoleX, &consoleY);\r
7816       wp.length = sizeof(WINDOWPLACEMENT);\r
7817       wp.flags = 0;\r
7818       wp.showCmd = SW_SHOW;\r
7819       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7820       wp.rcNormalPosition.left = consoleX;\r
7821       wp.rcNormalPosition.right = consoleX + consoleW;\r
7822       wp.rcNormalPosition.top = consoleY;\r
7823       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
7824       SetWindowPlacement(hDlg, &wp);\r
7825     }\r
7826     return FALSE;\r
7827 \r
7828   case WM_SETFOCUS:\r
7829     SetFocus(hInput);\r
7830     return 0;\r
7831 \r
7832   case WM_CLOSE:\r
7833     ExitEvent(0);\r
7834     /* not reached */\r
7835     break;\r
7836 \r
7837   case WM_SIZE:\r
7838     if (IsIconic(hDlg)) break;\r
7839     newSizeX = LOWORD(lParam);\r
7840     newSizeY = HIWORD(lParam);\r
7841     if (sizeX != newSizeX || sizeY != newSizeY) {\r
7842       RECT rectText, rectInput;\r
7843       POINT pt;\r
7844       int newTextHeight, newTextWidth;\r
7845       GetWindowRect(hText, &rectText);\r
7846       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
7847       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
7848       if (newTextHeight < 0) {\r
7849         newSizeY += -newTextHeight;\r
7850         newTextHeight = 0;\r
7851       }\r
7852       SetWindowPos(hText, NULL, 0, 0,\r
7853         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
7854       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
7855       pt.x = rectInput.left;\r
7856       pt.y = rectInput.top + newSizeY - sizeY;\r
7857       ScreenToClient(hDlg, &pt);\r
7858       SetWindowPos(hInput, NULL, \r
7859         pt.x, pt.y, /* needs client coords */   \r
7860         rectInput.right - rectInput.left + newSizeX - sizeX,\r
7861         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
7862     }\r
7863     sizeX = newSizeX;\r
7864     sizeY = newSizeY;\r
7865     break;\r
7866 \r
7867   case WM_GETMINMAXINFO:\r
7868     /* Prevent resizing window too small */\r
7869     mmi = (MINMAXINFO *) lParam;\r
7870     mmi->ptMinTrackSize.x = 100;\r
7871     mmi->ptMinTrackSize.y = 100;\r
7872     break;\r
7873 \r
7874   /* [AS] Snapping */\r
7875   case WM_ENTERSIZEMOVE:\r
7876     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
7877 \r
7878   case WM_SIZING:\r
7879     return OnSizing( &sd, hDlg, wParam, lParam );\r
7880 \r
7881   case WM_MOVING:\r
7882     return OnMoving( &sd, hDlg, wParam, lParam );\r
7883 \r
7884   case WM_EXITSIZEMOVE:\r
7885     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
7886   }\r
7887 \r
7888   return DefWindowProc(hDlg, message, wParam, lParam);\r
7889 }\r
7890 \r
7891 \r
7892 VOID\r
7893 ConsoleCreate()\r
7894 {\r
7895   HWND hCons;\r
7896   if (hwndConsole) return;\r
7897   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
7898   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
7899 }\r
7900 \r
7901 \r
7902 VOID\r
7903 ConsoleOutput(char* data, int length, int forceVisible)\r
7904 {\r
7905   HWND hText;\r
7906   int trim, exlen;\r
7907   char *p, *q;\r
7908   char buf[CO_MAX+1];\r
7909   POINT pEnd;\r
7910   RECT rect;\r
7911   static int delayLF = 0;\r
7912   CHARRANGE savesel, sel;\r
7913 \r
7914   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
7915   p = data;\r
7916   q = buf;\r
7917   if (delayLF) {\r
7918     *q++ = '\r';\r
7919     *q++ = '\n';\r
7920     delayLF = 0;\r
7921   }\r
7922   while (length--) {\r
7923     if (*p == '\n') {\r
7924       if (*++p) {\r
7925         *q++ = '\r';\r
7926         *q++ = '\n';\r
7927       } else {\r
7928         delayLF = 1;\r
7929       }\r
7930     } else if (*p == '\007') {\r
7931        MyPlaySound(&sounds[(int)SoundBell]);\r
7932        p++;\r
7933     } else {\r
7934       *q++ = *p++;\r
7935     }\r
7936   }\r
7937   *q = NULLCHAR;\r
7938   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
7939   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7940   /* Save current selection */\r
7941   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
7942   exlen = GetWindowTextLength(hText);\r
7943   /* Find out whether current end of text is visible */\r
7944   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
7945   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
7946   /* Trim existing text if it's too long */\r
7947   if (exlen + (q - buf) > CO_MAX) {\r
7948     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
7949     sel.cpMin = 0;\r
7950     sel.cpMax = trim;\r
7951     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7952     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
7953     exlen -= trim;\r
7954     savesel.cpMin -= trim;\r
7955     savesel.cpMax -= trim;\r
7956     if (exlen < 0) exlen = 0;\r
7957     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
7958     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
7959   }\r
7960   /* Append the new text */\r
7961   sel.cpMin = exlen;\r
7962   sel.cpMax = exlen;\r
7963   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7964   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
7965   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
7966   if (forceVisible || exlen == 0 ||\r
7967       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
7968        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
7969     /* Scroll to make new end of text visible if old end of text\r
7970        was visible or new text is an echo of user typein */\r
7971     sel.cpMin = 9999999;\r
7972     sel.cpMax = 9999999;\r
7973     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7974     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7975     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
7976     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
7977   }\r
7978   if (savesel.cpMax == exlen || forceVisible) {\r
7979     /* Move insert point to new end of text if it was at the old\r
7980        end of text or if the new text is an echo of user typein */\r
7981     sel.cpMin = 9999999;\r
7982     sel.cpMax = 9999999;\r
7983     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7984   } else {\r
7985     /* Restore previous selection */\r
7986     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
7987   }\r
7988   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
7989 }\r
7990 \r
7991 /*---------*/\r
7992 \r
7993 \r
7994 void\r
7995 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
7996 {\r
7997   char buf[100];\r
7998   char *str;\r
7999   COLORREF oldFg, oldBg;\r
8000   HFONT oldFont;\r
8001   RECT rect;\r
8002 \r
8003   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8004 \r
8005   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8006   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8007   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8008 \r
8009   rect.left = x;\r
8010   rect.right = x + squareSize;\r
8011   rect.top  = y;\r
8012   rect.bottom = y + squareSize;\r
8013   str = buf;\r
8014 \r
8015   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8016                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8017              y, ETO_CLIPPED|ETO_OPAQUE,\r
8018              &rect, str, strlen(str), NULL);\r
8019 \r
8020   (void) SetTextColor(hdc, oldFg);\r
8021   (void) SetBkColor(hdc, oldBg);\r
8022   (void) SelectObject(hdc, oldFont);\r
8023 }\r
8024 \r
8025 void\r
8026 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8027               RECT *rect, char *color, char *flagFell)\r
8028 {\r
8029   char buf[100];\r
8030   char *str;\r
8031   COLORREF oldFg, oldBg;\r
8032   HFONT oldFont;\r
8033 \r
8034   if (appData.clockMode) {\r
8035     if (tinyLayout)\r
8036       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8037     else\r
8038       sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell);\r
8039     str = buf;\r
8040   } else {\r
8041     str = color;\r
8042   }\r
8043 \r
8044   if (highlight) {\r
8045     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8046     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8047   } else {\r
8048     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8049     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8050   }\r
8051   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8052 \r
8053   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8054              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8055              rect, str, strlen(str), NULL);\r
8056 \r
8057   (void) SetTextColor(hdc, oldFg);\r
8058   (void) SetBkColor(hdc, oldBg);\r
8059   (void) SelectObject(hdc, oldFont);\r
8060 }\r
8061 \r
8062 \r
8063 int\r
8064 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8065            OVERLAPPED *ovl)\r
8066 {\r
8067   int ok, err;\r
8068 \r
8069   /* [AS]  */\r
8070   if( count <= 0 ) {\r
8071     if (appData.debugMode) {\r
8072       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8073     }\r
8074 \r
8075     return ERROR_INVALID_USER_BUFFER;\r
8076   }\r
8077 \r
8078   ResetEvent(ovl->hEvent);\r
8079   ovl->Offset = ovl->OffsetHigh = 0;\r
8080   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8081   if (ok) {\r
8082     err = NO_ERROR;\r
8083   } else {\r
8084     err = GetLastError();\r
8085     if (err == ERROR_IO_PENDING) {\r
8086       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8087       if (ok)\r
8088         err = NO_ERROR;\r
8089       else\r
8090         err = GetLastError();\r
8091     }\r
8092   }\r
8093   return err;\r
8094 }\r
8095 \r
8096 int\r
8097 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8098             OVERLAPPED *ovl)\r
8099 {\r
8100   int ok, err;\r
8101 \r
8102   ResetEvent(ovl->hEvent);\r
8103   ovl->Offset = ovl->OffsetHigh = 0;\r
8104   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8105   if (ok) {\r
8106     err = NO_ERROR;\r
8107   } else {\r
8108     err = GetLastError();\r
8109     if (err == ERROR_IO_PENDING) {\r
8110       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8111       if (ok)\r
8112         err = NO_ERROR;\r
8113       else\r
8114         err = GetLastError();\r
8115     }\r
8116   }\r
8117   return err;\r
8118 }\r
8119 \r
8120 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8121 void CheckForInputBufferFull( InputSource * is )\r
8122 {\r
8123     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8124         /* Look for end of line */\r
8125         char * p = is->buf;\r
8126         \r
8127         while( p < is->next && *p != '\n' ) {\r
8128             p++;\r
8129         }\r
8130 \r
8131         if( p >= is->next ) {\r
8132             if (appData.debugMode) {\r
8133                 fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );\r
8134             }\r
8135 \r
8136             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8137             is->count = (DWORD) -1;\r
8138             is->next = is->buf;\r
8139         }\r
8140     }\r
8141 }\r
8142 \r
8143 DWORD\r
8144 InputThread(LPVOID arg)\r
8145 {\r
8146   InputSource *is;\r
8147   OVERLAPPED ovl;\r
8148 \r
8149   is = (InputSource *) arg;\r
8150   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8151   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8152   while (is->hThread != NULL) {\r
8153     is->error = DoReadFile(is->hFile, is->next,\r
8154                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8155                            &is->count, &ovl);\r
8156     if (is->error == NO_ERROR) {\r
8157       is->next += is->count;\r
8158     } else {\r
8159       if (is->error == ERROR_BROKEN_PIPE) {\r
8160         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8161         is->count = 0;\r
8162       } else {\r
8163         is->count = (DWORD) -1;\r
8164         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8165         break; \r
8166       }\r
8167     }\r
8168 \r
8169     CheckForInputBufferFull( is );\r
8170 \r
8171     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8172 \r
8173     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8174 \r
8175     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8176   }\r
8177 \r
8178   CloseHandle(ovl.hEvent);\r
8179   CloseHandle(is->hFile);\r
8180 \r
8181   if (appData.debugMode) {\r
8182     fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );\r
8183   }\r
8184 \r
8185   return 0;\r
8186 }\r
8187 \r
8188 \r
8189 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8190 DWORD\r
8191 NonOvlInputThread(LPVOID arg)\r
8192 {\r
8193   InputSource *is;\r
8194   char *p, *q;\r
8195   int i;\r
8196   char prev;\r
8197 \r
8198   is = (InputSource *) arg;\r
8199   while (is->hThread != NULL) {\r
8200     is->error = ReadFile(is->hFile, is->next,\r
8201                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8202                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8203     if (is->error == NO_ERROR) {\r
8204       /* Change CRLF to LF */\r
8205       if (is->next > is->buf) {\r
8206         p = is->next - 1;\r
8207         i = is->count + 1;\r
8208       } else {\r
8209         p = is->next;\r
8210         i = is->count;\r
8211       }\r
8212       q = p;\r
8213       prev = NULLCHAR;\r
8214       while (i > 0) {\r
8215         if (prev == '\r' && *p == '\n') {\r
8216           *(q-1) = '\n';\r
8217           is->count--;\r
8218         } else { \r
8219           *q++ = *p;\r
8220         }\r
8221         prev = *p++;\r
8222         i--;\r
8223       }\r
8224       *q = NULLCHAR;\r
8225       is->next = q;\r
8226     } else {\r
8227       if (is->error == ERROR_BROKEN_PIPE) {\r
8228         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8229         is->count = 0; \r
8230       } else {\r
8231         is->count = (DWORD) -1;\r
8232       }\r
8233     }\r
8234 \r
8235     CheckForInputBufferFull( is );\r
8236 \r
8237     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8238 \r
8239     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8240 \r
8241     if (is->count < 0) break;  /* Quit on error */\r
8242   }\r
8243   CloseHandle(is->hFile);\r
8244   return 0;\r
8245 }\r
8246 \r
8247 DWORD\r
8248 SocketInputThread(LPVOID arg)\r
8249 {\r
8250   InputSource *is;\r
8251 \r
8252   is = (InputSource *) arg;\r
8253   while (is->hThread != NULL) {\r
8254     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8255     if ((int)is->count == SOCKET_ERROR) {\r
8256       is->count = (DWORD) -1;\r
8257       is->error = WSAGetLastError();\r
8258     } else {\r
8259       is->error = NO_ERROR;\r
8260       is->next += is->count;\r
8261       if (is->count == 0 && is->second == is) {\r
8262         /* End of file on stderr; quit with no message */\r
8263         break;\r
8264       }\r
8265     }\r
8266     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8267 \r
8268     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8269 \r
8270     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8271   }\r
8272   return 0;\r
8273 }\r
8274 \r
8275 VOID\r
8276 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8277 {\r
8278   InputSource *is;\r
8279 \r
8280   is = (InputSource *) lParam;\r
8281   if (is->lineByLine) {\r
8282     /* Feed in lines one by one */\r
8283     char *p = is->buf;\r
8284     char *q = p;\r
8285     while (q < is->next) {\r
8286       if (*q++ == '\n') {\r
8287         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8288         p = q;\r
8289       }\r
8290     }\r
8291     \r
8292     /* Move any partial line to the start of the buffer */\r
8293     q = is->buf;\r
8294     while (p < is->next) {\r
8295       *q++ = *p++;\r
8296     }\r
8297     is->next = q;\r
8298 \r
8299     if (is->error != NO_ERROR || is->count == 0) {\r
8300       /* Notify backend of the error.  Note: If there was a partial\r
8301          line at the end, it is not flushed through. */\r
8302       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8303     }\r
8304   } else {\r
8305     /* Feed in the whole chunk of input at once */\r
8306     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8307     is->next = is->buf;\r
8308   }\r
8309 }\r
8310 \r
8311 /*---------------------------------------------------------------------------*\\r
8312  *\r
8313  *  Menu enables. Used when setting various modes.\r
8314  *\r
8315 \*---------------------------------------------------------------------------*/\r
8316 \r
8317 typedef struct {\r
8318   int item;\r
8319   int flags;\r
8320 } Enables;\r
8321 \r
8322 VOID\r
8323 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8324 {\r
8325   while (enab->item > 0) {\r
8326     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8327     enab++;\r
8328   }\r
8329 }\r
8330 \r
8331 Enables gnuEnables[] = {\r
8332   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8333   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8334   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8335   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8336   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8337   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8338   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8339   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8340   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8341   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8342   { -1, -1 }\r
8343 };\r
8344 \r
8345 Enables icsEnables[] = {\r
8346   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8347   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8348   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8349   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8350   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8351   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8352   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8353   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8354   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8355   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8356   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8357   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8358   { -1, -1 }\r
8359 };\r
8360 \r
8361 #ifdef ZIPPY\r
8362 Enables zippyEnables[] = {\r
8363   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8364   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8365   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8366   { -1, -1 }\r
8367 };\r
8368 #endif\r
8369 \r
8370 Enables ncpEnables[] = {\r
8371   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8372   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8373   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8374   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8375   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8376   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8377   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8378   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8379   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8380   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8381   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8382   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8383   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8384   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8385   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8386   { -1, -1 }\r
8387 };\r
8388 \r
8389 Enables trainingOnEnables[] = {\r
8390   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8391   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8392   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8393   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8394   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8395   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8396   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8397   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8398   { -1, -1 }\r
8399 };\r
8400 \r
8401 Enables trainingOffEnables[] = {\r
8402   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8403   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8404   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8405   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8406   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8407   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8408   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8409   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8410   { -1, -1 }\r
8411 };\r
8412 \r
8413 /* These modify either ncpEnables or gnuEnables */\r
8414 Enables cmailEnables[] = {\r
8415   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8416   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8417   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8418   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8419   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8420   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8421   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8422   { -1, -1 }\r
8423 };\r
8424 \r
8425 Enables machineThinkingEnables[] = {\r
8426   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8427   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8428   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8429   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8430   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8431   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8432   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8433   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8434   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8435   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8436   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8437   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8438   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8439   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8440   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8441   { -1, -1 }\r
8442 };\r
8443 \r
8444 Enables userThinkingEnables[] = {\r
8445   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8446   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8447   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8448   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8449   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8450   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8451   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8452   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8453   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8454   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8455   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8456   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8457   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8458   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8459   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8460   { -1, -1 }\r
8461 };\r
8462 \r
8463 /*---------------------------------------------------------------------------*\\r
8464  *\r
8465  *  Front-end interface functions exported by XBoard.\r
8466  *  Functions appear in same order as prototypes in frontend.h.\r
8467  * \r
8468 \*---------------------------------------------------------------------------*/\r
8469 VOID\r
8470 ModeHighlight()\r
8471 {\r
8472   static UINT prevChecked = 0;\r
8473   static int prevPausing = 0;\r
8474   UINT nowChecked;\r
8475 \r
8476   if (pausing != prevPausing) {\r
8477     prevPausing = pausing;\r
8478     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8479                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8480     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8481   }\r
8482 \r
8483   switch (gameMode) {\r
8484   case BeginningOfGame:\r
8485     if (appData.icsActive)\r
8486       nowChecked = IDM_IcsClient;\r
8487     else if (appData.noChessProgram)\r
8488       nowChecked = IDM_EditGame;\r
8489     else\r
8490       nowChecked = IDM_MachineBlack;\r
8491     break;\r
8492   case MachinePlaysBlack:\r
8493     nowChecked = IDM_MachineBlack;\r
8494     break;\r
8495   case MachinePlaysWhite:\r
8496     nowChecked = IDM_MachineWhite;\r
8497     break;\r
8498   case TwoMachinesPlay:\r
8499     nowChecked = IDM_TwoMachines;\r
8500     break;\r
8501   case AnalyzeMode:\r
8502     nowChecked = IDM_AnalysisMode;\r
8503     break;\r
8504   case AnalyzeFile:\r
8505     nowChecked = IDM_AnalyzeFile;\r
8506     break;\r
8507   case EditGame:\r
8508     nowChecked = IDM_EditGame;\r
8509     break;\r
8510   case PlayFromGameFile:\r
8511     nowChecked = IDM_LoadGame;\r
8512     break;\r
8513   case EditPosition:\r
8514     nowChecked = IDM_EditPosition;\r
8515     break;\r
8516   case Training:\r
8517     nowChecked = IDM_Training;\r
8518     break;\r
8519   case IcsPlayingWhite:\r
8520   case IcsPlayingBlack:\r
8521   case IcsObserving:\r
8522   case IcsIdle:\r
8523     nowChecked = IDM_IcsClient;\r
8524     break;\r
8525   default:\r
8526   case EndOfGame:\r
8527     nowChecked = 0;\r
8528     break;\r
8529   }\r
8530   if (prevChecked != 0)\r
8531     (void) CheckMenuItem(GetMenu(hwndMain),\r
8532                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8533   if (nowChecked != 0)\r
8534     (void) CheckMenuItem(GetMenu(hwndMain),\r
8535                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8536 \r
8537   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8538     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8539                           MF_BYCOMMAND|MF_ENABLED);\r
8540   } else {\r
8541     (void) EnableMenuItem(GetMenu(hwndMain), \r
8542                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8543   }\r
8544 \r
8545   prevChecked = nowChecked;\r
8546 }\r
8547 \r
8548 VOID\r
8549 SetICSMode()\r
8550 {\r
8551   HMENU hmenu = GetMenu(hwndMain);\r
8552   SetMenuEnables(hmenu, icsEnables);\r
8553   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8554     MF_BYPOSITION|MF_ENABLED);\r
8555 #ifdef ZIPPY\r
8556   if (appData.zippyPlay) {\r
8557     SetMenuEnables(hmenu, zippyEnables);\r
8558   }\r
8559 #endif\r
8560 }\r
8561 \r
8562 VOID\r
8563 SetGNUMode()\r
8564 {\r
8565   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8566 }\r
8567 \r
8568 VOID\r
8569 SetNCPMode()\r
8570 {\r
8571   HMENU hmenu = GetMenu(hwndMain);\r
8572   SetMenuEnables(hmenu, ncpEnables);\r
8573   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8574     MF_BYPOSITION|MF_GRAYED);\r
8575     DrawMenuBar(hwndMain);\r
8576 }\r
8577 \r
8578 VOID\r
8579 SetCmailMode()\r
8580 {\r
8581   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8582 }\r
8583 \r
8584 VOID \r
8585 SetTrainingModeOn()\r
8586 {\r
8587   int i;\r
8588   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8589   for (i = 0; i < N_BUTTONS; i++) {\r
8590     if (buttonDesc[i].hwnd != NULL)\r
8591       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8592   }\r
8593   CommentPopDown();\r
8594 }\r
8595 \r
8596 VOID SetTrainingModeOff()\r
8597 {\r
8598   int i;\r
8599   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8600   for (i = 0; i < N_BUTTONS; i++) {\r
8601     if (buttonDesc[i].hwnd != NULL)\r
8602       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8603   }\r
8604 }\r
8605 \r
8606 \r
8607 VOID\r
8608 SetUserThinkingEnables()\r
8609 {\r
8610   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8611 }\r
8612 \r
8613 VOID\r
8614 SetMachineThinkingEnables()\r
8615 {\r
8616   HMENU hMenu = GetMenu(hwndMain);\r
8617   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8618 \r
8619   SetMenuEnables(hMenu, machineThinkingEnables);\r
8620 \r
8621   if (gameMode == MachinePlaysBlack) {\r
8622     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8623   } else if (gameMode == MachinePlaysWhite) {\r
8624     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8625   } else if (gameMode == TwoMachinesPlay) {\r
8626     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8627   }\r
8628 }\r
8629 \r
8630 \r
8631 VOID\r
8632 DisplayTitle(char *str)\r
8633 {\r
8634   char title[MSG_SIZ], *host;\r
8635   if (str[0] != NULLCHAR) {\r
8636     strcpy(title, str);\r
8637   } else if (appData.icsActive) {\r
8638     if (appData.icsCommPort[0] != NULLCHAR)\r
8639       host = "ICS";\r
8640     else \r
8641       host = appData.icsHost;\r
8642     sprintf(title, "%s: %s", szTitle, host);\r
8643   } else if (appData.noChessProgram) {\r
8644     strcpy(title, szTitle);\r
8645   } else {\r
8646     strcpy(title, szTitle);\r
8647     strcat(title, ": ");\r
8648     strcat(title, first.tidy);\r
8649   }\r
8650   SetWindowText(hwndMain, title);\r
8651 }\r
8652 \r
8653 \r
8654 VOID\r
8655 DisplayMessage(char *str1, char *str2)\r
8656 {\r
8657   HDC hdc;\r
8658   HFONT oldFont;\r
8659   int remain = MESSAGE_TEXT_MAX - 1;\r
8660   int len;\r
8661 \r
8662   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8663   messageText[0] = NULLCHAR;\r
8664   if (*str1) {\r
8665     len = strlen(str1);\r
8666     if (len > remain) len = remain;\r
8667     strncpy(messageText, str1, len);\r
8668     messageText[len] = NULLCHAR;\r
8669     remain -= len;\r
8670   }\r
8671   if (*str2 && remain >= 2) {\r
8672     if (*str1) {\r
8673       strcat(messageText, "  ");\r
8674       remain -= 2;\r
8675     }\r
8676     len = strlen(str2);\r
8677     if (len > remain) len = remain;\r
8678     strncat(messageText, str2, len);\r
8679   }\r
8680   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8681 \r
8682   if (IsIconic(hwndMain)) return;\r
8683   hdc = GetDC(hwndMain);\r
8684   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8685   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8686              &messageRect, messageText, strlen(messageText), NULL);\r
8687   (void) SelectObject(hdc, oldFont);\r
8688   (void) ReleaseDC(hwndMain, hdc);\r
8689 }\r
8690 \r
8691 VOID\r
8692 DisplayError(char *str, int error)\r
8693 {\r
8694   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8695   int len;\r
8696 \r
8697   if (error == 0) {\r
8698     strcpy(buf, str);\r
8699   } else {\r
8700     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8701                         NULL, error, LANG_NEUTRAL,\r
8702                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8703     if (len > 0) {\r
8704       sprintf(buf, "%s:\n%s", str, buf2);\r
8705     } else {\r
8706       ErrorMap *em = errmap;\r
8707       while (em->err != 0 && em->err != error) em++;\r
8708       if (em->err != 0) {\r
8709         sprintf(buf, "%s:\n%s", str, em->msg);\r
8710       } else {\r
8711         sprintf(buf, "%s:\nError code %d", str, error);\r
8712       }\r
8713     }\r
8714   }\r
8715   \r
8716   ErrorPopUp("Error", buf);\r
8717 }\r
8718 \r
8719 \r
8720 VOID\r
8721 DisplayMoveError(char *str)\r
8722 {\r
8723   fromX = fromY = -1;\r
8724   ClearHighlights();\r
8725   DrawPosition(FALSE, NULL);\r
8726   if (appData.popupMoveErrors) {\r
8727     ErrorPopUp("Error", str);\r
8728   } else {\r
8729     DisplayMessage(str, "");\r
8730     moveErrorMessageUp = TRUE;\r
8731   }\r
8732 }\r
8733 \r
8734 VOID\r
8735 DisplayFatalError(char *str, int error, int exitStatus)\r
8736 {\r
8737   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
8738   int len;\r
8739   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
8740 \r
8741   if (error != 0) {\r
8742     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8743                         NULL, error, LANG_NEUTRAL,\r
8744                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8745     if (len > 0) {\r
8746       sprintf(buf, "%s:\n%s", str, buf2);\r
8747     } else {\r
8748       ErrorMap *em = errmap;\r
8749       while (em->err != 0 && em->err != error) em++;\r
8750       if (em->err != 0) {\r
8751         sprintf(buf, "%s:\n%s", str, em->msg);\r
8752       } else {\r
8753         sprintf(buf, "%s:\nError code %d", str, error);\r
8754       }\r
8755     }\r
8756     str = buf;\r
8757   }\r
8758   if (appData.debugMode) {\r
8759     fprintf(debugFP, "%s: %s\n", label, str);\r
8760   }\r
8761   if (appData.popupExitMessage) {\r
8762     (void) MessageBox(hwndMain, str, label, MB_OK|\r
8763                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
8764   }\r
8765   ExitEvent(exitStatus);\r
8766 }\r
8767 \r
8768 \r
8769 VOID\r
8770 DisplayInformation(char *str)\r
8771 {\r
8772   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
8773 }\r
8774 \r
8775 \r
8776 VOID\r
8777 DisplayNote(char *str)\r
8778 {\r
8779   ErrorPopUp("Note", str);\r
8780 }\r
8781 \r
8782 \r
8783 typedef struct {\r
8784   char *title, *question, *replyPrefix;\r
8785   ProcRef pr;\r
8786 } QuestionParams;\r
8787 \r
8788 LRESULT CALLBACK\r
8789 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8790 {\r
8791   static QuestionParams *qp;\r
8792   char reply[MSG_SIZ];\r
8793   int len, err;\r
8794 \r
8795   switch (message) {\r
8796   case WM_INITDIALOG:\r
8797     qp = (QuestionParams *) lParam;\r
8798     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8799     SetWindowText(hDlg, qp->title);\r
8800     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
8801     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
8802     return FALSE;\r
8803 \r
8804   case WM_COMMAND:\r
8805     switch (LOWORD(wParam)) {\r
8806     case IDOK:\r
8807       strcpy(reply, qp->replyPrefix);\r
8808       if (*reply) strcat(reply, " ");\r
8809       len = strlen(reply);\r
8810       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
8811       strcat(reply, "\n");\r
8812       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
8813       EndDialog(hDlg, TRUE);\r
8814       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
8815       return TRUE;\r
8816     case IDCANCEL:\r
8817       EndDialog(hDlg, FALSE);\r
8818       return TRUE;\r
8819     default:\r
8820       break;\r
8821     }\r
8822     break;\r
8823   }\r
8824   return FALSE;\r
8825 }\r
8826 \r
8827 VOID\r
8828 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
8829 {\r
8830     QuestionParams qp;\r
8831     FARPROC lpProc;\r
8832     \r
8833     qp.title = title;\r
8834     qp.question = question;\r
8835     qp.replyPrefix = replyPrefix;\r
8836     qp.pr = pr;\r
8837     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
8838     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
8839       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
8840     FreeProcInstance(lpProc);\r
8841 }\r
8842 \r
8843 /* [AS] Pick FRC position */\r
8844 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8845 {\r
8846     static int * lpIndexFRC;\r
8847     BOOL index_is_ok;\r
8848     char buf[16];\r
8849 \r
8850     switch( message )\r
8851     {\r
8852     case WM_INITDIALOG:\r
8853         lpIndexFRC = (int *) lParam;\r
8854 \r
8855         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
8856 \r
8857         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
8858         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
8859         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
8860         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
8861 \r
8862         break;\r
8863 \r
8864     case WM_COMMAND:\r
8865         switch( LOWORD(wParam) ) {\r
8866         case IDOK:\r
8867             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8868             EndDialog( hDlg, 0 );\r
8869             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
8870             return TRUE;\r
8871         case IDCANCEL:\r
8872             EndDialog( hDlg, 1 );   \r
8873             return TRUE;\r
8874         case IDC_NFG_Edit:\r
8875             if( HIWORD(wParam) == EN_CHANGE ) {\r
8876                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
8877 \r
8878                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
8879             }\r
8880             return TRUE;\r
8881         case IDC_NFG_Random:\r
8882             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
8883             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
8884             return TRUE;\r
8885         }\r
8886 \r
8887         break;\r
8888     }\r
8889 \r
8890     return FALSE;\r
8891 }\r
8892 \r
8893 int NewGameFRC()\r
8894 {\r
8895     int result;\r
8896     int index = appData.defaultFrcPosition;\r
8897     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
8898 \r
8899     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
8900 \r
8901     if( result == 0 ) {\r
8902         appData.defaultFrcPosition = index;\r
8903     }\r
8904 \r
8905     return result;\r
8906 }\r
8907 \r
8908 /* [AS] Game list options */\r
8909 typedef struct {\r
8910     char id;\r
8911     char * name;\r
8912 } GLT_Item;\r
8913 \r
8914 static GLT_Item GLT_ItemInfo[] = {\r
8915     { GLT_EVENT,      "Event" },\r
8916     { GLT_SITE,       "Site" },\r
8917     { GLT_DATE,       "Date" },\r
8918     { GLT_ROUND,      "Round" },\r
8919     { GLT_PLAYERS,    "Players" },\r
8920     { GLT_RESULT,     "Result" },\r
8921     { GLT_WHITE_ELO,  "White Rating" },\r
8922     { GLT_BLACK_ELO,  "Black Rating" },\r
8923     { GLT_TIME_CONTROL,"Time Control" },\r
8924     { GLT_VARIANT,    "Variant" },\r
8925     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
8926     { 0, 0 }\r
8927 };\r
8928 \r
8929 const char * GLT_FindItem( char id )\r
8930 {\r
8931     const char * result = 0;\r
8932 \r
8933     GLT_Item * list = GLT_ItemInfo;\r
8934 \r
8935     while( list->id != 0 ) {\r
8936         if( list->id == id ) {\r
8937             result = list->name;\r
8938             break;\r
8939         }\r
8940 \r
8941         list++;\r
8942     }\r
8943 \r
8944     return result;\r
8945 }\r
8946 \r
8947 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
8948 {\r
8949     const char * name = GLT_FindItem( id );\r
8950 \r
8951     if( name != 0 ) {\r
8952         if( index >= 0 ) {\r
8953             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
8954         }\r
8955         else {\r
8956             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
8957         }\r
8958     }\r
8959 }\r
8960 \r
8961 void GLT_TagsToList( HWND hDlg, char * tags )\r
8962 {\r
8963     char * pc = tags;\r
8964 \r
8965     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
8966 \r
8967     while( *pc ) {\r
8968         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8969         pc++;\r
8970     }\r
8971 \r
8972     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
8973 \r
8974     pc = GLT_ALL_TAGS;\r
8975 \r
8976     while( *pc ) {\r
8977         if( strchr( tags, *pc ) == 0 ) {\r
8978             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
8979         }\r
8980         pc++;\r
8981     }\r
8982 \r
8983     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
8984 }\r
8985 \r
8986 char GLT_ListItemToTag( HWND hDlg, int index )\r
8987 {\r
8988     char result = '\0';\r
8989     char name[128];\r
8990 \r
8991     GLT_Item * list = GLT_ItemInfo;\r
8992 \r
8993     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
8994         while( list->id != 0 ) {\r
8995             if( strcmp( list->name, name ) == 0 ) {\r
8996                 result = list->id;\r
8997                 break;\r
8998             }\r
8999 \r
9000             list++;\r
9001         }\r
9002     }\r
9003 \r
9004     return result;\r
9005 }\r
9006 \r
9007 void GLT_MoveSelection( HWND hDlg, int delta )\r
9008 {\r
9009     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9010     int idx2 = idx1 + delta;\r
9011     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9012 \r
9013     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9014         char buf[128];\r
9015 \r
9016         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9017         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9018         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9019         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9020     }\r
9021 }\r
9022 \r
9023 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9024 {\r
9025     static char glt[64];\r
9026     static char * lpUserGLT;\r
9027 \r
9028     switch( message )\r
9029     {\r
9030     case WM_INITDIALOG:\r
9031         lpUserGLT = (char *) lParam;\r
9032         \r
9033         strcpy( glt, lpUserGLT );\r
9034 \r
9035         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9036 \r
9037         /* Initialize list */\r
9038         GLT_TagsToList( hDlg, glt );\r
9039 \r
9040         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9041 \r
9042         break;\r
9043 \r
9044     case WM_COMMAND:\r
9045         switch( LOWORD(wParam) ) {\r
9046         case IDOK:\r
9047             {\r
9048                 char * pc = lpUserGLT;\r
9049                 int idx = 0;\r
9050                 int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9051                 char id;\r
9052 \r
9053                 do {\r
9054                     id = GLT_ListItemToTag( hDlg, idx );\r
9055 \r
9056                     *pc++ = id;\r
9057                     idx++;\r
9058                 } while( id != '\0' );\r
9059             }\r
9060             EndDialog( hDlg, 0 );\r
9061             return TRUE;\r
9062         case IDCANCEL:\r
9063             EndDialog( hDlg, 1 );\r
9064             return TRUE;\r
9065 \r
9066         case IDC_GLT_Default:\r
9067             strcpy( glt, GLT_DEFAULT_TAGS );\r
9068             GLT_TagsToList( hDlg, glt );\r
9069             return TRUE;\r
9070 \r
9071         case IDC_GLT_Restore:\r
9072             strcpy( glt, lpUserGLT );\r
9073             GLT_TagsToList( hDlg, glt );\r
9074             return TRUE;\r
9075 \r
9076         case IDC_GLT_Up:\r
9077             GLT_MoveSelection( hDlg, -1 );\r
9078             return TRUE;\r
9079 \r
9080         case IDC_GLT_Down:\r
9081             GLT_MoveSelection( hDlg, +1 );\r
9082             return TRUE;\r
9083         }\r
9084 \r
9085         break;\r
9086     }\r
9087 \r
9088     return FALSE;\r
9089 }\r
9090 \r
9091 int GameListOptions()\r
9092 {\r
9093     char glt[64];\r
9094     int result;\r
9095     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9096 \r
9097     strcpy( glt, appData.gameListTags );\r
9098 \r
9099     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9100 \r
9101     if( result == 0 ) {\r
9102         /* [AS] Memory leak here! */\r
9103         appData.gameListTags = strdup( glt ); \r
9104     }\r
9105 \r
9106     return result;\r
9107 }\r
9108 \r
9109 \r
9110 VOID\r
9111 DisplayIcsInteractionTitle(char *str)\r
9112 {\r
9113   char consoleTitle[MSG_SIZ];\r
9114 \r
9115   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9116   SetWindowText(hwndConsole, consoleTitle);\r
9117 }\r
9118 \r
9119 void\r
9120 DrawPosition(int fullRedraw, Board board)\r
9121 {\r
9122   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9123 }\r
9124 \r
9125 \r
9126 VOID\r
9127 ResetFrontEnd()\r
9128 {\r
9129   fromX = fromY = -1;\r
9130   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9131     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9132     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9133     dragInfo.lastpos = dragInfo.pos;\r
9134     dragInfo.start.x = dragInfo.start.y = -1;\r
9135     dragInfo.from = dragInfo.start;\r
9136     ReleaseCapture();\r
9137     DrawPosition(TRUE, NULL);\r
9138   }\r
9139 }\r
9140 \r
9141 \r
9142 VOID\r
9143 CommentPopUp(char *title, char *str)\r
9144 {\r
9145   HWND hwnd = GetActiveWindow();\r
9146   EitherCommentPopUp(0, title, str, FALSE);\r
9147   SetActiveWindow(hwnd);\r
9148 }\r
9149 \r
9150 VOID\r
9151 CommentPopDown(void)\r
9152 {\r
9153   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9154   if (commentDialog) {\r
9155     ShowWindow(commentDialog, SW_HIDE);\r
9156   }\r
9157   commentDialogUp = FALSE;\r
9158 }\r
9159 \r
9160 VOID\r
9161 EditCommentPopUp(int index, char *title, char *str)\r
9162 {\r
9163   EitherCommentPopUp(index, title, str, TRUE);\r
9164 }\r
9165 \r
9166 \r
9167 VOID\r
9168 RingBell()\r
9169 {\r
9170   MyPlaySound(&sounds[(int)SoundMove]);\r
9171 }\r
9172 \r
9173 VOID PlayIcsWinSound()\r
9174 {\r
9175   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9176 }\r
9177 \r
9178 VOID PlayIcsLossSound()\r
9179 {\r
9180   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9181 }\r
9182 \r
9183 VOID PlayIcsDrawSound()\r
9184 {\r
9185   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9186 }\r
9187 \r
9188 VOID PlayIcsUnfinishedSound()\r
9189 {\r
9190   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9191 }\r
9192 \r
9193 VOID\r
9194 PlayAlarmSound()\r
9195 {\r
9196   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9197 }\r
9198 \r
9199 \r
9200 VOID\r
9201 EchoOn()\r
9202 {\r
9203   HWND hInput;\r
9204   consoleEcho = TRUE;\r
9205   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9206   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9207   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9208 }\r
9209 \r
9210 \r
9211 VOID\r
9212 EchoOff()\r
9213 {\r
9214   CHARFORMAT cf;\r
9215   HWND hInput;\r
9216   consoleEcho = FALSE;\r
9217   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9218   /* This works OK: set text and background both to the same color */\r
9219   cf = consoleCF;\r
9220   cf.crTextColor = COLOR_ECHOOFF;\r
9221   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9222   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9223 }\r
9224 \r
9225 /* No Raw()...? */\r
9226 \r
9227 void Colorize(ColorClass cc, int continuation)\r
9228 {\r
9229   currentColorClass = cc;\r
9230   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9231   consoleCF.crTextColor = textAttribs[cc].color;\r
9232   consoleCF.dwEffects = textAttribs[cc].effects;\r
9233   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9234 }\r
9235 \r
9236 char *\r
9237 UserName()\r
9238 {\r
9239   static char buf[MSG_SIZ];\r
9240   DWORD bufsiz = MSG_SIZ;\r
9241 \r
9242   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9243         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9244   }\r
9245   if (!GetUserName(buf, &bufsiz)) {\r
9246     /*DisplayError("Error getting user name", GetLastError());*/\r
9247     strcpy(buf, "User");\r
9248   }\r
9249   return buf;\r
9250 }\r
9251 \r
9252 char *\r
9253 HostName()\r
9254 {\r
9255   static char buf[MSG_SIZ];\r
9256   DWORD bufsiz = MSG_SIZ;\r
9257 \r
9258   if (!GetComputerName(buf, &bufsiz)) {\r
9259     /*DisplayError("Error getting host name", GetLastError());*/\r
9260     strcpy(buf, "Unknown");\r
9261   }\r
9262   return buf;\r
9263 }\r
9264 \r
9265 \r
9266 int\r
9267 ClockTimerRunning()\r
9268 {\r
9269   return clockTimerEvent != 0;\r
9270 }\r
9271 \r
9272 int\r
9273 StopClockTimer()\r
9274 {\r
9275   if (clockTimerEvent == 0) return FALSE;\r
9276   KillTimer(hwndMain, clockTimerEvent);\r
9277   clockTimerEvent = 0;\r
9278   return TRUE;\r
9279 }\r
9280 \r
9281 void\r
9282 StartClockTimer(long millisec)\r
9283 {\r
9284   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9285                              (UINT) millisec, NULL);\r
9286 }\r
9287 \r
9288 void\r
9289 DisplayWhiteClock(long timeRemaining, int highlight)\r
9290 {\r
9291   HDC hdc;\r
9292   hdc = GetDC(hwndMain);\r
9293   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9294 \r
9295   if (!IsIconic(hwndMain)) {\r
9296     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &blackRect : &whiteRect, "White", flag);\r
9297   }\r
9298   if (highlight && iconCurrent == iconBlack) {\r
9299     iconCurrent = iconWhite;\r
9300     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9301     if (IsIconic(hwndMain)) {\r
9302       DrawIcon(hdc, 2, 2, iconCurrent);\r
9303     }\r
9304   }\r
9305   (void) ReleaseDC(hwndMain, hdc);\r
9306   if (hwndConsole)\r
9307     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9308 }\r
9309 \r
9310 void\r
9311 DisplayBlackClock(long timeRemaining, int highlight)\r
9312 {\r
9313   HDC hdc;\r
9314   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9315 \r
9316   hdc = GetDC(hwndMain);\r
9317   if (!IsIconic(hwndMain)) {\r
9318     DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9319   }\r
9320   if (highlight && iconCurrent == iconWhite) {\r
9321     iconCurrent = iconBlack;\r
9322     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9323     if (IsIconic(hwndMain)) {\r
9324       DrawIcon(hdc, 2, 2, iconCurrent);\r
9325     }\r
9326   }\r
9327   (void) ReleaseDC(hwndMain, hdc);\r
9328   if (hwndConsole)\r
9329     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9330 }\r
9331 \r
9332 \r
9333 int\r
9334 LoadGameTimerRunning()\r
9335 {\r
9336   return loadGameTimerEvent != 0;\r
9337 }\r
9338 \r
9339 int\r
9340 StopLoadGameTimer()\r
9341 {\r
9342   if (loadGameTimerEvent == 0) return FALSE;\r
9343   KillTimer(hwndMain, loadGameTimerEvent);\r
9344   loadGameTimerEvent = 0;\r
9345   return TRUE;\r
9346 }\r
9347 \r
9348 void\r
9349 StartLoadGameTimer(long millisec)\r
9350 {\r
9351   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9352                                 (UINT) millisec, NULL);\r
9353 }\r
9354 \r
9355 void\r
9356 AutoSaveGame()\r
9357 {\r
9358   char *defName;\r
9359   FILE *f;\r
9360   char fileTitle[MSG_SIZ];\r
9361 \r
9362   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9363   f = OpenFileDialog(hwndMain, "a", defName,\r
9364                      appData.oldSaveStyle ? "gam" : "pgn",\r
9365                      GAME_FILT, \r
9366                      "Save Game to File", NULL, fileTitle, NULL);\r
9367   if (f != NULL) {\r
9368     SaveGame(f, 0, "");\r
9369     fclose(f);\r
9370   }\r
9371 }\r
9372 \r
9373 \r
9374 void\r
9375 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9376 {\r
9377   if (delayedTimerEvent != 0) {\r
9378     if (appData.debugMode) {\r
9379       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9380     }\r
9381     KillTimer(hwndMain, delayedTimerEvent);\r
9382     delayedTimerEvent = 0;\r
9383     delayedTimerCallback();\r
9384   }\r
9385   delayedTimerCallback = cb;\r
9386   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9387                                 (UINT) millisec, NULL);\r
9388 }\r
9389 \r
9390 DelayedEventCallback\r
9391 GetDelayedEvent()\r
9392 {\r
9393   if (delayedTimerEvent) {\r
9394     return delayedTimerCallback;\r
9395   } else {\r
9396     return NULL;\r
9397   }\r
9398 }\r
9399 \r
9400 void\r
9401 CancelDelayedEvent()\r
9402 {\r
9403   if (delayedTimerEvent) {\r
9404     KillTimer(hwndMain, delayedTimerEvent);\r
9405     delayedTimerEvent = 0;\r
9406   }\r
9407 }\r
9408 \r
9409 /* Start a child process running the given program.\r
9410    The process's standard output can be read from "from", and its\r
9411    standard input can be written to "to".\r
9412    Exit with fatal error if anything goes wrong.\r
9413    Returns an opaque pointer that can be used to destroy the process\r
9414    later.\r
9415 */\r
9416 int\r
9417 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9418 {\r
9419 #define BUFSIZE 4096\r
9420 \r
9421   HANDLE hChildStdinRd, hChildStdinWr,\r
9422     hChildStdoutRd, hChildStdoutWr;\r
9423   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9424   SECURITY_ATTRIBUTES saAttr;\r
9425   BOOL fSuccess;\r
9426   PROCESS_INFORMATION piProcInfo;\r
9427   STARTUPINFO siStartInfo;\r
9428   ChildProc *cp;\r
9429   char buf[MSG_SIZ];\r
9430   DWORD err;\r
9431 \r
9432   if (appData.debugMode) {\r
9433     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9434   }\r
9435 \r
9436   *pr = NoProc;\r
9437 \r
9438   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9439   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9440   saAttr.bInheritHandle = TRUE;\r
9441   saAttr.lpSecurityDescriptor = NULL;\r
9442 \r
9443   /*\r
9444    * The steps for redirecting child's STDOUT:\r
9445    *     1. Create anonymous pipe to be STDOUT for child.\r
9446    *     2. Create a noninheritable duplicate of read handle,\r
9447    *         and close the inheritable read handle.\r
9448    */\r
9449 \r
9450   /* Create a pipe for the child's STDOUT. */\r
9451   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9452     return GetLastError();\r
9453   }\r
9454 \r
9455   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9456   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9457                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9458                              FALSE,     /* not inherited */\r
9459                              DUPLICATE_SAME_ACCESS);\r
9460   if (! fSuccess) {\r
9461     return GetLastError();\r
9462   }\r
9463   CloseHandle(hChildStdoutRd);\r
9464 \r
9465   /*\r
9466    * The steps for redirecting child's STDIN:\r
9467    *     1. Create anonymous pipe to be STDIN for child.\r
9468    *     2. Create a noninheritable duplicate of write handle,\r
9469    *         and close the inheritable write handle.\r
9470    */\r
9471 \r
9472   /* Create a pipe for the child's STDIN. */\r
9473   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9474     return GetLastError();\r
9475   }\r
9476 \r
9477   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9478   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9479                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9480                              FALSE,     /* not inherited */\r
9481                              DUPLICATE_SAME_ACCESS);\r
9482   if (! fSuccess) {\r
9483     return GetLastError();\r
9484   }\r
9485   CloseHandle(hChildStdinWr);\r
9486 \r
9487   /* Arrange to (1) look in dir for the child .exe file, and\r
9488    * (2) have dir be the child's working directory.  Interpret\r
9489    * dir relative to the directory WinBoard loaded from. */\r
9490   GetCurrentDirectory(MSG_SIZ, buf);\r
9491   SetCurrentDirectory(installDir);\r
9492   SetCurrentDirectory(dir);\r
9493 \r
9494   /* Now create the child process. */\r
9495 \r
9496   siStartInfo.cb = sizeof(STARTUPINFO);\r
9497   siStartInfo.lpReserved = NULL;\r
9498   siStartInfo.lpDesktop = NULL;\r
9499   siStartInfo.lpTitle = NULL;\r
9500   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9501   siStartInfo.cbReserved2 = 0;\r
9502   siStartInfo.lpReserved2 = NULL;\r
9503   siStartInfo.hStdInput = hChildStdinRd;\r
9504   siStartInfo.hStdOutput = hChildStdoutWr;\r
9505   siStartInfo.hStdError = hChildStdoutWr;\r
9506 \r
9507   fSuccess = CreateProcess(NULL,\r
9508                            cmdLine,        /* command line */\r
9509                            NULL,           /* process security attributes */\r
9510                            NULL,           /* primary thread security attrs */\r
9511                            TRUE,           /* handles are inherited */\r
9512                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9513                            NULL,           /* use parent's environment */\r
9514                            NULL,\r
9515                            &siStartInfo, /* STARTUPINFO pointer */\r
9516                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9517 \r
9518   err = GetLastError();\r
9519   SetCurrentDirectory(buf); /* return to prev directory */\r
9520   if (! fSuccess) {\r
9521     return err;\r
9522   }\r
9523 \r
9524   /* Close the handles we don't need in the parent */\r
9525   CloseHandle(piProcInfo.hThread);\r
9526   CloseHandle(hChildStdinRd);\r
9527   CloseHandle(hChildStdoutWr);\r
9528 \r
9529   /* Prepare return value */\r
9530   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9531   cp->kind = CPReal;\r
9532   cp->hProcess = piProcInfo.hProcess;\r
9533   cp->pid = piProcInfo.dwProcessId;\r
9534   cp->hFrom = hChildStdoutRdDup;\r
9535   cp->hTo = hChildStdinWrDup;\r
9536 \r
9537   *pr = (void *) cp;\r
9538 \r
9539   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9540      2000 where engines sometimes don't see the initial command(s)\r
9541      from WinBoard and hang.  I don't understand how that can happen,\r
9542      but the Sleep is harmless, so I've put it in.  Others have also\r
9543      reported what may be the same problem, so hopefully this will fix\r
9544      it for them too.  */\r
9545   Sleep(500);\r
9546 \r
9547   return NO_ERROR;\r
9548 }\r
9549 \r
9550 \r
9551 void\r
9552 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9553 {\r
9554   ChildProc *cp; int result;\r
9555 \r
9556   cp = (ChildProc *) pr;\r
9557   if (cp == NULL) return;\r
9558 \r
9559   switch (cp->kind) {\r
9560   case CPReal:\r
9561     /* TerminateProcess is considered harmful, so... */\r
9562     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9563     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9564     /* The following doesn't work because the chess program\r
9565        doesn't "have the same console" as WinBoard.  Maybe\r
9566        we could arrange for this even though neither WinBoard\r
9567        nor the chess program uses a console for stdio? */\r
9568     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9569 \r
9570     /* [AS] Special termination modes for misbehaving programs... */\r
9571     if( signal == 9 ) { \r
9572         result = TerminateProcess( cp->hProcess, 0 );\r
9573 \r
9574         if ( appData.debugMode) {\r
9575             fprintf( debugFP, "Terminating process %u, result=%d\n", cp->pid, result );\r
9576         }\r
9577     }\r
9578     else if( signal == 10 ) {\r
9579         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9580 \r
9581         if( dw != WAIT_OBJECT_0 ) {\r
9582             result = TerminateProcess( cp->hProcess, 0 );\r
9583 \r
9584             if ( appData.debugMode) {\r
9585                 fprintf( debugFP, "Process %u still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9586             }\r
9587 \r
9588         }\r
9589     }\r
9590 \r
9591     CloseHandle(cp->hProcess);\r
9592     break;\r
9593 \r
9594   case CPComm:\r
9595     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9596     break;\r
9597 \r
9598   case CPSock:\r
9599     closesocket(cp->sock);\r
9600     WSACleanup();\r
9601     break;\r
9602 \r
9603   case CPRcmd:\r
9604     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9605     closesocket(cp->sock);\r
9606     closesocket(cp->sock2);\r
9607     WSACleanup();\r
9608     break;\r
9609   }\r
9610   free(cp);\r
9611 }\r
9612 \r
9613 void\r
9614 InterruptChildProcess(ProcRef pr)\r
9615 {\r
9616   ChildProc *cp;\r
9617 \r
9618   cp = (ChildProc *) pr;\r
9619   if (cp == NULL) return;\r
9620   switch (cp->kind) {\r
9621   case CPReal:\r
9622     /* The following doesn't work because the chess program\r
9623        doesn't "have the same console" as WinBoard.  Maybe\r
9624        we could arrange for this even though neither WinBoard\r
9625        nor the chess program uses a console for stdio */\r
9626     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9627     break;\r
9628 \r
9629   case CPComm:\r
9630   case CPSock:\r
9631     /* Can't interrupt */\r
9632     break;\r
9633 \r
9634   case CPRcmd:\r
9635     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9636     break;\r
9637   }\r
9638 }\r
9639 \r
9640 \r
9641 int\r
9642 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9643 {\r
9644   char cmdLine[MSG_SIZ];\r
9645 \r
9646   if (port[0] == NULLCHAR) {\r
9647     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9648   } else {\r
9649     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9650   }\r
9651   return StartChildProcess(cmdLine, "", pr);\r
9652 }\r
9653 \r
9654 \r
9655 /* Code to open TCP sockets */\r
9656 \r
9657 int\r
9658 OpenTCP(char *host, char *port, ProcRef *pr)\r
9659 {\r
9660   ChildProc *cp;\r
9661   int err;\r
9662   SOCKET s;\r
9663   struct sockaddr_in sa, mysa;\r
9664   struct hostent FAR *hp;\r
9665   unsigned short uport;\r
9666   WORD wVersionRequested;\r
9667   WSADATA wsaData;\r
9668 \r
9669   /* Initialize socket DLL */\r
9670   wVersionRequested = MAKEWORD(1, 1);\r
9671   err = WSAStartup(wVersionRequested, &wsaData);\r
9672   if (err != 0) return err;\r
9673 \r
9674   /* Make socket */\r
9675   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9676     err = WSAGetLastError();\r
9677     WSACleanup();\r
9678     return err;\r
9679   }\r
9680 \r
9681   /* Bind local address using (mostly) don't-care values.\r
9682    */\r
9683   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9684   mysa.sin_family = AF_INET;\r
9685   mysa.sin_addr.s_addr = INADDR_ANY;\r
9686   uport = (unsigned short) 0;\r
9687   mysa.sin_port = htons(uport);\r
9688   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9689       == SOCKET_ERROR) {\r
9690     err = WSAGetLastError();\r
9691     WSACleanup();\r
9692     return err;\r
9693   }\r
9694 \r
9695   /* Resolve remote host name */\r
9696   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9697   if (!(hp = gethostbyname(host))) {\r
9698     unsigned int b0, b1, b2, b3;\r
9699 \r
9700     err = WSAGetLastError();\r
9701 \r
9702     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9703       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9704       hp->h_addrtype = AF_INET;\r
9705       hp->h_length = 4;\r
9706       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9707       hp->h_addr_list[0] = (char *) malloc(4);\r
9708       hp->h_addr_list[0][0] = (char) b0;\r
9709       hp->h_addr_list[0][1] = (char) b1;\r
9710       hp->h_addr_list[0][2] = (char) b2;\r
9711       hp->h_addr_list[0][3] = (char) b3;\r
9712     } else {\r
9713       WSACleanup();\r
9714       return err;\r
9715     }\r
9716   }\r
9717   sa.sin_family = hp->h_addrtype;\r
9718   uport = (unsigned short) atoi(port);\r
9719   sa.sin_port = htons(uport);\r
9720   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9721 \r
9722   /* Make connection */\r
9723   if (connect(s, (struct sockaddr *) &sa,\r
9724               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9725     err = WSAGetLastError();\r
9726     WSACleanup();\r
9727     return err;\r
9728   }\r
9729 \r
9730   /* Prepare return value */\r
9731   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9732   cp->kind = CPSock;\r
9733   cp->sock = s;\r
9734   *pr = (ProcRef *) cp;\r
9735 \r
9736   return NO_ERROR;\r
9737 }\r
9738 \r
9739 int\r
9740 OpenCommPort(char *name, ProcRef *pr)\r
9741 {\r
9742   HANDLE h;\r
9743   COMMTIMEOUTS ct;\r
9744   ChildProc *cp;\r
9745   char fullname[MSG_SIZ];\r
9746 \r
9747   if (*name != '\\')\r
9748     sprintf(fullname, "\\\\.\\%s", name);\r
9749   else\r
9750     strcpy(fullname, name);\r
9751 \r
9752   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
9753                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
9754   if (h == (HANDLE) -1) {\r
9755     return GetLastError();\r
9756   }\r
9757   hCommPort = h;\r
9758 \r
9759   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
9760 \r
9761   /* Accumulate characters until a 100ms pause, then parse */\r
9762   ct.ReadIntervalTimeout = 100;\r
9763   ct.ReadTotalTimeoutMultiplier = 0;\r
9764   ct.ReadTotalTimeoutConstant = 0;\r
9765   ct.WriteTotalTimeoutMultiplier = 0;\r
9766   ct.WriteTotalTimeoutConstant = 0;\r
9767   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
9768 \r
9769   /* Prepare return value */\r
9770   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9771   cp->kind = CPComm;\r
9772   cp->hFrom = h;\r
9773   cp->hTo = h;\r
9774   *pr = (ProcRef *) cp;\r
9775 \r
9776   return NO_ERROR;\r
9777 }\r
9778 \r
9779 int\r
9780 OpenLoopback(ProcRef *pr)\r
9781 {\r
9782   DisplayFatalError("Not implemented", 0, 1);\r
9783   return NO_ERROR;\r
9784 }\r
9785 \r
9786 \r
9787 int\r
9788 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
9789 {\r
9790   ChildProc *cp;\r
9791   int err;\r
9792   SOCKET s, s2, s3;\r
9793   struct sockaddr_in sa, mysa;\r
9794   struct hostent FAR *hp;\r
9795   unsigned short uport;\r
9796   WORD wVersionRequested;\r
9797   WSADATA wsaData;\r
9798   int fromPort;\r
9799   char stderrPortStr[MSG_SIZ];\r
9800 \r
9801   /* Initialize socket DLL */\r
9802   wVersionRequested = MAKEWORD(1, 1);\r
9803   err = WSAStartup(wVersionRequested, &wsaData);\r
9804   if (err != 0) return err;\r
9805 \r
9806   /* Resolve remote host name */\r
9807   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
9808   if (!(hp = gethostbyname(host))) {\r
9809     unsigned int b0, b1, b2, b3;\r
9810 \r
9811     err = WSAGetLastError();\r
9812 \r
9813     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
9814       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
9815       hp->h_addrtype = AF_INET;\r
9816       hp->h_length = 4;\r
9817       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
9818       hp->h_addr_list[0] = (char *) malloc(4);\r
9819       hp->h_addr_list[0][0] = (char) b0;\r
9820       hp->h_addr_list[0][1] = (char) b1;\r
9821       hp->h_addr_list[0][2] = (char) b2;\r
9822       hp->h_addr_list[0][3] = (char) b3;\r
9823     } else {\r
9824       WSACleanup();\r
9825       return err;\r
9826     }\r
9827   }\r
9828   sa.sin_family = hp->h_addrtype;\r
9829   uport = (unsigned short) 514;\r
9830   sa.sin_port = htons(uport);\r
9831   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
9832 \r
9833   /* Bind local socket to unused "privileged" port address\r
9834    */\r
9835   s = INVALID_SOCKET;\r
9836   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9837   mysa.sin_family = AF_INET;\r
9838   mysa.sin_addr.s_addr = INADDR_ANY;\r
9839   for (fromPort = 1023;; fromPort--) {\r
9840     if (fromPort < 0) {\r
9841       WSACleanup();\r
9842       return WSAEADDRINUSE;\r
9843     }\r
9844     if (s == INVALID_SOCKET) {\r
9845       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9846         err = WSAGetLastError();\r
9847         WSACleanup();\r
9848         return err;\r
9849       }\r
9850     }\r
9851     uport = (unsigned short) fromPort;\r
9852     mysa.sin_port = htons(uport);\r
9853     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9854         == SOCKET_ERROR) {\r
9855       err = WSAGetLastError();\r
9856       if (err == WSAEADDRINUSE) continue;\r
9857       WSACleanup();\r
9858       return err;\r
9859     }\r
9860     if (connect(s, (struct sockaddr *) &sa,\r
9861       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
9862       err = WSAGetLastError();\r
9863       if (err == WSAEADDRINUSE) {\r
9864         closesocket(s);\r
9865         s = -1;\r
9866         continue;\r
9867       }\r
9868       WSACleanup();\r
9869       return err;\r
9870     }\r
9871     break;\r
9872   }\r
9873 \r
9874   /* Bind stderr local socket to unused "privileged" port address\r
9875    */\r
9876   s2 = INVALID_SOCKET;\r
9877   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9878   mysa.sin_family = AF_INET;\r
9879   mysa.sin_addr.s_addr = INADDR_ANY;\r
9880   for (fromPort = 1023;; fromPort--) {\r
9881     if (fromPort == prevStderrPort) continue; // don't reuse port\r
9882     if (fromPort < 0) {\r
9883       (void) closesocket(s);\r
9884       WSACleanup();\r
9885       return WSAEADDRINUSE;\r
9886     }\r
9887     if (s2 == INVALID_SOCKET) {\r
9888       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9889         err = WSAGetLastError();\r
9890         closesocket(s);\r
9891         WSACleanup();\r
9892         return err;\r
9893       }\r
9894     }\r
9895     uport = (unsigned short) fromPort;\r
9896     mysa.sin_port = htons(uport);\r
9897     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9898         == SOCKET_ERROR) {\r
9899       err = WSAGetLastError();\r
9900       if (err == WSAEADDRINUSE) continue;\r
9901       (void) closesocket(s);\r
9902       WSACleanup();\r
9903       return err;\r
9904     }\r
9905     if (listen(s2, 1) == SOCKET_ERROR) {\r
9906       err = WSAGetLastError();\r
9907       if (err == WSAEADDRINUSE) {\r
9908         closesocket(s2);\r
9909         s2 = INVALID_SOCKET;\r
9910         continue;\r
9911       }\r
9912       (void) closesocket(s);\r
9913       (void) closesocket(s2);\r
9914       WSACleanup();\r
9915       return err;\r
9916     }\r
9917     break;\r
9918   }\r
9919   prevStderrPort = fromPort; // remember port used\r
9920   sprintf(stderrPortStr, "%d", fromPort);\r
9921 \r
9922   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
9923     err = WSAGetLastError();\r
9924     (void) closesocket(s);\r
9925     (void) closesocket(s2);\r
9926     WSACleanup();\r
9927     return err;\r
9928   }\r
9929 \r
9930   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
9931     err = WSAGetLastError();\r
9932     (void) closesocket(s);\r
9933     (void) closesocket(s2);\r
9934     WSACleanup();\r
9935     return err;\r
9936   }\r
9937   if (*user == NULLCHAR) user = UserName();\r
9938   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
9939     err = WSAGetLastError();\r
9940     (void) closesocket(s);\r
9941     (void) closesocket(s2);\r
9942     WSACleanup();\r
9943     return err;\r
9944   }\r
9945   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
9946     err = WSAGetLastError();\r
9947     (void) closesocket(s);\r
9948     (void) closesocket(s2);\r
9949     WSACleanup();\r
9950     return err;\r
9951   }\r
9952 \r
9953   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
9954     err = WSAGetLastError();\r
9955     (void) closesocket(s);\r
9956     (void) closesocket(s2);\r
9957     WSACleanup();\r
9958     return err;\r
9959   }\r
9960   (void) closesocket(s2);  /* Stop listening */\r
9961 \r
9962   /* Prepare return value */\r
9963   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9964   cp->kind = CPRcmd;\r
9965   cp->sock = s;\r
9966   cp->sock2 = s3;\r
9967   *pr = (ProcRef *) cp;\r
9968 \r
9969   return NO_ERROR;\r
9970 }\r
9971 \r
9972 \r
9973 InputSourceRef\r
9974 AddInputSource(ProcRef pr, int lineByLine,\r
9975                InputCallback func, VOIDSTAR closure)\r
9976 {\r
9977   InputSource *is, *is2 = NULL;\r
9978   ChildProc *cp = (ChildProc *) pr;\r
9979 \r
9980   is = (InputSource *) calloc(1, sizeof(InputSource));\r
9981   is->lineByLine = lineByLine;\r
9982   is->func = func;\r
9983   is->closure = closure;\r
9984   is->second = NULL;\r
9985   is->next = is->buf;\r
9986   if (pr == NoProc) {\r
9987     is->kind = CPReal;\r
9988     consoleInputSource = is;\r
9989   } else {\r
9990     is->kind = cp->kind;\r
9991     /* \r
9992         [AS] Try to avoid a race condition if the thread is given control too early:\r
9993         we create all threads suspended so that the is->hThread variable can be\r
9994         safely assigned, then let the threads start with ResumeThread.\r
9995     */\r
9996     switch (cp->kind) {\r
9997     case CPReal:\r
9998       is->hFile = cp->hFrom;\r
9999       cp->hFrom = NULL; /* now owned by InputThread */\r
10000       is->hThread =\r
10001         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10002                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10003       break;\r
10004 \r
10005     case CPComm:\r
10006       is->hFile = cp->hFrom;\r
10007       cp->hFrom = NULL; /* now owned by InputThread */\r
10008       is->hThread =\r
10009         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10010                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10011       break;\r
10012 \r
10013     case CPSock:\r
10014       is->sock = cp->sock;\r
10015       is->hThread =\r
10016         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10017                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10018       break;\r
10019 \r
10020     case CPRcmd:\r
10021       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10022       *is2 = *is;\r
10023       is->sock = cp->sock;\r
10024       is->second = is2;\r
10025       is2->sock = cp->sock2;\r
10026       is2->second = is2;\r
10027       is->hThread =\r
10028         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10029                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10030       is2->hThread =\r
10031         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10032                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10033       break;\r
10034     }\r
10035 \r
10036     if( is->hThread != NULL ) {\r
10037         ResumeThread( is->hThread );\r
10038     }\r
10039 \r
10040     if( is2 != NULL && is2->hThread != NULL ) {\r
10041         ResumeThread( is2->hThread );\r
10042     }\r
10043   }\r
10044 \r
10045   return (InputSourceRef) is;\r
10046 }\r
10047 \r
10048 void\r
10049 RemoveInputSource(InputSourceRef isr)\r
10050 {\r
10051   InputSource *is;\r
10052 \r
10053   is = (InputSource *) isr;\r
10054   is->hThread = NULL;  /* tell thread to stop */\r
10055   CloseHandle(is->hThread);\r
10056   if (is->second != NULL) {\r
10057     is->second->hThread = NULL;\r
10058     CloseHandle(is->second->hThread);\r
10059   }\r
10060 }\r
10061 \r
10062 \r
10063 int\r
10064 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10065 {\r
10066   DWORD dOutCount;\r
10067   int outCount = SOCKET_ERROR;\r
10068   ChildProc *cp = (ChildProc *) pr;\r
10069   static OVERLAPPED ovl;\r
10070 \r
10071   if (pr == NoProc) {\r
10072     ConsoleOutput(message, count, FALSE);\r
10073     return count;\r
10074   } \r
10075 \r
10076   if (ovl.hEvent == NULL) {\r
10077     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10078   }\r
10079   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10080 \r
10081   switch (cp->kind) {\r
10082   case CPSock:\r
10083   case CPRcmd:\r
10084     outCount = send(cp->sock, message, count, 0);\r
10085     if (outCount == SOCKET_ERROR) {\r
10086       *outError = WSAGetLastError();\r
10087     } else {\r
10088       *outError = NO_ERROR;\r
10089     }\r
10090     break;\r
10091 \r
10092   case CPReal:\r
10093     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10094                   &dOutCount, NULL)) {\r
10095       *outError = NO_ERROR;\r
10096       outCount = (int) dOutCount;\r
10097     } else {\r
10098       *outError = GetLastError();\r
10099     }\r
10100     break;\r
10101 \r
10102   case CPComm:\r
10103     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10104                             &dOutCount, &ovl);\r
10105     if (*outError == NO_ERROR) {\r
10106       outCount = (int) dOutCount;\r
10107     }\r
10108     break;\r
10109   }\r
10110   return outCount;\r
10111 }\r
10112 \r
10113 int\r
10114 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10115                        long msdelay)\r
10116 {\r
10117   /* Ignore delay, not implemented for WinBoard */\r
10118   return OutputToProcess(pr, message, count, outError);\r
10119 }\r
10120 \r
10121 \r
10122 void\r
10123 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10124                         char *buf, int count, int error)\r
10125 {\r
10126   DisplayFatalError("Not implemented", 0, 1);\r
10127 }\r
10128 \r
10129 /* see wgamelist.c for Game List functions */\r
10130 /* see wedittags.c for Edit Tags functions */\r
10131 \r
10132 \r
10133 VOID\r
10134 ICSInitScript()\r
10135 {\r
10136   FILE *f;\r
10137   char buf[MSG_SIZ];\r
10138   char *dummy;\r
10139 \r
10140   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10141     f = fopen(buf, "r");\r
10142     if (f != NULL) {\r
10143       ProcessICSInitScript(f);\r
10144       fclose(f);\r
10145     }\r
10146   }\r
10147 }\r
10148 \r
10149 \r
10150 VOID\r
10151 StartAnalysisClock()\r
10152 {\r
10153   if (analysisTimerEvent) return;\r
10154   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10155                                         (UINT) 2000, NULL);\r
10156 }\r
10157 \r
10158 LRESULT CALLBACK\r
10159 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10160 {\r
10161   static HANDLE hwndText;\r
10162   RECT rect;\r
10163   static int sizeX, sizeY;\r
10164   int newSizeX, newSizeY, flags;\r
10165   MINMAXINFO *mmi;\r
10166 \r
10167   switch (message) {\r
10168   case WM_INITDIALOG: /* message: initialize dialog box */\r
10169     /* Initialize the dialog items */\r
10170     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10171     SetWindowText(hDlg, analysisTitle);\r
10172     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10173     /* Size and position the dialog */\r
10174     if (!analysisDialog) {\r
10175       analysisDialog = hDlg;\r
10176       flags = SWP_NOZORDER;\r
10177       GetClientRect(hDlg, &rect);\r
10178       sizeX = rect.right;\r
10179       sizeY = rect.bottom;\r
10180       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10181           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10182         WINDOWPLACEMENT wp;\r
10183         EnsureOnScreen(&analysisX, &analysisY);\r
10184         wp.length = sizeof(WINDOWPLACEMENT);\r
10185         wp.flags = 0;\r
10186         wp.showCmd = SW_SHOW;\r
10187         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10188         wp.rcNormalPosition.left = analysisX;\r
10189         wp.rcNormalPosition.right = analysisX + analysisW;\r
10190         wp.rcNormalPosition.top = analysisY;\r
10191         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10192         SetWindowPlacement(hDlg, &wp);\r
10193 \r
10194         GetClientRect(hDlg, &rect);\r
10195         newSizeX = rect.right;\r
10196         newSizeY = rect.bottom;\r
10197         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10198                               newSizeX, newSizeY);\r
10199         sizeX = newSizeX;\r
10200         sizeY = newSizeY;\r
10201       }\r
10202     }\r
10203     return FALSE;\r
10204 \r
10205   case WM_COMMAND: /* message: received a command */\r
10206     switch (LOWORD(wParam)) {\r
10207     case IDCANCEL:\r
10208       EditGameEvent();\r
10209       return TRUE;\r
10210     default:\r
10211       break;\r
10212     }\r
10213     break;\r
10214 \r
10215   case WM_SIZE:\r
10216     newSizeX = LOWORD(lParam);\r
10217     newSizeY = HIWORD(lParam);\r
10218     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10219     sizeX = newSizeX;\r
10220     sizeY = newSizeY;\r
10221     break;\r
10222 \r
10223   case WM_GETMINMAXINFO:\r
10224     /* Prevent resizing window too small */\r
10225     mmi = (MINMAXINFO *) lParam;\r
10226     mmi->ptMinTrackSize.x = 100;\r
10227     mmi->ptMinTrackSize.y = 100;\r
10228     break;\r
10229   }\r
10230   return FALSE;\r
10231 }\r
10232 \r
10233 VOID\r
10234 AnalysisPopUp(char* title, char* str)\r
10235 {\r
10236   FARPROC lpProc;\r
10237   char *p, *q;\r
10238 \r
10239   /* [AS] */\r
10240   EngineOutputPopUp();\r
10241   return;\r
10242 \r
10243   if (str == NULL) str = "";\r
10244   p = (char *) malloc(2 * strlen(str) + 2);\r
10245   q = p;\r
10246   while (*str) {\r
10247     if (*str == '\n') *q++ = '\r';\r
10248     *q++ = *str++;\r
10249   }\r
10250   *q = NULLCHAR;\r
10251   if (analysisText != NULL) free(analysisText);\r
10252   analysisText = p;\r
10253 \r
10254   if (analysisDialog) {\r
10255     SetWindowText(analysisDialog, title);\r
10256     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10257     ShowWindow(analysisDialog, SW_SHOW);\r
10258   } else {\r
10259     analysisTitle = title;\r
10260     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10261     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10262                  hwndMain, (DLGPROC)lpProc);\r
10263     FreeProcInstance(lpProc);\r
10264   }\r
10265   analysisDialogUp = TRUE;  \r
10266 }\r
10267 \r
10268 VOID\r
10269 AnalysisPopDown()\r
10270 {\r
10271   if (analysisDialog) {\r
10272     ShowWindow(analysisDialog, SW_HIDE);\r
10273   }\r
10274   analysisDialogUp = FALSE;  \r
10275 }\r
10276 \r
10277 \r
10278 VOID\r
10279 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10280 {\r
10281   highlightInfo.sq[0].x = fromX;\r
10282   highlightInfo.sq[0].y = fromY;\r
10283   highlightInfo.sq[1].x = toX;\r
10284   highlightInfo.sq[1].y = toY;\r
10285 }\r
10286 \r
10287 VOID\r
10288 ClearHighlights()\r
10289 {\r
10290   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10291     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10292 }\r
10293 \r
10294 VOID\r
10295 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10296 {\r
10297   premoveHighlightInfo.sq[0].x = fromX;\r
10298   premoveHighlightInfo.sq[0].y = fromY;\r
10299   premoveHighlightInfo.sq[1].x = toX;\r
10300   premoveHighlightInfo.sq[1].y = toY;\r
10301 }\r
10302 \r
10303 VOID\r
10304 ClearPremoveHighlights()\r
10305 {\r
10306   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10307     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10308 }\r
10309 \r
10310 VOID\r
10311 ShutDownFrontEnd()\r
10312 {\r
10313   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10314   DeleteClipboardTempFiles();\r
10315 }\r
10316 \r
10317 void\r
10318 BoardToTop()\r
10319 {\r
10320     if (IsIconic(hwndMain))\r
10321       ShowWindow(hwndMain, SW_RESTORE);\r
10322 \r
10323     SetActiveWindow(hwndMain);\r
10324 }\r
10325 \r
10326 /*\r
10327  * Prototypes for animation support routines\r
10328  */\r
10329 static void ScreenSquare(int column, int row, POINT * pt);\r
10330 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10331      POINT frames[], int * nFrames);\r
10332 \r
10333 \r
10334 #define kFactor 4\r
10335 \r
10336 void\r
10337 AnimateMove(board, fromX, fromY, toX, toY)\r
10338      Board board;\r
10339      int fromX;\r
10340      int fromY;\r
10341      int toX;\r
10342      int toY;\r
10343 {\r
10344   ChessSquare piece;\r
10345   POINT start, finish, mid;\r
10346   POINT frames[kFactor * 2 + 1];\r
10347   int nFrames, n;\r
10348 \r
10349   if (!appData.animate) return;\r
10350   if (doingSizing) return;\r
10351   if (fromY < 0 || fromX < 0) return;\r
10352   piece = board[fromY][fromX];\r
10353   if (piece >= EmptySquare) return;\r
10354 \r
10355   ScreenSquare(fromX, fromY, &start);\r
10356   ScreenSquare(toX, toY, &finish);\r
10357 \r
10358   /* All pieces except knights move in straight line */\r
10359   if (piece != WhiteKnight && piece != BlackKnight) {\r
10360     mid.x = start.x + (finish.x - start.x) / 2;\r
10361     mid.y = start.y + (finish.y - start.y) / 2;\r
10362   } else {\r
10363     /* Knight: make diagonal movement then straight */\r
10364     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10365        mid.x = start.x + (finish.x - start.x) / 2;\r
10366        mid.y = finish.y;\r
10367      } else {\r
10368        mid.x = finish.x;\r
10369        mid.y = start.y + (finish.y - start.y) / 2;\r
10370      }\r
10371   }\r
10372   \r
10373   /* Don't use as many frames for very short moves */\r
10374   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10375     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10376   else\r
10377     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10378 \r
10379   animInfo.from.x = fromX;\r
10380   animInfo.from.y = fromY;\r
10381   animInfo.to.x = toX;\r
10382   animInfo.to.y = toY;\r
10383   animInfo.lastpos = start;\r
10384   animInfo.piece = piece;\r
10385   for (n = 0; n < nFrames; n++) {\r
10386     animInfo.pos = frames[n];\r
10387     DrawPosition(FALSE, NULL);\r
10388     animInfo.lastpos = animInfo.pos;\r
10389     Sleep(appData.animSpeed);\r
10390   }\r
10391   animInfo.pos = finish;\r
10392   DrawPosition(FALSE, NULL);\r
10393   animInfo.piece = EmptySquare;\r
10394 }\r
10395 \r
10396 /*      Convert board position to corner of screen rect and color       */\r
10397 \r
10398 static void\r
10399 ScreenSquare(column, row, pt)\r
10400      int column; int row; POINT * pt;\r
10401 {\r
10402   if (flipView) {\r
10403     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10404     pt->y = lineGap + row * (squareSize + lineGap);\r
10405   } else {\r
10406     pt->x = lineGap + column * (squareSize + lineGap);\r
10407     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10408   }\r
10409 }\r
10410 \r
10411 /*      Generate a series of frame coords from start->mid->finish.\r
10412         The movement rate doubles until the half way point is\r
10413         reached, then halves back down to the final destination,\r
10414         which gives a nice slow in/out effect. The algorithmn\r
10415         may seem to generate too many intermediates for short\r
10416         moves, but remember that the purpose is to attract the\r
10417         viewers attention to the piece about to be moved and\r
10418         then to where it ends up. Too few frames would be less\r
10419         noticeable.                                             */\r
10420 \r
10421 static void\r
10422 Tween(start, mid, finish, factor, frames, nFrames)\r
10423      POINT * start; POINT * mid;\r
10424      POINT * finish; int factor;\r
10425      POINT frames[]; int * nFrames;\r
10426 {\r
10427   int n, fraction = 1, count = 0;\r
10428 \r
10429   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10430   for (n = 0; n < factor; n++)\r
10431     fraction *= 2;\r
10432   for (n = 0; n < factor; n++) {\r
10433     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10434     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10435     count ++;\r
10436     fraction = fraction / 2;\r
10437   }\r
10438   \r
10439   /* Midpoint */\r
10440   frames[count] = *mid;\r
10441   count ++;\r
10442   \r
10443   /* Slow out, stepping 1/2, then 1/4, ... */\r
10444   fraction = 2;\r
10445   for (n = 0; n < factor; n++) {\r
10446     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10447     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10448     count ++;\r
10449     fraction = fraction * 2;\r
10450   }\r
10451   *nFrames = count;\r
10452 }\r
10453 \r
10454 void\r
10455 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10456 {\r
10457 #if 0\r
10458     char buf[256];\r
10459 \r
10460     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10461         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10462 \r
10463     OutputDebugString( buf );\r
10464 #endif\r
10465 \r
10466     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10467 \r
10468     EvalGraphSet( first, last, current, pvInfoList );\r
10469 }\r
10470 \r
10471 void SetProgramStats( FrontEndProgramStats * stats )\r
10472 {\r
10473 #if 0\r
10474     char buf[1024];\r
10475 \r
10476     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10477         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10478 \r
10479     OutputDebugString( buf );\r
10480 #endif\r
10481 \r
10482     EngineOutputUpdate( stats );\r
10483 }\r